E-NO
Kubernetes 9 min de lecture

Dépannage du déploiement Kubernetes avec GitLab CI/CD : exemples pratiques

calendar_today Publié : 2026-07-09
update Dernière mise à jour : 2026-07-09
analytics Efficacité SEO : 100%
Illustration du guide technique pour « Dépannage du déploiement Kubernetes avec GitLab CI/CD : exemples pratiques ».

Intro

Le déploiement vers Kubernetes depuis GitLab CI/CD devient fiable quand on maîtrise les quatre pièces mobiles : le runner, le kubeconfig, l'authentification au registre et la vérification du rollout. Ce guide propose des correctifs concrets pour les échecs les plus fréquents et des extraits copier‑coller adaptables dès aujourd'hui. Vous apprendrez à créer un kubeconfig au privilège minimal, diagnostiquer les erreurs de pipeline et de cluster, vérifier les rollouts et exécuter un petit pilote avant d'étendre.

Acronymes : CI/CD (Continuous Integration / Continuous Delivery) : intégration continue / livraison continue. RBAC (Role‑Based Access Control) : contrôle d'accès basé sur les rôles. API (Application Programming Interface) : interface de programmation d'application.

Aperçu du workflow

Un chemin clair, étape par étape, du commit aux pods sains maintient les problèmes isolés à une seule phase :

  1. Build – produire une image conteneur taguée avec le SHA du commit.
  2. Push – s'authentifier au registre et pousser l'image.
  3. Configurer – fournir kubectl et un kubeconfig n'accordant que les permissions nécessaires.
  4. Appliquer – exécuter kubectl apply ou helm upgrade --install.
  5. Attendre – surveiller le statut du rollout avec un délai d'expiration.
  6. Vérifier – lancer un test de fumée rapide contre le Service ou l'Ingress.
  7. Rollback – revenir au dernier ReplicaSet sain si la vérification échoue.

Gardez chaque étape dans son propre job afin de voir exactement où survient l'échec.

Configuration : runners et kubeconfig

Compte de service et RBAC (limité à l'espace de noms)

Créez un compte de service dédié avec les droits minimaux requis dans l'espace de noms cible :

apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-deployer
  namespace: demo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: ci-deployer
  namespace: demo
rules:
- apiGroups: ["", "apps", "extensions
tesources: ["pods", "deployments", "replicasets", "services", "configmaps", "secrets
ebri: ["get", "list", "watch", "create", "update", "patch", "delete
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-deployer
  namespace: demo
subjects:
- kind: ServiceAccount
  name: ci-deployer
  namespace: demo
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ci-deployer

Générez un jeton à durée de vie courte (Kubernetes 1.24+) :

kubectl -n demo create token ci-deployer > ci-deployer.token

Construisez un kubeconfig pointant vers votre serveur API et incluant les données CA, puis stockez le YAML complet comme variable CI protégée et masquée nommée KUBE_CONFIG_B64 (encodé base64) ou KUBE_CONFIG_YAML (multiligne).

Écriture du kubeconfig dans le job

# .gitlab-ci.yml (extrait kubeconfig)
before_script:
  - mkdir -p ~/.kube
  - |
    if [ -n "$KUBE_CONFIG_B64" ]; then
      echo "$KUBE_CONFIG_B64" | base64 -d > ~/.kube/config
    else
      echo "$KUBE_CONFIG_YAML" > ~/.kube/config
    fi
  - chmod 600 ~/.kube/config

Choix de l'image du runner

Utilisez une image de job déjà pourvue de kubectl (par exemple bitnami/kubectl:latest) ou installez kubectl à l'exécution. Si vous construisez des images avec Docker‑in‑Docker, le runner doit s'exécuter en mode privilégié. Lorsque les runners privilégiés ne sont pas possibles, passez à un constructeur sans racine comme Kaniko ou BuildKit.

Pipeline de référence : build, push et deploy

stages: [build, deploy]

variables:
  IMAGE: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
  KUBE_NAMESPACE: "demo"

# Build et push de l'image avec Docker-in-Docker (nécessite un runner privilégié)
build:
  stage: build
  image: docker:25
  services:
    - name: docker:25-dind
      command: ["--mtu=1460\
  variables:
    DOCKER_TLS_CERTDIR: /certs
  script:
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
    - docker build -t "$IMAGE" .
    - docker push "$IMAGE"
  rules:
    - if: "$CI_COMMIT_BRANCH"

# Déploiement avec kubectl apply et attente du rollout
deploy:
  stage: deploy
  image: bitnami/kubectl:latest
  before_script:
    - mkdir -p ~/.kube
    - if [ -n "$KUBE_CONFIG_B64" ]; then echo "$KUBE_CONFIG_B64" | base64 -d > ~/.kube/config; else echo "$KUBE_CONFIG_YAML" > ~/.kube/config; fi
    - chmod 600 ~/.kube/config
  script:
    # S'assurer que l'espace de noms existe
    - kubectl get ns "$KUBE_NAMESPACE" || kubectl create ns "$KUBE_NAMESPACE"

    # Créer ou mettre à jour le secret de pull d'image pour l'espace de noms (une fois par ns)
    - |
      kubectl -n "$KUBE_NAMESPACE" create secret docker-registry gitlab-regcred \
        --docker-server="$CI_REGISTRY" \
        --docker-username="$CI_REGISTRY_USER" \
        --docker-password="$CI_REGISTRY_PASSWORD" \
        --docker-email="[email protected]" \
        --dry-run=client -o yaml | kubectl apply -f -

    # Appliquer les manifests (le deployment utilise imagePullSecrets)
    - kubectl -n "$KUBE_NAMESPACE" apply -f k8s/

    # Définir l'image du conteneur sur le tag SHA du commit (patch optionnel)
    - kubectl -n "$KUBE_NAMESPACE" set image deploy/myapp myapp-container="$IMAGE" --record=true || true

    # Attendre le rollout avec timeout
    - kubectl -n "$KUBE_NAMESPACE" rollout status deploy/myapp --timeout=120s

    # Vérification simple via ClusterIP avec busybox curl (optionnel)
    - kubectl -n "$KUBE_NAMESPACE" run tmp-curl --image=busybox:1.36 --restart=Never --rm -it -- curl -sS myapp:8080/healthz
  environment:
    name: demo/$CI_COMMIT_REF_NAME
    url: https://demo.example.com
  rules:
    - if: "$CI_COMMIT_BRANCH == \"main\""

Exemple de manifest Deployment (k8s/deploy.yaml) :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      serviceAccountName: ci-deployer
      imagePullSecrets:
        - name: gitlab-regcred
      containers:
        - name: myapp-container
          image: registry.example.com/group/project:CHANGE_ME
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: myapp
spec:
  selector:
    app: myapp
  ports:
    - port: 8080
      targetPort: 8080

Dépannage : échecs de pipeline

SymptômeCause probableCorrectif rapide
Job bloqué en pendingAucun runner ne correspond aux tags du job ou tous les runners sont occupésAjoutez des tags correspondant à un runner enregistré ; vérifiez l'état du runner dans les paramètres du projet
docker build échoue ou ne peut pas se connecter au démon DockerDinD non activé ou runner non privilégiéActivez le mode privilégié pour le runner ou passez à Kaniko ; assurez‑vous que DOCKER_HOST et DOCKER_TLS_CERTDIR sont définis avec DinD
kubectl: command not foundL'image du job ne contient pas kubectlUtilisez une image avec kubectl (ex. bitnami/kubectl) ou installez‑le dans le job
The request could not be authenticated / ForbiddenJeton kubeconfig invalide/expiré ou RBAC insuffisantRafraîchissez le jeton du compte de service, vérifiez l'URL du serveur API et le CA dans le kubeconfig, et accordez les verbes requis sur les ressources de l'espace de noms cible
apply échoue avec no matches for kind ou invalidMauvaise apiVersion ou incompatibilité de schémaValidez localement avec kubectl apply --dry-run=client -f k8s/ et fixez les bonnes valeurs apiVersion
Espace de noms introuvableDéploiement dans un espace de noms qui n'existe pasCréez l'espace de noms avant d'appliquer les ressources (kubectl create ns <nom>)

Dépannage : erreurs de pull d'image

Le pod affiche ErrImagePull ou ImagePullBackOff :

  1. Mauvais tag d'image
  • Vérifiez le tag exact dans le Deployment :
     kubectl -n demo get deploy myapp -o jsonpath='{.spec.template.spec.containers[0].image}'
  • Privilégiez des tags immuables comme $CI_COMMIT_SHORT_SHA.
  1. Authentification au registre manquante
  • Assurez‑vous qu'un secret docker-registry existe dans le même espace de noms et est référencé par imagePullSecrets ou le compte de service.
  • Recréez le secret si les identifiants ont changé :
     kubectl -n demo delete secret gitlab-regcred --ignore-not-found
     kubectl -n demo create secret docker-registry gitlab-regcred \
       --docker-server="$CI_REGISTRY" \
       --docker-username="$CI_REGISTRY_USER" \
       --docker-password="$CI_REGISTRY_PASSWORD" \
       --docker-email="[email protected]"
  1. Surprises de politique et de cache
  • Si vous utilisez :latest, définissez imagePullPolicy: Always pour éviter les images obsolètes.
  • Mieux : épinglez au SHA du commit et conservez imagePullPolicy: IfNotPresent.
  • Les secrets sont limités à l'espace de noms ; créez le même secret de pull dans chaque espace de noms cible.

Pour confirmer la correction, supprimez le pod en échec et laissez le ReplicaSet le recréer, ou déclenchez un redémarrage du rollout :

kubectl -n demo rollout restart deploy/myapp

Dépannage : vérifications de rollout et débogage

Automatisez l'attente et enrichissez les logs d'échec :

kubectl -n demo rollout status deploy/myapp --timeout=120s || {
  echo "Rollout did not complete in time"
  kubectl -n demo get pods -o wide
  kubectl -n demo describe deploy/myapp
  kubectl -n demo get events --sort-by=.lastTimestamp | tail -n 50
  exit 1
}

Si le rollout échoue ou les pods plantent :

  • Describe et événementskubectl -n demo describe pod <nom> montre les problèmes de pull d'image, d'ordonnancement et de sondes. kubectl -n demo get events --sort-by=.lastTimestamp fait remonter les erreurs récentes.
  • Logskubectl -n demo logs deploy/myapp --all-containers=true --tail=200 pour les erreurs applicatives.
  • Sondes et ressources – Les échecs de readiness bloquent le rollout. Vérifiez le chemin et le port de la sonde ; augmentez initialDelaySeconds si l'application démarre lentement. Contrôlez les demandes/limites CPU et mémoire ; OOMKilled indique une limite mémoire trop basse.
  • Services et Ingress – Assurez‑vous que les sélecteurs du Service correspondent exactement aux labels des pods. Confirmez que targetPort correspond au port du conteneur. Pour l'Ingress, validez l'hôte, le TLS et que les backends du Service sont sains.
  • ConfigMaps et Secrets – Clés manquantes ou montages incorrects provoquent souvent CrashLoopBackOff. Comparez le spec du pod en cours au manifest : kubectl -n demo get pod <nom> -o yaml.

Rollback rapide :

kubectl -n demo rollout undo deploy/myapp

Vérifications de sécurité scriptées dans CI

Ajoutez des contrôles pré‑déploiement et post‑déploiement pour réduire l'incertitude :

# Valider les manifests avant apply
kubectl -n demo apply --dry-run=client -f k8s/

# Voir ce qui va changer
kubectl -n demo diff -f k8s/ || true

# Après rollout, confirmer la readiness et la santé HTTP
kubectl -n demo get pods -l app=myapp -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.phase}{"\n"}{end}'

# Curl via ClusterIP depuis un pod temporaire
kubectl -n demo run netcheck --image=busybox:1.36 --rm -it --restart=Never -- \
  sh -c 'wget -qO- http://myapp:8080/healthz'

Plan pilote local

Commencez petit, mesurez et gardez le tout facile à inspecter localement avant d'élargir.

Périmètre

  • Un service nommé myapp dans un espace de noms de staging demo-pilot.
  • Tags d'image immuables utilisant le SHA du commit.

Pipeline

  • Jobs : build, deploy, verify.
  • Pré‑déploiement : kubectl apply --dry-run=client -f k8s/ et kubectl diff -f k8s/.
  • Déploiement : appliquer les manifests, définir l'image sur le SHA, attendre 120 s.
  • Vérification : curl /healthz, récupérer les logs et lister les événements.

Critères de succès mesurables

  • Rollout terminé en moins de 2 minutes.
  • Endpoint de santé renvoie HTTP 200.
  • Aucun événement ImagePullBackOff ou CrashLoopBackOff parmi les 50 derniers événements.

Sécurité et rollback

  • Gardez kubectl rollout undo deploy/myapp dans le job en cas d'échec.
  • Utilisez un espace de noms et un compte de service distincts ; aucun secret de production.

Lorsque le pilote s'exécute proprement sur plusieurs itérations, reproduisez le même schéma vers un environnement de staging plus large ou en canary.

Conclusion

Séparez les étapes de déploiement, fournissez un kubeconfig sécurisé, et scriptez à la fois l'attente du rollout et la vérification pour réduire le temps de débogage. Démarrez avec le plan pilote, mesurez les résultats, et étendez‑vous dès que vous obtenez systématiquement des rollouts propres, des sondes saines et des rollbacks en une commande.

Recherches connexes

Score de qualité de l’article

Utilité pour le lecteur 100%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO