Chapitre 6 : Workloads spécialisés¶
!!! abstract "Objectifs du chapitre" - Choisir le type de workload adapté : Deployment, StatefulSet, Job, CronJob ou DaemonSet - Exécuter une tâche ponctuelle avec un Job et une tâche planifiée avec un CronJob - Déployer un Pod sur chaque nœud avec un DaemonSet - Décrire et utiliser les patterns multi-conteneurs : init container, sidecar, ambassador - Acquis d'apprentissage visé : AA2
1. Quel workload pour quel besoin ?¶
Jusqu'ici, vous avez déployé des applications qui tournent en permanence. D'autres besoins existent : une tâche qui doit se terminer, un traitement planifié, un agent à installer sur chaque machine. Kubernetes propose un objet pour chaque cas.
| Besoin | Objet | Comportement |
|---|---|---|
| Application sans état, toujours disponible | Deployment | N réplicas interchangeables, relancés en continu |
| Application avec état (base de données) | StatefulSet | Pods à identité stable, un volume par Pod |
| Tâche qui doit se terminer (une fois) | Job | Exécute jusqu'au succès, puis s'arrête |
| Tâche répétée à heure fixe | CronJob | Crée un Job selon un calendrier |
| Un Pod par nœud | DaemonSet | Un Pod sur chaque nœud, y compris les nouveaux |
2. Job : une tâche qui se termine¶
Un Job crée un ou plusieurs Pods et attend qu'ils se terminent avec succès. Contrairement à un Deployment, un Pod terminé n'est pas relancé : le travail est fini.
apiVersion: batch/v1
kind: Job
metadata:
name: calcul
spec:
backoffLimit: 3 # nombre de tentatives avant échec
ttlSecondsAfterFinished: 300 # suppression automatique 5 min après la fin
template:
spec:
restartPolicy: Never # Never ou OnFailure (jamais Always)
containers:
- name: calcul
image: busybox:1.36
command: ["sh", "-c", "echo début; sleep 5; echo fin"]
| Champ | Rôle | Valeur par défaut |
|---|---|---|
completions |
Nombre de Pods à terminer avec succès | 1 |
parallelism |
Nombre de Pods exécutés en même temps | 1 |
backoffLimit |
Nombre de nouvelles tentatives en cas d'échec | 6 |
ttlSecondsAfterFinished |
Délai avant suppression automatique du Job terminé | (aucun : le Job reste) |
Le restartPolicy d'un Job est Never (un nouveau Pod est créé après un échec) ou OnFailure (le conteneur est relancé dans le même Pod). Un Pod terminé avec succès affiche le statut Completed ; il reste visible (avec ses logs) tant que le Job existe.
3. CronJob : une tâche planifiée¶
Un CronJob crée un Job à chaque échéance d'un calendrier, écrit dans la syntaxe cron.
flowchart LR
CJ["CronJob<br/>*/5 * * * *"] -- "toutes les 5 min" --> J1["Job<br/>sauvegarde-001"]
CJ --> J2["Job<br/>sauvegarde-002"]
J1 --> P1["Pod<br/>Completed"]
J2 --> P2["Pod<br/>Completed"]
La syntaxe schedule comporte cinq champs :
| Minute | Heure | Jour du mois | Mois | Jour de la semaine |
|---|---|---|---|---|
| 0-59 | 0-23 | 1-31 | 1-12 | 0-6 (0 = dimanche) |
| Expression | Signification |
|---|---|
*/5 * * * * |
Toutes les 5 minutes |
0 2 * * * |
Tous les jours à 02:00 |
0 3 * * 0 |
Tous les dimanches à 03:00 |
apiVersion: batch/v1
kind: CronJob
metadata:
name: sauvegarde
spec:
schedule: "*/5 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: sauvegarde
image: busybox:1.36
command: ["sh", "-c", "date; echo sauvegarde effectuée"]
| Champ | Rôle |
|---|---|
concurrencyPolicy |
Si l'exécution précédente n'est pas finie : Allow (cumuler, défaut), Forbid (sauter la nouvelle), Replace (remplacer l'ancienne) |
successfulJobsHistoryLimit |
Nombre de Jobs réussis conservés (3 par défaut) |
failedJobsHistoryLimit |
Nombre de Jobs échoués conservés (1 par défaut) |
suspend |
true pour suspendre le calendrier sans supprimer le CronJob |
Deux points pratiques :
- l'heure du calendrier est, par défaut, celle du cluster (en général UTC) ; le champ
timeZonepermet d'en choisir une autre ; - pour tester sans attendre l'échéance, déclenchez un Job manuellement :
Un cas d'usage classique est la sauvegarde planifiée d'une base de données, que vous mettrez en place au Mini-projet 2.
4. DaemonSet : un Pod par nœud¶
Un DaemonSet garantit qu'un Pod s'exécute sur chaque nœud du cluster. Quand un nœud rejoint le cluster, le Pod y est créé automatiquement ; quand il le quitte, le Pod disparaît.
flowchart LR
DS["DaemonSet<br/>agent"] --> A["Pod agent<br/>k3s-server"]
DS --> B["Pod agent<br/>k3s-agent1"]
DS --> C["Pod agent<br/>k3s-agent2"]
On l'utilise pour des services liés à la machine : collecte de logs, agents de supervision, composants réseau. Dans k3s, ServiceLB (qui expose les Services de type LoadBalancer) repose sur des DaemonSets : vous avez vu ses Pods svclb-... au chapitre 3.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: agent
spec:
selector:
matchLabels:
app: agent
template:
metadata:
labels:
app: agent
spec:
containers:
- name: agent
image: busybox:1.36
command:
["sh", "-c", "while true; do echo agent actif; sleep 30; done"]
Il n'y a pas de champ replicas : le nombre de Pods dépend du nombre de nœuds. Le placement sur certains nœuds seulement relève du chapitre 10.
5. Patterns multi-conteneurs¶
Les conteneurs d'un même Pod partagent le réseau (ils se joignent par localhost) et peuvent partager des volumes. On en tire des patterns classiques :
| Pattern | Principe | Exemple |
|---|---|---|
| Init container | S'exécute avant les conteneurs de l'application, séquentiellement, et doit se terminer avec succès | Attendre qu'une base soit disponible, préparer des fichiers |
| Sidecar | S'exécute à côté de l'application, pendant toute sa vie | Collecteur de logs, proxy, synchronisation de fichiers |
| Ambassador | Un conteneur-proxy représente un service externe pour l'application, qui l'appelle sur localhost |
Proxy vers une base ou un service distant |
flowchart LR
subgraph POD["Pod"]
I["Init container<br/>(se termine)"] --> APP["Conteneur<br/>application"]
APP <-- "volume partagé<br/>ou localhost" --> SC["Sidecar /<br/>Ambassador"]
end
SC -- "trafic ou logs" --> EXT["Service externe<br/>ou collecte"]
Init container¶
Les initContainers démarrent un par un, dans l'ordre. Les conteneurs de l'application ne démarrent qu'après leur succès. Tant que l'initialisation n'est pas terminée, le Pod affiche Init:0/1.
spec:
initContainers:
- name: attendre-db
image: busybox:1.36
command:
[
"sh",
"-c",
"until nc -z db 5432; do echo attente de la base; sleep 2; done",
]
containers:
- name: api
image: monimage:1.0.0
Sidecar avec volume partagé¶
spec:
containers:
- name: app
image: busybox:1.36
command:
["sh", "-c", "while true; do date >> /logs/app.log; sleep 5; done"]
volumeMounts:
- name: logs
mountPath: /logs
- name: lecteur-logs # sidecar
image: busybox:1.36
command: ["sh", "-c", "tail -F /logs/app.log"]
volumeMounts:
- name: logs
mountPath: /logs
volumes:
- name: logs
emptyDir: {}
Avec plusieurs conteneurs, il faut préciser lequel on cible : kubectl logs <pod> -c <conteneur> et kubectl exec <pod> -c <conteneur> -- .... Pour un init container : kubectl logs <pod> -c attendre-db.
Sidecars natifs
Selon la version de Kubernetes, un mode dédié de sidecar existe (un init container avec restartPolicy: Always). Il n'est pas utilisé dans ce cours : les sidecars y sont de simples conteneurs du Pod.
!!! tip "À retenir" - Job : tâche qui se termine (restartPolicy à Never ou OnFailure). CronJob : crée des Jobs selon un calendrier cron. - Test immédiat d'un CronJob : kubectl create job --from=cronjob/<nom> <job>. - DaemonSet : un Pod par nœud, sans replicas. - Les conteneurs d'un Pod partagent réseau (localhost) et volumes. - Init container : prérequis avant l'application. Sidecar : assistant permanent. Ambassador : proxy vers l'extérieur. - Avec plusieurs conteneurs, on cible avec -c dans logs et exec.
Pour vérifier votre compréhension¶
Pourquoi un Job n'utilise-t-il pas restartPolicy: Always ?
Always relancerait indéfiniment le conteneur, même après un succès. Un Job doit se terminer : on utilise Never ou OnFailure.
Que signifie */10 * * * *, et que signifie 30 1 * * 1 ?
Toutes les 10 minutes ; tous les lundis à 01:30.
Comment tester un CronJob sans attendre son échéance ?
En créant un Job à partir de lui : kubectl create job --from=cronjob/<nom> <nom-du-job>.
Quelle différence entre un Deployment à 3 réplicas et un DaemonSet sur un cluster de 3 nœuds ?
Le Deployment place 3 Pods où le scheduler le décide (éventuellement plusieurs sur un même nœud). Le DaemonSet place exactement un Pod par nœud, et s'adapte quand le nombre de nœuds change.
Un Pod affiche Init:0/1 depuis plusieurs minutes. Où chercher la cause ?
Dans les logs de l'init container (kubectl logs <pod> -c <init>) et dans kubectl describe pod. L'init container n'a pas terminé avec succès, par exemple parce que la base qu'il attend n'est pas joignable.
Quel pattern choisir pour exporter les logs d'une application qui écrit dans un fichier ?
Un sidecar qui lit le fichier dans un volume partagé (emptyDir) et l'envoie vers la collecte ou l'affiche sur sa sortie standard.