Aller au contenu
Conteneurs & Orchestration medium

Déployer votre première application sur Kubernetes

35 min de lecture

logo kubernetes

Déployer une application sur Kubernetes consiste à déclarer comment elle doit tourner, puis à lui donner un point d'accès stable. Dans ce guide, vous allez créer un namespace, déployer nginx avec un Deployment, l'exposer via un Service, vérifier qu'il répond, puis observer son comportement quand vous augmentez le nombre de réplicas.

  • Créer un namespace pour organiser votre application
  • Déployer une application avec kubectl create deployment
  • Exposer l'application avec un Service
  • Comprendre le lien entre labels, sélecteurs et endpoints
  • Scaler et observer l'application
  • Voir ce que Kubernetes fait en arrière-plan

Lisez le schéma ci-dessous comme une chaîne de responsabilité. Chaque niveau crée et surveille le niveau inférieur : vous ne déclarez que le Deployment, le reste apparaît tout seul. Le Service est volontairement à part, au même niveau que le Deployment : il ne descend pas de lui, il vise les Pods de côté, par leur label.

À la fin de ce guide, vous aurez :

Ressources créées dans le namespace demo-app : le Deployment nginx crée un ReplicaSet qui maintient trois Pods portant le label app=nginx, et le Service nginx cible ces Pods par ce même label

  • Un namespace demo-app pour organiser les ressources
  • Un Deployment qui gère des Pods nginx via un ReplicaSet
  • Un Service qui cible les Pods portant le label app=nginx

Un namespace organise et isole logiquement vos ressources dans le cluster. C'est une bonne habitude à prendre dès le début.

Créer le namespace
kubectl create namespace demo-app
Résultat
namespace/demo-app created

Vérifiez qu'il existe :

Vérifier
kubectl get namespace demo-app
Résultat
NAME STATUS AGE
demo-app Active 5s

Un Deployment dit à Kubernetes : "je veux X pods qui exécutent cette image".

La commande kubectl create deployment crée un Deployment en une ligne :

Créer le déploiement
kubectl create deployment nginx --image=nginx:1.28 -n demo-app
Résultat
deployment.apps/nginx created
OptionSignification
nginxNom du Deployment
--image=nginx:1.28Image Docker à utiliser (version figée)
-n demo-appNamespace cible

La création du Deployment est instantanée, le démarrage du Pod ne l'est pas : le nœud doit d'abord télécharger l'image, ce qui prend de quelques secondes à une minute selon votre connexion. Si la commande suivante affiche 0/1, relancez-la quelques secondes plus tard avant de conclure à un problème.

Voir l'état du Deployment
kubectl get deployment nginx -n demo-app
Résultat
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 1/1 1 1 10s
ColonneSignification
READYPods prêts / Pods souhaités
UP-TO-DATEPods à jour avec la dernière configuration
AVAILABLEPods qui peuvent servir du trafic

1/1 signifie : 1 pod est prêt sur 1 demandé. Quand ces chiffres correspondent, le déploiement est opérationnel.

Le Deployment ne crée pas directement les Pods. Il passe par un ReplicaSet :

Voir le ReplicaSet
kubectl get replicasets -n demo-app
Résultat
NAME DESIRED CURRENT READY AGE
nginx-6d8867d6c 1 1 1 15s

Le Deployment a créé un ReplicaSet (nginx-6d8867d6c), qui lui-même crée et surveille les Pods. Vous n'avez pas besoin de manipuler le ReplicaSet directement, c'est le Deployment qui le gère.

C'est le Pod qui exécute réellement nginx : le Deployment et le ReplicaSet ne sont que des objets de gestion. Regardez la colonne READY : le 1/1 compte ici les conteneurs prêts dans le Pod, alors que le 1/1 du Deployment comptait les Pods prêts. Deux colonnes de même apparence, deux unités différentes.

Lister les pods
kubectl get pods -n demo-app
Résultat
NAME READY STATUS RESTARTS AGE
nginx-6d8867d6c-z2j62 1/1 Running 0 18s

Le nom du pod (nginx-6d8867d6c-z2j62) est généré automatiquement :

  • nginx : nom du Deployment
  • 6d8867d6c : identifiant du ReplicaSet
  • z2j62 : identifiant unique du pod

Le pod tourne, mais il n'est pas accessible de manière stable. Pour l'atteindre, créez un Service.

Exposer le Deployment
kubectl expose deployment nginx --port=80 --type=ClusterIP -n demo-app
Résultat
service/nginx exposed
OptionSignification
--port=80Port sur lequel le Service écoute
--type=ClusterIPAccessible uniquement depuis l'intérieur du cluster

Vérifiez le Service créé :

Voir le Service
kubectl get service nginx -n demo-app
Résultat
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP 10.43.51.181 <none> 80/TCP 8s
ColonneSignification
CLUSTER-IPAdresse IP interne au cluster
EXTERNAL-IP<none> car ClusterIP n'est pas exposé à l'extérieur
PORT(S)Port d'écoute

Étape 4 : Comprendre comment le Service trouve les Pods

Section intitulée « Étape 4 : Comprendre comment le Service trouve les Pods »

C'est ici que vous comprenez vraiment comment Kubernetes relie les choses.

Le Deployment a créé des Pods avec le label app=nginx. Vous ne l'avez jamais écrit : kubectl create deployment l'a posé pour vous à partir du nom du Deployment. Le second label, pod-template-hash, est ajouté par Kubernetes et identifie le ReplicaSet d'origine ; c'est lui qui permet de distinguer les anciens Pods des nouveaux pendant une mise à jour.

Afficher les labels
kubectl get pods -n demo-app --show-labels
Résultat
NAME READY STATUS RESTARTS AGE LABELS
nginx-6d8867d6c-z2j62 1/1 Running 0 45s app=nginx,pod-template-hash=6d8867d6c

Deux lignes seulement comptent dans cette sortie très bavarde : Selector et Endpoints. La première est ce que vous avez déclaré, la seconde est ce que Kubernetes en a déduit. Comparer les deux est le réflexe de diagnostic le plus rentable sur un Service : un sélecteur correct avec des endpoints vides signifie qu'aucun Pod ne porte le label attendu.

Détails du Service
kubectl describe service nginx -n demo-app
Extrait du résultat
Name: nginx
Namespace: demo-app
Selector: app=nginx
Type: ClusterIP
IP: 10.43.51.181
Port: <unset> 80/TCP
Endpoints: 10.42.0.15:80
ChampSignification
SelectorLabel que le Service cherche (app=nginx)
EndpointsAdresses IP des Pods qui correspondent au sélecteur

Le lien est maintenant clair :

  1. Le Deployment crée des Pods avec le label app=nginx
  2. Le Service sélectionne les Pods ayant app=nginx
  3. Les Endpoints montrent les Pods effectivement ciblés

Cette commande crée un Pod temporaire, exécute wget, affiche le résultat puis supprime le Pod :

Tester l'accès via DNS interne
kubectl run test-curl --image=busybox:1.36 --rm -i --restart=Never -n demo-app -- wget -qO- http://nginx:80
Résultat (extrait)
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

L'application répond ! Le Service nginx est accessible depuis n'importe quel Pod du namespace via son nom DNS (nginx ou nginx.demo-app.svc.cluster.local).

OptionSignification
--rmSupprime le pod après exécution
-iMode interactif (récupère la sortie)
--restart=NeverNe pas redémarrer si la commande échoue
wget -qO-Télécharge et affiche le contenu

Option 2 : Port-forward (accès depuis votre poste)

Section intitulée « Option 2 : Port-forward (accès depuis votre poste) »

Pour accéder à nginx depuis votre navigateur :

Rediriger le port localement
kubectl port-forward svc/nginx 8080:80 -n demo-app
Résultat
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

Ouvrez http://localhost:8080 dans votre navigateur. Vous verrez la page d'accueil nginx.

Appuyez sur Ctrl+C pour arrêter le port-forward.

Actuellement, vous avez 1 pod. Pour en avoir 3 :

Passer à 3 réplicas
kubectl scale deployment nginx --replicas=3 -n demo-app
Résultat
deployment.apps/nginx scaled

Vérifiez que les nouveaux pods démarrent :

Voir les pods
kubectl get pods -n demo-app
Résultat
NAME READY STATUS RESTARTS AGE
nginx-6d8867d6c-57bpf 1/1 Running 0 10s
nginx-6d8867d6c-lptvk 1/1 Running 0 10s
nginx-6d8867d6c-z2j62 1/1 Running 0 2m

3 pods tournent maintenant. Vérifiez que le Service les voit tous :

Voir les endpoints mis à jour
kubectl describe service nginx -n demo-app | grep Endpoints
Résultat
Endpoints: 10.42.0.15:80,10.42.0.16:80,10.42.0.17:80

Le Service a détecté les nouveaux Pods et les a ajoutés à sa liste d'endpoints. Il répartit automatiquement les requêtes entre eux.

describe est plus verbeux que get parce qu'il ajoute deux choses absentes d'un tableau : le modèle de Pod complet, c'est-à-dire ce que le Deployment va reproduire à chaque réplica, et la liste des événements récents en bas de sortie. Quand quelque chose ne va pas, ce sont ces événements de fin de sortie qu'il faut lire en premier.

Détails complets
kubectl describe deployment nginx -n demo-app
Extrait du résultat
Name: nginx
Namespace: demo-app
Labels: app=nginx
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.28@sha256:146adea4768b83c607d0bdfa4188464e3da6e0a3ad4475db1d1d8f64f27c29cc

Cette commande montre la configuration complète et les événements récents.

Les logs viennent toujours d'un Pod (pas du Deployment). Identifiez d'abord un Pod :

Lister les pods
kubectl get pods -n demo-app

Puis affichez ses logs :

Logs d'un Pod spécifique
kubectl logs nginx-6d8867d6c-z2j62 -n demo-app --tail=10
Résultat
2026/03/21 09:52:26 [notice] 1#1: start worker process 41
...

kubectl permet de lire les logs via le nom du Deployment :

Logs via Deployment (raccourci)
kubectl logs deployment/nginx -n demo-app --tail=10
Résultat
Found 3 pods, using pod/nginx-6d8867d6c-z2j62
2026/03/21 09:52:26 [notice] 1#1: start worker process 41
...

Cette forme est pratique, mais les logs viennent toujours d'un seul Pod, kubectl en choisit un pour vous.

Pour suivre les logs en temps réel :

Logs en streaming
kubectl logs deployment/nginx -n demo-app -f

Appuyez sur Ctrl+C pour arrêter.

Les événements forment le journal de bord du namespace : c'est là que Kubernetes explique pourquoi il a agi. Le tri par .lastTimestamp est indispensable, car sans lui la sortie n'est pas chronologique et devient illisible dès la dizaine de lignes.

Événements récents
kubectl get events -n demo-app --sort-by='.lastTimestamp'

Vous y retrouvez la création des Pods, le scaling, et les erreurs éventuelles. Un détail à connaître : les événements ne sont pas conservés indéfiniment, ils expirent au bout d'une heure par défaut. Un incident survenu la veille n'y figurera plus.

Les commandes impératives sont parfaites pour apprendre et explorer. En production, on préfère décrire les ressources dans des fichiers YAML versionnés dans Git.

Voici l'équivalent de ce que vous avez créé :

demo-app.yaml
apiVersion: v1
kind: Namespace
metadata:
name: demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: demo-app
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.28@sha256:146adea4768b83c607d0bdfa4188464e3da6e0a3ad4475db1d1d8f64f27c29cc
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx
namespace: demo-app
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80
type: ClusterIP

Pour appliquer ce fichier :

Appliquer la configuration
kubectl apply -f demo-app.yaml

Récapitulons ce qui s'est passé quand vous avez exécuté ces commandes :

  1. Vous avez créé un Deployment → Kubernetes a créé un ReplicaSet
  2. Le ReplicaSet a créé les Pods → Kubernetes a assigné chaque Pod à un nœud
  3. Chaque nœud a téléchargé l'image et démarré le conteneur nginx
  4. Vous avez créé un Service → Kubernetes a enregistré les Pods correspondant au sélecteur app=nginx
  5. Le Service a reçu une IP stable → Les endpoints pointent vers les Pods
  6. Vous avez scalé → Le ReplicaSet a créé 2 Pods supplémentaires
  7. Le Service a mis à jour ses endpoints automatiquement

C'est ça le modèle Kubernetes : vous déclarez ce que vous voulez, Kubernetes s'occupe de le réaliser et de le maintenir.

Pour supprimer tout ce que vous avez créé, supprimez le namespace :

Supprimer le namespace et son contenu
kubectl delete namespace demo-app
Résultat
namespace "demo-app" deleted

Cette commande supprime toutes les ressources du namespace (Deployment, ReplicaSet, Service, Pods).

Un Deployment bloqué à 0/1 ne dit jamais pourquoi de lui-même : l'objet Deployment se contente de constater que son Pod n'est pas prêt. La raison se trouve un cran plus bas, dans les événements du Pod. Commencez donc toujours par les deux commandes suivantes avant de modifier quoi que ce soit.

Voir pourquoi le pod ne démarre pas
kubectl describe deployment nginx -n demo-app
kubectl get events -n demo-app
Cause fréquenteSolution
Image introuvableVérifiez le nom de l'image (nginx:1.28 pas ngix:1.28)
Pas de ressourcesRéduisez le nombre de réplicas ou libérez de la mémoire
ImagePullBackOffProblème de connexion à la registry ou image privée

CrashLoopBackOff signifie que le conteneur démarre puis s'arrête, en boucle. Le piège : au moment où vous tapez kubectl logs, le conteneur vient de redémarrer et n'a encore rien écrit. Les logs qui vous intéressent sont ceux de l'exécution précédente, celle qui a planté.

Voir les logs du pod
kubectl logs <nom-du-pod> -n demo-app --previous

C'est le rôle du flag --previous. Notez aussi que Kubernetes espace les redémarrages successifs, jusqu'à cinq minutes d'attente : un Pod en CrashLoopBackOff depuis longtemps peut sembler figé alors qu'il attend simplement sa prochaine tentative.

Un Service qui ne répond pas n'est presque jamais cassé au sens technique : il fonctionne, mais il ne pointe vers rien. Le diagnostic tient donc en une seule vérification, et elle vient avant toute autre.

Vérifier les endpoints
kubectl describe service nginx -n demo-app

Vérifiez que Endpoints n'est pas vide. S'il l'est, le sélecteur du Service ne correspond à aucun Pod, vérifiez les labels avec kubectl get pods --show-labels.

C'est normal, donnez-lui quelques secondes. Kubernetes nettoie les ressources dans l'ordre. Si ça dure trop longtemps, des finalizers peuvent bloquer la suppression.

Ces cinq commandes créent des objets sans passer par un fichier. Elles sont faites pour explorer vite : rien n'est versionné, rien n'est relisible plus tard, et vous ne pouvez pas rejouer la séquence à l'identique sur un autre cluster. C'est acceptable en apprentissage, pas au-delà.

ActionCommande
Créer namespacekubectl create namespace nom
Créer Deploymentkubectl create deployment nom --image=image -n namespace
Exposer avec Servicekubectl expose deployment nom --port=80 -n namespace
Scalerkubectl scale deployment nom --replicas=N -n namespace
Supprimer toutkubectl delete namespace nom

Celles-ci ne modifient rien : vous pouvez les lancer sans risque, y compris sur un cluster de production. Ce sont les seules du guide dont vous aurez besoin tous les jours. Retenez surtout la troisième, qui relie une panne d'accès à sa cause la plus fréquente, un sélecteur qui ne trouve aucun Pod.

ActionCommande
Voir les podskubectl get pods -n namespace
Voir les labelskubectl get pods -n namespace --show-labels
Voir les endpointskubectl describe service nom -n namespace
Voir les logskubectl logs <pod> -n namespace
Port-forwardkubectl port-forward svc/nom 8080:80 -n namespace
Tester depuis un podkubectl run test --rm -i --restart=Never --image=busybox -- wget -qO- http://service

Deux commandes suffisent, et la seconde est celle qu'on oublie. kubectl diff compare votre fichier à l'état réel du cluster et affiche ce qui changerait, sans rien appliquer. Prenez l'habitude de la lancer avant chaque apply : c'est ce qui vous évitera d'écraser une modification faite à la main entre-temps.

ActionCommande
Appliquer une configkubectl apply -f fichier.yaml
Voir les différenceskubectl diff -f fichier.yaml
  1. Un Deployment gère vos Pods via un ReplicaSet, il les crée, les surveille et les recrée si nécessaire
  2. Un Service expose vos Pods, il fournit une adresse stable même quand les Pods changent
  3. Le Service cible les Pods par labels, pas le Deployment, vérifiez toujours les Endpoints
  4. Utilisez un namespace pour organiser et nettoyer proprement
  5. Figez les versions d'images (nginx:1.28 pas nginx:latest)
  6. Les commandes impératives sont pour apprendre, en production, passez au déclaratif (YAML)
  7. kubectl describe est votre meilleur ami pour comprendre ce qui se passe
  8. Si vous cassez quelque chose, supprimez le namespace et recommencez
  • Architecture Kubernetes : Ce qui s'est réellement passé dans le cluster pendant ce déploiement.
  • Les Deployments : Les stratégies de mise à jour et de rollback derrière la commande utilisée.
  • Les Services : ClusterIP, NodePort et LoadBalancer, pour choisir la bonne exposition.
  • Les ConfigMaps : La sortie des paramètres hors de l'image, étape suivante de toute application.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn