Skip to content

Runbook de validation EKS — fastapi-eks-project

Tests de validation pour le cluster EKS, organisĂ©s par catĂ©gorie. Principe directeur : valider par la preuve (test), pas par l'intention (le manifest). Chaque contrĂŽle de sĂ©curitĂ© ou de rĂ©silience se valide par un test positif (ce qui doit passer) ET un test nĂ©gatif (ce qui doit ĂȘtre bloquĂ©).


Table des catégories

  1. NetworkPolicy (enforcement réseau)
  2. HPA / Autoscaling
  3. TLS / cert-manager
  4. Pod Security Admission (PSA)
  5. Probes / Health
  6. Connectivité / Smoke tests
  7. BoĂźte Ă  outils

1. NetworkPolicy

Prérequis pour que ce soit enforced sur EKS : vpc-cni déclaré en addon EKS managé avec enableNetworkPolicy = "true". En self-managed, les NetworkPolicy sont dans etcd mais INERTES (INC-046).

Vérifier que l'addon est bien managé et actif :

aws eks list-addons --cluster-name fastapi-eks-cluster
# Doit lister "vpc-cni" (et pas renvoyer [])
kubectl get pods -n kube-system -l k8s-app=aws-node

Test négatif (le deny fonctionne)

Depuis un pod soumis Ă  une politique egress restrictive, tenter une sortie NON autorisĂ©e. La policy fastapi autorise seulement 5432 (RDS), 443 (AWS), 53 (DNS). Le port 80 doit ĂȘtre bloquĂ©.

kubectl exec -it -n fastapi deployment/fastapi -- sh
# dans le pod :
curl -m 5 http://example.com

Attendu : curl: (28) Connection timed out after 5001 milliseconds Si la page HTML s'affiche : policies INERTES (addon pas managé ou enableNetworkPolicy absent).

Test positif (les allow rules passent)

Valider que le trafic autorisé fonctionne toujours. /healthz/ready fait un SELECT 1 sur RDS, donc il exerce DNS (port 53, résolution du hostname RDS) + port 5432 (connexion TCP).

curl -s https://api.devopsyouss.com/healthz/ready

Attendu : {"status":"ok"} (HTTP 200) Si 503 : une rĂšgle allow (DNS ou 5432) bloque Ă  tort.

Test inter-pods (optionnel)

Vérifier qu'un pod hors allowlist ne joint pas un pod protégé.

# pod témoin dans un autre namespace, tente de joindre le service fastapi
kubectl run probe --image=curlimages/curl -n default --restart=Never --rm -it -- \
  curl -m 5 http://fastapi.fastapi.svc.cluster.local/

Selon les rÚgles ingress, attendu = réponse OK (ingress 8080 sans from) ou timeout.


2. HPA / Autoscaling

Prérequis : metrics-server installé (via bootstrap Ansible) + HPA défini (min:1 max:5, cpu:70%).

Étape 1 — metrics-server remonte des mĂ©triques

kubectl top nodes
kubectl top pods -n fastapi

Attendu : des valeurs chiffrées (ex 3m 60Mi). Si error: Metrics API not available, metrics-server KO.

Étape 2 — le HPA lit les mĂ©triques

kubectl get hpa -n fastapi

Attendu : cpu: 5%/70% (un pourcentage réel). PiÚge classique EKS : cpu: <unknown>/70% = metrics-server ne remonte pas, HPA aveugle.

Étape 3 — dĂ©clencher le scale-up par la charge

Méthode recommandée : charge DEPUIS l'intérieur du cluster (pas de limite de bande passante internet). La charge externe depuis le homelab plafonne à ~46 req/s (latence ~1s/req), insuffisant pour saturer le CPU.

# Terminal 1 — observation
watch -n 2 kubectl get hpa,pods -n fastapi

# Terminal 2 — charge interne via fortio (namespace default, sans NetworkPolicy)
kubectl run loadgen --image=fortio/fortio --restart=Never --rm -it -n default -- \
  load -c 150 -qps 0 -t 120s http://fastapi.fastapi.svc.cluster.local/
  • viser / (JSON statique, CPU-bound) et NON /healthz/ready (I/O-bound sur la DB, ne fait pas monter le CPU)
  • -qps 0 = dĂ©bit max, -c 150 = 150 connexions concurrentes

Calcul HPA : cible = ramener le CPU moyen à 70%. À 194% sur 1 replica → ceil(1 × 194/70) = 3 replicas. Comme la CPU limit (500m) vaut 200% de la request (250m), le CPU moyen reste > 70% → scale jusqu'à maxReplicas (5).

Étape 4 — observer le scale-down

À l'arrĂȘt de la charge, le scale-down N'EST PAS immĂ©diat : fenĂȘtre de stabilisation par dĂ©faut de 5 min (anti-flapping). Les replicas restent hauts puis redescendent vers min (1). Normal, pas un bug.

Charge externe (alternative, avec réserve)

# apache2-utils (ab) ou hey ; limité par la bande passante homelab -> AWS
ab -t 120 -n 1000000 -c 200 https://api.devopsyouss.com/

Bon pour un smoke test HTTPS, insuffisant pour saturer le CPU (cf 46 req/s observé).


3. TLS / cert-manager

Vérifier les ClusterIssuers

kubectl get clusterissuers

Attendu : letsencrypt-staging et letsencrypt-prod, READY: True.

Vérifier le certificat

kubectl get certificate -n fastapi
kubectl describe certificate fastapi-tls -n fastapi

Attendu : READY: True, et dans les events The certificate has been successfully issued. La colonne / le champ de renouvellement confirme que cert-manager gĂšre le cycle de vie (renouvellement ~30j avant expiration).

Vérifier la chaßne TLS cÎté client

# Voir l'émetteur et la validité
curl -vI https://api.devopsyouss.com 2>&1 | grep -Ei "issuer|subject|expire"
# Ou inspection complĂšte
echo | openssl s_client -connect api.devopsyouss.com:443 -servername api.devopsyouss.com 2>/dev/null \
  | openssl x509 -noout -issuer -subject -dates

Staging : issuer (STAGING) Let's Encrypt, NON trusté navigateur (normal). Prod : issuer Let's Encrypt, cadenas vert.

Bascule staging -> prod

  1. Éditer certificate.yaml : issuerRef.name: letsencrypt-staging -> letsencrypt-prod
  2. kubectl apply -f certificate.yaml
  3. kubectl delete secret fastapi-tls -n fastapi (force la ré-émission)
  4. Surveiller kubectl describe certificate fastapi-tls -n fastapi

4. Pod Security Admission (PSA)

Prérequis : namespace fastapi labellisé pod-security.kubernetes.io/enforce: restricted (posé par le bootstrap Ansible, INC-045).

Vérifier les labels du namespace

kubectl get ns fastapi --show-labels

Attendu : pod-security.kubernetes.io/enforce=restricted.

Test négatif (pod non conforme rejeté)

kubectl run nginx-test --image=nginx -n fastapi

Attendu : rejet à l'admission (violates PodSecurity "restricted": runAsNonRoot, seccompProfile, etc.). Si le pod est créé : PSA non enforced.

Test positif (pod conforme admis)

Le pod fastapi (runAsNonRoot, seccomp RuntimeDefault, capabilities drop ALL, readOnlyRootFilesystem) doit ĂȘtre admis et passer Running 1/1.

kubectl get pods -n fastapi

5. Probes / Health

L'app expose deux endpoints dédiés (issue #47) : - /healthz/live : le process répond (liveness) - /healthz/ready : SELECT 1 sur la DB, renvoie 503 si la DB est injoignable (readiness)

# Direct sur un pod (port 8080)
kubectl exec -n fastapi deployment/fastapi -- wget -qO- http://localhost:8080/healthz/live
kubectl exec -n fastapi deployment/fastapi -- wget -qO- http://localhost:8080/healthz/ready
# Via l'URL publique
curl -s https://api.devopsyouss.com/healthz/ready

Note : /healthz seul renvoie 404, ce n'est pas une route. Les routes sont /healthz/live et /healthz/ready.


6. Connectivité / Smoke tests

# Nodes prĂȘts
kubectl get nodes

# Tout le namespace applicatif
kubectl get all -n fastapi

# DNS interne (depuis un pod autorisé)
kubectl exec -n fastapi deployment/fastapi -- nslookup kubernetes.default

# Endpoint public de bout en bout
curl -s https://api.devopsyouss.com/        # {"message":"Hello all the World"}
curl -s https://api.devopsyouss.com/docs     # Swagger UI

# Hostname de l'ELB (pour le CNAME Cloudflare)
kubectl get svc -n envoy-gateway-system

# Logs applicatifs
kubectl logs -n fastapi -l app=fastapi -f

7. BoĂźte Ă  outils

Outil Usage Note
kubectl top CPU/mémoire pods et nodes nécessite metrics-server
fortio charge interne (HPA) image fortio/fortio, Ă  lancer en pod dans le cluster
ab (apache2-utils) charge externe / smoke HTTPS limité par la bande passante homelab
hey charge externe (alternative Ă  ab) binaire Go unique
openssl s_client inspection chaĂźne TLS issuer, dates, SAN
curl -vI headers HTTP + infos TLS rapides
nslookup / dig résolution DNS tester la rÚgle egress 53
aws eks list-addons vérifier les addons managés clé pour l'enforcement NetworkPolicy

Rappel des deux tests qui ferment la boucle sécurité

ContrÎle Test négatif (deny) Test positif (allow)
NetworkPolicy curl http://example.com dans le pod -> timeout /healthz/ready -> 200 (DNS+5432 OK)
PSA kubectl run nginx dans fastapi -> rejeté pod fastapi -> Running 1/1
HPA (n/a) charge fortio -> scale 1 vers 5, puis scale-down
TLS cert staging non trusté navigateur cert prod -> cadenas vert