Skip to content

Latest commit

 

History

History

k8s

README.md

Déploiement Kubernetes (Kind)

Manifestes pour faire tourner le stack Task Manager sur un cluster Kind local. Voir ../docs/infra/ pour les explications détaillées et la correspondance docker-compose → Kubernetes.

Prérequis

  • docker, kind, kubectl installés.
  • Les ports 80/443 de l'hôte libres (mappés vers les NodePort 30080/30443 de la Gateway).

Fichiers

Fichier Rôle
kind-config.yaml Cluster Kind 4 nœuds (1 control-plane + 3 workers) + mapping 80→30080 / 443→30443
namespace.yaml Namespace task-manager + labels Pod Security Admission (enforce: baseline, warn/audit: restricted)
rbac.yaml Un ServiceAccount par workload, sans jeton monté, sans aucun droit
secrets.yaml Secrets divers (RedisInsight encryption key) - RabbitMQ utilise le secret généré par l'opérateur (rabbitmq-default-user)
database.yaml Cluster CNPG (3 instances, anti-affinité) + Pooler (2)
rabbitmq.yaml Cluster RabbitMQ (3 nœuds, géré par l'opérateur officiel)
infra.yaml Valkey, Mailpit, Prometheus, RedisInsight
api-config.yaml ConfigMap partagée API/worker
migrate-job.yaml Job Alembic (alembic upgrade head)
api-deployment.yaml API FastAPI + Service
worker-deployment.yaml Worker d'e-mails
frontend-deployment.yaml Frontend Nginx + Service
gateway.yaml GatewayClass eg + EnvoyProxy (NodePort figé) + Gateway task-manager
httproutes.yaml Une HTTPRoute pour l'app, une par console de dev (*.localhost)
network-policies.yaml Segmentation réseau (qui peut parler à qui)
hpa.yaml Autoscaling horizontal : api 2→6, frontend 2→4 (CPU 70 % / mém. 80 %)
pdb.yaml PodDisruptionBudget minAvailable: 1 (api, frontend, mail-worker)
deploy.sh Déploiement complet et idempotent (opérateurs, images, workloads)
clean.sh Suppression du cluster Kind (--images pour purger les images)
db-backup.sh Backup/restore logique de la base (pg_dump) dans db-backups/

Lancement

Tout est automatisé par deploy.sh (à la racine du dépôt, exécuté depuis ../) :

cd ..
./k8s/deploy.sh

Ou manuellement, dans l'ordre :

# 1. Cluster + Envoy Gateway (install.yaml embarque les CRD Gateway API).
#    --server-side : les CRD dépassent la limite de l'annotation last-applied.
kind create cluster --config k8s/kind-config.yaml
kubectl apply --server-side -f https://github.com/envoyproxy/gateway/releases/download/v1.9.1/install.yaml
kubectl rollout status deployment/envoy-gateway -n envoy-gateway-system --timeout=300s

# 2. Metrics Server (requis par le HPA ; sans le patch TLS, il ne remonte rien sur Kind).
#    ⚠️ --kubelet-insecure-tls désactive la vérification du certificat kubelet :
#    contournement Kind uniquement, jamais sur un vrai cluster.
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl patch -n kube-system deployment metrics-server --type=json \
  -p '[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
kubectl -n kube-system rollout status deployment metrics-server --timeout=120s

# 2b. Opérateur CloudNativePG (vérifier la dernière version sur le repo CNPG)
kubectl apply --server-side -f \
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.30/releases/cnpg-1.30.0.yaml
kubectl wait --for=condition=Available deployment/cnpg-controller-manager -n cnpg-system --timeout=180s

# 2c. Opérateur RabbitMQ Cluster (version v2.15.0)
RABBITMQ_OPERATOR_VERSION="v2.15.0"
kubectl apply -f \
  "https://github.com/rabbitmq/cluster-operator/releases/download/${RABBITMQ_OPERATOR_VERSION}/cluster-operator.yml"
kubectl wait --for=condition=Available deployment/rabbitmq-cluster-operator -n rabbitmq-system --timeout=180s

# 3. Build + load des images dans Kind
docker build -t task-manager-api:latest ./backend
docker build -t task-manager-frontend:latest --target production \
  --build-arg VITE_MAIL_UI_URL=http://mailpit.localhost \
  --build-arg VITE_REDIS_INSIGHT_URL=http://redisinsight.localhost \
  ./frontend
kind load docker-image task-manager-api:latest task-manager-frontend:latest --name task-manager

# 4. Namespace, secrets, BDD, dépendances
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/rbac.yaml
kubectl apply -f k8s/secrets.yaml
kubectl apply -f k8s/database.yaml
kubectl wait --for=condition=Ready cluster/task-manager-db -n task-manager --timeout=300s
kubectl apply -f k8s/infra.yaml
kubectl apply -f k8s/rabbitmq.yaml
kubectl wait --for=condition=Available rabbitmqcluster/rabbitmq -n task-manager --timeout=300s

# 5. Migrations, puis config + workloads
kubectl apply -f k8s/migrate-job.yaml
kubectl wait --for=condition=complete job/db-migrate -n task-manager --timeout=180s
kubectl apply -f k8s/api-config.yaml
kubectl apply -f k8s/api-deployment.yaml
kubectl apply -f k8s/frontend-deployment.yaml
kubectl apply -f k8s/worker-deployment.yaml
kubectl apply -f k8s/hpa.yaml
kubectl apply -f k8s/pdb.yaml
kubectl apply -f k8s/gateway.yaml
kubectl wait --for=condition=Programmed gateway/task-manager -n task-manager --timeout=300s
kubectl apply -f k8s/httproutes.yaml
kubectl apply -f k8s/network-policies.yaml

URLs

L'application est accessible sur http://localhost.

Les consoles de dev sont exposées par la Gateway sur des hostnames dédiés — une HTTPRoute chacune, hostnames étant un champ de la route et non de la règle :

Console URL Identifiants
🖥️ Application http://localhost —
📬 Mailpit (webmail de test) http://mailpit.localhost —
🐰 RabbitMQ (management) http://rabbitmq.localhost Générés automatiquement par l'opérateur (secret rabbitmq-default-user)
🔎 RedisInsight (cache Valkey) http://redisinsight.localhost —
📊 Prometheus http://prometheus.localhost —

Résolution DNS — avec systemd-resolved, les noms *.localhost pointent déjà vers 127.0.0.1 : rien à configurer. Sinon, ajouter dans /etc/hosts :

127.0.0.1  mailpit.localhost rabbitmq.localhost redisinsight.localhost prometheus.localhost

Boutons de l'en-tête du frontend — les raccourcis Mailpit / RedisInsight de l'app sont figés au build du frontend. deploy.sh passe déjà les bons --build-arg (VITE_MAIL_UI_URL, VITE_REDIS_INSIGHT_URL) : rien à faire. En build manuel, reprendre la commande de l'étape 3 — sinon les boutons pointent vers localhost:8025 / localhost:5540.

Sauvegarde / restauration de la base (pg_dump)

Backup logique simple : 1 backup = 1 fichier .sql.gz dans db-backups/ (sur ton disque, ignoré par Git). Se connecte au primaire via kubectl exec.

./k8s/db-backup.sh backup                 # dump → db-backups/task_manager-<horodatage>.sql.gz
./k8s/db-backup.sh list                   # liste les dumps
./k8s/db-backup.sh restore <fichier>      # applique un dump (demande confirmation)
./k8s/db-backup.sh restore <fichier> -y   # sans confirmation

Le dump inclut le schéma (avec alembic_version) et les données ; la restauration recrée proprement les objets (--clean --if-exists) puis les recharge. Pas de PITR — on restaure à l'instant d'un dump.

Sécurité

SecurityContext — les pods qu'on maîtrise sont durcis :

  • api / worker / migrate : runAsNonRoot (uid 1000), readOnlyRootFilesystem, allowPrivilegeEscalation: false, capabilities: drop [ALL], seccomp RuntimeDefault. /tmp monté en emptyDir (gunicorn/temp).
  • frontend : image nginx-unprivileged (uid 101, écoute 8080), même durcissement.
  • postgres / pooler : déjà non-root (uid 26) via l'opérateur CNPG — non modifiés.
  • Restent en défaut : valkey, rabbitmq, mailpit, prometheus, redisinsight (images tierces ; readOnlyRootFilesystem demande une validation par image).

NetworkPolicy (network-policies.yaml) — segmentation testée (kindnet l'applique). Une politique d'ingress par cible ; tout le reste est refusé en entrée :

gateway (envoy) → frontend → api → ┬→ pooler → postgres
                                   ├→ rabbitmq
                                   ├→ valkey
                                   └→ mailpit

Le frontend ne peut jamais joindre postgres/pooler directement (vérifié : un pod non listé sur la base time-out ; l'api passe). L'egress (dont le DNS) reste libre.

Mise à jour d'une image

Après un changement de code, reconstruire + recharger + redémarrer :

docker build -t task-manager-api:latest ./backend
kind load docker-image task-manager-api:latest --name task-manager
kubectl rollout restart deployment/api deployment/mail-worker -n task-manager

Nettoyage

kind delete cluster --name task-manager