Workloads

Comprendre les Pods, le plus petit objet déployable sur Kubernetes, et les abstractions de haut niveaux vous permettant de les lancer.

Un workload (charge de travail) est une application fonctionnant sur Kubernetes. Que votre workload soit un composant unique ou un agrégat de composants, sur Kubernetes celui-ci fonctionnera dans une série de pods. Dans Kubernetes, un Pod represente un ensemble de conteneur (containers) en fonctionnement sur votre cluster.

Les pods Kubernetes ont un cycle de vie définit (defined lifecycle). Par exemple, quand un pod est en fonction sur votre cluster et qu’une panne critique survient sur le noeud (node) où se situe ce pod, tous les pods du noeud seront en échec. Kubernetes traite ce niveau d’échec comme un état final : Vous devez créer un nouveau Pod pour retrouver l’état initial même si le noeud redevient sain.

Cependant, pour vous simplifier la vie, vous n’avez pas a gérer chaque Pod directement. Vous pouvez utiliser une ressource workload qui gère votre groupe de pods à votre place. Ces ressources configurent des controleurs (controllers) qui s’assurent que le bon nombre et le bon type de pod soit en fonction pour égaler l’état que vous avez spécifié.

Kubernetes fournit plusieurs ressources workload pré-faites :

Dans l’écosystème étendu de Kubernetes, vous pouvez trouver des ressources workload de fournisseurs tiers qui offrent des fonctionnalités supplémentaires. L’utilisation d’un CustomResourceDefinition permet d’ajouter une ressource workload d’un fournisseur tiers si vous souhaitez rajouter une fonctionnalité ou un comportement spécifique qui ne fait pas partie du noyau de Kubernetes. Par exemple, si vous voulez lancer un groupe de Pods pour votre application mais que vous devez arrêter leurs fonctionnement tant qu’ils ne sont pas tous disponibles, alors vous pouvez implémenter ou installer une extension qui permet cette fonctionnalité.

A suivre

Vous pouvez continuer la lecture des ressources, vous pouvez aussi apprendre à connaitre les taches qui leurs sont liées :

Pour en apprendre plus sur les méchanismes de Kubernetes, de séparation du code et de la configuration, allez voir Configuration.

Il y a deux concepts supportés qui fournissent un contexte sur le sujet : comment Kubernetes gère les pods pour les applications :

Une fois que votre application est lancée, vous souhaitez peut etre la rendre disponible sur internet comme un Service ou comme une application web uniquement en utilsant un Ingress.

1 - Pods

1.1 - Aperçu du Pod

Pod Concept Kubernetes

Cette page fournit un aperçu du Pod, l'objet déployable le plus petit dans le modèle d'objets Kubernetes.

Comprendre les Pods

Un Pod est l'unité d'exécution de base d'une application Kubernetes--l'unité la plus petite et la plus simple dans le modèle d'objets de Kubernetes--que vous créez ou déployez. Un Pod représente des process en cours d'exécution dans votre cluster.

Un Pod encapsule un conteneur applicatif (ou, dans certains cas, plusieurs conteneurs), des ressources de stockage, une identité réseau (adresse IP) unique, ainsi que des options qui contrôlent comment le ou les conteneurs doivent s'exécuter. Un Pod représente une unité de déploiement : une instance unique d'une application dans Kubernetes, qui peut consister soit en un unique container soit en un petit nombre de conteneurs qui sont étroitement liés et qui partagent des ressources.

Docker est le runtime de conteneurs le plus courant utilisé dans un Pod Kubernetes, mais les Pods prennent également en charge d'autres runtimes de conteneurs.

Les Pods dans un cluster Kubernetes peuvent être utilisés de deux manières différentes :

Chaque Pod est destiné à exécuter une instance unique d'une application donnée. Si vous désirez mettre à l'échelle votre application horizontalement, (pour fournir plus de ressources au global en exécutant plus d'instances), vous devez utiliser plusieurs Pods, un pour chaque instance. Dans Kubernetes, on parle typiquement de réplication. Des Pods répliqués sont en général créés et gérés en tant que groupe par une ressource de charge de travail et son _contrôleur_. Voir Pods et contrôleurs pour plus d'informations.

Comment les Pods gèrent plusieurs conteneurs

Les Pods sont conçus pour supporter plusieurs process coopérants (sous forme de conteneurs) qui forment une unité de service cohésive. Les conteneurs d'un même Pod sont automatiquement co-localisés et co-programmés sur la même machine physique ou virtuelle dans le cluster. Ces conteneurs peuvent partager des ressources et dépendances, communiquer entre eux, et coordonner quand et comment ils sont arrêtés.

Notez que grouper plusieurs conteneurs co-localisés et co-gérés dans un unique Pod est un cas d'utilisation relativement avancé. Vous devez utiliser ce pattern seulement dans des instances spécifiques dans lesquelles vos conteneurs sont étroitement liés. Par exemple, vous pourriez avoir un conteneur qui agit comme un serveur web pour des fichiers contenus dans un volume partagé, et un conteneur "sidecar" séparé qui met à jour ces fichiers depuis une source externe, comme dans le diagramme suivant :

example pod diagram

Certains Pods ont des init containers en plus d'app containers. Les Init containers s'exécutent et terminent avant que les conteneurs d'application soient démarrés.

Les Pods fournissent deux types de ressources partagées pour leurs conteneurs : réseau et stockage.

Réseau

Chaque Pod se voit assigner une adresse IP unique pour chaque famille d'adresses. Tous les conteneurs d'un Pod partagent le même namespace réseau, y compris l'adresse IP et les ports réseau. Les conteneurs à l'intérieur d'un Pod peuvent communiquer entre eux en utilisant localhost. Lorsque les conteneurs dans un Pod communiquent avec des entités en dehors du Pod, ils doivent coordonner comment ils utilisent les ressources réseau partagées (comme les ports).

Stockage

Un Pod peut spécifier un jeu de volumes de stockage partagés. Tous les conteneurs dans le Pod peuvent accéder aux volumes partagés, permettant à ces conteneurs de partager des données. Les volumes permettent aussi les données persistantes d'un Pod de survivre au cas où un des conteneurs doit être redémarré. Voir Volumes pour plus d'informations sur la façon dont Kubernetes implémente le stockage partagé dans un Pod.

Travailler avec des Pods

Vous aurez rarement à créer directement des Pods individuels dans Kubernetes--même des Pods à un seul conteneur. Ceci est dû au fait que les Pods sont conçus comme des entités relativement éphémères et jetables. Lorsqu'un Pod est créé (directement par vous ou indirectement par un _contrôleur_), il est programmé pour s'exécuter sur un dans votre cluster. Le Pod reste sur ce nœud jusqu'à ce que le process se termine, l'objet pod soit supprimé, le pod soit expulsé par manque de ressources, ou le nœud soit en échec.

Note:

Redémarrer un conteneur dans un Pod ne doit pas être confondu avec redémarrer un Pod. Un Pod n'est pas un process, mais un environnement pour exécuter un conteneur. Un Pod persiste jusqu'à ce qu'il soit supprimé.

Les Pods ne se guérissent pas par eux-mêmes. Si un Pod est programmé sur un Nœud qui échoue, ou si l'opération de programmation elle-même échoue, le Pod est supprimé ; de plus, un Pod ne survivra pas à une expulsion due à un manque de ressources ou une mise en maintenance du Nœud. Kubernetes utilise une abstraction de plus haut niveau, appelée un contrôleur, qui s'occupe de gérer les instances de Pods relativement jetables. Ainsi, même s'il est possible d'utiliser des Pods directement, il est beaucoup plus courant dans Kubernetes de gérer vos Pods en utilisant un contrôleur.

Pods et contrôleurs

Vous pouvez utiliser des ressources de charges de travail pour créer et gérer plusieurs Pods pour vous. Un contrôleur pour la ressource gère la réplication, le plan de déploiement et la guérison automatique en cas de problèmes du Pod. Par exemple, si un noeud est en échec, un contrôleur note que les Pods de ce noeud ont arrêté de fonctionner et créent des Pods pour les remplacer. L'ordonnanceur place le Pod de remplacement sur un noeud en fonctionnement.

Voici quelques exemples de ressources de charges de travail qui gèrent un ou plusieurs Pods :

Templates de Pod

Les Templates de Pod sont des spécifications pour créer des Pods, et sont inclus dans les ressources de charges de travail comme les Deployments, les Jobs et les DaemonSets.

Chaque contrôleur pour une ressource de charges de travail utilise le template de pod à l'intérieur de l'objet pour créer les Pods. Le template de pod fait partie de l'état désiré de la ressource de charges de travail que vous avez utilisé pour exécuter votre application.

L'exemple ci-dessous est un manifest pour un Job simple avec un template qui démarre un conteneur. Le conteneur dans ce Pod affiche un message puis se met en pause.

apiVersion: batch/v1
kind: Job
metadata:
  name: hello
spec:
  template:
    # Ceci est un template de pod
    spec:
      containers:
      - name: hello
        image: busybox
        command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
      restartPolicy: OnFailure
    # Le template de pod se termine ici

Modifier le template de pod ou changer pour un nouvau template de pod n'a pas d'effet sur les pods déjà existants. Les Pods ne reçoivent pas une mise à jour du template directement ; au lieu de cela, un nouveau Pod est créé pour correspondre au nouveau template de pod.

Par exemple, un contrôleur de Deployment s'assure que les Pods en cours d'exécution correspondent au template de pod en cours. Si le template est mis à jour, le contrôleur doit supprimer les pods existants et créer de nouveaux Pods avec le nouveau template. Chaque contrôleur de charges de travail implémente ses propres règles pour gérer les changements du template de Pod.

Sur les noeuds, le kubelet n'observe ou ne gère pas directement les détails concernant les templates de pods et leurs mises à jours ; ces détails sont abstraits. Cette abstraction et cette séparation des préoccupations simplifie la sémantique du système, et rend possible l'extension du comportement du cluster sans changer le code existant.

A suivre

1.2 - Pods

Les Pods sont les plus petites unités informatiques déployables qui peuvent être créées et gérées dans Kubernetes.

Qu'est-ce qu'un pod ?

Un pod (terme anglo-saxon décrivant un groupe de baleines ou une gousse de pois) est un groupe d'un ou plusieurs conteneurs (comme des conteneurs Docker), ayant du stockage/réseau partagé, et une spécification sur la manière d'exécuter ces conteneurs. Les éléments d'un pod sont toujours co-localisés et co-ordonnancés, et s'exécutent dans un contexte partagé. Un pod modélise un "hôte logique" spécifique à une application - il contient un ou plusieurs conteneurs applicatifs qui sont étroitement liés — dans un monde pré-conteneurs, être exécuté sur la même machine physique ou virtuelle signifierait être exécuté sur le même hôte logique.

Bien que Kubernetes prenne en charge d'autres runtimes de conteneurs que Docker, Docker est le runtime le plus connu, et cela aide à décrire des pods en termes Docker.

Le contexte partagé d'un pod est un ensemble de namespaces Linux, cgroups, et potentiellement d'autres facettes d'isolation - les mêmes choses qui isolent un conteneur Docker. Dans le contexte d'un pod, les applications individuelles peuvent se voir appliquer d'autres sous-isolations.

Les conteneurs d'un pod partagent une adresse IP et un espace de ports, et peuvent communiquer via localhost. Ils peuvent aussi communiquer entre eux en utilisant des communications inter-process standard comme les sémaphores SystemV ou la mémoire partagée POSIX. Les conteneurs appartenant à des pods distincts ont des adresses IP distinctes et ne peuvent pas communiquer par IPC sans configuration spécifique. Ces conteneurs communiquent en général entre eux via les adresses IP de leurs pods.

Les applications à l'intérieur d'un pod ont aussi accès à des volumes partagés, qui sont définis dans le cadre d'un pod et sont mis à disposition pour être montés dans le système de fichiers de chaque application.

En terme de concepts Docker, un pod est modélisé par un groupe de conteneurs Docker ayant des namespaces et des volumes partagés.

Tout comme des conteneurs applicatifs individuels, les pods sont considérés comme des entités relativement éphémères (plutôt que durables). Comme discuté dans Cycle de vie d'un pod, les pods sont créés, des ID uniques (UID) leurs sont assignés, et ils sont ordonnancés sur des nœuds où il restent jusqu'à leur arrêt (selon la politique de redémarrage) ou suppression. Si un nœud meurt, les pods ordonnancés sur ce nœud sont programmés pour être terminés, après un délai d'attente. Un pod donné (défini par un UID) n'est pas "re-ordonnancé" sur un nouveau nœud ; par contre, il peut être remplacé par un pod identique, ayant le même nom si désiré, mais avec un nouvel UID (voir replication controller pour plus de détails).

Lorsque quelque chose, comme un volume, a le même cycle de vie qu'un pod, il existe aussi longtemps que le pod (avec l'UID donné) existe. Si ce pod est supprimé pour une quelconque raison, même si un remplaçant identique est recréé, la chose liée (par ex. le volume) est aussi détruite et créée à nouveau.

pod diagram

Un pod multi-conteneurs contenant un extracteur de fichiers et un serveur web utilisant un volume persistant comme espace de stockage partagé entre les conteneurs.

Intérêts des pods

Gestion

Les pods fournissent une unité de service cohérente afin d'avoir un modèle coopératif entre plusieurs processus. Ils simplifient le déploiement et la gestion d'applications en fournissant une abstraction de plus haut niveau que l'ensemble des applications les constituant. Les pods servent d'unité de déploiement, de mise à l'échelle horizontale, et de réplication. La co-localisation (co-ordonnancement), la fin partagée (par ex. l'arrêt), la réplication coordonnée, le partage de ressources et la gestion des dépendances sont traités automatiquement pour les conteneurs dans un pod.

Partage de ressources et communication

Les pods permettent le partage de ressources et la communication entre ses constituants.

Les applications dans un pod utilisent toutes le même réseau (même adresse IP et espace de ports) et peuvent donc "se trouver" entre elles et communiquer en utilisant localhost. À cause de cela, les applications dans un pod doivent coordonner leurs usages de ports. Chaque pod a une adresse IP dans un réseau plat partagé ayant un accès complet aux autres hôtes et pods à travers le réseau.

Le nom d'hôte est défini avec le nom du pod pour les conteneurs applicatifs à l'intérieur du pod. Plus de détails sur le réseau.

En plus de définir les conteneurs applicatifs s'exécutant dans le pod, le pod spécifie un ensemble de volumes de stockage partagés. Les volumes permettent aux données de survivre aux redémarrages de conteneurs et d'être partagés entre les applications d'un même pod.

Cas d'utilisation de pods

Des pods peuvent être utilisés pour héberger verticalement des piles applicatives intégrées (par ex. LAMP), mais leur principal intérêt est la mise en place de programmes auxiliaires co-localisés et co-gérés, comme :

Des pods individuels ne sont pas destinés à exécuter plusieurs instances de la même application, en général.

Pour une explication plus détaillée, voir The Distributed System ToolKit: Patterns for Composite Containers.

Alternatives envisagées

Pourquoi ne pas simplement exécuter plusieurs programmes dans un unique conteneur (Docker) ?

  1. Transparence. Rendre les conteneurs à l'intérieur du pod visibles par l'infrastucture permet à l'infrastucture de fournir des services à ces conteneurs, comme la gestion des processus et le monitoring des ressources. Ceci apporte un certain nombre de facilités aux utilisateurs.
  2. Découpler les dépendances logicielles. Les conteneurs individuels peuvent être versionnés, reconstruits et redéployés de manière indépendante. Kubernetes pourrait même un jour prendre en charge la mise à jour à chaud de conteneurs individuels.
  3. Facilité d'utilisation. Les utilisateurs n'ont pas besoin d'exécuter leur propre gestionnaire de processus, de se soucier de la propagation de signaux et de codes de sortie, etc.
  4. Efficacité. L'infrastructure prenant plus de responsabilités, les conteneurs peuvent être plus légers.

Pourquoi ne pas prendre en charge le co-ordonnancement de conteneurs basé sur les affinités ?

Cette approche pourrait fournir la co-localisation, mais ne fournirait pas la plupart des bénéfices des pods, comme le partage de ressources, IPC, la garantie d'une fin partagée et une gestion simplifiée.

Durabilité des pods (ou manque de)

Les pods ne doivent pas être considérés comme des entités durables. Ils ne survivent pas à des erreurs d'ordonnancement, à un nœud en échec ou à d'autres expulsions, suite à un manque de ressources ou une mise en maintenance d'un nœud.

En général, les utilisateurs n'ont pas à créer directement des pods. Ils doivent presque toujours utiliser des contrôleurs, même pour des singletons, comme par exemple des Deployments. Les contrôleurs fournissent l'auto-guérison à l'échelle du cluster, ainsi que la réplication et la gestion des déploiements (rollout). Les contrôleurs comme StatefulSet peuvent aussi prendre en charge des pods avec état (stateful).

L'utilisation d'APIs collectives comme principale primitive exposée à l'utilisateur est courante dans les systèmes d'ordonnancement de clusters, comme Borg, Marathon, Aurora, et Tupperware.

Un Pod est exposé en tant que primitive afin de faciliter :

Arrêt de pods

Les pods représentant des processus s'exécutant sur des nœuds d'un cluster, il est important de permettre à ces processus de se terminer proprement lorsqu'ils ne sont plus nécessaires (plutôt que d'être violemment tués avec un signal KILL et n'avoir aucune chance de libérer ses ressources). Les utilisateurs doivent pouvoir demander une suppression et savoir quand les processus se terminent, mais aussi être capable de s'assurer que la suppression est réellement effective. Lorsqu'un utilisateur demande la suppression d'un pod, le système enregistre le délai de grâce prévu avant que le pod puisse être tué de force, et qu'un signal TERM soit envoyé au processus principal de chaque conteneur. Une fois la période de grâce expirée, le signal KILL est envoyé à ces processus, et le pod est alors supprimé de l'API server. Si Kubelet ou le gestionnaire de conteneurs est redémarré lors de l'attente de l'arrêt des processus, l'arrêt sera réessayé avec la période de grâce complète.

Un exemple de déroulement :

  1. Un utilisateur envoie une commande pour supprimer un Pod, avec une période de grâce par défaut (30s)
  2. Le Pod dans l'API server est mis à jour avec le temps au delà duquel le Pod est considéré "mort" ainsi que la période de grâce.
  3. Le Pod est affiché comme "Terminating" dans les listes des commandes client
  4. (en même temps que 3) Lorsque Kubelet voit qu'un Pod a été marqué "Terminating", le temps ayant été mis en 2, il commence le processus de suppression du pod.
    1. Si un des conteneurs du Pod a défini un preStop hook, il est exécuté à l'intérieur du conteneur. Si le preStop hook est toujours en cours d'exécution à la fin de la période de grâce, l'étape 2 est invoquée avec une courte (2 secondes) période de grâce supplémentaire une seule fois. Vous devez modifier terminationGracePeriodSeconds si le hook preStop a besoin de plus de temps pour se terminer.
    2. Le signal TERM est envoyé aux conteneurs. Notez que tous les conteneurs du Pod ne recevront pas le signal TERM en même temps et il peut être nécessaire de définir des preStop hook si l'ordre d'arrêt est important.
  5. (en même temps que 3) Le Pod est supprimé des listes d'endpoints des services, et n'est plus considéré comme faisant partie des pods en cours d'exécution pour les contrôleurs de réplication. Les Pods s'arrêtant lentement ne peuvent pas continuer à servir du trafic, les load balancers (comme le service proxy) les supprimant de leurs rotations.
  6. Lorsque la période de grâce expire, les processus s'exécutant toujours dans le Pod sont tués avec SIGKILL.
  7. Kubelet va supprimer le Pod dans l'API server en indiquant une période de grâce de 0 (suppression immédiate). Le Pod disparaît de l'API et n'est plus visible par le client.

Par défaut, toutes les suppressions ont une période de grâce de 30 secondes. La commande kubectl delete prend en charge l'option --grace-period=<secondes> permettant à l'utilisateur de spécifier sa propre valeur. La valeur 0 force la suppression du pod. Avec kubectl version >= 1.5, vous devez spécifier un flag supplémentaire --force avec --grace-period=0 pour pouvoir forcer la suppression.

Suppression forcée de pods

La suppression forcée d'un pod est définie comme la suppression immédiate d'un pod de l'état du cluster et d'etcd. Lorqu'une suppression forcée est effectuée, l'apiserver n'attend pas la confirmation de kubelet que le pod a été terminé sur le nœud sur lequel il s'exécutait. Il supprime le pod de l'API immédiatement pour qu'un nouveau pod puisse être créé avec le même nom. Sur le nœud, les pods devant se terminer immédiatement se verront donner une courte période de grâce avant d'être tués de force.

Les suppressions forcées peuvent être potentiellement dangereuses pour certains pods et doivent être effectuées avec précaution. Dans le cas de pods d'un StatefulSet, veuillez vous référer à la documentation pour supprimer des Pods d'un StatefulSet.

Mode privilégié pour les conteneurs d'un pod

Depuis Kubernetes v1.1, tout conteneur d'un pod peut activer le mode privilégié, en utilisant le flag privileged du SecurityContext de la spec du conteneur. Ceci est utile pour les conteneurs voulant utiliser les capacités de Linux comme manipuler la pile réseau ou accéder aux périphériques. Les processus dans un tel conteneur ont pratiquement les mêmes privilèges que les processus en dehors d'un conteneur. En mode privilégié, il doit être plus facile d'écrire des plugins réseau et volume en tant que pods séparés ne devant pas être compilés dans kubelet.

Si le master exécute Kubernetes v1.1 ou supérieur, et les nœuds exécutent une version antérieure à v1.1, les nouveaux pods privilégiés seront acceptés par l'api-server, mais ne seront pas lancés. Il resteront en état "pending". Si l'utilisateur appelle kubectl describe pod FooPodName, l'utilisateur peut voir la raison pour laquelle le pod est en état "pending". La table d'événements dans la sortie de la commande "describe" indiquera : Error validating pod "FooPodName"."FooPodNamespace" from api, ignoring: spec.containers[0].securityContext.privileged: forbidden '<*>(0xc2089d3248)true'

Si le master exécute une version antérieure à v1.1, les pods privilégiés ne peuvent alors pas être créés. Si l'utilisateur tente de créer un pod ayant un conteneur privilégié, l'utilisateur obtiendra l'erreur suivante : The Pod "FooPodName" is invalid. spec.containers[0].securityContext.privileged: forbidden '<*>(0xc20b222db0)true'

Objet de l'API

Le Pod est une ressource au plus haut niveau dans l'API REST Kubernetes. Plus de détails sur l'objet de l'API peuvent être trouvés à : Objet de l'API Pod.

Lorsque vous créez un manifest pour un objet Pod, soyez certain que le nom spécifié est un nom de sous-domaine DNS valide.

1.3 - Cycle de vie d'un Pod

Cette page décrit le cycle de vie d'un Pod.

Phase du Pod

Le champ status d'un Pod est un objet PodStatus, contenant un champ phase.

La phase d'un Pod est un résumé simple et de haut niveau de l'étape à laquelle le Pod se trouve dans son cycle de vie. La phase n'est pas faite pour être un cumul complet d'observations de l'état du conteneur ou du Pod, ni pour être une machine à état compréhensible.

Le nombre et la signification des valeurs de phase d'un pod sont soigneusement gardés. Hormis ce qui est documenté ici, rien ne doit être supposé sur des Pods ayant une valeur de phase donnée.

Voici les valeurs possibles pour phase :

Valeur Description
Pending Le Pod a été accepté par Kubernetes, mais une ou plusieurs images de conteneurs n'ont pas encore été créées. Ceci inclut le temps avant d'être affecté ainsi que le temps à télécharger les images à travers le réseau, ce qui peut prendre un certain temps.
Running Le pod a été affecté à un nœud et tous les conteneurs ont été créés. Au moins un conteneur est toujours en cours d'exécution, ou est en train de démarrer ou redémarrer.
Succeeded Tous les conteneurs du pod ont terminé avec succès et ne seront pas redémarrés.
Failed Tous les conteneurs d'un pod ont terminé, et au moins un conteneur a terminé en échec : soit le conteneur a terminé avec un status non zéro, soit il a été arrêté par le système.
Unknown Pour quelque raison l'état du pod ne peut pas être obtenu, en général en cas d'erreur de communication avec l'hôte du Pod.

Conditions du Pod

Un Pod a un PodStatus, qui contient un tableau de PodConditions à travers lesquelles le Pod est ou non passé. Chaque élément du tableau de PodCondition a six champs possibles :

Sondes du Conteneur

Une Sonde (Probe) est un diagnostic exécuté périodiquement par kubelet sur un Conteneur. Pour exécuter un diagnostic, kubelet appelle un Handler implémenté par le Conteneur. Il existe trois types de handlers :

Chaque sonde a un résultat parmi ces trois :

kubelet peut optionnellement exécuter et réagir à trois types de sondes sur des conteneurs en cours d'exécution :

Quand devez-vous utiliser une liveness probe ?

Si le process de votre Conteneur est capable de crasher de lui-même lorsqu'il rencontre un problème ou devient inopérant, vous n'avez pas forcément besoin d'une liveness probe ; kubelet va automatiquement exécuter l'action correcte en accord avec la politique de redémarrage (restartPolicy) du Pod.

Si vous désirez que votre Conteneur soit tué et redémarré si une sonde échoue, alors spécifiez une liveness probe et indiquez une valeur pour restartPolicy à Always ou OnFailure.

Quand devez-vous utiliser une readiness probe ?

FEATURE STATE: Kubernetes v1.0 [stable]

Si vous voulez commencer à envoyer du trafic à un Pod seulement lorsqu'une sonde réussit, spécifiez une readiness probe. Dans ce cas, la readiness probe peut être la même que la liveness probe, mais l'existence de la readiness probe dans la spec veut dire que le Pod va démarrer sans recevoir aucun trafic et va commencer à recevoir du trafic après que la sonde réussisse. Si votre Conteneur doit charger une grande quantité de données, des fichiers de configuration ou exécuter des migrations au démarrage, spécifiez une readiness probe.

Si vous désirez que le Conteneur soit capable de se mettre en maintenance tout seul, vous pouvez spécifier une readiness probe qui vérifie un point de terminaison spécifique au readiness et différent de la liveness probe.

Notez que si vous voulez uniquement être capable de dérouter les requêtes lorsque le Pod est supprimé, vous n'avez pas forcément besoin d'une readiness probe; lors de sa suppression, le Pod se met automatiquement dans un état non prêt, que la readiness probe existe ou non. Le Pod reste dans le statut non prêt le temps que les Conteneurs du Pod s'arrêtent.

Quand devez-vous utiliser une startup probe ?

FEATURE STATE: Kubernetes v1.16 [alpha]

Si votre conteneur démarre habituellement en plus de initialDelaySeconds + failureThreshold × periodSeconds, vous devriez spécifier une startup probe qui vérifie le même point de terminaison que la liveness probe. La valeur par défaut pour periodSeconds est 30s. Vous devriez alors mettre sa valeur failureThreshold suffisamment haute pour permettre au conteneur de démarrer, sans changer les valeurs par défaut de la liveness probe. Ceci aide à se protéger de deadlocks.

Pour plus d'informations sur la manière de mettre en place une liveness, readiness ou startup probe, voir Configurer des Liveness, Readiness et Startup Probes.

Statut d'un Pod et d'un Conteneur

Pour des informations détaillées sur le statut d'un Pod et d'un Conteneur, voir PodStatus et ContainerStatus. Notez que l'information rapportée comme statut d'un Pod dépend du ContainerState actuel.

États d'un Conteneur

Une fois que le Pod est assigné à un nœud par le scheduler, kubelet commence à créer les conteneurs en utilisant le runtime de conteneurs. Il existe trois états possibles pour les conteneurs : en attente (Waiting), en cours d'exécution (Running) et terminé (Terminated). Pour vérifier l'état d'un conteneur, vous pouvez utiliser kubectl describe pod [POD_NAME]. L'état est affiché pour chaque conteneur du Pod.

Pod readiness

FEATURE STATE: Kubernetes v1.14 [stable]

Votre application peut injecter des données dans PodStatus.

Pod readiness. Pour utiliser cette fonctionnalité, remplissez readinessGates dans le PodSpec avec une liste de conditions supplémentaires que le kubelet évalue pour la disponibilité du Pod.

Les Readiness gates sont déterminées par l'état courant des champs status.condition du Pod. Si Kubernetes ne peut pas trouver une telle condition dans le champs status.conditions d'un Pod, the statut de la condition est mise par défaut à "False".

Voici un exemple :

kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready  # une PodCondition intégrée
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"   # une PodCondition supplémentaire
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
...

Les conditions du Pod que vous ajoutez doivent avoir des noms qui sont conformes au format des étiquettes de Kubernetes.

Statut de la disponibilité d'un Pod

La commande kubectl patch ne peut pas patcher le statut d'un objet. Pour renseigner ces status.conditions pour le pod, les applications et operators doivent utiliser l'action PATCH. Vous pouvez utiliser une bibliothèque client Kubernetes pour écrire du code qui renseigne les conditions particulières pour la disponibilité dun Pod.

Pour un Pod utilisant des conditions particulières, ce Pod est considéré prêt seulement lorsque les deux déclarations ci-dessous sont vraies :

Lorsque les conteneurs d'un Pod sont prêts mais qu'au moins une condition particulière est manquante ou False, le kubelet renseigne la condition du Pod à ContainersReady.

Politique de redémarrage

La structure PodSpec a un champ restartPolicy avec comme valeur possible Always, OnFailure et Never. La valeur par défaut est Always. restartPolicy s'applique à tous les Conteneurs du Pod. restartPolicy s'applique seulement aux redémarrages des Conteneurs par kubelet sur le même nœud. Des conteneurs terminés qui sont redémarrés par kubelet sont redémarrés avec un délai exponentiel (10s, 20s, 40s ...) plafonné à cinq minutes, qui est réinitialisé après dix minutes d'exécution normale. Comme discuté dans le document sur les Pods, une fois attaché à un nœud, un Pod ne sera jamais rattaché à un autre nœud.

Durée de vie d'un Pod

En général, les Pods restent jusqu'à ce qu'un humain ou un process de contrôleur les supprime explicitement.

Le plan de contrôle nettoie les Pods terminés (avec une phase à Succeeded ou Failed), lorsque le nombre de Pods excède le seuil configuré (determiné par terminated-pod-gc-threshold dans le kube-controller-manager). Ceci empêche une fuite de ressources lorsque les Pods sont créés et supprimés au fil du temps.

Il y a différents types de ressources pour créer des Pods :

Toutes les ressources de charges de travail contiennent une PodSpec. Il est recommandé de créer la ressource de charges de travail appropriée et laisser le contrôleur de la ressource créer les Pods pour vous, plutôt que de créer directement les Pods vous-même.

Si un nœud meurt ou est déconnecté du reste du cluster, Kubernetes applique une politique pour mettre la phase de tous les Pods du nœud perdu à Failed.

Exemples

Exemple avancé de liveness probe

Les Liveness probes sont exécutées par kubelet, toutes les requêtes sont donc faites dans l'espace réseau de kubelet.

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - args:
    - liveness
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    livenessProbe:
      httpGet:
        # lorsque "host" n'est pas défini, "PodIP" sera utilisé
        # host: my-host
        # lorsque "scheme" n'est pas défini, "HTTP" sera utilisé. "HTTP" et "HTTPS" sont les seules valeurs possibles
        # scheme: HTTPS
        path: /healthz
        port: 8080
        httpHeaders:
        - name: X-Custom-Header
          value: Awesome
      initialDelaySeconds: 15
      timeoutSeconds: 1
    name: liveness

Exemples d'états

A suivre

1.4 - Contraintes de propagation de topologie pour les Pods

FEATURE STATE: Kubernetes v1.18 [beta]

Vous pouvez utiliser des contraintes de propagation de topologie pour contrôler comment les Pods sont propagés à travers votre cluster parmi les domaines de défaillance comme les régions, zones, noeuds et autres domaines de topologie définis par l'utilisateur. Ceci peut aider à mettre en place de la haute disponibilité et à utiliser efficacement les ressources.

Conditions préalables

Autoriser la Feature Gate

La feature gate EvenPodsSpread doit être autorisée pour l'API Server et le scheduler.

Labels de noeuds

Les contraintes de propagation de topologie reposent sur les labels de noeuds pour identifier le ou les domaines de topologie dans lesquels se trouve chacun des noeuds. Par exemple, un noeud pourrait avoir les labels: node=node1,zone=us-east-1a,region=us-east-1

Supposons que vous ayez un cluster de 4 noeuds ayant les labels suivants:

NAME    STATUS   ROLES    AGE     VERSION   LABELS
node1   Ready    <none>   4m26s   v1.16.0   node=node1,zone=zoneA
node2   Ready    <none>   3m58s   v1.16.0   node=node2,zone=zoneA
node3   Ready    <none>   3m17s   v1.16.0   node=node3,zone=zoneB
node4   Ready    <none>   2m43s   v1.16.0   node=node4,zone=zoneB

Une vue logique du cluster est celle-ci :

+---------------+---------------+
|     zoneA     |     zoneB     |
+-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+

Plutôt que d'appliquer des labels manuellement, vous pouvez aussi réutiliser les labels réputés qui sont créés et renseignés automatiquement dans la plupart des clusters.

Contraintes de propagation pour les Pods

API

Le champ pod.spec.topologySpreadConstraints est introduit dans 1.16 comme suit :

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  topologySpreadConstraints:
    - maxSkew: <integer>
      minDomains: <integer>
      topologyKey: <string>
      whenUnsatisfiable: <string>
      labelSelector: <object>

Vous pouvez définir une ou plusieurs topologySpreadConstraint pour indiquer au kube-scheduler comment placer chaque nouveau Pod par rapport aux Pods déjà existants dans votre cluster. Les champs sont :

Vous pouvez en savoir plus sur ces champ en exécutant kubectl explain Pod.spec.topologySpreadConstraints.

Exemple : Une TopologySpreadConstraint

Supposons que vous ayez un cluster de 4 noeuds où 3 Pods étiquettés foo:bar sont placés sur node1, node2 et node3 respectivement (P représente un Pod) :

+---------------+---------------+
|     zoneA     |     zoneB     |
+-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+
|   P   |   P   |   P   |       |
+-------+-------+-------+-------+

Si nous voulons qu'un nouveau Pod soit uniformément réparti avec les Pods existants à travers les zones, la spec peut être :

pods/topology-spread-constraints/one-constraint.yaml
kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

topologyKey: zone implique que la distribution uniforme sera uniquement appliquée pour les noeuds ayant le label "zone:<any value>" présent. whenUnsatisfiable: DoNotSchedule indique au scheduler de laisser le Pod dans l'état Pending si le Pod entrant ne peut pas satisfaire la contrainte.

Si le scheduler plaçait ce Pod entrant dans "zoneA", la distribution des Pods deviendrait [3, 1], et le biais serait de 2 (3 - 1) - ce qui va à l'encontre de maxSkew: 1. Dans cet exemple, le Pod entrant peut uniquement être placé dans "zoneB":

+---------------+---------------+      +---------------+---------------+
|     zoneA     |     zoneB     |      |     zoneA     |     zoneB     |
+-------+-------+-------+-------+      +-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |  OR  | node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+      +-------+-------+-------+-------+
|   P   |   P   |   P   |   P   |      |   P   |   P   |  P P  |       |
+-------+-------+-------+-------+      +-------+-------+-------+-------+

Vous pouvez ajuster la spec du Pod pour pour répondre à divers types d'exigences :

Example: Plusieurs TopologySpreadConstraints

Cela s'appuie sur l'exemple précédent. Supposons que vous ayez un cluster de 4 noeuds où 3 Pods étiquetés foo:bar sont placés sur node1, node2 et node3 respectivement (P représente un Pod):

+---------------+---------------+
|     zoneA     |     zoneB     |
+-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+
|   P   |   P   |   P   |       |
+-------+-------+-------+-------+

Vous pouvez utiliser 2 TopologySpreadConstraints pour contrôler la répartition des Pods aussi bien dans les zones que dans les noeuds :

pods/topology-spread-constraints/two-constraints.yaml
kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  - maxSkew: 1
    topologyKey: node
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

Dans ce cas, pour satisfaire la première contrainte, le Pod entrant peut uniquement être placé dans "zoneB" ; alors que pour satisfaire la seconde contrainte, le Pod entrant peut uniquement être placé dans "node4". Le résultat étant l'intersection des résultats des 2 contraintes, l'unique option possible est de placer le Pod entrant dans "node4".

Plusieurs contraintes peuvent entraîner des conflits. Supposons que vous ayez un cluster de 3 noeuds couvrant 2 zones :

+---------------+-------+
|     zoneA     | zoneB |
+-------+-------+-------+
| node1 | node2 | node3 |
+-------+-------+-------+
|  P P  |   P   |  P P  |
+-------+-------+-------+

Si vous appliquez "two-constraints.yaml" à ce cluster, vous noterez que "mypod" reste dans l'état Pending. Cela parce que : pour satisfaire la première contrainte, "mypod" peut uniquement être placé dans "zoneB"; alors que pour satisfaire la seconde contrainte, "mypod" peut uniquement être placé sur "node2". Ainsi, le résultat de l'intersection entre "zoneB" et "node2" ne retourne rien.

Pour surmonter cette situation, vous pouvez soit augmenter maxSkew, soit modifier une des contraintes pour qu'elle utilise whenUnsatisfiable: ScheduleAnyway.

Conventions

Il existe quelques conventions implicites qu'il est intéressant de noter ici :

Contraintes par défaut au niveau du cluster

FEATURE STATE: Kubernetes v1.18 [alpha]

Il est possible de définir des contraintes de propagation de topologie par défaut pour un cluster. Les contraintes de propagation de topologie sont appliquées à un Pod si et seulement si :

Les contraintes par défaut peuvent être définies comme arguments du plugin PodTopologySpread dans un profil de scheduling. Les contraintes sont spécifiées avec la même API ci-dessus, à l'exception que labelSelector doit être vide. Les sélecteurs sont calculés à partir des services, replication controllers, replica sets ou stateful sets auxquels le Pod appartient.

Un exemple de configuration pourrait ressembler à :

apiVersion: kubescheduler.config.k8s.io/v1alpha2
kind: KubeSchedulerConfiguration

profiles:
  - schedulerName: default-scheduler
  - pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints:
            - maxSkew: 1
              topologyKey: topology.kubernetes.io/zone
              whenUnsatisfiable: ScheduleAnyway

Note:

Le score produit par les contraintes de scheduling par défaut peuvent rentrer en conflit avec le score produit par le plugin DefaultPodTopologySpread. Il est recommandé de désactiver ce plugin dans le profil de scheduling lorsque vous utilisez des contraintes par défaut pour PodTopologySpread.

Comparaison avec PodAffinity/PodAntiAffinity

Dans Kubernetes, les directives relatives aux "Affinités" contrôlent comment les Pods sont programmés - plus regroupés ou plus dispersés.

La fonctionnalité "EvenPodsSpread" fournit des options flexibles pour distribuer des Pods uniformément sur différents domaines de topologie - pour mettre en place de la haute disponibilité ou réduire les coûts. Cela peut aussi aider au rolling update des charges de travail et à la mise à l'échelle de réplicas. Voir Motivations pour plus de détails.

Limitations connues

En version 1.18, pour laquelle cette fonctionnalité est en Beta, il y a quelques limitations connues :

1.5 - Init Containers

Cette page fournit une vue d'ensemble des conteneurs d'initialisation (init containers) : des conteneurs spécialisés qui s'exécutent avant les conteneurs d'application dans un Pod. Les init containers peuvent contenir des utilitaires ou des scripts d'installation qui ne sont pas présents dans une image d'application.

Vous pouvez spécifier des init containers dans la spécification du Pod à côté du tableau containers (qui décrit les conteneurs d'application)

Comprendre les init containers

Un Pod peut avoir plusieurs conteneurs exécutant des applications mais peut aussi avoir un ou plusieurs init containers, qui sont exécutés avant que les conteneurs d'application ne démarrent.

Les init containers se comportent comme les conteneurs réguliers, avec quelques différences :

Si le init container d'un Pod échoue, Kubernetes redémarre le Pod à répétition jusqu'à ce que le init container se termine avec succès. Cependant, si le Pod a une restartPolicy à "Never", Kubernetes ne redémarre pas le Pod.

Afin de spécifier un init container pour un Pod, il faut ajouter le champ initContainers dans la spécification du Pod, comme un tableau d'objets de type Container, au même niveau que le tableau d'applications containers. Le statut des init containers est retourné dans le champ .status.initContainerStatuses comme un tableau des statuts du conteneur (comparable au champ .status.containerStatuses).

Différences avec les conteneurs réguliers

Les init containers supportent tous les champs et fonctionnalités des conteneurs d'application incluant les limites de ressources, les volumes et les paramètres de sécurité. Cependant, les demandes de ressources pour un init container sont gérées différemment des limites de ressources, tel que documenté dans Ressources.

De plus, les init containers ne supportent pas les readiness probes parce que ces conteneurs s'exécutent jusqu'au bout avant que le Pod soit prêt.

Si l'on spécifie plusieurs init containers pour un Pod, Kubelet exécute chaque init container de manière séquentielle. Chaque init container doit se terminer avec succès avant que le prochain ne puisse s'exécuter. Lorsque tous les init containers se sont exécutés jusqu'au bout, Kubelet initialise les conteneurs d'application pour le Pod et les exécute comme d'habitude.

Utiliser les init containers

Puisque les init containers ont des images séparées des conteneurs d'application, ils apportent certains avantages pour du code de mise en route :

Exemples

Voici plusieurs idées pour utiliser les init containers :

Les init containers en utilisation

Cet exemple définit un simple Pod possédant deux init containers. Le premier attend myservice et le second attend mydb. Une fois que les deux init containers terminent leur exécution, le Pod exécute le conteneur d'application décrit dans sa section spec.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo "L''app s''exécute!" && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo en attente de myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo en attente de mydb; sleep 2; done"]

Les fichiers YAML suivants résument les services mydb et myservice :

apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

Vous pouvez démarrer ce Pod en exécutant :

kubectl apply -f myapp.yaml
pod/myapp-pod created

Et vérifier son statut avec :

kubectl get -f myapp.yaml
NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m

ou pour plus de détails :

kubectl describe -f myapp.yaml
Name:          myapp-pod
Namespace:     default
[...]
Labels:        app.kubernetes.io/name=MyApp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Events:
  FirstSeen    LastSeen    Count    From                      SubObjectPath                           Type          Reason        Message
  ---------    --------    -----    ----                      -------------                           --------      ------        -------
  16s          16s         1        {default-scheduler }                                              Normal        Scheduled     Successfully assigned myapp-pod to 172.17.4.201
  16s          16s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulling       pulling image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulled        Successfully pulled image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Created       Created container with docker id 5ced34a04634; Security:[seccomp=unconfined]
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container with docker id 5ced34a04634

Pour voir les logs des init containers dans ce Pod, exécuter :

kubectl logs myapp-pod -c init-myservice # Inspecter le premier init container
kubectl logs myapp-pod -c init-mydb      # Inspecter le second init container

À ce stade, ces init containers attendent de découvrir les services nommés mydb et myservice.

Voici une configuration que vous pouvez utiliser pour faire apparaître ces Services :

---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

Pour créer les services mydb et myservice :

kubectl apply -f services.yaml
service/myservice created
service/mydb created

Vous verrez ensuite que ces init containers se terminent et que le Pod myapp-pod évolue vers l'état "Running" (en exécution) :

kubectl get -f myapp.yaml
NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

Cet exemple simple devrait suffire à vous inspirer pour créer vos propres init containers. A suivre contient un lien vers un exemple plus détaillé.

Comportement détaillé

Pendant le démarrage d'un Pod, chaque init container démarre en ordre, après que le réseau et les volumes ont été initialisés. Chaque conteneur doit se terminer avec succès avant que le prochain ne démarre. Si un conteneur n'arrive pas à démarrer à cause d'un problème d'exécution ou se termine avec un échec, il est redémarré selon la restartPolicy du Pod. Toutefois, si la restartPolicy du Pod est configurée à "Always", les init containers utilisent la restartPolicy "OnFailure".

Un Pod ne peut pas être Ready tant que tous les init containers ne se sont pas exécutés avec succès. Les ports d'un init container ne sont pas agrégés sous un Service. Un Pod qui s'initialise est dans l'état Pending mais devrait avoir une condition Initialized configurée à "true".

Si le Pod redémarre ou est redémarré, tous les init containers doivent s'exécuter à nouveau.

Les changements aux spec d'un init containers sont limités au champ image du conteneur. Changer le champ image d'un init container équivaut à redémarrer le Pod.

Puisque les init containers peuvent être redémarrés, réessayés ou ré-exécutés, leur code doit être idempotent. En particulier, le code qui écrit dans des fichiers sur EmptyDirs devrait être préparé à la possibilité qu'un fichier de sortie existe déjà.

Les init containers ont tous les champs d'un conteneur d'application. Cependant, Kubernetes interdit l'utilisation de readinessProbe parce que les init containers ne peuvent pas définir une "readiness" distincte de la complétion. Ceci est appliqué lors de la validation.

L'utilisation de activeDeadlineSeconds sur le Pod et livenessProbe sur le conteneur permet d'empêcher les init containers d'échouer tout le temps. La deadline active inclut les init containers.

Le nom de chaque application et init container dans un Pod doit être unique; une erreur de validation est générée pour tout conteneur partageant un nom avec un autre.

Ressources

Étant donné l'ordonnancement et l'exécution des init containers, les règles suivantes s'appliquent pour l'utilisation des ressources :

Les quotas et limites sont appliqués sur la base de la requête/limite effective d'un Pod.

Les groupes de contrôle au niveau du Pod (cgroups) sont basés sur la requête/limite effective de Pod, la même que celle du scheduler.

Raisons du redémarrage d'un Pod

Un Pod peut redémarrer, ce qui cause la ré-exécution des init containers, pour les raisons suivantes :

A suivre

2 - Contrôleurs

2.1 - ReplicaSet

Un ReplicaSet (ensemble de réplicas en français) a pour but de maintenir un ensemble stable de Pods à un moment donné. Cet objet est souvent utilisé pour garantir la disponibilité d'un certain nombre identique de Pods.

Comment un ReplicaSet fonctionne

Un ReplicaSet est défini avec des champs, incluant un selecteur qui spécifie comment identifier les Pods qu'il peut posséder, un nombre de replicas indiquant le nombre de Pods qu'il doit maintenir et un modèle de Pod spécifiant les données que les nouveaux Pods que le replicatSet va créer jusqu'au nombre de replicas demandé.

Un ReplicaSet va atteindre son objectif en créant et supprimant des Pods pour atteindre le nombre de réplicas désirés. Quand un ReplicaSet a besoin de créer de nouveaux Pods, il utilise alors son Pod template.

Le lien d'un ReplicaSet à ses Pods est fait par le champ metadata.ownerReferences, qui spécifie la ressource de l'objet par lequel il est détenu. Tous les Pods acquis par un ReplicaSet ont leurs propres informations d'identification de leur Replicaset, avec leur propre champ ownerReferences. C'est par ce lien que le ReplicaSet connait l'état des Pods qu'il maintient et agit en fonction de ces derniers.

Un ReplicaSet identifie des nouveaux Pods à acquérir en utilisant son selecteur. Si il y a un Pod qui n'a pas de OwnerReference ou que OwnerReference n'est pas un controller et qu'il correspond à un sélecteur de ReplicaSet, il va immédiatement être acquis par ce ReplicaSet.

Quand utiliser un ReplicaSet ?

Un ReplicaSet garantit qu’un nombre spécifié de réplicas de Pod soient exécutés à un moment donné. Cependant, un Deployment est un concept de plus haut niveau qui gère les ReplicaSets et fournit des mises à jour déclaratives aux Pods ainsi que de nombreuses autres fonctionnalités utiles. Par conséquent, nous vous recommandons d’utiliser des Deployments au lieu d’utiliser directement des ReplicaSets, sauf si vous avez besoin d'une orchestration personnalisée des mises à jour ou si vous n'avez pas besoin de mises à jour.

Cela signifie qu'il est possible que vous n'ayez jamais besoin de manipuler des objets ReplicaSet : utilisez plutôt un déploiement et définissez votre application dans la section spec.

Exemple

controllers/frontend.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # modify replicas according to your case
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: gcr.io/google_samples/gb-frontend:v3

Enregistrer ce manifeste dans frontend.yaml et le soumettre à un cluster Kubernetes va créer le ReplicaSet défini et les pods qu’il gère.

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

Vous pouvez ensuite récupérer les ReplicaSets actuellement déployés :

kubectl get rs

Et voir le frontend que vous avez créé :

NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

Vous pouvez également vérifier l'état du ReplicaSet :

kubectl describe rs/frontend

Et vous verrez une sortie similaire à :

Name:		frontend
Namespace:	default
Selector:	tier=frontend,tier in (frontend)
Labels:		app=guestbook
		tier=frontend
Annotations:	<none>
Replicas:	3 current / 3 desired
Pods Status:	3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:       app=guestbook
                tier=frontend
  Containers:
   php-redis:
    Image:      gcr.io/google_samples/gb-frontend:v3
    Port:       80/TCP
    Requests:
      cpu:      100m
      memory:   100Mi
    Environment:
      GET_HOSTS_FROM:   dns
    Mounts:             <none>
  Volumes:              <none>
Events:
  FirstSeen    LastSeen    Count    From                SubobjectPath    Type        Reason            Message
  ---------    --------    -----    ----                -------------    --------    ------            -------
  1m           1m          1        {replicaset-controller }             Normal      SuccessfulCreate  Created pod: frontend-qhloh
  1m           1m          1        {replicaset-controller }             Normal      SuccessfulCreate  Created pod: frontend-dnjpy
  1m           1m          1        {replicaset-controller }             Normal      SuccessfulCreate  Created pod: frontend-9si5l

Et enfin, vous pourrez afficher les Pods déployés :

kubectl get Pods

Vous devriez voir des informations sur les Pods avec une sortie similaire à :

NAME             READY     STATUS    RESTARTS   AGE
frontend-9si5l   1/1       Running   0          1m
frontend-dnjpy   1/1       Running   0          1m
frontend-qhloh   1/1       Running   0          1m

Vous pouvez également vérifier que la OwnerReference de ces pods est définie sur le frontend ReplicaSet. Pour ce faire, récupérez le yaml de l’un des pods :

kubectl get pods frontend-9si5l -o yaml

La sortie sera similaire à celle-ci, avec les informations de l'interface ReplicaSet frontend définies dans le champ ownerReferences des métadonnées:

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: 2019-01-31T17:20:41Z
  generateName: frontend-
  labels:
    tier: frontend
  name: frontend-9si5l
  namespace: default
  ownerReferences:
  - apiVersion: extensions/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: frontend
    uid: 892a2330-257c-11e9-aecd-025000000001
...

Acquisitions de Pod en dehors du template

Bien que vous puissiez créer des pods manuellement sans problème, il est fortement recommandé de s’assurer que ces pods n'ont pas de labels correspondant au sélecteur de l’un de vos ReplicaSets. Car un ReplicaSet n’est pas limité à posséder les pods spécifiés par son modèle - il peut acquérir d’autres pods de la manière spécifiée dans les sections précédentes.

Prenez l'exemple précédent de ReplicaSet, ainsi que les pods spécifiés dans le manifeste suivant :

pods/pod-rs.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod1
  labels:
    tier: frontend
spec:
  containers:
  - name: hello1
    image: gcr.io/google-samples/hello-app:2.0

---

apiVersion: v1
kind: Pod
metadata:
  name: pod2
  labels:
    tier: frontend
spec:
  containers:
  - name: hello2
    image: gcr.io/google-samples/hello-app:1.0

Ces pods n’ayant pas de contrôleur (ni d’objet) en tant que référence propriétaire, ils correspondent au sélecteur de du ReplicaSet frontend, ils seront donc immédiatement acquis par ce ReplicaSet.

Supposons que vous créiez les pods une fois le ReplicaSet frontend déployé et qui a déjà déployé ses replicas de Pods initiaux afin de remplir son exigence de nombre de replicas :

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

Les nouveaux pods seront acquis par le ReplicaSet, puis immédiatement terminés car le ReplicaSet dépasserait alors le compte désiré.

En récupérant les pods :

kubectl get Pods

La sortie montre que les nouveaux pods sont soit déjà terminés, soit en voie de l'être :

NAME             READY   STATUS        RESTARTS   AGE
frontend-9si5l   1/1     Running       0          1m
frontend-dnjpy   1/1     Running       0          1m
frontend-qhloh   1/1     Running       0          1m
pod2             0/1     Terminating   0          4s

Cependant, si vous créez d'abord les pods :

kubectl apply -f https://kubernetes.io/examples/pods/pod-rs.yaml

Et puis créez le ReplicaSet :

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

Vous verrez que le ReplicaSet a acquis les pods et n'a créé que les nouveaux Pods manquants, conformément à ses spécifications, jusqu'au nombre souhaité de Pods. En récupérant les Pods :

kubectl get Pods

La sortie va donner :

NAME             READY   STATUS    RESTARTS   AGE
frontend-pxj4r   1/1     Running   0          5s
pod1             1/1     Running   0          13s
pod2             1/1     Running   0          13s

De cette manière, un ReplicaSet peut avoir un ensemble de Pods hétérogène.

Écrire un manifest de ReplicaSet

Comme avec tous les autres objets API Kubernetes, un ReplicaSet a besoin des champs apiVersion, kind et metadata. Pour ReplicaSets, l'attribut kind est toujours ReplicaSet.

Dans Kubernetes 1.9, la version de l'API apps/v1 pour le type ReplicaSet est la version actuelle et activée par défaut. La version de l'API apps/v1beta2 est obsolète.

Reportez-vous aux premières lignes de l'exemple frontend.yaml pour obtenir des conseils.

Un ReplicaSet a également besoin de .spec section.

Pod Template

L'attribut .spec.template est un modèle de pod qui requiert d'avoir des labels. Dans notre exemple frontend.yaml, nous avons un label : tier: frontend. Il faut faire attention à ne pas avoir des selecteurs que d'autres controllers utilisent, afin d'éviter que le ReplicaSet n'adopte ce pod.

Pour le champ restart policy, .spec.template.spec.restartPolicy, la seule valeur autorisée est Always, qui est la valeur par défaut.

Sélecteur de Pod

Le champ .spec.selector est un label selector. Tel que discuté précédemment, ce sont les labels utilisés pour identifier les Pods potentiels à acquérir. Dans notre exemple avec frontend.yaml, le sélecteur était :

matchLabels:
	tier: frontend

Dans le ReplicaSet, .spec.template.metadata.labels doit correspondre à spec.selector, ou sinon il sera rejeté par l'API.

Note:

Pour 2 ReplicaSets spécifiant le même .spec.selector mais différents .spec.template.metadata.labels et .spec.template.spec, chaque ReplicaSet ignore les pods créés par l'autre ReplicaSet.

Replicas

Vous pouvez spécifier le nombre de pods à exécuter simultanément en définissant .spec.replicas. Le ReplicaSet va créer/supprimer ses pods pour correspondre à ce nombre.

Si vous ne spécifiez pas .spec.replicas, la valeur par défaut est 1.

Travailler avec des ReplicaSets

Suppression d'un ReplicaSet et de ses pods

Pour supprimer un ReplicaSet et tous ses pods, utilisez kubectl delete. The Garbage collector supprime automatiquement tous les pods associés par défaut.

Lors de l’utilisation de l’API REST ou de la bibliothèque client-go, vous devez définir propagationPolicy sur Background ou Foreground dans l'option -d. Par exemple :

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Foreground"}' \
> -H "Content-Type: application/json"

Supprimer juste un ReplicaSet

Vous pouvez supprimer un ReplicaSet sans affecter ses pods à l’aide de kubectl delete avec l'option --cascade=orphan. Lorsque vous utilisez l'API REST ou la bibliothèque client-go, vous devez définir propagationPolicy sur Orphan. Par exemple :

kubectl proxy --port=8080
curl -X DELETE  'localhost:8080/apis/extensions/v1beta1/namespaces/default/replicasets/frontend' \
> -d '{"kind":"DeleteOptions","apiVersion":"v1","propagationPolicy":"Orphan"}' \
> -H "Content-Type: application/json"

Une fois l’original supprimé, vous pouvez créer un nouveau ReplicaSet pour le remplacer. Tant que l'ancien et le nouveau .spec.selector sont identiques, le nouveau adoptera les anciens Pods. Cependant, le ReplicaSet ne fera aucun effort pour que les pods existants correspondent à un nouveau Pod template. Pour mettre à jour les Pods à une nouvelle spec de manière contrôlée, utilisez un Deployment, car les ReplicaSets ne supportent pas de rolling update directement.

Isoler les pods d'un ReplicaSet

Vous pouvez supprimer les pods d'un ReplicaSet en modifiant leurs labels. Cette technique peut être utilisée pour enlever les pods pour le débogage, récupération de données, etc. Les pods ainsi supprimés seront automatiquement remplacés (en supposant que le nombre de réplicas n’est pas également modifié).

Scaling d'un ReplicaSet

Un ReplicaSet peut facilement être scalé en mettant simplement à jour le champ .spec.replicas. Le contrôleur ReplicaSet garantit que le nombre souhaité de pods avec un sélecteur de label correspondant soient disponibles et opérationnels.

ReplicaSet en tant que Horizontal Pod Autoscaler Target

Un ReplicaSet peut également être une cible pour Horizontal Pod Autoscalers (HPA). Un ReplicaSet peut être mis à l'échelle automatiquement par un HPA. Voici un exemple HPA qui cible le ReplicaSet que nous avons créé dans l'exemple précédent.

controllers/hpa-rs.yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-scaler
spec:
  scaleTargetRef:
    kind: ReplicaSet
    name: frontend
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

Enregistrer ce manifeste dans hpa-rs.yaml et le soumettre à un cluster Kubernetes devrait créer le HPA défini qui scale automatiquement le ReplicaSet cible en fonction de l'utilisation du processeur des pods répliqués.

kubectl apply -f https://k8s.io/examples/controllers/hpa-rs.yaml

Vous pouvez aussi utiliser la commande kubectl autoscale pour accomplir la même chose. (et c'est plus facile !)

kubectl autoscale rs frontend --max=10

Alternatives au ReplicaSet

Deployment (recommandé)

Le Deployment est un object qui peut posséder les ReplicaSets et les mettres à jour ainsi que leurs Pods de façon déclarative, côté serveur et avec des rolling updates.

Alors que les ReplicaSets peuvent être utilisés indépendamment, ils sont principalement utilisés aujourd'hui par Deployments comme mécanisme pour orchestrer la création, suppresion et mises à jour des Pods. Lorsque vous utilisez des Deployments, vous n’aurez plus à vous soucier de la gestion des ReplicaSets ainsi créés. Les déploiements possèdent et gèrent leurs ReplicaSets. C'est pourquoi il est recommandé d’utiliser les déploiements lorsque vous voulez des ReplicaSets.

Pods nus

Contrairement au cas où un utilisateur a créé directement des pods, un ReplicaSet remplace les pods supprimés ou terminés pour quelque raison que ce soit, par exemple en cas de défaillance d'un nœud ou de maintenance de nœud perturbateur, telle qu'une mise à jour kernel. Pour cette raison, nous vous recommandons d'utiliser un ReplicaSet même si votre application ne nécessite qu'un seul pod. Pensez-y de la même manière qu’un superviseur de processus, mais il supervise plusieurs pods sur plusieurs nœuds au lieu de processus individuels sur un seul nœud. Un ReplicaSet délègue les redémarrages de conteneurs locaux à un agent du nœud (par exemple, Kubelet ou Docker).

Job

Utilisez un Job au lieu d'un ReplicaSet pour les pods qui doivent se terminer seuls (c'est à dire des batch jobs).

DaemonSet

Utilisez un DaemonSet au lieu d’un ReplicaSet pour les pods qui fournissent une fonction au niveau du noeud, comme le monitoring ou la gestion des logs de ce noeud. Ces pods ont une durée de vie qui est liée durée de vie d’une machine : le pod doit être en cours d’exécution sur la machine avant le démarrage des autres Pods et sont sûrs de se terminer lorsque la machine est prête à être redémarrée/arrêtée.

ReplicationController

Les ReplicaSets sont les successeurs de ReplicationControllers. Les deux servent le même objectif et se comportent de la même manière, à la différence près que ReplicationController ne prend pas en charge les les exigences de sélecteur décrites dans le labels user guide. En tant que tels, les ReplicaSets sont préférés aux ReplicationControllers.

2.2 - Déploiements

Un Deployment (déploiement en français) fournit des mises à jour déclaratives pour Pods et ReplicaSets.

Vous décrivez un état désiré dans un déploiement et le controlleur déploiement change l'état réel à l'état souhaité à un rythme contrôlé. Vous pouvez définir des Deployments pour créer de nouveaux ReplicaSets, ou pour supprimer des déploiements existants et adopter toutes leurs ressources avec de nouveaux déploiements.

Note:

Ne gérez pas les ReplicaSets appartenant à un Deployment. Pensez à ouvrir un ticket dans le dépot Kubernetes principal si votre cas d'utilisation n'est pas traité ci-dessous.

Cas d'utilisation

Voici des cas d'utilisation typiques pour les déploiements:

Création d'un déploiement

Voici un exemple de déploiement. Il crée un ReplicaSet pour faire apparaître trois pods nginx:

controllers/nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

Dans cet exemple:

Suivez les étapes ci-dessous pour créer le déploiement ci-dessus:

Avant de commencer, assurez-vous que votre cluster Kubernetes est opérationnel.

  1. Créez le déploiement en exécutant la commande suivante:

    Note:

    Vous pouvez spécifier l'indicateur --record pour écrire la commande exécutée dans l'annotation de ressource kubernetes.io/change-cause. C'est utile pour une future introspection. Par exemple, pour voir les commandes exécutées dans chaque révision de déploiement.
    kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
    
  2. Exécutez kubectl get deployments pour vérifier si le déploiement a été créé. Si le déploiement est toujours en cours de création, la sortie est similaire à:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   0/3     0            0           1s
    

    Lorsque vous inspectez les déploiements de votre cluster, les champs suivants s'affichent:

    Notez que le nombre de réplicas souhaitées est de 3 selon le champ .spec.replicas.

  3. Pour voir l'état du déploiement, exécutez:

    kubectl rollout status deployment.v1.apps/nginx-deployment
    

    La sortie est similaire à ceci:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    deployment "nginx-deployment" successfully rolled out
    
  4. Exécutez à nouveau kubectl get deployments quelques secondes plus tard. La sortie est similaire à ceci:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           18s
    

    Notez que le déploiement a créé les trois répliques et que toutes les répliques sont à jour (elles contiennent le dernier modèle de pod) et disponibles.

  5. Pour voir le ReplicaSet (rs) créé par le déploiement, exécutez kubectl get rs. La sortie est similaire à ceci:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-75675f5897   3         3         3       18s
    

    Notez que le nom du ReplicaSet est toujours formaté comme: [DEPLOYMENT-NAME]-[RANDOM-STRING]. La chaîne aléatoire est générée aléatoirement et utilise le pod-template-hash comme graine.

  6. Pour voir les labels générées automatiquement pour chaque Pod, exécutez kubectl get pods --show-labels. La sortie est similaire à ceci:

    NAME                                READY     STATUS    RESTARTS   AGE       LABELS
    nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    

Le ReplicaSet créé garantit qu'il y a trois pods nginx.

Note:

Vous devez spécifier un sélecteur approprié et des labels de template de pod dans un déploiement (dans ce cas, app: nginx). Ne superposez pas les étiquettes ou les sélecteurs avec d'autres contrôleurs (y compris d'autres déploiements et StatefulSets). Kubernetes n'empêche pas les chevauchements de noms, et si plusieurs contrôleurs ont des sélecteurs qui se chevauchent, ces contrôleurs peuvent entrer en conflit et se comporter de façon inattendue.

Étiquette pod-template-hash

Note:

Ne modifiez pas ce label.

Le label pod-template-hash est ajoutée par le contrôleur de déploiement à chaque ReplicaSet créé ou adopté par un déploiement.

Ce label garantit que les ReplicaSets enfants d'un déploiement ne se chevauchent pas. Il est généré en hachant le PodTemplate du ReplicaSet et en utilisant le hachage résultant comme valeur de label qui est ajoutée au sélecteur ReplicaSet, aux labels de template de pod et dans tous les pods existants que le ReplicaSet peut avoir.

Mise à jour d'un déploiement

Note:

Le re-déploiement d'un déploiement est déclenché si et seulement si le modèle de pod du déploiement (c'est-à-dire .spec.template) est modifié, par exemple si les labels ou les images de conteneur du template sont mis à jour. D'autres mises à jour, telles que la mise à l'échelle du déploiement, ne déclenchent pas de rollout.

Suivez les étapes ci-dessous pour mettre à jour votre déploiement:

  1. Mettons à jour les pods nginx pour utiliser l'image nginx: 1.9.1 au lieu de l'image nginx: 1.7.9.

    kubectl --record deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1
    

    ou utilisez la commande suivante:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1 --record
    

    La sortie est similaire à ceci:

    deployment.apps/nginx-deployment image updated
    

    Alternativement, vous pouvez éditer le déploiement et changer .spec.template.spec.containers[0].image de nginx: 1.7.9 à nginx: 1.9.1:

    kubectl edit deployment.v1.apps/nginx-deployment
    

    La sortie est similaire à ceci:

    deployment.apps/nginx-deployment edited
    
  2. Pour voir l'état du déploiement, exécutez:

    kubectl rollout status deployment.v1.apps/nginx-deployment
    

    La sortie est similaire à ceci:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    

    ou

    deployment "nginx-deployment" successfully rolled out
    

Obtenez plus de détails sur votre déploiement mis à jour:

Rollover (alias plusieurs mises à jour en vol)

Chaque fois qu'un nouveau déploiement est observé par le contrôleur de déploiement, un ReplicaSet est créé pour afficher les pods souhaités. Si le déploiement est mis à jour, le ReplicaSet existant qui contrôle les pods dont les étiquettes correspondent à .spec.selector mais dont le modèle ne correspond pas à .spec.template est réduit. Finalement, le nouveau ReplicaSet est mis à l'échelle à .spec.replicas et tous les anciens ReplicaSets sont mis à l'échelle à 0.

Si vous mettez à jour un déploiement alors qu'un déploiement existant est en cours, le déploiement crée un nouveau ReplicaSet conformément à la mise à jour et commence à le mettre à l'échelle, et arrête de mettre à jour le ReplicaSet qu'il augmentait précédemment - il l'ajoutera à sa liste de anciens ReplicaSets et commencera à le réduire.

Par exemple, supposons que vous créez un déploiement pour créer 5 répliques de nginx: 1.7.9, puis mettez à jour le déploiement pour créer 5 répliques de nginx: 1.9.1, alors que seulement 3 répliques de nginx:1.7.9 avait été créés. Dans ce cas, le déploiement commence immédiatement à tuer les 3 pods nginx: 1.7.9 qu'il avait créés et commence à créer des pods nginx: 1.9.1. Il n'attend pas que les 5 répliques de nginx: 1.7.9 soient créées avant de changer de cap.

Mises à jour du sélecteur de labels

Il est généralement déconseillé de mettre à jour le sélecteur de labels et il est suggéré de planifier vos sélecteurs à l'avance. Dans tous les cas, si vous devez effectuer une mise à jour du sélecteur de labels, soyez très prudent et assurez-vous d'avoir saisi toutes les implications.

Note:

Dans la version d'API apps/v1, le sélecteur de label d'un déploiement est immuable après sa création.

Annulation d'un déploiement

Parfois, vous souhaiterez peut-être annuler un déploiement; par exemple, lorsque le déploiement n'est pas stable, comme en cas d'échecs à répétition (CrashLoopBackOff). Par défaut, tout l'historique des déploiements d'un déploiement est conservé dans le système afin que vous puissiez le restaurer à tout moment (vous pouvez le modifier en modifiant la limite de l'historique des révisions).

Note:

La révision d'un déploiement est créée lorsque le déploiement d'un déploiement est déclenché. Cela signifie qu'une nouvelle révision est créée si et seulement si le template de pod de déploiement (.spec.template) est modifié, par exemple si vous mettez à jour les labels ou les images de conteneur du template. D'autres mises à jour, telles que la mise à l'échelle du déploiement, ne créent pas de révision de déploiement, de sorte que vous puissiez faciliter la mise à l'échelle manuelle ou automatique simultanée. Cela signifie que lorsque vous revenez à une révision antérieure, seule la partie du template de pod de déploiement est annulée.

Vérification de l'historique de déploiement d'un déploiement

Suivez les étapes ci-dessous pour vérifier l'historique de déploiement:

  1. Tout d'abord, vérifiez les révisions de ce déploiement:

    kubectl rollout history deployment.v1.apps/nginx-deployment
    

    La sortie est similaire à ceci:

    deployments "nginx-deployment"
    REVISION    CHANGE-CAUSE
    1           kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml --record=true
    2           kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1 --record=true
    3           kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.91 --record=true
    

    CHANGE-CAUSE est copié de l'annotation de déploiement kubernetes.io/change-cause dans ses révisions lors de la création. Vous pouvez spécifier le messageCHANGE-CAUSE en:

  2. Pour voir les détails de chaque révision, exécutez:

    kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2
    

    La sortie est similaire à ceci:

    deployments "nginx-deployment" revision 2
      Labels:       app=nginx
              pod-template-hash=1159050644
      Annotations:  kubernetes.io/change-cause=kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1 --record=true
      Containers:
       nginx:
        Image:      nginx:1.9.1
        Port:       80/TCP
         QoS Tier:
            cpu:      BestEffort
            memory:   BestEffort
        Environment Variables:      <none>
      No volumes.
    

Revenir à une révision précédente

Suivez les étapes ci-dessous pour restaurer le déploiement de la version actuelle à la version précédente, qui est la version 2.

  1. Vous avez maintenant décidé d'annuler le déploiement actuel et le retour à la révision précédente:

    kubectl rollout undo deployment.v1.apps/nginx-deployment
    

    La sortie est similaire à ceci:

    deployment.apps/nginx-deployment
    

    Alternativement, vous pouvez revenir à une révision spécifique en la spécifiant avec --to-revision:

    kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2
    

    La sortie est similaire à ceci:

    deployment.apps/nginx-deployment
    

    Pour plus de détails sur les commandes liées au déploiement, lisez kubectl rollout.

    Le déploiement est maintenant rétabli à une précédente révision stable. Comme vous pouvez le voir, un événement DeploymentRollback pour revenir à la révision 2 est généré à partir du contrôleur de déploiement.

  2. Vérifiez si la restauration a réussi et que le déploiement s'exécute comme prévu, exécutez:

    kubectl get deployment nginx-deployment
    

    La sortie est similaire à ceci:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           30m
    
  3. Obtenez la description du déploiement:

    kubectl describe deployment nginx-deployment
    

    La sortie est similaire à ceci:

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Sun, 02 Sep 2018 18:17:55 -0500
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=4
                            kubernetes.io/change-cause=kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1 --record=true
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.9.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   nginx-deployment-c4747d96c (3/3 replicas created)
    Events:
      Type    Reason              Age   From                   Message
      ----    ------              ----  ----                   -------
      Normal  ScalingReplicaSet   12m   deployment-controller  Scaled up replica set nginx-deployment-75675f5897 to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 0
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-595696685f to 1
      Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
      Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0
    

Mise à l'échelle d'un déploiement

Vous pouvez mettre à l'échelle un déploiement à l'aide de la commande suivante:

kubectl scale deployment.v1.apps/nginx-deployment --replicas=10

La sortie est similaire à ceci:

deployment.apps/nginx-deployment scaled

En supposant que l'horizontal Pod autoscaling est activé dans votre cluster, vous pouvez configurer une mise à l'échelle automatique pour votre déploiement et choisir le nombre minimum et maximum de pods que vous souhaitez exécuter en fonction de l'utilisation du processeur de vos pods existants.

kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80

La sortie est similaire à ceci:

deployment.apps/nginx-deployment scaled

Mise à l'échelle proportionnelle

Les déploiements RollingUpdate prennent en charge l'exécution simultanée de plusieurs versions d'une application. Lorsque vous ou un autoscaler mettez à l'échelle un déploiement RollingUpdate qui se trouve au milieu d'un déploiement (en cours ou en pause), le contrôleur de déploiement équilibre les réplicas supplémentaires dans les ReplicaSets actifs existants (ReplicaSets avec pods) afin d'atténuer le risque. Ceci est appelé mise à l'échelle proportionnelle.

Par exemple, vous exécutez un déploiement avec 10 réplicas, maxSurge=3, et maxUnavailable=2.

Dans notre exemple ci-dessus, 3 répliques sont ajoutées à l'ancien ReplicaSet et 2 répliques sont ajoutées au nouveau ReplicaSet. Le processus de déploiement devrait éventuellement déplacer toutes les répliques vers le nouveau ReplicaSet, en supposant que les nouvelles répliques deviennent saines. Pour confirmer cela, exécutez:

kubectl get deploy

La sortie est similaire à ceci:

NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     15        18        7            8           7m

Le statut de déploiement confirme la façon dont les réplicas ont été ajoutés à chaque ReplicaSet.

kubectl get rs

La sortie est similaire à ceci:

NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   7         7         0         7m
nginx-deployment-618515232    11        11        11        7m

Pause et reprise d'un déploiement

Vous pouvez suspendre un déploiement avant de déclencher une ou plusieurs mises à jour, puis le reprendre. Cela vous permet d'appliquer plusieurs correctifs entre la pause et la reprise sans déclencher de déploiements inutiles.

Note:

Vous ne pouvez pas annuler un déploiement suspendu avant de le reprendre.

Statut de déploiement

Un déploiement entre dans différents états au cours de son cycle de vie. Il peut être progressant lors du déploiement d'un nouveau ReplicaSet, il peut être effectué, ou il peut ne pas progresser.

Progression du déploiement

Kubernetes marque un déploiement comme progressing lorsqu'une des tâches suivantes est effectuée:

Vous pouvez surveiller la progression d'un déploiement à l'aide de kubectl rollout status.

Déploiement effectué

Kubernetes marque un déploiement comme effectué lorsqu'il présente les caractéristiques suivantes:

Vous pouvez vérifier si un déploiement est terminé en utilisant kubectl rollout status. Si le déploiement s'est terminé avec succès, kubectl rollout status renvoie un code de sortie de 0.

kubectl rollout status deployment.v1.apps/nginx-deployment

La sortie est similaire à ceci:

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out
$ echo $?
0

Déploiement échoué

Votre déploiement peut rester bloqué en essayant de déployer son nouveau ReplicaSet sans jamais terminer. Cela peut se produire en raison de certains des facteurs suivants:

Vous pouvez détecter cette condition en spécifiant un paramètre d'échéance dans votre spécification de déploiement: (.spec.progressDeadlineSeconds). .spec.progressDeadlineSeconds indique le nombre de secondes pendant lesquelles le contrôleur de déploiement attend avant d'indiquer (dans l'état de déploiement) que la progression du déploiement est au point mort.

La commande kubectl suivante définit la spécification avec progressDeadlineSeconds pour que le contrôleur signale l'absence de progression pour un déploiement après 10 minutes:

kubectl patch deployment.v1.apps/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

La sortie est similaire à ceci:

deployment.apps/nginx-deployment patched

Une fois le délai dépassé, le contrôleur de déploiement ajoute un DeploymentCondition avec les attributs suivants aux .status.conditions du déploiement:

Voir les conventions Kubernetes API pour plus d'informations sur les conditions d'état.

Note:

Kubernetes ne prend aucune mesure sur un déploiement bloqué, sauf pour signaler une condition d'état avec Reason=ProgressDeadlineExceeded. Les orchestrateurs de niveau supérieur peuvent en tirer parti et agir en conséquence, par exemple, restaurer le déploiement vers sa version précédente.

Note:

Si vous suspendez un déploiement, Kubernetes ne vérifie pas la progression par rapport à votre échéance spécifiée. Vous pouvez suspendre un déploiement en toute sécurité au milieu d'un déploiement et reprendre sans déclencher la condition de dépassement du délai.

Vous pouvez rencontrer des erreurs transitoires avec vos déploiements, soit en raison d'un délai d'attente bas que vous avez défini, soit en raison de tout autre type d'erreur pouvant être traité comme transitoire. Par exemple, supposons que votre quota soit insuffisant. Si vous décrivez le déploiement, vous remarquerez la section suivante:

kubectl describe deployment nginx-deployment

La sortie est similaire à ceci:

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

Si vous exécutez kubectl get deployment nginx-deployment -o yaml, l'état de déploiement est similaire à ceci:

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

Finalement, une fois la date limite de progression du déploiement dépassée, Kubernetes met à jour le statut et la raison de la condition de progression:

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

Vous pouvez résoudre un problème de quota insuffisant en réduisant votre déploiement, en réduisant d'autres contrôleurs que vous exécutez ou en augmentant le quota de votre namespace. Si vous remplissez les conditions de quota et que le contrôleur de déploiement termine ensuite le déploiement de déploiement, vous verrez la mise à jour de l'état du déploiement avec une condition réussie (Status=True et Reason=NewReplicaSetAvailable).

Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable

Type=Available avec Status=True signifie que votre déploiement a une disponibilité minimale. La disponibilité minimale est dictée par les paramètres spécifiés dans la stratégie de déploiement. Type=Progressing avec Status=True signifie que votre déploiement est soit au milieu d'un déploiement et qu'il progresse ou qu'il a terminé avec succès sa progression et que les nouvelles répliques minimales requises sont disponibles (voir la raison de la condition pour les détails - dans notre cas, Reason=NewReplicaSetAvailable signifie que le déploiement est terminé).

Vous pouvez vérifier si un déploiement n'a pas pu progresser en utilisant kubectl rollout status. kubectl rollout status renvoie un code de sortie différent de zéro si le déploiement a dépassé le délai de progression.

kubectl rollout status deployment.v1.apps/nginx-deployment

La sortie est similaire à ceci:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline
$ echo $?
1

Agir sur un déploiement échoué

Toutes les actions qui s'appliquent à un déploiement complet s'appliquent également à un déploiement ayant échoué. Vous pouvez le mettre à l'échelle à la hausse/baisse, revenir à une révision précédente ou même la suspendre si vous devez appliquer plusieurs réglages dans le modèle de pod de déploiement.

Politique de nettoyage

Vous pouvez définir le champ .spec.revisionHistoryLimit dans un déploiement pour spécifier le nombre d'anciens ReplicaSets pour ce déploiement que vous souhaitez conserver. Le reste sera effacé en arrière-plan. Par défaut, c'est 10.

Note:

La définition explicite de ce champ sur 0 entraînera le nettoyage de tout l'historique de votre déploiement, de sorte que le déploiement ne pourra pas revenir en arrière.

Déploiement des Canaries

Si vous souhaitez déployer des versions sur un sous-ensemble d'utilisateurs ou de serveurs à l'aide du déploiement, vous pouvez créer plusieurs déploiements, un pour chaque version, en suivant le modèle canari décrit dans gestion des ressources.

Écriture d'une spécification de déploiement

Comme pour toutes les autres configurations Kubernetes, un déploiement a besoin des champs apiVersion, kind et metadata. Pour des informations générales sur l'utilisation des fichiers de configuration, voir déploiement d'applications, configuration des conteneurs, et Utilisation de kubectl pour gérer les ressources.

Un déploiement nécessite également un .spec section.

Pod Template

Les .spec.template et .spec.selector sont les seuls champs obligatoires du .spec.

Le .spec.template est un Pod template. Il a exactement le même schéma qu'un Pod, sauf qu'il est imbriqué et n'a pas de apiVersion ou de kind.

En plus des champs obligatoires pour un pod, un Pod Template dans un déploiement doit spécifier des labels appropriées et une stratégie de redémarrage appropriée. Pour les labels, assurez-vous de ne pas chevaucher l'action d'autres contrôleurs. Voir sélecteur).

Seulement un .spec.template.spec.restartPolicy égal à Always est autorisé, ce qui est la valeur par défaut s'il n'est pas spécifié.

Répliques

.spec.replicas est un champ facultatif qui spécifie le nombre de pods souhaités. Il vaut par défaut 1.

Sélecteur

.spec.selector est un champ obligatoire qui spécifie un sélecteur de labels pour les pods ciblés par ce déploiement.

.spec.selector doit correspondre .spec.template.metadata.labels, ou il sera rejeté par l'API.

Dans la version d'API apps/v1, .spec.selector et .metadata.labels ne sont pas définis par défaut sur .spec.template.metadata.labels s'ils ne sont pas définis. Ils doivent donc être définis explicitement. Notez également que .spec.selector est immuable après la création du déploiement dans apps/v1.

Un déploiement peut mettre fin aux pods dont les étiquettes correspondent au sélecteur si leur modèle est différent de .spec.template ou si le nombre total de ces pods dépasse .spec.replicas. Il fait apparaître de nouveaux pods avec .spec.template si le nombre de pods est inférieur au nombre souhaité.

Note:

Vous ne devez pas créer d'autres pods dont les labels correspondent à ce sélecteur, soit directement, en créant un autre déploiement, soit en créant un autre contrôleur tel qu'un ReplicaSet ou un ReplicationController. Si vous le faites, le premier déploiement pense qu'il a créé ces autres pods. Kubernetes ne vous empêche pas de le faire.

Si vous avez plusieurs contrôleurs qui ont des sélecteurs qui se chevauchent, les contrôleurs se battront entre eux et ne se comporteront pas correctement.

Stratégie

.spec.strategy spécifie la stratégie utilisée pour remplacer les anciens pods par de nouveaux. .spec.strategy.type peut être "Recreate" ou "RollingUpdate". "RollingUpdate" est la valeur par défaut.

Déploiment Recreate

Tous les pods existants sont tués avant que de nouveaux ne soient créés lorsque .spec.strategy.type==Recreate.

Déploiement de mise à jour continue

Le déploiement met à jour les pods dans une mise à jour continue quand .spec.strategy.type==RollingUpdate. Vous pouvez spécifier maxUnavailable et maxSurge pour contrôler le processus de mise à jour continue.

Max non disponible

.spec.strategy.rollingUpdate.maxUnavailable est un champ facultatif qui spécifie le nombre maximal de pods qui peuvent être indisponibles pendant le processus de mise à jour. La valeur peut être un nombre absolu (par exemple, 5) ou un pourcentage des pods souhaités (par exemple, 10%). Le nombre absolu est calculé à partir du pourcentage en arrondissant vers le bas. La valeur ne peut pas être 0 si .spec.strategy.rollingUpdate.maxSurge est 0. La valeur par défaut est 25%.

Par exemple, lorsque cette valeur est définie sur 30%, l'ancien ReplicaSet peut être réduit à 70% des pods souhaités immédiatement au démarrage de la mise à jour continue. Une fois que les nouveaux pods sont prêts, l'ancien ReplicaSet peut être réduit davantage, suivi d'une augmentation du nouveau ReplicaSet, garantissant que le nombre total de pods disponibles à tout moment pendant la mise à jour est d'au moins 70% des pods souhaités.

Max Surge

.spec.strategy.rollingUpdate.maxSurge est un champ facultatif qui spécifie le nombre maximal de pods pouvant être créés sur le nombre de pods souhaité. La valeur peut être un nombre absolu (par exemple, 5) ou un pourcentage des pods souhaités (par exemple, 10%). La valeur ne peut pas être 0 si MaxUnavailable est 0. Le nombre absolu est calculé à partir du pourcentage en arrondissant. La valeur par défaut est 25%.

Par exemple, lorsque cette valeur est définie sur 30%, le nouveau ReplicaSet peut être mis à l'échelle immédiatement au démarrage de la mise à jour continue, de sorte que le nombre total d'anciens et de nouveaux pods ne dépasse pas 130% des pods souhaités. Une fois que les anciens pods ont été détruits, le nouveau ReplicaSet peut être augmenté davantage, garantissant que le nombre total de pods en cours d'exécution à tout moment pendant la mise à jour est au maximum de 130% des pods souhaités.

Progress Deadline Seconds

.spec.progressDeadlineSeconds est un champ facultatif qui spécifie le nombre de secondes pendant lesquelles vous souhaitez attendre que votre déploiement progresse avant que le système ne signale que le déploiement a échoué - refait surface comme une condition avec Type=Progressing, Status=False et Reason=ProgressDeadlineExceeded dans l'état de la ressource. Le contrôleur de déploiement continuera de réessayer le déploiement. À l'avenir, une fois la restauration automatique implémentée, le contrôleur de déploiement annulera un déploiement dès qu'il observera une telle condition.

S'il est spécifié, ce champ doit être supérieur à .spec.minReadySeconds.

Min Ready Seconds

.spec.minReadySeconds est un champ facultatif qui spécifie le nombre minimum de secondes pendant lequel un pod nouvellement créé doit être prêt sans qu'aucun de ses conteneurs ne plante, pour qu'il soit considéré comme disponible. Cette valeur par défaut est 0 (le pod sera considéré comme disponible dès qu'il sera prêt). Pour en savoir plus sur le moment où un pod est considéré comme prêt, consultez Sondes de conteneur.

Rollback To

Le champ .spec.rollbackTo est obsolète dans les versions d'API extensions/v1beta1 et apps/v1beta1 et n'est plus pris en charge dans les versions d'API commençant par apps/v1beta2. Utilisez, kubectl rollout undo pour Revenir à une révision précédente.

Limite de l'historique des révisions

L'historique de révision d'un déploiement est stocké dans les ReplicaSets qu'il contrôle.

.spec.revisionHistoryLimit est un champ facultatif qui spécifie le nombre d'anciens ReplicaSets à conserver pour permettre la restauration. Ces anciens ReplicaSets consomment des ressources dans etcd et encombrent la sortie de kubectl get rs. La configuration de chaque révision de déploiement est stockée dans ses ReplicaSets; par conséquent, une fois un ancien ReplicaSet supprimé, vous perdez la possibilité de revenir à cette révision du déploiement. Par défaut, 10 anciens ReplicaSets seront conservés, mais sa valeur idéale dépend de la fréquence et de la stabilité des nouveaux déploiements.

Plus précisément, la définition de ce champ à zéro signifie que tous les anciens ReplicaSets avec 0 réplicas seront nettoyés. Dans ce cas, un nouveau panneau déroulant Déploiement ne peut pas être annulé, car son historique de révision est nettoyé.

Paused

.spec.paused est un champ booléen facultatif pour suspendre et reprendre un déploiement. La seule différence entre un déploiement suspendu et un autre qui n'est pas suspendu, c'est que toute modification apportée au PodTemplateSpec du déploiement suspendu ne déclenchera pas de nouveaux déploiements tant qu'il sera suspendu. Un déploiement n'est pas suspendu par défaut lors de sa création.

Alternative aux déploiements

kubectl rolling-update

kubectl rolling-update met à jour les pods et les ReplicationControllers de la même manière. Mais les déploiements sont recommandés, car ils sont déclaratifs, côté serveur et ont des fonctionnalités supplémentaires, telles que la restauration de toute révision précédente même après la mise à jour progressive..

2.3 - StatefulSets

StatefulSet est l'objet de l'API de charge de travail utilisé pour gérer des applications avec état (stateful).

Gère le déploiement et la mise à l'échelle d'un ensemble de Pods, et fournit des garanties sur l'ordre et l'unicité de ces Pods.

Comme un Déploiement, un StatefulSet gère des Pods qui sont basés sur une même spécification de conteneur. Contrairement à un Deployment, un StatefulSet maintient une identité pour chacun de ces Pods. Ces Pods sont créés à partir de la même spec, mais ne sont pas interchangeables : chacun a un identifiant persistant qu'il garde à travers tous ses re-scheduling.

Si vous voulez utiliser des volumes de stockage pour fournir de la persistance à votre charge de travail, vous pouvez utiliser un StatefulSet comme partie de la solution. Même si des Pods individuels d'un StatefulSet sont susceptibles d'échouer, les identifiants persistants des Pods rendent plus facile de faire correspondre les volumes existants aux nouveaux Pods remplaçant ceux ayant échoué.

Utiliser des StatefulSets

Les StatefulSets sont utiles pour des applications qui nécessitent une ou plusieurs des choses suivantes :

Ci-dessus, stable est synonyme de persistance suite au (re)scheduling de Pods. Si une application ne nécessite aucun identifiant stable ou de déploiement, suppression ou mise à l'échelle stables, vous devriez déployer votre application en utilisant un objet de charge de travail fournissant un ensemble de réplicas sans état (stateless).

Un Deployment ou ReplicaSet peut être mieux adapté pour vos applications sans état.

Limitations

Composants

L'exemple ci-dessous décrit les composants d'un StatefulSet.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # doit correspondre à .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # est 1 par défaut
  template:
    metadata:
      labels:
        app: nginx # doit correspondre à .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi

Dans l'exemple ci-dessus :

Le nom d'un objet StatefulSet doit être un nom de sous-domaine DNS valide.

Sélecteur de Pod

Vous devez renseigner le champ .spec.selector d'un StatefulSet pour qu'il corresponde aux labels de son .spec.template.metadata.labels. Avant Kubernetes 1.8, le champ .spec.selector était mis par défaut s'il était omis. Pour les versions 1.8 et ultérieures, ne pas spécifier de sélecteur de Pod résulte en une erreur de validation lors de la création du StatefulSet.

Identité du Pod

Les Pods d'un StatefulSet ont une identité unique comprenant un ordinal, une identité réseau stable et un stockage stable. L'identité est accrochée au Pod, indépendamment du noeud sur lequel il est (re)programmé.

Index Ordinal

Pour un StatefulSet avec N réplicas, chaque Pod du StatefulSet se verra assigné un ordinal entier, de 0 à N-1, unique sur l'ensemble des pods.

ID réseau stable

Chaque Pod dans un StatefulSet dérive son nom d'hôte du nom du StatefulSet et de l'ordinal du Pod. Le modèle pour le nom d'hôte généré est $(nom statefulset)-$(ordinal). L'exemple ci-dessus créera trois Pods nommés web-0,web-1,web-2. Un StatefulSet peut utiliser un Service Headless pour contrôler le domaine de ses Pods. Le domaine pris en charge par ce Service prend la forme : $(nom service).$(namespace).svc.cluster.local, où "cluster.local" est le domaine du cluster. Chaque fois qu'un Pod est créé, il obtient un sous-domaine DNS correspondant, prenant la forme : $(nom pod).$(domaine du service gouvernant), où le service gouvernant est défini par le champ serviceName du StatefulSet.

En fonction de la façon dont est configuré le DNS dans votre cluster, vous ne pourrez peut-être pas rechercher immédiatement le nom DNS d'un pod nouvellement exécuté. Ce problème peut se produire lorsque d'autres clients dans le cluster ont déjà envoyé des requêtes pour le nom d'hôte du Pod avant sa création. La mise en cache négative (normale pour le DNS) signifie que les résultats des recherches précédentes ayant échoué sont mémorisés et réutilisés, même après que le Pod ait démarré, pendant au moins quelques secondes.

Si vous avez besoin de découvrir les Pods rapidement après leur création, vous avez plusieurs options :

Comme mentionné dans la section limitations, vous êtes responsable de créer le Service Headless responsable de l'identité réseau des Pods.

Voici quelques exemples de choix pour le domaine du cluster, le nom du service, le nom du StatefulSet et comment cela affecte les noms DNS des pods du StatefulSet.

Domaine Cluster Service (ns/nom) StatefulSet (ns/nom) Domaine StatefulSet DNS Pod Nom d'hôte
cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1}
cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1}
kube.local foo/nginx foo/web nginx.foo.svc.kube.local web-{0..N-1}.nginx.foo.svc.kube.local web-{0..N-1}

Note:

Le domaine cluster sera cluster.local à moins qu'il soit configuré autrement.

Stockage stable

Kubernetes crée un PersistentVolume pour chaque VolumeClaimTemplate. Dans l'exemple nginx ci-dessus, chaque Pod se verra affecter un unique PersistentVolume avec un StorageClass de my-storage-class et 1 GiB de stockage provisionné. Si aucun StorageClass n'est spécifié, alors le StorageClass par défaut sera utilisé. Lorsqu'un Pod est (re)schedulé sur un noeud, ses volumeMounts montent les PersistentVolumes associés aux
PersistentVolumeClaims. Notez que les PersistentVolumes associés avec les PersistentVolumeClaims des Pods ne sont pas supprimés lorsque les Pods, ou le StatefulSet, sont supprimés. Ceci doit être fait manuellement.

Étiquette du nom de Pod

Lorsque le StatefulSet Contrôleur crée un Pod, il ajoute une étiquette, statefulset.kubernetes.io/pod-name, renseignée avec le nom du Pod. Cette étiquette vous permet d'attacher un Service à un Pod spécifique du StatefulSet.

Garanties de déploiement et de mise à l'échelle

Le StatefulSet ne devrait pas spécifier un pod.Spec.TerminationGracePeriodSeconds à 0. Cette pratique est dangereuse et fortement déconseillée. Pour plus d'explications, veuillez vous référer à forcer la suppression de Pods de StatefulSet.

Lorsque l'exemple nginx ci-dessus est créé, trois Pods seront déployés dans l'ordre web-0, web-1, web-2. web-1 ne sera pas déployé avant que web-0 soit Running et Ready, et web-2 ne sera pas déployé avant que web-1 soit Running et Ready. Si web-0 venait à échouer, après que web-1 soit Running et Ready, mais avant que web-2 soit lancé, web-2 ne serait pas lancé avant que web-0 soit correctement relancé et redevienne Running et Ready.

Si un utilisateur venait à mettre à l'échelle l'exemple déployé en patchant le StatefulSet pour que replicas=1, web-2 serait terminé en premier. web-1 ne serait pas terminé avant que web-2 ne soit complètement arrêté et supprimé. Si web-0 venait à échouer après que web-2 soit terminé et complètement arrêté, mais avant que web-1 soit terminé, web-1 ne serait pas terminé avant que web-0 soit Running et Ready.

Politiques de gestion d'un Pod

Dans Kubernetes 1.7 et ultérieurs, le StatefulSet vous permet d'assouplir ses garanties d'ordre, tout en préservant ses garanties d'unicité et d'identité via son champ .spec.podManagementPolicy.

Gestion de Pod OrderedReady

La gestion de Pod OrderedReady est la valeur par défaut pour les StatefulSets. Il implémente le comportement décrit ci-dessus.

Gestion de Pod Parallel

La gestion de Pod Parallel indique au contrôleur de StatefulSet de lancer ou terminer tous les Pods en parallèle, et de ne pas attendre que les Pods deviennent Running et Ready ou complètement terminés avant de lancer ou terminer un autre Pod. Cette option affecte seulement le comportement pour les opérations de mise à l'échelle. Les mises à jour ne sont pas affectées.

Stratégies de mise à jour

Dans Kubernetes 1.7 et ultérieurs, le champ .spec.updateStrategy d'un StatefulSet vous permet de configurer et désactiver les rolling updates automatisés pour les conteneurs, étiquettes, requête/limites de ressources, et annotations pour les Pods d'un StatefulSet.

On Delete

La stratégie de mise à jour OnDelete implémente l'ancien comportement (1.6 et précédents). Lorsque .spec.updateStrategy.type d'un StatefulSet est mis à OnDelete, le contrôleur de StatefulSet ne mettra pas à jour automatiquement les Pods dans un StatefulSet. Les utilisateurs doivent supprimer manuellement les Pods pour forcer le contrôleur à créer de nouveaux Pods qui réflètent les modifications faites à un .spec.template d'un StatefulSet.

Rolling Updates

La stratégie de mise à jour RollingUpdate implémente le rolling update automatisé pour les Pods d'un StatefulSet. C'est la stratégie par défaut lorsque .spec.updateStrategy n'est pas spécifié. Lorsqu'un .spec.updateStrategy.type d'un StatefulSet est mis à RollingUpdate, le contrôleur de StatefulSet va supprimer et recréer chaque Pod d'un StatefulSet. Il va procéder dans le même ordre que pour la terminaison d'un Pod (de l'ordinal le plus grand au plus petit), mettant à jour chaque Pod, un seul à la fois. Il va attendre qu'un Pod mis à jour soit Running et Ready avant de mettre à jour son prédécesseur.

Partitions

La stratégie de mise à jour RollingUpdate peut être partitionnée, en spécifiant une .spec.updateStrategy.rollingUpdate.partition. Si une partition est spécifiée, tous les Pods ayant un ordinal plus grand ou égal à la partition seront mis à jour lorsque le .spec.template du StatefulSet sera mis à jour. Tous les Pods ayant un ordinal inférieur à la partition ne sera pas mis à jour, et, même s'ils sont supprimés, ils seront recréés avec l'ancienne version. Si une .spec.updateStrategy.rollingUpdate.partition d'un StatefulSet est plus grand que son .spec.replicas, les mises à jour de son .spec.template ne seront pas propagés à ses Pods. Dans la plupart des cas vous n'aurez pas à utiliser de partition, mais elles sont utiles si vous désirez organiser une mise à jour, déployer une version canari, ou effectuer un déploiement par étapes.

Rollback forcé

En utilisant des Rolling Updates avec la politique de gestion d'un Pod par défaut (OrderedReady), il est possible de se retrouver dans un état inconsistant nécessitant une intervention manuelle pour réparation.

Si vous mettez à jour le template de Pod dans une configuration qui ne devient jamais Running et Ready (par exemple, du fait d'un mauvais binaire ou d'une erreur de configuration au niveau de l'application), le StatefulSet va arrêter le rollout et attendre.

Dans cet état, il n'est pas suffisant de revenir à une bonne configuration du template de Pod. En raison d'une erreur connue, le StatefulSet va continuer à attendre que le Pod en échec Pod devienne Ready (ce qui n'arrive jamais) avant qu'il tente de revenir à la bonne configuration.

Après être revenu au bon template, vous devez aussi supprimer tous les Pods que le StatefulSet avait déjà essayé de démarrer avec la mauvaise configuration. Le StatefulSet va alors commencer à recréer les Pods en utilisant le bon template.

A suivre