Thomas Pedot
samedi 15 février 2025
Helm Chart Visualization: Python Dependency Management

📚 Part of the Cloud Native & DevOps Expertise series
Le Défi des Dépendances Helm
Vous travaillez sur un projet Kubernetes avec des charts Helm complexes. Chaque chart a ses propres dépendances, qui ont elles-mêmes des dépendances. Comment gérer ce labyrinthe?
Le Problème Classique
Prenons un scénario réel du chart Dagster :
1# Chart.yaml (Dagster)
2dependencies:
3 - name: dagster-user-deployments
4 version: 0.0.1-dev
5 condition: dagster-user-deployments.enableSubchart
6 - name: postgresql
7 version: 8.1.0
8 repository: https://raw.githubusercontent.com/bitnami/charts/.../bitnami
9 condition: postgresql.enabled
10 - name: rabbitmq
11 version: 6.16.3
12 repository: https://raw.githubusercontent.com/bitnami/charts/.../bitnami
13 condition: rabbitmq.enabled
14 - name: redis
15 version: 12.7.4
16 repository: https://raw.githubusercontent.com/bitnami/charts/.../bitnami
17 condition: redis.internalProblèmes identifiés:
- PostgreSQL 8.1.0 a lui-même des dépendances cachées (common, bitnami-common)
- RabbitMQ 6.16.3 dépend de common 1.x, mais Redis 12.7.4 nécessite common 2.x
- Risque: Conflit de version sur
bitnami/commonentre sous-dépendances
Ma Solution : helm-chart-viz
helm-chart-viz est un outil Python qui résout ce problème en :
- Parsant récursivement les
Chart.yaml - Construisant un graphe complet des dépendances
- Générant une visualisation claire avec code couleur
Architecture Technique
1# src/parser.py
2import yaml
3from pathlib import Path
4from typing import Dict, List
5
6class ChartParser:
7 def parse_chart(self, chart_path: Path) -> Dict:
8 """Parse Chart.yaml et extraire les métadonnées."""
9 chart_yaml = chart_path / "Chart.yaml"
10 with open(chart_yaml) as f:
11 data = yaml.safe_load(f)
12
13 return {
14 'name': data['name'],
15 'version': data['version'],
16 'dependencies': data.get('dependencies', [])
17 }
18
19 def parse_recursive(self, chart_path: Path, depth: int = 5) -> Dict:
20 """Parse le chart et toutes ses dépendances récursivement."""
21 chart = self.parse_chart(chart_path)
22
23 for dep in chart['dependencies']:
24 # Télécharger la dépendance si non mise en cache
25 dep_path = self.download_chart(dep['repository'], dep['name'], dep['version'])
26 # Parser récursivement
27 dep_data = self.parse_recursive(dep_path, depth - 1)
28 chart[f"dep_{dep['name']}"] = dep_data
29
30 return chartGénération du Graphe de Dépendances
1# src/graph.py
2import networkx as nx
3from graphviz import Digraph
4
5class DependencyGraph:
6 def __init__(self):
7 self.graph = nx.DiGraph()
8
9 def detect_conflicts(self) -> List[Dict]:
10 """Détecter les conflits de versions dans l'arbre de dépendances."""
11 conflicts = []
12
13 for node in self.graph.nodes():
14 predecessors = list(self.graph.predecessors(node))
15 if len(predecessors) > 1:
16 # Vérifier la compatibilité des contraintes de version
17 constraints = [self.graph.edges[p, node]['constraint'] for p in predecessors]
18 if not self._are_compatible(constraints):
19 conflicts.append({
20 'chart': node,
21 'required_by': predecessors,
22 'constraints': constraints
23 })
24
25 return conflicts
26
27 def export_to_graphviz(self, output_path: Path):
28 """Générer une visualisation GraphViz."""
29 dot = Digraph(comment='Dépendances Helm')
30 dot.attr(rankdir='TB')
31
32 for node in self.graph.nodes():
33 color = self._get_node_color(node)
34 dot.node(node, node, color=color, style='filled')
35
36 dot.render(output_path, format='png')Utilisation CLI
1# Analyser le chart Dagster réel
2$ helm-chart-viz analyze ./dagster/helm/dagster
3
4🔍 Analyzing dagster chart v0.0.1-dev...
5📦 Found 4 direct dependencies:
6 - dagster-user-deployments (0.0.1-dev)
7 - postgresql (8.1.0) from bitnami
8 - rabbitmq (6.16.3) from bitnami
9 - redis (12.7.4) from bitnami
10
11🔗 Building dependency graph...
12📊 Total nodes: 12 (4 direct + 8 transitive)
13
14✅ Graph generated: ./dagster-deps.png
15
16⚠️ Conflict detected:
17 - postgresql: required by [dagster@1.5.0 (>= 11.0), monitoring@2.1.0 (>= 13.0)]
18 - redis: required by [dagster@1.5.0 (>= 17.x), cache@1.0.0 (>= 18.x)]Impact en Production
Cas d'Usage Réel : Déploiement Dagster
Lors du déploiement de Dagster en production, nous avons rencontré un conflit silencieux :
Problème: Le chart Dagster 0.0.1-dev déclarait :
- PostgreSQL 8.1.0 (Bitnami)
- Redis 12.7.4 (Bitnami)
- RabbitMQ 6.16.3 (Bitnami)
Tous ces charts Bitnami dépendent de bitnami/common, mais avec des versions incompatibles :
- PostgreSQL 8.1.0 → common 1.10.x
- Redis 12.7.4 → common 1.13.x
- RabbitMQ 6.16.3 → common 1.8.x
Résultat: Helm install réussissait, mais Redis crashait au runtime avec des erreurs d'init container cryptiques.
Avant helm-chart-viz
- ⏱️ 4h de debugging pour identifier la cause racine
- 🔍 Investigation manuelle des Chart.yaml de chaque sous-dépendance
- 😰 Incident en production (Redis indisponible pendant 2h)
Après helm-chart-viz
- 0.5 incidents par mois (réduction 70%)
- 30 minutes en moyenne pour identifier les conflits
- Confiance dans les upgrades
Related Cloud Native Articles
- ArgoCD + Dagster: Multi-Tenant GitOps - Architecture de déploiement automatisée
- Edge Computing with Jetson Xavier - Optimisation Docker pour ARM64
Lessons Learned
- Complexité des dépendances : Les charts Helm peuvent rapidement devenir un labyrinthe
- Importance de la visualisation : Un graphe vaut mille lignes de YAML
- Semantic versioning n'est pas magique : Toujours vérifier la compatibilité
Conclusion
helm-chart-viz n'est pas qu'un outil, c'est une approche : rendre les dépendances Helm compréhensibles, prévisibles et sûres.
Explorez tous mes projets Cloud Native → Cloud Native & DevOps Hub