Déployer des applications sur Kubernetes

KubernetesBeginner
Pratiquer maintenant

Introduction

Dans la première section du cours, vous avez exploré un cluster Kubernetes sans le modifier. Vous avez appris que kubectl envoie des requêtes au serveur d’API et que les contrôleurs Kubernetes comparent en permanence l’état souhaité à l’état réel.

Vous allez maintenant effectuer vos premières requêtes concernant une application. Plutôt que d’indiquer à Kubernetes chaque opération élémentaire à exécuter, vous allez décrire le résultat souhaité dans des fichiers YAML appelés manifestes. Kubernetes enregistre ces définitions d’objets et s’efforce de les mettre en œuvre.

Vous commencerez par un seul Pod afin d’observer clairement la structure de base d’un manifeste. Vous définirez ensuite un Deployment qui gère deux Pods. La comparaison entre le Pod autonome et les Pods gérés par le Deployment vous montrera pourquoi les contrôleurs de niveau supérieur sont généralement privilégiés pour les applications.

Cet atelier se concentre volontairement sur la création de charges de travail. Vous apprendrez à exposer les applications via des Services plus tard, lorsque les notions de Pods, de labels et de Deployments vous seront familières.

Comprendre les objets déclaratifs Kubernetes

Démarrage de l’environnement : Ce lab démarre pour vous un cluster Kubernetes complet. La configuration du plan de contrôle, du nœud et des composants réseau prend généralement 2 à 3 minutes. Veuillez patienter jusqu’à la fin du chargement de l’environnement avant de commencer.

Dans cette étape, vous allez relier la notion d’état souhaité abordée dans l’atelier précédent aux manifestes Kubernetes et préparer un espace de travail pour vos premières définitions d’applications.

Des commandes à l’état souhaité

Kubernetes prend en charge deux grandes approches de gestion :

  • Avec une commande impérative, vous demandez directement une action, par exemple « créer un Pod nommé first-nginx ».
  • Avec un manifeste déclaratif, vous enregistrez la configuration souhaitée de l’objet dans un fichier et demandez à Kubernetes d’aligner le cluster sur cette configuration.

Les fichiers déclaratifs sont précieux, car vous pouvez les lire avant d’appliquer une modification, les réutiliser, examiner les différences et les conserver dans un système de gestion de versions. Ce cours privilégiera cette approche déclarative.

Chaque objet Kubernetes renvoyé par l’API possède plusieurs champs de premier niveau importants :

  • apiVersion sélectionne le groupe et la version de l’API Kubernetes.
  • kind identifie le type d’objet, comme Pod ou Deployment.
  • metadata décrit l’identité de l’objet, notamment son nom et ses labels.
  • spec décrit l’état souhaité de l’objet.
  • status indique l’état observé. Il est normalement renseigné par Kubernetes après la création, et non écrit dans votre manifeste.

Cela forme une boucle essentielle :

manifest spec -> API server stores desired state -> controllers act -> object status reports actual state

Préparer le répertoire des manifestes

Accédez au répertoire préparé pour cet atelier :

cd /home/labex/project/k8s-manifests

Vérifiez votre emplacement avec pwd, qui signifie print working directory :

pwd
/home/labex/project/k8s-manifests

Créez un court fichier de notes contenant les quatre champs de manifeste que vous utiliserez. La commande printf écrit chaque chaîne entre guillemets sur une ligne distincte, et > redirige cette sortie vers un fichier en le remplaçant s’il existe déjà.

printf '%s\n' apiVersion kind metadata spec > manifest-fields.txt

Affichez le fichier pour le vérifier :

cat manifest-fields.txt
apiVersion
kind
metadata
spec

Ce fichier de notes constitue un petit point de contrôle pédagogique : ces quatre champs apparaîtront dans les deux manifestes que vous allez créer.

Rédiger et valider un manifeste de Pod

Dans cette étape, vous allez rédiger un manifeste YAML pour un Pod et en valider la structure avant de l’envoyer au cluster.

Découvrir le Pod

Un Pod est l’objet Kubernetes déployable le plus petit. Il fournit à un ou plusieurs conteneurs étroitement liés une identité réseau et un contexte de stockage partagés. Dans la plupart des exemples destinés aux débutants, chaque Pod contient un seul conteneur.

Le Pod de cet atelier exécutera NGINX, un petit serveur web. L’image est figée sur nginx:1.27-alpine. Fixer une version rend le résultat plus reproductible que l’utilisation du tag évolutif latest. L’image a déjà été mise en cache dans le cluster afin que votre travail ne dépende pas d’un téléchargement depuis Internet.

Créer le fichier YAML

Assurez-vous d’être dans le répertoire des manifestes :

cd /home/labex/project/k8s-manifests

Vous allez utiliser un here-document pour créer le fichier. Le shell redirige chaque ligne située entre <<'EOF' et le EOF final vers first-pod.yaml. Les guillemets autour du premier EOF empêchent le shell d’interpréter les caractères spéciaux présents dans le YAML.

cat <<'EOF' > first-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: first-nginx
  namespace: default
  labels:
    app: first-nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.27-alpine
      imagePullPolicy: IfNotPresent
      ports:
        - name: http
          containerPort: 80
          protocol: TCP
EOF

Le YAML représente la hiérarchie à l’aide de l’indentation. Utilisez systématiquement des espaces ; les tabulations peuvent rendre le YAML invalide. Un tiret, comme dans - name: nginx, commence un élément de liste.

Lisez l’objet de haut en bas :

  • apiVersion: v1 sélectionne l’API principale utilisée par les Pods.
  • kind: Pod déclare le type de ressource.
  • metadata.name attribue au Pod le nom stable first-nginx.
  • metadata.namespace: default le place dans l’espace de noms applicatif standard du cours plutôt que dans un espace de noms système.
  • metadata.labels ajoute app=first-nginx, ce qui permettra ultérieurement de sélectionner ce Pod.
  • spec.containers est la liste des conteneurs que le Pod doit exécuter.
  • imagePullPolicy: IfNotPresent utilise l’image mise en cache lorsqu’elle est disponible.
  • Le port nommé http utilise containerPort: 80 et protocol: TCP. Il indique où NGINX écoute à l’intérieur du conteneur ; il n’expose pas le Pod à l’extérieur du cluster.

Valider avant de créer

Utilisez une simulation côté client pour analyser le fichier sans créer le Pod. L’option -f signifie file, et --dry-run=client conserve la requête localement :

kubectl apply --dry-run=client -f first-pod.yaml
pod/first-nginx created (dry run)

Les mots dry run sont essentiels : la syntaxe est valide, mais le cluster n’a pas été modifié.

Demandez à kubectl d’afficher l’objet normalisé au format YAML :

L’option de sortie -o signifie output format. En fournissant yaml, vous demandez à kubectl de restituer l’objet analysé au format YAML au lieu d’afficher uniquement un résultat sur une ligne :

kubectl apply --dry-run=client -f first-pod.yaml -o yaml

Vous verrez vos champs ainsi que des valeurs par défaut ajoutées par le client. Cette méthode est utile pour détecter les erreurs d’indentation, de nom de champ ou de type avant une véritable application.

Créer et examiner votre premier Pod

Dans cette étape, vous allez appliquer le manifeste validé, observer Kubernetes faire évoluer le Pod vers son état souhaité et examiner l’objet obtenu.

Appliquer le manifeste

Accédez au répertoire des manifestes si nécessaire :

cd /home/labex/project/k8s-manifests

Appliquez le fichier sans l’option de simulation :

kubectl apply -f first-pod.yaml
pod/first-nginx created

kubectl apply envoie l’objet au serveur d’API. Le serveur d’API enregistre la spécification souhaitée du Pod, tandis que le planificateur et le kubelet coopèrent pour l’exécuter sur le nœud.

Attendre que le Pod soit prêt

La création d’un Pod est asynchrone : kubectl apply peut renvoyer un résultat avant que le conteneur soit prêt. Utilisez kubectl wait pour attendre la condition Ready du Pod. La commande se termine avec succès lorsque la condition devient vraie, ou échoue après 60 secondes :

kubectl wait --for=condition=Ready pod/first-nginx --timeout=60s
pod/first-nginx condition met

Répertoriez maintenant le Pod. Cet atelier répète -o wide afin que chaque atelier puisse être utilisé indépendamment : -o sélectionne un format de sortie et wide ajoute des informations telles que l’adresse IP du Pod et le nom du nœud :

kubectl get pod first-nginx -o wide
NAME          READY   STATUS    RESTARTS   AGE   IP           NODE
first-nginx   1/1     Running   ...        ...   ...          labex-v135

READY=1/1 signifie que l’unique conteneur est prêt, tandis que STATUS=Running correspond à la phase du Pod. L’affichage détaillé indique également l’adresse IP du Pod et le nœud qui lui a été attribué. Les adresses IP et les âges des Pods sont générés automatiquement ; ils peuvent donc différer.

Examiner les labels et la propriété

Affichez les labels du Pod :

kubectl get pod first-nginx --show-labels

Recherchez app=first-nginx. Les labels sont enregistrés avec l’objet et deviendront importants lorsque les Deployments et les Services sélectionneront des Pods.

Demandez à Kubernetes si un autre objet contrôle ce Pod. -o jsonpath='...' extrait certains champs au lieu d’afficher l’objet complet. L’expression parcourt metadata.ownerReferences ; {"\n"} ajoute un retour à la ligne final afin que l’invite du shell apparaisse sur la ligne suivante :

kubectl get pod first-nginx -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}{"\n"}'
Owner:

Le propriétaire est vide, car vous avez créé directement ce Pod autonome. S’il est supprimé, aucun contrôleur de niveau supérieur ne saura qu’il doit être recréé. Vous comparerez ce comportement avec celui des Pods gérés dans les étapes suivantes.

Point de contrôle

Vous avez transformé un fichier local décrivant l’état souhaité en un objet Kubernetes en cours d’exécution. L’API a accepté le Pod, le planificateur lui a attribué un nœud et le kubelet a rendu son conteneur opérationnel. Le Pod existe, mais aucun contrôleur ne gère son cycle de vie.

Définir un Deployment

Dans cette étape, vous allez définir un Deployment demandant à Kubernetes de maintenir deux copies d’un Pod NGINX.

Pourquoi utiliser un Deployment ?

Un Pod autonome est utile pour l’apprentissage, mais les applications ont généralement besoin d’un contrôleur. Un Deployment définit un nombre souhaité de répliques et un modèle de Pod. Il crée un ReplicaSet, qui maintient ensuite le nombre de Pods demandé.

La chaîne de propriété est la suivante :

Deployment -> ReplicaSet -> Pods -> containers

Si un Pod géré disparaît, le ReplicaSet constate que le nombre réel de répliques est inférieur au nombre souhaité et crée un remplacement. Les prochains ateliers utiliseront les Deployments pour mettre à l’échelle les applications et effectuer des mises à jour progressives.

Créer le manifeste du Deployment

Revenez au répertoire des manifestes :

cd /home/labex/project/k8s-manifests

Créez course-web-deployment.yaml à l’aide d’un here-document :

cat <<'EOF' > course-web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: course-web
  labels:
    app: course-web
spec:
  replicas: 2
  selector:
    matchLabels:
      app: course-web
  template:
    metadata:
      labels:
        app: course-web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27-alpine
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
EOF

Le Deployment utilise apps/v1, l’API stable des Deployments. Sa section spec introduit trois champs importants :

  • replicas: 2 indique le nombre souhaité de Pods.
  • selector.matchLabels identifie les Pods gérés par le Deployment.
  • template constitue le modèle utilisé pour créer chaque Pod.

Le sélecteur et template.metadata.labels utilisent tous deux app: course-web. Ils doivent correspondre ; sinon, le Deployment ne pourrait pas identifier les Pods créés à partir de son propre modèle.

Valider le Deployment

Analysez le manifeste sans modifier le cluster :

kubectl apply --dry-run=client -f course-web-deployment.yaml
deployment.apps/course-web created (dry run)

Utilisez kubectl diff pour comparer le manifeste avec l’état actuel du cluster. Un code de sortie différent de zéro signifie simplement que l’objet serait modifié ; || true empêche le shell de considérer cette différence attendue comme une erreur :

kubectl diff -f course-web-deployment.yaml || true

Comme course-web n’existe pas encore, la sortie affiche l’objet complet comme un ajout, avec des lignes commençant par +. Contrairement à apply, diff ne modifie pas le cluster.

Déployer et examiner l’application gérée

Dans cette étape, vous allez appliquer le Deployment, attendre ses deux répliques et examiner les ressources associées en les sélectionnant à l’aide de leur label commun.

Appliquer le Deployment et attendre sa disponibilité

Commencez par accéder au répertoire contenant le manifeste. Appliquez ensuite l’état souhaité enregistré ; -f indique à kubectl de lire ce fichier :

cd /home/labex/project/k8s-manifests
kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web created

Attendez la fin du déploiement progressif. Il s’agit du processus qui amène les Pods du Deployment à respecter le modèle et le nombre de répliques souhaités :

kubectl rollout status deployment/course-web --timeout=60s
deployment "course-web" successfully rolled out

Répertoriez le Deployment :

kubectl get deployment course-web
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
course-web   2/2     2            2           ...

READY=2/2 signifie que les deux répliques souhaitées sont prêtes. UP-TO-DATE=2 signifie qu’elles utilisent toutes deux le modèle de Pod actuel, et AVAILABLE=2 indique qu’elles sont toutes deux disponibles.

Examiner les ressources associées

Utilisez le sélecteur de labels -l app=course-web pour répertorier les ressources associées :

kubectl get deployment,replicaset,pods -l app=course-web

La sortie contient un Deployment, un ReplicaSet et deux Pods. Les suffixes générés du ReplicaSet et des Pods varient :

NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/course-web   2/2     2            2           ...

NAME                                    DESIRED   CURRENT   READY   AGE
replicaset.apps/course-web-...          2         2         2       ...

NAME                              READY   STATUS    RESTARTS   AGE
pod/course-web-...-...            1/1     Running   ...        ...
pod/course-web-...-...            1/1     Running   ...        ...

Cette vue confirme que les deux répliques souhaitées par le Deployment sont devenues deux Pods prêts. À l’étape suivante, vous suivrez les liens de propriété entre ces ressources.

Point de contrôle

Vous avez appliqué un manifeste de Deployment et attendu que l’état souhaité soit atteint. Kubernetes a créé un ReplicaSet et deux Pods, et le label commun app=course-web vous a permis de les répertorier comme un seul groupe applicatif.

Suivre la propriété des contrôleurs

Dans cette étape, vous allez suivre les références de propriété Kubernetes depuis un Pod géré jusqu’à son ReplicaSet, puis jusqu’au Deployment. Vous réappliquerez également le manifeste afin d’observer l’idempotence du mode déclaratif.

Examiner le propriétaire d’un Pod

Enregistrez le nom d’un Pod généré dans une variable du shell. La syntaxe NAME=$(command) correspond à une substitution de commande : le shell exécute la commande et enregistre sa sortie dans NAME. Ici, -l app=course-web sélectionne les Pods correspondants et JSONPath extrait le nom généré du premier Pod :

POD_NAME=$(kubectl get pods -l app=course-web -o jsonpath='{.items[0].metadata.name}')

Affichez-le pour savoir quel Pod a été sélectionné :

echo "$POD_NAME"

Examinez maintenant son propriétaire direct. Les guillemets autour de "$POD_NAME" transmettent le nom enregistré comme un seul argument sûr pour la commande :

kubectl get pod "$POD_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: ReplicaSet/course-web-...

Contrairement au Pod autonome first-nginx, un Pod géré par un Deployment possède un ReplicaSet comme propriétaire. Le ReplicaSet est lui-même détenu par le Deployment.

Affichez le propriétaire du ReplicaSet. La première commande reprend le même modèle de substitution, mais en enregistrant cette fois le nom d’un ReplicaSet dans RS_NAME :

RS_NAME=$(kubectl get replicaset -l app=course-web -o jsonpath='{.items[0].metadata.name}')
kubectl get replicaset "$RS_NAME" -o jsonpath='Owner: {.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}{"\n"}'
Owner: Deployment/course-web

Réappliquer l’état souhaité

Appliquez à nouveau le même manifeste :

kubectl apply -f course-web-deployment.yaml
deployment.apps/course-web unchanged

unchanged illustre une propriété importante du mode déclaratif : réappliquer plusieurs fois le même état souhaité est sans danger. Kubernetes n’a besoin d’intervenir que lorsque la configuration souhaitée et la configuration réelle diffèrent.

Point de contrôle

Vous disposez maintenant d’un Pod autonome et d’une application gérée par un Deployment. Tous deux exécutent des conteneurs, mais le Deployment ajoute une hiérarchie de contrôleurs qui maintient deux répliques et fournit une base pour les futures mises à l’échelle et mises à jour progressives.

Résumé

Vous êtes passé de l’exploration en lecture seule du cluster à la gestion déclarative d’une application. Vous avez appris le rôle de apiVersion, kind, metadata et spec, validé des manifestes avec des simulations côté client, créé et examiné un Pod autonome, puis déployé deux répliques gérées par un Deployment.

Plus important encore, vous avez observé la chaîne de propriété des contrôleurs, du Deployment au ReplicaSet, puis aux Pods. Cette base fondée sur l’état souhaité vous permettra ensuite de diagnostiquer des applications, de les exposer via des Services, de mettre les répliques à l’échelle et d’effectuer des mises à jour progressives.