Docker et Kubernetes : Déploiement d'une application full stack

Docker et Kubernetes

Résumé : Ce guide présente une approche pratique pour conteneuriser une application full stack (frontend + backend + base de données), la tester en local avec Docker Compose puis la déployer en production sur Kubernetes. Il inclut des exemples de Dockerfile, de manifests Kubernetes, des commandes essentielles et des bonnes pratiques CI/CD et sécurité.

1. Architecture cible

Exemple d'architecture pour une app full stack :

  • Frontend : React (build static + serveur Nginx)
  • Backend : API Node.js/Express (ou Flask/Django)
  • Base de données : PostgreSQL (stateful)
  • Ingress : Ingress Controller (NGINX, Traefik)
  • Observabilité : Prometheus + Grafana + EFK (Elasticsearch, Fluentd, Kibana)

2. Docker : conteneurisation

Dockerfile exemple — Backend Node.js

# Dockerfile (backend)
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

Dockerfile exemple — Frontend (React) servie par Nginx

# Dockerfile (frontend)
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx","-g","daemon off;"]

Docker Compose (local)

# docker-compose.yml (développement local)
version: "3.8"
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: changeme
      POSTGRES_DB: appdb
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - appnet

  backend:
    build: ./backend
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://appuser:changeme@db:5432/appdb
    depends_on:
      - db
    networks:
      - appnet

  frontend:
    build: ./frontend
    ports:
      - "8080:80"
    depends_on:
      - backend
    networks:
      - appnet

volumes:
  db-data:

networks:
  appnet:

Avec Docker Compose, tu peux simuler l’environnement complet en local avant d'aller sur Kubernetes.

3. Kubernetes : manifests essentiels

Deployment + Service — Backend

# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: myrepo/backend:latest
          ports:
            - containerPort: 3000
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-secret
                  key: DATABASE_URL
---
# backend-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: backend
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
  type: ClusterIP

StatefulSet — PostgreSQL

# postgres-statefulset.yaml (simplifié)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pg-data
spec:
  accessModes: [ "ReadWriteOnce" ]
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: "postgres"
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:15
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_USER
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: POSTGRES_USER
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: POSTGRES_PASSWORD
        volumeMounts:
        - name: pgdata
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: pgdata
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

Ingress (exemple NGINX)

# ingress.yaml (exemple)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: backend
                port:
                  number: 3000

4. CI/CD et workflow

Pipeline typique :

  1. Tests unitaires & linting
  2. Build d'images Docker (frontend & backend)
  3. Scan de sécurité d'images (ex. Trivy)
  4. Push vers un registry (Docker Hub, GitHub Container Registry, GCR, ECR)
  5. Déploiement sur K8s (kubectl apply / Helm / Argo CD)

Extrait d'étape GitHub Actions pour builder et push :

# .github/workflows/ci.yml (extrait)
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build backend image
        run: docker build -t ghcr.io/${{ github.repository_owner }}/backend:${{ github.sha }} ./backend
      - name: Push backend image
        run: |
          echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker push ghcr.io/${{ github.repository_owner }}/backend:${{ github.sha }}

5. Observabilité et monitoring

Outils recommandés :

  • Logs : EFK (Elasticsearch, Fluentd/Fluent Bit, Kibana)
  • Métriques : Prometheus + Grafana
  • Tracing distribué : Jaeger / OpenTelemetry

Astuce : exporter métriques app (Prometheus client) et ajouter probes readiness/liveness dans les manifests K8s.

6. Sécurité et bonnes pratiques

  • Utiliser Secrets (Kubernetes Secrets / External Secrets) — ne commit jamais les credentials.
  • Limiter les permissions via RBAC et limiter les capacités des containers (securityContext).
  • Scanner les images pour vulnérabilités (Trivy / Clair).
  • Activer TLS au niveau de l'Ingress (Let's Encrypt + cert-manager).
  • Garder les images légères (alpine) et appliquer les mises à jour régulières.

7. Commandes utiles

# build et push
docker build -t myrepo/backend:1.0 ./backend
docker push myrepo/backend:1.0

# déployer sur k8s
kubectl apply -f k8s/backend-deployment.yaml
kubectl rollout status deployment/backend

# logs & debug
kubectl logs -l app=backend -c backend
kubectl get pods -o wide

# accéder à un pod
kubectl exec -it  -- /bin/sh

8. Exemples de problèmes courants & solutions

Pods CrashLoopBackOff

Vérifier les logs (`kubectl logs`), les variables d'environnement (DB url), et readiness/liveness probes.

Problèmes de persistance

Valider les PersistentVolumeClaims et la classe de stockage (StorageClass) utilisée sur le cluster.

Ingress non accessible

Vérifier que l'Ingress Controller est installé et que les annotations/host correspondent aux DNS et au certificat TLS.

9. Checklist de déploiement

  1. Conteneuriser & tester localement (Docker Compose)
  2. Configurer registry privé/public sécurisé
  3. Déployer manifests K8s en staging
  4. Mettre en place monitoring et alerting
  5. Exécuter tests de charge et résilience
  6. Automatiser via GitOps (Argo CD) ou pipeline CI/CD
Retour au blog