Partie 5 : Migration Helm et résolution finale

Partie 5 : Migration Helm et résolution finale

Novembre 2025

Pourquoi Helm ?

Après avoir fait tourner le site sur K3s avec des manifests YAML bruts, je voulais :

  • Versioning des déploiements
  • Rollback facile en cas de problème
  • Configuration paramétrable (dev/staging/prod)
  • Packaging réutilisable

Helm est le package manager de Kubernetes, comme apt pour Debian ou npm pour Node.js.

Création du Chart Helm

Structure du chart

helm-charts/homefonta/
├── Chart.yaml
├── values.yaml
├── templates/
│   ├── deployment.yaml
│   ├── service.yaml
│   ├── ingress.yaml
│   └── _helpers.tpl

Chart.yaml

apiVersion: v2
name: homefonta
description: Home-Fonta.fr Flask application
type: application
version: 0.1.0
appVersion: "3.1"

values.yaml (configuration)

replicaCount: 3

image:
  repository: nocoblas/home-fonta-web
  pullPolicy: Always
  tag: "v3.1"

fullnameOverride: "home-fonta-web"

service:
  type: ClusterIP
  port: 80
  targetPort: 80  # NGINX écoute sur 80

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod-dns"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
  hosts:
    - host: home-fonta.fr
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: wildcard-homefonta-tls
      hosts:
        - home-fonta.fr

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 128Mi

livenessProbe:
  enabled: true
  httpGet:
    path: /
    port: 80  # Port NGINX, pas Gunicorn !
  initialDelaySeconds: 30
  periodSeconds: 10

readinessProbe:
  enabled: true
  httpGet:
    path: /
    port: 80
  initialDelaySeconds: 10
  periodSeconds: 5

# Fix temporaire pour le problème CNI
nodeSelector:
  kubernetes.io/hostname: rpi4-master

Premier déploiement avec Helm

# Installation
helm install home-fonta . -n home-fonta --create-namespace

# Vérification
helm list -n home-fonta
NAME         NAMESPACE    STATUS    CHART           VERSION
home-fonta   home-fonta   deployed  homefonta-0.1.0  3.1

# Les pods
kubectl get pods -n home-fonta
NAME                              READY   STATUS    
home-fonta-web-59c5486fb5-8x9kl  1/1     Running
home-fonta-web-59c5486fb5-nl22t  1/1     Running
home-fonta-web-59c5486fb5-v7x4s  1/1     Running

Le cauchemar : Retour de la lenteur !

Après la migration Helm, catastrophe : le site est redevenu lent !

curl -I https://home-fonta.fr
# 15 secondes d'attente...

curl https://home-fonta.fr/static/style.css
# 504 Gateway Timeout

Investigation

# Logs de l'ingress
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller

upstream timed out (110: Operation timed out) while connecting to upstream

Cause trouvée : Templates mal générés

Le template Helm avait mal généré les configurations :

# CE QUI ÉTAIT GÉNÉRÉ (incorrect)
spec:
  containers:
  - name: web
    ports:
    - containerPort: 8000  # Port Gunicorn interne
    
# CE QU'IL FALLAIT
spec:
  containers:
  - name: web
    ports:
    - containerPort: 80    # Port NGINX exposé

Debugging approfondi

1. Vérification du service

kubectl get service home-fonta-web -n home-fonta -o yaml

# Le problème
spec:
  ports:
  - port: 80
    targetPort: 8000  # Pointait vers Gunicorn !
    
# Correction dans values.yaml
service:
  port: 80
  targetPort: 80  # Pointer vers NGINX

2. Test avec port-forward

# Test direct du pod
kubectl port-forward -n home-fonta pod/home-fonta-web-xxx 8080:80
curl http://localhost:8080
# Fonctionne !

# Donc le problème est entre le service et le pod

3. Correction des templates Jinja2

Autre problème découvert : Les templates avaient une syntaxe cassée !

<!-- CASSÉ (double fermeture) -->
<img src="{{ url_for('static', filename='images/aigle.jpg') }}') }}">

<!-- CORRIGÉ -->
<img src="{{ url_for('static', filename='images/aigle.jpg') }}">

Script de correction :

#!/bin/bash
# fix_templates.sh
for file in templates/*.html; do
    sed -i "s/') }}\"/\"/g" "$file"
done

Solution finale : Refonte complète

Nouveau Dockerfile production

# Multi-stage build pour optimisation
FROM python:3.12-slim AS python-builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .
COPY templates/ templates/

# Stage production
FROM python:3.12-slim

RUN apt-get update && apt-get install -y --no-install-recommends \
    nginx supervisor \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copier depuis builder
COPY --from=python-builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=python-builder /app /app

# Copier les statiques
COPY static/ /app/static/

# Config NGINX
COPY nginx/default.conf /etc/nginx/sites-enabled/default

# Config Supervisor
RUN echo '[supervisord]' > /etc/supervisor/conf.d/app.conf && \
    echo 'nodaemon=true' >> /etc/supervisor/conf.d/app.conf && \
    echo '[program:nginx]' >> /etc/supervisor/conf.d/app.conf && \
    echo 'command=/usr/sbin/nginx -g "daemon off;"' >> /etc/supervisor/conf.d/app.conf && \
    echo '[program:gunicorn]' >> /etc/supervisor/conf.d/app.conf && \
    echo 'command=gunicorn --bind 127.0.0.1:8000 --workers 2 app:app' >> /etc/supervisor/conf.d/app.conf

EXPOSE 80

CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

Mise à jour Helm

# Rebuild et push de l'image
docker build -t nocoblas/home-fonta-web:v3.1 .
docker push nocoblas/home-fonta-web:v3.1

# Mise à jour Helm
helm upgrade home-fonta . -n home-fonta

# Rollout restart pour forcer
kubectl rollout restart deployment home-fonta-web -n home-fonta

Résultat final : VICTOIRE !

# Test de performance
for i in {1..10}; do
  time curl -s https://home-fonta.fr > /dev/null
done

# Moyenne : 0.152s ! (était 15s)

Métriques finales

Métrique Début Docker K3s (Flask) K3s (NGINX) Amélioration
Temps de réponse N/A 2s 15s 15ms 100x
Replicas 0 1 3 3 HA
SSL Auto
Rollback Manuel Manuel Helm 1 cmd
Monitoring Docker kubectl kubectl + Helm Pro

Leçons finales apprises

  1. Les templates Helm peuvent casser : Toujours vérifier le YAML généré
  2. targetPort est critique : Doit pointer vers le bon port du container
  3. Multi-stage builds : Réduisent la taille de l'image
  4. Supervisor : Pratique pour gérer plusieurs processus
  5. Port-forward : Outil de debug indispensable
  6. NGINX + Flask : Architecture qui fonctionne vraiment

Bilan global du projet

Ce qui a été accompli

Migration complète : Site statique → Python → Docker → Flask → K3s → Helm
Performance x100 : De 15s à 150ms
Haute disponibilité : 3 replicas sur cluster
SSL automatique : Let's Encrypt + cert-manager
Infrastructure as Code : Ansible + Helm
CI/CD ready : Push to deploy

Compétences acquises

  • Docker : Build, multi-stage, compose
  • Kubernetes : Pods, Services, Ingress, PV/PVC
  • Helm : Charts, values, templates
  • Networking : Reverse proxy, load balancing, SSL
  • Troubleshooting : Logs, port-forward, debugging CNI
  • Linux : systemd, iptables, cgroups
  • Automation : Ansible, scripts bash
  • Architecture : Microservices, separation of concerns

Et maintenant ?

Le site tourne parfaitement, mais les redirections vers mes service (passbolt, mon nas, mon stack-arr etc. Ne fonctionnaient plus.

A force de tester pleins de possibilité pour régler le problème en plus de toutes les modifications faites pour trouver le problème de latence j'ai fini par casser tout le system, plus aucun site ne fonctonnait.
Après une semaine de recherche pour récupérer tout ça et une semaine de pause j'ai fini par me faire à l'idée que j'allais tout recommencer afin de revenir sur quelque chose de propre.

Aller hop ! j'ai récupéré les cartes sd des raspberry, j'ai réinstallé l'os et j'ai tout repris à zero, enfin presque !
Il ne faut pas oublier les palaybooks ansible que j'avais créé, ça m'a fait gagner un temps fou pour revenir sur une install k3s toute propre et pouvoir reprendre helm sans le poluer grace à mon passif de bugs, enfin j'ai pu récupérer une infra propre et fonctionnelle.

cette fois-ci c'est bon, le projet continue :

Phase 2 : GitOps avec ArgoCD

  • Git comme source de vérité
  • Déploiements automatiques
  • Rollback par git revert

Phase 3 : CI/CD avec GitHub Actions

  • Build automatique
  • Tests
  • Déploiement sur push

Phase 4 : Migration de la stack média

  • Sonarr, Radarr, qBittorrent
  • Plex, Overseerr
  • NFS sur le NAS pour le stockage

Phase 5 : Monitoring complet

  • Prometheus + Grafana
  • Loki pour les logs
  • Alertmanager

Réflexion finale

Ce qui devait être une simple migration de 4 pages HTML est devenu une transformation complète de mon infrastructure. De fichiers statiques à un cluster Kubernetes production-ready.

Était-ce overkill ? Peut-être.
Ai-je appris énormément ? Absolument !
Le referais-je ? Sans hésiter !

Le HomeLab est le terrain de jeu parfait pour apprendre les technologies modernes. Les erreurs ne coûtent rien (sauf du temps), et les compétences acquises sont directement applicables en entreprise.

Mon conseil : Commencez petit, mais visez grand. Chaque étape est un apprentissage.


Le voyage continue... Rendez-vous pour la Phase 2 !

Ressources


Merci d'avoir suivi cette aventure technique !

Read more