RĂ©sumĂ© Infra EKS â fastapi-eks-project
Contexte projet
Portfolio DevOps â FastAPI + PostgreSQL sur AWS EKS.
GitLab : gitlab.com/yk-devops/fastapi-eks-project
Dev Container : VS Code avec AWS CLI, Terraform, kubectl, Helm.
Stack technique
App : FastAPI + PostgreSQL + Alembic + PyJWT + bcrypt
Cloud : AWS eu-west-3 (Paris)
IaC : Terraform 1.15
Container : Docker â ECR â EKS
CI/CD : GitLab CI/CD (runner self-hosted Ubuntu 22.04 ESXi)
Routing : Envoy Gateway (Gateway API)
TLS : cert-manager + Let's Encrypt (DNS-01 via Cloudflare)
Domaine : devopsyouss.com (Cloudflare)
Architecture AWS
Internet (HTTPS â api.devopsyouss.com)
â
ELB (créé par Envoy Gateway â ports 80 + 443)
â
Envoy Proxy Pod (TLS terminĂ© ici â cert rempli par cert-manager + Let's Encrypt)
â
Service ClusterIP (fastapi)
â
FastAPI Pod (namespace: fastapi)
â
RDS PostgreSQL (subnet privé)
Terraform â 2 workspaces sĂ©parĂ©s
persistent/ (jamais détruit)
ECR : fastapi-eks/fastapi
IAM : gitlab_ci (ECR push) + gitlab_ci_infra (terraform)
Secrets Manager : fastapi-eks/app (DB_PASSWORD + SECRET_KEY) â consommĂ© par ESO (#33)
State S3 : yk-devops-terraform-state/fastapi-eks/persistent/
ephemeral/ (apply matin, destroy soir)
VPC : 10.0.0.0/16, 2 subnets publics + 2 privés
NAT Gateway : obligatoire pour nodes EKS
EKS : fastapi-eks-cluster, version 1.32, node t3.medium
RDS : PostgreSQL 16, db.t3.micro, subnet privé
IRSA ESO (#33) : provider OIDC du cluster + rĂŽle fastapi-eks-eso-irsa (lecture du secret fastapi-eks/app)
State S3 : yk-devops-terraform-state/fastapi-eks/ephemeral/
Scripts quotidiens
./aws-start.sh # matin â terraform apply + kubectl config
./aws-stop.sh # soir â kubectl delete gateway + terraform destroy
Pipeline CI/CD GitLab
.gitlab-ci.yml (app pipeline)
Stages : test â security â build â scan-image â promote â deploy
test : pytest + PostgreSQL service
security : SAST, Secret Detection, Trivy fs, tfsec, kube-linter
build : Kaniko â ECR (tag: SHA-candidate)
scan-image : Trivy image scan depuis ECR
promote : Kaniko â ECR (tag: SHA + latest)
deploy : kustomize edit set image + kubectl apply -k k8s/base/ (manual, si input deploy=true). Secret géré par ESO, plus de kubectl create secret
Déclenché sur : push, MR, develop, main
Deploy : Run Pipeline (web) + input deploy=true (dropdown), infra EKS up requise
Inputs : deploy = false | true (mappé sur la variable DEPLOY)
.gitlab-ci-infra.yml (infra pipeline)
Stages : infra â bootstrap â teardown â destroy
infra-status : aws eks list-clusters, rds, ecr...
infra-start : terraform apply + kubectl get nodes
bootstrap : Ansible â Envoy Gateway + metrics-server + cert-manager + ClusterIssuers + External Secrets Operator + exposition (GatewayClass + Gateway + Certificate) + RBAC user CI (ESO + HTTPRoute) (action=start)
infra-stop : aws-stop.sh (cleanup K8s + terraform destroy)
Déclenché sur : Run Pipeline (web) + Schedule 20h
Inputs : action = status | start | stop
Variables GitLab CI/CD
APP PIPELINE :
AWS_ACCESS_KEY_ID â gitlab_ci (ECR push)
AWS_SECRET_ACCESS_KEY â gitlab_ci (ECR push)
AWS_DEFAULT_REGION â eu-west-3
ECR_REGISTRY â 199167114788.dkr.ecr.eu-west-3.amazonaws.com
ECR_REPOSITORY â fastapi-eks/fastapi
SECRET_KEY â JWT secret
INFRA PIPELINE :
AWS_INFRA_ACCESS_KEY_ID â gitlab_ci_infra
AWS_INFRA_SECRET_ACCESS_KEY â gitlab_ci_infra
TF_VAR_db_password â <REDACTED - stored in GitLab CI as Masked variable>
CLOUDFLARE_API_TOKEN â token API Cloudflare (Masked + Protected, Zone > Zone Read + DNS Edit)
Note ESO (#33) :
TF_VAR_db_passwordalimente aussi le secret Secrets Manager persistent (mĂȘme valeur que RDS).SECRET_KEYest gĂ©nĂ©rĂ© par Terraform (random_password) et stockĂ© dans Secrets Manager. Depuis MR-D, le job deploy ne crĂ©e plus le Secret (ESO le synchronise) : la variable CISECRET_KEYest devenue inutile cĂŽtĂ© deploy et peut ĂȘtre supprimĂ©e des settings GitLab.
Kubernetes manifests (k8s/)
k8s/base/ (déployé en bloc via kustomize : kubectl apply -k k8s/base/)
âââ serviceaccount.yaml â SA dĂ©diĂ© fastapi, automountServiceAccountToken false
âââ configmap.yaml â DB_HOSTNAME, DB_PORT, DB_NAME...
âââ deployment.yaml â FastAPI pods, probes /healthz, resources, securityContext
âââ service.yaml â ClusterIP port 80â8080
âââ hpa.yaml â min:1, max:5, cpu:70%, mem:80%
âââ pdb.yaml â PodDisruptionBudget minAvailable 1
âââ networkpolicy-default-deny.yaml â deny-all ingress + egress
âââ networkpolicy-fastapi.yaml â ingress 8080 ; egress 5432 (RDS), 443 (AWS), 53 (DNS)
âââ networkpolicy-postgres.yaml â rĂšgles pour le pod postgres (prĂ©sent en local-kind)
âââ secretstore.yaml â ESO SecretStore (AWS Secrets Manager, auth IRSA contrĂŽleur)
âââ externalsecret.yaml â ESO ExternalSecret â Secret fastapi-secrets (DB_PASSWORD, SECRET_KEY)
âââ httproute.yaml â Gateway API HTTPRoute (app : route api.devopsyouss.com, ownĂ© par le deploy CI, #60)
Le namespace fastapi (+ labels PSA enforce restricted) n'est PAS dans k8s/base :
il est créé par le bootstrap Ansible (cluster-admin). Le user CI deploy en
least-privilege ne peut pas patcher un objet cluster-scoped (INC-045).
â
Les NetworkPolicy sont enforced sur EKS depuis #55 : vpc-cni déclaré en addon
EKS managé avec enableNetworkPolicy=true (résout INC-046). Validé par test négatif
(egress port 80 â timeout) + test positif (/healthz/ready â 200). Voir validation-runbook.md.
â
L'exposition (GatewayClass + Gateway + Certificate) est posée par le bootstrap
Ansible, pas par le deploy CI (#60, ADR 010). Raison : GatewayClass cluster-scoped +
CRDs Gateway API / cert-manager non couverts par "edit" (mĂȘmes classes qu'INC-045/047/048).
Le HTTPRoute reste owné par l'app (k8s/base) avec son binding RBAC explicite
fastapi-deploy-httproute, pour faire évoluer les routes (canary, blueprints) sans re-bootstrap.
k8s/platform/ (exposition â appliquĂ©e par le bootstrap Ansible, cluster-admin, #60)
âââ gateway.yaml â GatewayClass envoy + Gateway fastapi-gateway (listeners 80/443, TLS Terminate)
âââ certificate.yaml â Certificate fastapi-tls (cert-manager, ClusterIssuer letsencrypt-prod)
k8s/overlays/
âââ ingress-alb/ â ALB Ingress Controller (option A, Ă©cartĂ©e #34)
âââ local-kind/ â lab local (postgres en pod + ns baseline), dev only (#46)
IAM â Least Privilege
gitlab_ci :
â ECR push uniquement (app pipeline)
â ecr:GetAuthorizationToken, BatchCheck, InitiateUpload...
gitlab_ci_infra :
â terraform_ci policy (ec2:*, eks:*, rds:*, iam:limited + OIDC provider + secretsmanager read pour IRSA #33)
â infra_status policy (read-only EKS, EC2, RDS, ECR, ELB)
Coûts AWS (ephemeral 4h/jour)
NAT Gateway : 0.045$/h Ă 4 = 0.18$/jour
EKS Control : 0.10$/h Ă 4 = 0.40$/jour
t3.medium : 0.047$/h Ă 4 = 0.19$/jour
RDS micro : 0.02$/h Ă 4 = 0.08$/jour
Total : ~0.85$/jour â ~17$/mois (20 jours)
Persistent (toujours) :
ECR : ~0.01$/mois
IAM : gratuit
Ătat Sprint 3
â
Terraform persistent/ephemeral
â
Kubernetes manifests (base + overlays)
â
Envoy Gateway opérationnel
â
FastAPI + RDS validé sur EKS
â
Pipeline build/scan/promote
â
Pipeline infra start/stop/status
â
IAM Least Privilege (2 users)
â
Schedule nightly destroy
â
Stage deploy CI/CD validé end-to-end via kustomize (#52)
â
PSA enforce restricted effectif sur EKS (#48, INC-045)
â
NetworkPolicy enforced via VPC CNI managé (#55, INC-046)
â ALB Ingress Controller (#34) â Ă©cartĂ©, direction gateway-api actĂ©e pour portabilitĂ© multi-cloud
â
HTTPS public api.devopsyouss.com â cert-manager + Let's Encrypt DNS-01 Cloudflare (#28)
đ§ Secrets Manager + ESO (#33) â MR-AâD livrĂ©es (secret persistent fastapi-eks/app, provider OIDC + rĂŽle IRSA ephemeral, install ESO via bootstrap, manifests SecretStore/ExternalSecret, deploy sans Secret impĂ©ratif). Sync ESO de fastapi-secrets validĂ©e live le 2026-05-29. Reste : valider le deploy app end-to-end via le pipeline develop, puis fermer #33
Commandes utiles
# AWS
awslogin # ouvrir session aws-vault
aws sts get-caller-identity # vérifier l'identité
aws eks list-clusters # lister clusters EKS
aws ecr list-images --repository-name fastapi-eks/fastapi
# Kubernetes
kubectl get nodes
kubectl get all -n fastapi
kubectl logs -n fastapi -l app=fastapi -f
kubectl port-forward -n fastapi deployment/fastapi 8080:8080
# Terraform
cd terraform/ephemeral && terraform output # voir outputs
cd terraform/persistent && terraform state list
# Pipeline infra
./aws-start.sh # démarrer l'infra
./aws-stop.sh # arrĂȘter l'infra