Manuel de travail avec Kubernetes

Un guide pour faciliter votre travail avec Kubernetes. Il contient des explications simples, des exemples et des conseils pour vous aider à comprendre les concepts importants, l'outil kubectl et faciliter la création et la gestion de nombreuses ressources Kubernetes.

Manuel de travail avec Kubernetes
Photo par NIR HIMI / Unsplash

Aide et conseils

Commande Kubectl

Utilisez les commandes suivantes et recherchez la section : Examples

# Get help and usage examples on kubectl commands
kubectl -h
kubectl $subcommand [$resource_name] -h

Notez que toutes les ressources Kubernetes ne peuvent pas être créées à l'aide des commandes kubectl. Utilisez la commande suivante pour obtenir la liste des ressources qui peuvent être créées de cette manière :

# Find resources that can be created with the kubectl command
kubectl create -h

Il existe également des sous-commandes kubectl qui créent des ressources que nous ne pouvons pas créer directement à l'aide de « kubectl create ». La sous-commande « expose » en est un exemple, pour créer des ressources de services Kubernetes et « run » pour créer des pods.

Pour générer des manifestes de ressources à partir des commandes kubectl, qui peuvent ensuite être adaptés et déployés, utilisez les commandes suivantes :

# Generate resources manifests from kubectl commands
kubectl create ... --save-config=true --dry-run=client -o yaml > $manifest_file

# Create resources from generated manifest file
kubectl apply -f $manifest_file

Documentation des champs de ressources

Utilisez les commandes kubectl suivantes pour obtenir de la documentation sur les champs de ressources Kubernetes :

# Get Kubernetes resources fields documentation
kubectl explain $resource_name
kubectl explain $resource_name.$resource_field

# For help on how to use that command
kubectl explain -h

Nous pouvons également utiliser la documentation de référence de l'API des ressources Kubernetes en ligne : Référence de l'API des ressources Kubernetes

Configuration de Kubectl

Fichier de configuration Kubectl

Le fichier de configuration utilisé par kubectl pour l'authentification et l'interaction avec différents clusters Kubernetes est « ~/.kube/config » par défaut. Dans ce fichier, nous trouverons les paramètres de configuration des clusters Kubernetes tels que les points de terminaison et les certificats des serveurs API, les utilisateurs et les certificats des clients, etc.

Kubectl voit une configuration de cluster à partir de son fichier de configuration par défaut ('~/.kube/config') comme contexte.

Définir le contexte actuel pour kubectl

Pour indiquer à kubectl d’utiliser un cluster spécifique pour l’interaction, nous définissons le contexte en conséquence.

# Set the current context to kubernetes-admin@kubernetes1
$ kubectl config use-context kubernetes-admin@kubernetes1
Switched to context "kubernetes-admin@kubernetes1".

Liste des contextes disponibles pour kubectl

Pour lister les contextes kubectl disponibles, nous utilisons :

# List available contexts 
$ kubectl config get-contexts
CURRENT   NAME                           CLUSTER       AUTHINFO            NAMESPACE
*         kubernetes-admin@kubernetes1   kubernetes1    kubernetes-admin
          kubernetes-admin@kubernetes2   kubernetes2   kubernetes-admin
          kubernetes-admin@kubernetes3   kubernetes3   kubernetes-admin

Le contexte que nous utilisons actuellement est marqué d'un *.

Afficher le contexte actuel pour kubectl

Nous pouvons également utiliser la commande suivante pour afficher le contexte actuel :

# Show the current context
$ kubectl config current-context
kubernetes-admin@kubernetes1

Définir l'espace de noms par défaut pour le contexte actuel de kubectl

Pour définir l'espace de noms par défaut de kubectl pour le contexte actuel, nous utilisons :

# Set the default namespace for the current kubectl context
$ kubectl config set-context kubernetes-admin@kubernetes1 --namespace=not_default
Context "kubernetes-admin@kubernetes1" modified.

# Verify
$ kubectl get pods
No resources found in not_default namespace.

Afficher la configuration de kubectl

Pour afficher le contenu du fichier de configuration kubectl, utilisez :

# View kubectl configuration file
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://172.28.247.116:6443
  name: kubernetes1
contexts:
- context:
    cluster: kubernetes1
    namespace: not_default
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes1
current-context: kubernetes-admin@kubernetes1
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: DATA+OMITTED
    client-key-data: DATA+OMITTED

Gestion des pods

Voir Comprendre les pods Kubernetes pour un aperçu du fonctionnement des Pods.

Pod simple

pod

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  containers:
  - name: nginx
    image: nginx

Pod sélectionnant des nœuds spécifiques

pod.spec.nodeName

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  nodeName: k8s-worker1
  containers:
  (...)

pod.spec.nodeSelector

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  nodeSelector: 
    kubernetes.io/hostname: k8s-worker1
  containers:
  (...)

pod.spec.tolerations

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  tolerations:
  - key: nodepool
    value: monitoring
    effect: NoSchedule
  containers:
  (...)

Pod avec conteneur utilisant une commande

pod.spec.containers.command

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  containers:
  - name: busybox
    image: busybox
    command:
    - sh
    - -c
    - while true; do sleep 10; done

Cette commande de veille (sleep) est utile pour s'assurer que le pod reste à l'état Running .

Pod avec volume + conteneur side-car

pod.spec.volumes | pod.spec.volumeMounts

Un conteneur sidecar est un deuxième conteneur à l'intérieur d'un pod qui fournit des fonctionnalités supplémentaires. Dans l'exemple de manifeste ci-dessous, les journaux (logs) du conteneur principal sont stockés dans un fichier. Le conteneur sidecar est utilisé pour générer ces logs sur la sortie standard (stdout), afin que nous puissions les lire avec la commande « kubectl logs ».

Pour partager les fichiers de logs entre les deux conteneurs, nous utilisons un volume nommé logs, ayant comme backend du stockage éphémère « emptyDir » (données effacées après le redémarrage du pod) et montons ce volume à l'intérieur des deux conteneurs sur /output , là où notre fichier de logs app.log sera stocké.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  volumes:
  - name: logs
    emptyDir:
     medium: ""
  containers:
  - name: main
    image: busybox
    command: ['sh', '-c', 'while true; do echo "App logs" > /output/app.log; sleep 5; done']
    volumeMounts:
    - name: logs
      mountPath: /output
  - name: sidecar
    image: busybox
    command: ['tail', '-f', '/output/app.log']
    volumeMounts:
    - name: logs
      mountPath: /output

Pod avec initContainers

pod.spec.containers.initContainers

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  volumes:
  - name: statics
    emptyDir:
      medium: ""
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: statics
      mountPath: /var/www/html
  initContainers:
  - name: busybox
    image: busybox
    volumeMounts:
    - name: statics
      mountPath: /var/www/html
    command:
    - sh
    - -c
    - touch /var/www/html/init.txt

Pod utilisant des données de configMaps et des secrets

pod.spec.containers.env | pod.spec.volumes | pod.spec.containers.volumeMounts

Pour plus de détails sur la création de ressources « configMap » ou « secrets », exécutez :

kubectl create configmaps -h
kubectl create secrets -h

Utilisez les options « --save-config=true --dry-run=client -o yaml » pour générer des manifestes équivalents aux commandes si nécessaire. Pour une analyse approfondie des secrets de Kubernetes, consultez Comprendre les secrets Kubernetes.

Pour une analyse approfondie et des exemples de comportement concrets sur les différentes manières de créer des secrets, jetez un œil à Créer des secrets génériques Kubernetes. Remplacez « secret generic » par « configmap » dans toutes les commandes pour l'équivalent avec la ressource configMaps.

Ici, nous allons rendre les données des configMaps et des secrets disponibles dans notre pod sous forme de fichiers et de variables d'environnement.

Tout d’abord, créons deux exemples de configMaps et un secret :

# Create configMap from literal key=value pairs
# Will be made available inside the pod as environment variables
$ kubectl create cm app-config --from-literal=log_level=warn --from-literal=sso_client_id=dfj8473ll -n my-namespace

# Create configMap from a configuration file
# Will be made available inside the pod as a file
$ kubectl create cm nginx-config --from-file=nginx.conf -n my-namespace

# Create secret from literal key=value pairs
# Kubernetes secrets are not encrypted (just base64 encoded)
# Will be made available inside the pod as file
$ kubectl create secret generic database-creds --from-literal=db-password=mysuperpass -n my-namespace

Résultats:

$ kubectl get cm/app-config -n my-namespace -o yaml
apiVersion: v1
data:
  log_level: warn
  sso_client_id: dfj8473ll
kind: ConfigMap
metadata:
  creationTimestamp: "<creation_timestamp>"
  name: app-config
  namespace: my-namespace
  resourceVersion: "48708"
  uid: 91c9acf6-ea50-4bd7-a771-7cca36b58109
  
$ kubectl get cm/nginx-config -n my-namespace -o yaml
apiVersion: v1
data:
  nginx.conf: "http {\n    server {\n\tlisten 80;\n\tserver_name myapp.local;\n\n\tlocation
    / {\n            proxy_set_header Host $host;\n\t    proxy_set_header X-Real-IP
    $remote_addr;\n\n            proxy_pass http://127.0.0.1:9000;\n        }\n\n
    \       # Logging\n        # Log levels : debug, info, notice, warn, error, crit,
    alert, or emerg\n\t# Choosing warn for example => logging all messages of sevrity
    \n\t# warn and above (error, crit, alert and emerg)\n        access_log /var/log/nginx/myapp-access.log
    combined;\n  \terror_log /var/log/nginx/myapp-error.log warn;\n    }\n}\n"
kind: ConfigMap
metadata:
  creationTimestamp: "<creation_timestamp>"
  name: nginx-config
  namespace: my-namespace
  resourceVersion: "48761"
  uid: 41c0f2b5-f8c3-4dc5-99ca-3c132b2dd19e
  
$ kubectl get secrets/database-creds -n my-namespace -o yaml
apiVersion: v1
data:
  db-password: bXlzdXBlcnBhc3M=
kind: Secret
metadata:
  creationTimestamp: "<creation_timestamp>"
  name: database-creds
  namespace: my-namespace
  resourceVersion: "49176"
  uid: 7f13f2e8-920a-49aa-bebf-8dde4edc4dd1
type: Opaque

Voici le manifeste du Pod utilisant les données des configMaps et des secrets créés précédemment :

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  volumes:
  - name: nginx-conf
    configMap:
      name: nginx-config
      items: # optional. By default filename at mount point will be 'data key' and the content: corresponding keys 'data value' (same thing for secrets)
      - key:  nginx.conf # key of the item inside the configMap
        mode: 0755 # file mode at mount path
        path: nginx-deployed.conf # custom filname at mount path
  - name: database-creds
    secret:
      secretName: database-creds
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: nginx-conf
      mountPath: /nginx
    - name: database-creds
      mountPath: /database
    env:
    - name: LOG_LEVEL
      valueFrom:
        configMapKeyRef: # or secretKeyRef
          name: app-config # name of the configMap containing the data
          key: log_level # key inside the configMap, to get the value from
    - name: SSO_CLIENT_ID
      valueFrom:
        configMapKeyRef:
          name: app-config # name of the configMap containing the data
          key: sso_client_id # key inside the configMap, to get the value from

Regardons maintenant les données à l’intérieur du conteneur du Pod :

$ kubectl exec -it pods/my-pod -n my-namespace -- bash
root@my-pod:/#

# Nginx config
root@my-pod:/# cat nginx/nginx-deployed.conf 
http {
    server {
        listen 80;
        server_name myapp.local;

        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;

            proxy_pass http://127.0.0.1:9000;
        }

        # Logging
        # Log levels : debug, info, notice, warn, error, crit, alert, or emerg
        # Choosing warn for example => logging all messages of sevrity
        # warn and above (error, crit, alert and emerg)
        access_log /var/log/nginx/myapp-access.log combined;
        error_log /var/log/nginx/myapp-error.log warn;
    }
}

# Database secret
root@my-pod:/# cat database/db-password 
mysuperpass

# Environment variables
root@my-pod:/# env | grep -E "SSO|LOG"
LOG_LEVEL=warn
SSO_CLIENT_ID=dfj8473ll

Pod utilisant des sondes et une politique de redémarrage

Champs du cycle de vie des pods | pod.spec.containers.xProbes | pod.spec.containers.restartPolicy

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: my-namespace
spec:
  containers:
  - name: nginx
    image: nginx
    restartPolicy: Always
    livenessProbe:
      exec:
        command:
        - sh
        - -c
        - cat /etc/nginx/nginx.conf
    readinessProbe:
      httpGet:
        port: 80
        scheme: HTTP
        path: /
$ kubectl get pods -n my-namespace -o wide
NAME     READY   STATUS    RESTARTS   AGE     IP            NODE          NOMINATED NODE   READINESS GATES
my-pod   1/1     Running   0          2m46s   10.244.1.12   k8s-worker1   <none>           <none>
  • La routine shell Running dans la colonne STATUS indique une réussite de la sonde livenessProbe
  • La routine shell 1/1 dans la colonne READY indique une réussite de la sonde ReadinessProbe , pour le conteneur unique à l'intérieur du Pod
  • Une fois la sonde ReadinessProbe réussie pour un conteneur spécifique d'un Pod, le point de terminaison du conteneur : endpoint (ip:port) est ajouté aux points de terminaison des services associés, au cas où il y aurait des services qui redirigeraient du trafic vers le pod. Pour en savoir plus sur les services et les points de terminaison Kubernetes, consultez la section Gestion des services et ingress

Déploiements

Documentation des déploiements | Référence de l'API des déploiements

Cas d'utilisation

Le déploiement (la ressource deployment) peut être utilisé pour gérer un ensemble de pods identiques sans état. Il crée un replicaset sous le capot pour gérer le nombre de pods répliqués.

Créer des déploiements
# Create deployment or generate command equivalent manifest
$ kubectl create deploy my-deploy --image=nginx --replicas=2 -n my-namespace [--save-config=true --dry-run=client -o yaml > deploy.yaml]
Fonctionnement

Le champ « .spec.template » des déploiements permet de définir un modèle pour les pods créés et gérés par le déploiement. La valeur du champ « .spec.selector.matchLabels » doit correspondre à celle du champ « .spec.template.metadata.labels » pour que le déploiement sélectionne correctement les pods à gérer.

Chaque fois que le modèle de pods de déploiement ('.spec.template') change, une nouvelle révision de déploiement est créée. Par exemple, l'ajout de réplicas supplémentaires à un déploiement ne crée pas de nouvelle révision, car seul le champ '.spec.replicas' change, et non '.spec.template'. Une nouvelle révision crée un nouveau replicaset, qui est progressivement mis à l'échelle pour correspondre au nombre de réplicas du déploiement, tandis que les anciens réplicas sont progressivement réduits à zéro.

Les noms des replicaset sont sous la forme :

  • <deployment_name>-<replicaset_hash>

Le hash du replicaset gérant les pods du déploiement se retrouve également dans les noms des pods qui se présentent sous la forme suivante :

  • <deployment_name>-<replicaset_hash>-<pod_hash>
Mises à jour

Ce n'est que lorsque le champ de modèle de pods de déploiement ('.spec.template') change qu'une nouvelle révision du déploiement est créée pour remplacer celle existante. La stratégie par défaut utilisée pour remplacer les pods de la révision existante, spécifiée dans le champ '.spec.strategy.type' du déploiement est 'RollingUpdates'. Pour en savoir plus sur les stratégies de mise à jour pour la ressource déploiement, consultez : stratégies de mise à jour du déploiement.

Avec « RollingUpdates », les anciennes révisions des pods sont progressivement remplacées par les nouvelles. Un certain nombre de pods de la nouvelle révision sont d'abord ajoutés et, en cas de succès, le même nombre de pods est supprimé de l'ancienne révision.

Le nombre de nouveaux pods de révision ajoutés est appelé « max surge » et peut être défini avec le champ de déploiement « .spec.strategy.rollingUpdate.maxSurge » (valeur par défaut à 25 % du nombre de réplica du déploiement).

Le nombre d'anciens pods de révision supprimés est appelé « max unavailable » et peut être défini avec le champ de déploiement « .spec.strategy.rollingUpdate.maxUnavailable » (valeur par défaut à 25 % du nombre de réplica du déploiement).

Voici un exemple de mise à jour du déploiement :

# Update deployments pods containers image
kubectl set image deployment/nginx-deployment nginx=nginx:1.161

# See progress
kubectl rollout status deployment/nginx-deployment [--timeout <seconds>]

Ici vous trouverez tous les statuts du déploiement disponibles et ce qu'ils signifient.

Pour apporter plusieurs modifications au champ « .spec.template » des déploiements sans déclencher la création de nouvelles révisions, nous pouvons procéder comme suit :

# Pause deployment updates
kubectl rollout pause deployment/nginx-deployment

# then, do all changes, exemple:
kubectl set image deployment/nginx-deployment nginx=nginx:1.17.x
kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi

# to apply all changes at once, run
kubectl rollout resume deployment/nginx-deployment

Pour ajouter davantage de réplica à un déploiement, manuellement ou automatiquement, utilisez :

# Manual scale
kubectl scale deployment/nginx-deployment --replicas=4

# Automated scaling with HPA (Horizontal Pod Autoscaler)
kubectl autoscale deployment/nginx-deployment --min=2 --max=8 --cpu-percent=80

Pour obtenir des informations sur les révisions :

# See deployments updates history
kubectl rollout history deployment/nginx-deployment

# See specific revision details
kubectl rollout history deployment/nginx-deployment --revision=2

Pour mettre à jour la limite de l'historique des révisions, utilisez le champ « .spec.revisionHistoryLimit » du déploiement .

Pour restaurer une ancienne révision du déploiement, utilisez :

# Rollback to the previous revision
kubectl rollout undo deployment/nginx-deployment

# Rollback to a specific revision
kubectl rollout undo deployment/nginx-deployment --to-revision=2

Lors du retour aux premières révisions, seule la partie du modèle de pods de déploiement ('.spec.template') est restaurée.

Pour les champs « .spec » de déploiement supplémentaires, consultez Rédiger une spécification de déploiement.

StatefulSets

Documentation des statefulsets | Référence de l'API des statefulsets

Cas d'utilisation

Le statefulset peut être utilisé pour créer et gérer un ensemble de pods qui nécessitent un stockage persistant stable ou une identité réseau unique et stable.

Stable signifie qu'une fois qu'un stockage persistant (PV + PVC) ou une identité réseau (DNS / Hostname) a été attribué aux pods du statefulset, nous avons la garantie qu'ils resteront les mêmes après la (re)planification des pods.

Les pods Stafulsets ne sont pas interchangeables : chacun possède un identifiant persistant qu'il conserve lors de toute reprogrammation.

Ordre des pods du statefulset

Les pods d'un statefulset sont par défaut numérotés de 0 à N-1, N correspondant au nombre de pods de réplication. Leurs noms se présentent sous la forme suivante :

  • Noms des pods du statefulset : ${statefulset_name}-${ordinal_number}

Le nombre ordinal à partir duquel démarrer (0 par défaut) est configurable. Jetez un œil à numérotation du XNUMXer pod du statefulset si intéressé.

Le contrôleur du statefulset ajoute également les deux labels suivants aux pods du statefulset :

  • app.kubernetes.io/pod-index: numéro ordinal du pod
  • statefulset.kubernentes.io/pod-name: nom du pod. Donne la possibilité d'attacher uniquement des pods spécifiques du statefulset aux services Kubernetes (les services sélectionnent les pods par labels)

Les pods d'un statefulset, lors du déploiement ou de la mise à l'échelle, sont créés séquentiellement de 0 à N-1 (N = nombre de pods réplica) et supprimés dans l'ordre inverse, de N-1 à 0. Ce comportement est défini par la politique de gestion des pods du statefulset.

Avant qu'un nouveau pod ne soit ajouté au statefulset, tous ses prédécesseurs doivent être « En cours d'exécution » et « Prêts ». Avant qu'un pod ne soit supprimé du statefulset, tous ses successeurs doivent être complètement arrêtés.

Identité réseau

L'identité réseau stable des pods du statefulset est fournie par un service headless. Pour en savoir plus sur les services headless, consultez services sans tête kubernetes. Contrairement aux services Kubernetes classiques qui équilibrent la charge du trafic vers les pods, l'objectif d'un service headless est de fournir une identité réseau fixe aux pods backend. Cette identité réseau stable est simplement un nom DNS unique pour chaque pod du statefulset. Ce nom DNS a la forme suivante :

  • ${pod_name}.${headless_service_name}.${namespace}.svc.${cluster_domain}

${cluster_domain} est le nom de domaine configuré pour votre cluster Kubernetes, cluster.local par exemple.

Le service headless doit exister avant la création du statefulset. Il s'agit simplement d'un service de type ClusterIP, avec ClusterIP défini sur None. Voici un exemple de manifeste :

apiVersion: v1
kind: Service
metadata:
  name: mysts-headless-service
  namespace: myns
  labels:
    app: mysts
spec:
  ports:
  - port: 80
    targetPort: 80
    name: http
  selector: 
    app: mysts
  clusterIP: None # the None value here makes the service headless

Lors de la création du statefulset, le nom du service headless doit être spécifié dans le champ « .spec.serviceName » comme suit :

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysts
  namespace: myns
  labels:
    app: mysts
spec:
  replicas: 2
  serviceName: "mysts-headless-service"
  (...)

Tous les champs que nous pouvons utiliser dans les manifestes de statefulset sont décrits ici : Référence API du Statefulset .

Stockage persistant

Le stockage persistant qui sera utilisé par les pods du statefuset est défini avec « .spec.volumeClaimTemplates ». Dans ce champ, nous définissons une liste de PersistentVolumeClaims (PVC) qui seront utilisés par chacun des pods du statefulset.

Ces PVC utiliseront soit une classe de stockage ('storageClassName') pour le provisionnement de stockage dynamique, soit référenceront directement un stockage déjà provisionné à l'aide de 'volumeName'. Voici quelques exemples :

# Dynamic storage provisioning

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysts
  namespace: myns
  labels:
    app: mysts
spec:
  (...)
  volumeClaimTemplates:
  - metadata:
      name: mypvc
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "standard"
      resources:
        requests:
          storage: 500Gi

# Static storage provisioning 

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysts
  namespace: myns
  labels:
    app: mysts
spec:
  (...)
  volumeClaimTemplates:
  - metadata:
      name: mypvc
    spec:
      accessModes: [ "ReadWriteOnce" ]
      volumeName: "mypv" # the name of an alredy existing PV

Dans le premier cas, un stockage persistant (représenté par une ressource PersistentVolume) de la classe de stockage définie sera créé pour chacun des pods du statefulset. Dans le second cas, le stockage persistant unique référencé (représenté par une ressource PersistentVolume) sera mis à disposition de tous les pods du statefulset.

Une fois que le stockage persistant est correctement défini dans « .spec.volumeClaimTemplates », le modèle de pods du statefulset « .spec.template » peut alors être utilisé pour monter le stockage persistant disponible pour chaque conteneur de pods à l'aide de « .spec.template.spec.containers[x].volumeMounts ». Voici un exemple :

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysts
  namespace: myns
  labels:
    app: mysts
spec:
  replicas: 2
  serviceName: "mysts-headless-service"
  selector:
    matchLabels:
      app: mysts
  template:
    metadata:
      labels:
        app: mysts
    spec:
      containers:
      - name: mysts
        image: mysts:latest
        volumeMounts:
          - mountPath: /var/local
            name: mypvc # name of the PVC defined spec.volumeClaimTemplates
    (...)

Voici la spécification de l'API pour le modèle de pods : Spécification du modèle de pod

Mises à jour

La stratégie par défaut utilisée pour mettre à jour un statefulset consiste à effectuer des mises à jour progressives, qui déploient les pods du statefulset un par un, des ordinaux les plus élevés aux plus bas. Le contrôleur du statefulset attend qu'un pod mis à jour soit en cours d'exécution et prêt avant de mettre à jour son prédécesseur.

La stratégie de mise à jour des statefulsets est contrôlée par le champ '.spec.updateStrategy.type'. En plus de la stratégie 'RollingUpdate', il existe également la stratégie 'OnDelete', qui indique aux statefulsets de ne pas mettre à jour immédiatement les pods, mais d'attendre plutôt une action de l'utilisateur (suppression d'un pod par exemple) avant qu'un pod ne soit mis à jour.

La mise à jour progressive des pods du statefulsets peut également être partitionnée, ce qui permet de mettre à jour automatiquement uniquement les pods dont le numéro ordinal est supérieur ou égal au numéro de partition. Pour plus de détails, consultez RollingUpdate partitionné du statefulset.

Dans certains cas rares, le statefulset peut être bloqué dans un état ambiguë nécessitant une intervention manuelle. Pour plus de détails, consultez état ambiguë du statefulset.

DaemonSets

Documentation des daemonsets | Référence de l'API des daemonsets

Cas d'utilisation

Nous utilisons des daemonsets si nous voulons nous assurer que les pods s'exécutent sur tous les nœuds du cluster Kubernetes. N'oubliez pas que les nœuds du plan de contrôle ont, par défaut, des « taints » pour éviter que les pods ne s'exécutent sur eux. Les pods Daemonsets ne font pas exception. Ainsi, pour garantir que nos pods Daemonsets s'exécutent sur tous les nœuds, y compris les nœuds maîtres du cluster, nous devons tolérer les « taints » nécessaires, par exemple :

  • node-role.kubernetes.io/control-plane, Exists, NoSchedule
  • node-role.kubernetes.io/master, Exists, NoSchedule

Un ensemble de tolérations est ajouté par défaut aux pods daemonsets. Pour obtenir une liste, consultez tolérations par défaut ajoutées au daemonset.

Gestion des services et ingress

Qu'est-ce qu'un service Kubernetes ?

Les Services Kubernetes exposent un ensemble de pods d'applications Kubernetes. Au lieu de communiquer directement avec les pods d'applications, nous communiquons avec les services qui équilibrent la charge du trafic vers les pods. Quel que soit le nombre de pods, l'application sera accessible de la même manière.

Les pods backend qui doivent être atteints via le service sont identifiés par leurs étiquettes/labels, connus du service. La ressource Endpoints est également créée et associée au service. Cette ressource de points de terminaison conserve un ensemble de IP:PORTdes pods backends à atteindre via le service (trafic de couche 4 réseau).

Il existe différents types de services qui couvrent différents besoins d’exposition dont nous allons parler ensuite.

Qu'est-ce qu'un ingress Kubernetes ?

Les Ingress Kubernetes exposent les applications Kubernetes via les protocoles HTTP et HTTPS de la couche 7 réseau. Principalement utilisé pour rendre les applications Web accessibles depuis l'extérieur du cluster.

Les Objets ingress sont créés dans des espaces de noms (namespace) spécifiques et contiennent des règles qui définissent où (sur quel service/port backend) envoyer le trafic HTTP/HTTPS ciblant des noms d'hôtes/noms de domaine spécifiques.

Les règles de routage HTTP/HTTPS définies dans les objets Ingress sont implémentées par contrôleur ingress. Les contrôleurs ingress sont simplement des pods, exposés via des équilibreurs de charge (dans le cloud) ou des services NodePort, qui reçoivent le trafic HTTP(S) et le transmettent aux services d'applications conformément aux règles ingress.

Il existe de nombreux contrôleurs d'entrée qui peuvent être déployés dans nos clusters Kubernetes (traefik, nginx, ingress-gce dans GCP...). Chaque contrôleur d'entrée examine 'spec.ingressClassName' dans les objets d'entrée, et seulement si ce champ correspond au nom du contrôleur, le contrôleur implémente les règles de routage.

Service ClusterIP

Accessible uniquement depuis l'intérieur du cluster. Pour exposer une ressource (pods, déploiements, statefulsets...) via un service ClusterIP, utilisez ceci :

# Create a ClusterIP service for a deployment or generate manifest
$ kubectl expose deployment/my-deploy -n my-namespace --name my-deploy-service --type=ClusterIP --port=80 --target-port=8080 [--save-config=true --dry-run=client -o yaml > deployment-service.yaml]

L'option '--port' indique le port d'écoute du service et '--target-port' indique le nom ou le numéro de port à l'intérieur des pods sur lesquels le service doit rediriger le trafic.

Service LoadBalancer

Service en implémentation libre. Mis en œuvre par les fournisseurs de cloud pour fournir un accès aux applications exécutées dans leurs services de clusters Kubernetes gérés, depuis l'extérieur du cluster.

Dans Google Cloud Platform (GCP), par exemple, la création d'un service de type LoadBalancer dans Google Kubernetes Engine (GKE) fournit une ressource Loadbalancer de couche 4, accessible depuis l'extérieur du cluster, transférant le trafic vers les pods backends cibles à l'intérieur du cluster.

Utilisez ceci pour exposer une ressource (pods, déploiements, statefulsets...) via un service LoadBalancer :

# Create a LoadBalancer service for a deployment or generate manifest
$ kubectl expose deployment/my-deploy -n my-namespace --name my-deploy-service --type=LoadBalancer --port=80 --target-port=8080 [--save-config=true --dry-run=client -o yaml > deployment-service.yaml]

L'option '--port' indique le port d'écoute du service et '--target-port' indique le nom ou le numéro de port à l'intérieur des pods sur lesquels le service doit rediriger le trafic.

Service NodePort

Expose les pods d'applications via des nœuds de cluster. Chaque service de type NodePort écoutera sur un port spécifique sur chaque nœud du cluster. Les applications exposées par NodePort sont ensuite accessibles depuis l'extérieur du cluster à l'aide de n'importe quelle adresse IP de nœud et du numéro de port de nœud réservé.

# Create a NodePort service for a deployment or generate manifest
$ kubectl expose deployment/my-deploy -n my-namespace --name my-deploy-service --type=NodePort --port=80 --target-port=8080 [--save-config=true --dry-run=client -o yaml > deployment-service.yaml]

L'option '--port' indique le port d'écoute du service et '--target-port' indique le nom ou le numéro de port à l'intérieur des pods sur lesquels le service doit rediriger le trafic.

Le numéro de port qui sera ouvert sur chaque nœud sera choisi de manière aléatoire. Pour définir ce port sur une valeur spécifique, modifiez le champ « nodePort » du manifeste du service NodePort.

Ingress

Pour créer une ressource ingress ou générer le manifeste d'un ingress, utilisez ceci :

$ kubectl create ingress my-ingress --rule="my-domain.com/app=backend-service-name:8080,tls=my-cert" --class="nginx" [--save-config=true --dry-run=client -o yaml > ingress.yaml]

# Use this rule instead for redirecting all requests
# regardless the hostname to backend-service-name on port 8080
--rule="/*=backend-service-name:8080"

Gestion du stockage

StorageClass

La Classe de stockage sert à fournir un provisionnement dynamique des ressources de stockage dans Kubernetes. Elle peut également être utilisée pour activer la fonction d'extension de stockage lors de l'utilisation d'un provisionnement statique des ressources de stockage.

Voir Référence de l'API StorageClass pour tous les champs d'un manifeste de ressources StorageClass.

Actuellement, la ressource StorageClass ne peut pas être créée à l'aide de la commande « kubectl create ». Voici un exemple de manifeste pour la création de cette ressource :

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local
provisioner: kubernetes.io/no-provisioner
allowVolumeExpansion: true  
reclaimPolicy: Retain 
  • provisioner: nom du fournisseur (provisionneur) utilisé pour provisionner dynamiquement les ressources de stockage de cette StorageClass. Le provisionneur nommé « kubernetes.io/no-provisioner » indique que la StorageClass ne fournit pas de provisionnement de ressources de stockage dynamiques
  • allowVolumeExpansion: permet l'extension des ressources de stockage créées via ou à l'aide de cette StorageClass. C'est ainsi que nous autorisons l'extension du stockage en mettant à jour le champ 'spec.resources.requests.storage' de la ressources PersistentVolumeClaim (PVC) .
  • reclaimPolicy: détermine ce qui se passe lorsque les PersistentVolumeClaim (PVC) des ressources de stockage provisionnées de manière dynamique sont supprimés. Les valeurs possibles sont :
    • Delete (par défaut) : supprimer la ressource de stockage associée
    • Retain: conserver la ressource de stockage et ses données. La ressource de stockage PV ne sera pas disponible pour une utilisation par un autre PVC
    • Recycle (obsolète) : supprimez les données des ressources de stockage et rendez le PV disponible pour une utilisation par un autre PVC

Volumes persistants (PV)

Les ressources PersistentVolume (PV) ou volumes persistent représentent les ressources de stockage sous-jacentes qui seront réellement utilisées par les pods pour stocker des données.

Voir Référence de l'API PersistentVolume pour tous les champs d'un manifeste de ressources PersistentVolume et également tous les volumes backends pris en charge (hostPath, nfs...).

Actuellement, la ressource PersistentVolume ne peut pas être créée à l'aide de la commande « kubectl create ». Voici un exemple de manifeste pour la création de cette ressource :

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  storageClassName: local
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /local
    type: Directory
  persistentVolumeReclaimPolicy: Retain
  • storageClassName: nom de la StorageClass à laquelle appartient ce PV
  • capacity.storage: taille du PV. 1k = 1000 octets et 1Ki = 1024 octets. Idem pour les mégaoctets et les gigaoctets (m, Mi et g, Gi)
  • persistentVolumeReclaimPolicy: détermine ce qui se passe lorsque les ressources PersistentVolumeClaim (PVC) associées aux ressources PersistentVolume (PV ou volumes persistent) provisionnés manuellement ou dynamiquement sont supprimées. Pour les PV provisionnés dynamiquement, la valeur de ce paramètre est extraite du champ 'reclaimPolicy' de la ressource StorageClass. Les valeurs possibles sont :
    • Delete (par défaut pour les PV provisionnés dynamiquement) : supprimer le PV
    • Retain (par défaut pour les PV provisionnés manuellement) : conserver le PV et ses données de ressources de stockage sous-jacentes. Le PV ne sera pas disponible pour une utilisation par un autre PVC
    • Recycle (obsolète) : supprimez les données des ressources de stockage et rendez le PV disponible pour une utilisation par un autre PVC

PersistentVolumeClaim (PVC)

Les ressources PersistentVolumeClaim (PVC) représentent les demandes des utilisateurs pour utiliser les ressources de stockage. Cette demande ou réclamation peut être configurée pour provisionner dynamiquement et utiliser les ressources de stockage représentées par la ressource PV ou utiliser directement un PV provisionné manuellement.

Les pods peuvent ensuite utiliser les ressources de stockage en déclarant des volumes référençant les PVC par leurs noms dans « spec.volumes.persitentVolumeClaim.claimName ».

Voir Référence de l'API PersistentVolumeClaim pour tous les champs d'un manifeste de ressources PersistentVolumeClaim.

Actuellement, la ressource PersistentVolumeClaim ne peut pas être créée à l'aide de la commande « kubectl create ».

Voici un exemple de manifeste pour le provisionnement de ressources de stockage dynamique via StorageClass :

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: my-sc # Name of the StorageClass for dynamic provisioning
  resources:
    requests:
      storage: 3Gi # requested volume size

Et deux autres pour utiliser des ressources de stockage provisionnées manuellement :

  • Première option:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi # requested volume size
  volumeName: my-pv # Directly specify the name of the PV to use

avec celui-ci, la taille de la ressource de stockage demandée ne sera pas extensible (champ 'spec.resrouces.requests.storage') et nous obtiendrons l'erreur suivante si nous essayons de l'étendre :

error: persistentvolumeclaims "my-pvc" could not be patched:
persistentvolumeclaims "my-pvc" is forbidden: only dynamically provisioned pvc can be resized and the storageclass that provisions the pvc must support resize
You can run `kubectl replace -f /tmp/kubectl edit-1069802193.yaml` to try this update again.
  • Deuxième possibilité:

Utilisez une StorageClass dont le champ « allowVolumeExpansion » est défini sur « true » et le champ « provisioner » sur « kubernetes.io/no-provisioner » (pas de provisionnement dynamique) dans le champ « spec.storageClassName » du PV et du PVC  :

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local # Name of the StorageClass that allows volume expansion
  resources:
    requests:
      storage: 3Gi # requested volume size

Gestion des RBAC

RBAC ou Role Based Access Control est un moyen d'implémenter le contrôle d'accès dans Kubernetes. Les RBAC peuvent avoir une portée limitée à des namespace du cluster ou une portée globle à tout cluster.

Nous utilisons Role et RoleBinding pour le contrôle d'accès sur un ensemble de ressources Kubernetes « à l'intérieur d'un espace de noms (namespace) spécifique uniquement ».

Nous utilisons ClusterRole et ClusterRoleBinding pour le contrôle d'accès sur « toutes les ressources de n'importe quel espace de noms (namespace) » du cluster.

Compte de service

Pour créer un compte de service (ServiceAccount) nommé my-sa dans le namespace my-sa-namespace :

# Command line
$ kubectl create sa my-sa -n my-sa-namespace

# or 

# Manifest
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-sa
  namespace: my-sa-namespace

Rôle ou ClusterRole

Les ressources Rôle ou ClusterRole représentent les règles de contrôle d'accès. C'est là que nous définissons les autorisations à accorder sur les ressources. Par exemple, « lire les pods », « mettre à jour les secrets », « créer des services », etc. Ainsi, pour créer ces ressources, nous avons besoin des noms des permissions disponibles (créer, obtenir, lister, mettre à jour...) et les noms des ressources concernées (pods, secrets, déploiements...).

# Create Role/ClusterRole or generate manifests
# Use the indicated dry run option to generate equivalent yaml manifest only

# Role resource
# Should be put inside a specific namespace 
# (-n <namespace>, otherwise go to default)
$ kubectl create role my-role --verb=get,list --resource=pods,secrets -n role-ns [--save-config=true --dry-run=client -o yaml > role.yaml]

# ClusterRole resource
# Can't be put inside a specific namespace
kubectl create clusterrole my-clusterrole --verb=get,list --resource=pods,secrets [--save-config=true --dry-run=client -o yaml > clusterrole.yaml]

RoleBinding ou ClusterRoleBinding

Les ressources RoleBinding ou ClusterRoleBinding associent une ressource Role ou ClusterRole à un principal (utilisateur, groupe ou compte de service). Cette association accorde au principal les autorisations définies dans le Rôle ou le ClusterRole.

# Create RoleBinding/ClusterRoleBinding or generate manifests
# Use the indicated dry run option to generate equivalent yaml manifest only

# RoleBinding resource
# Should be put inside a specific namespace
# (-n <namespace>, otherwise go to default)
$ kubectl create rolebinding my-rolebinding --role=my-role --serviceaccount=my-sa-namespace:my-sa -n role-ns [--save-config=true --dry-run=client -o yaml > rolebinding.yaml]

# ClusterRoleBinding resource
# Can't be put inside a specific namespace
kubectl create clusterrolebinding my-clusterrolebinding --clusterrole=my-clusterrole --serviceaccount=my-sa-namespace:my-sa [--save-config=true --dry-run=client -o yaml > clusterrolebinding.yaml]

Gestion de la mise en réseau des pods

Politique réseaux

Avant d'utiliser ceci, jetez un œil à les pré-requis.

Les Politiques réseaux peuvent être utilisées pour créer des règles de réseau qui autorisent/refusent le trafic entre les pods ou entre les pods et le monde extérieur.

Sélectionnez les pods sur lesquels la politique réseaux s'applique

Réalisé à l'aide de « spec.podSelector ».

Une valeur vide sélectionne tous les pods de l'espace de noms dans lequel la ressource NetworkPolicy est créée. Sinon, sélectionne les pods avec les labels fournis dans « spec.podSelector.matchLabels » comme illustré ci-dessous :

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
(...)
spec:
  podSelector:
    matchLabels:
      app: mysuperapp
(...)
Choisissez les types de politique réseaux

Réalisé à l'aide de « spec.policyTypes ».

Une liste contenant soit « Ingress », « Egress » ou les deux (« Ingress » et « Egress ») :

  • Ingress: lorsqu'il est spécifié, seul le trafic entrant autorisé par les règles « spec.ingress » sera autorisé. Le trafic sortant continuera de fonctionner comme avant
  • Egress: lorsque spécifié, seul le trafic sortant autorisé par les règles « spec.egress » sera autorisé. Le trafic entrant continuera de fonctionner comme avant
  • Lorsque « Ingress » et « Egress » sont tous deux spécifiés, tout le trafic entrant et sortant vers/depuis les pods sélectionnés sera refusé et seuls ceux définis par les règles « spec.ingress » et « spec.egress » seront autorisés
Créer des règles de politique réseaux

Possible avec « spec.ingress » pour filtrer le trafic réseau entrant et « spec.egress » pour le trafic sortant.

Pour les règles d'entrée, nous spécifions les sources autorisées à l'aide de « spec.ingress.from ». Pour les règles de sortie, nous spécifions les destinations cibles à l'aide de « spec.ingress.to ». Pour les deux types de règles, nous spécifions également les protocoles et ports autorisés à l'aide de « spec.ingress.ports » et « spec.egress.ports ».

Voici les possibilités offertes en matière de sélection des sources et des destinations dans « spec.ingress.from » et « spec.egress.to » :

  • ipBlock: une adresse IP spécifique ou une plage d'adresses IP
  • podSelectorpods de l'espace de nom (namespace) dans lequel est déployé la politique réseaux, sélectionnés par les labels de pods à l'aide de « podSelector.matchLabels »
  • namespaceSelectorpods d'un namespace spécifique, sélectionnés au travers des labels du namespace. Si 'podSelector' est également spécifié, sélectionne les pods correspondant aux labels définis dans 'podSelector.matchLabels', uniquement présent dans les namespace correspondant aux labels définies dans 'namespaceSelector.matchLabels'

Voici un exemple de manifeste d'une politique réseaux autorisant :

  • le trafic entrant vers tous les pods se trouvant dans le namespace app-backends sur le port 80, uniquement à partir des pods se trouvant dans le namespace app-frontends
  • le trafic sortant de tous les pods se trouvant dans le namespace app-bakends, uniquement vers les adresses IP de la plage « 10.0.0.0/24 »
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: my-network-policy
  namespace: app-backends
spec:
  podSelector: # empty => selects all pods inside the namespace where the NetworkPolicy is deployed (app-backends in this case)
  #  matchLabels:
  #    app: superapp # Otherwise, selects all pods with this label from NetworkPolicy namespace
  policyTypes:
  - Ingress
  - Egress
  ingress: # rules for inbound traffic to pods selected by spec.podSelector
  - from: # selecting the allowed sources
    - namespaceSelector: # allowing namespaces by their labels
        matchLabels:
          kubernetes.io/metadata.name: app-frontends # label for selecting the 'app-frontend' namespace
    - podSelector: # allowing pods by their labels
        matchLabels:
          app-frontends-tier: web # allow all pods with this label
    ports: # target protocols and ports we are allowing traffic to on pods selected by spec.podSelector
    - protocol: TCP
      port: 80
  egress: # rules for outbound traffic to pods selected by spec.podSelector
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24 # target IP range we are allowing traffic to from the pods selected by spec.podSelector
    ports:
    - protocol: TCP
      port: 5978

Observabilité et dépannage

Utilisation des ressources des nœuds et des pods + conteneurs

# Get pods containers resources usage from a specific namespace
$ kubectl top pods --containers -n $namesapce

# Get default namespaces pods resources usage
$ kubectl top pods 

# Get cluster nodes resources usage
$ kubectl top nodes

Journaux des pods

# Get all logs from my-pod inside default namespace
$ kubectl logs my-pod

# Get last 20 logs lines from my-deploy and follow outputs
$ kubectl logs --tail=20 -f deploy/my-deploy -n $namespace

# Get last 20 logs lines from my-sts
$ kubectl logs --tail=20 sts/my-sts -n $namespace

Événements du cluster

# Get events from a specific namespace
$ kubectl get events -n $namespace

# Get events from all namespaces
$ kubectl get events -A

Test des RBAC

# Can I get pods inside my-app namespace ?
$ kubectl auth can-i get pods -n my-app

# Can the my-sa service account residing inside 
# my-app namespace list secrets inside default namespace ?
$ kubectl auth can-i list secrets --as --as=system:serviceaccount:my-app:my-sa

# Can I do anything in my-app namespace ?
$ kubectl auth can-i '*' '*' -n my-app