[k8s] Introduction aux Labels et NameSpaces

<<< Chap 16 | Sommaire | >>>

Chap 18. Introduction aux Labels et NameSpaces

I. Labels et Sélecteurs

1.1 Exemple d’utilisation des labels (étiquettes)

Nous l’avons vu dans le chap 17, l’importance de l’utilisation des labels.

  • Sans label, il nous a fallu créer un script pour supprimer tous les volumes persistants.
$ for n in {01..10};do kubectl delete pv nfs-sts-pv${n};done
  • Avec un label prédéfini, tous les PVs portant l’étiquette vol=sts-pv, peuvent être détruits en une seule commande
$ kubectl get pv --show-labels
NAME           CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                           STORAGECLASS   REASON   AGE   LABELS
nfs-pv-01      100Mi      RWX            Recycle          Bound       default/nfs-pvc-02              slow                    16d   
nfs-sts-pv01   1Gi        RWO            Recycle          Available                                                           9d    vol=sts-pv
nfs-sts-pv02   1Gi        RWO            Recycle          Available                                                           9d    vol=sts-pv
nfs-sts-pv03   1Gi        RWO            Recycle          Available                                                           9d    vol=sts-pv
nfs-sts-pv04   1Gi        RWO            Recycle          Available                                                           9d    vol=sts-pv
nfs-sts-pv05   1Gi        RWO            Recycle          Available                                                           9d    vol=sts-pv
nfs-sts-pv06   1Gi        RWO            Recycle          Bound       default/nfs-pvc-sts-busybox-0   sts-disk                9d    vol=sts-pv
nfs-sts-pv07   1Gi        RWO            Recycle          Available                                   sts-disk                9d    vol=sts-pv
nfs-sts-pv08   1Gi        RWO            Recycle          Bound       default/nfs-pvc-sts-busybox-2   sts-disk                9d    vol=sts-pv
nfs-sts-pv09   1Gi        RWO            Recycle          Bound       default/nfs-pvc-sts-busybox-1   sts-disk                9d    vol=sts-pv
nfs-sts-pv10   1Gi        RWO            Recycle          Available                                   sts-disk                9d    vol=sts-pv

$ kubectl delete pv -l vol=sts-pv

Aussi, les labels nous ont permis de ne sélectionner que certains nodes, y compris le Master, pour déployer les pods créés par Daemonsets.  (voir chap 15)

  • Etape 1:
    • Appliquer un label dsnode avec comme valeur true, aux nodes Master k8sn01et Worker k8sn03,
$ kubectl label node k8sn01 dsnode=true

$ kubectl label node k8sn03 dsnode=true
  • Etape 2:
    • Ajouter ensuite l’option de sélection de Nodes nodeSelector via les labels dans le fichier daemon.yaml,
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: nginxds
  name: nginx-daemonset
spec:
  selector:
    matchLabels:
      app: nginxds
  template:
    metadata:
      labels:
        app: nginxds
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      nodeSelector:
        dsnode: "true"
      containers:
      - image: nginx
        name: nginx-daemonset

Pour rappel, l’option de tolerations est ce qui nous a permis de déployer un Pod daemonset directement dans le node Master. Autre que pour cette utilisation bien précise, je ne vous conseille pas de le faire.

1.2 Syntaxe des labels

Une étiquette est un morceau de matière (tissu, papier, etc.) sur lequel des informations concernant l’objet auquel il est attaché sont écrites, telles que la marque, le prix, un code barre, des indications de lavage. (Wikipedia)

Avec kubernetes, une étiquette ou label (en anglais), est ce qui nous permet d’identifier un ou plusieurs objects (Pod, Volume Persistant, etc.) ayant les mêmes propriétes, pour ensuite les placer dans le même groupe.

Les labels, tout comme avec configmap et secret (chap 12), utilisent un système de « clé-valeur ».

  • Clé d’étiquette (key)
    • Préfixe d’étiquette (facultatif)
      • Les préfixes d’étiquette ne peuvent pas dépasser 253 caractères
      • Le préfixe d’étiquette doit être un sous-domaine DNS
      • Le préfixe d’étiquette peut également être une série de sous-domaines DNS, séparés par « . »
      • Les préfixes d’étiquette doivent se terminer par « / »
        Exemple: app.kubernetes.io/
    • Nom de l’étiquette
      • Le nom de l’étiquette est requis
      • Les noms d’étiquettes peuvent comporter jusqu’à 63 caractères
      • Les caractères doivent être des caractères alphanumériques
      • Les noms d’étiquettes peuvent également inclure  » –  » ,  » _  » et  » . »
      • Les noms d’étiquettes doivent commencer et se terminer par un caractère alphanumérique
        Exemple: version
  • Valeur de l’étiquette (label)
    • Les valeurs d’étiquette peuvent comporter jusqu’à 63 caractères
    • Les caractères doivent être des caractères alphanumériques
    • Les valeurs d’étiquette peuvent également inclure  » –  » ,  » _  » et  » . »
    • Les valeurs des étiquettes doivent commencer et se terminer par un caractère alphanumérique
      Exemple: "5.7.21"

Chaque clé doit être unique, et devraît apparaitre dans YAML avec les informations suivantes,

metadata: 
  labels: 
    clé-1 : valeur-1
    clé-2 : valeur-2

1.3 Exemple d’utilisation des labels

apiVersion: apps/v1
kind: StatefulSet
metadata:
  labels:
    app.kubernetes.io/name: mysql
    app.kubernetes.io/instance: mysql-abcxzy
    app.kubernetes.io/version: "5.7.21"
    app.kubernetes.io/component: database
    app.kubernetes.io/part-of: wordpress
    app.kubernetes.io/managed-by: helm
    app.kubernetes.io/created-by: controller-manager
  • Sans préfixes, je vais reprendre l’exemple du chap 15
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: deploy-busybox

Autres exemples couramment utilisés de labels sans préfixes,

  • "release"
    • release : "stable"
    • release : "canary"
  • "environment"
    • environment : "dev"
    • environment : "qa"
    • environment : "production"
  • "tier"
    • tier : "frontend"
    • tier : "backend"
    • tier : "cache"
  • "partition"
    • partition : "customerA"
    • partition : "customerB"
  • "track"
    • track : "daily"
    • track : "weekly"

1.4 Manipulation des labels

Les deux commandes à connaître pour manipuler les labels sont,

  • kubectl get TYPE [NAME | -l label]
$ kubectl get --help | awk '/Usage/{getline; print}'
kubectl get
[(-o|--output=)json|yaml|wide|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...]
(TYPE[.VERSION][.GROUP] [NAME | -l label] | TYPE[.VERSION][.GROUP]/NAME ...) [flags] [options]
  • kubectl label
$ kubectl label --help | awk '/Usage/{getline; print}'
kubectl label [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version] [options]

Si je  reprends l’exemple d’un déploiement de type ReplicaSet de la page de kubernetes.

$ wget https://kubernetes.io/examples/controllers/frontend.yaml

$ kubectl create -f frontend.yaml

$ kubectl get pod -o wide --show-labels
NAME             READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE   READINESS GATES   LABELS
frontend-b2c29   1/1     Running   0          16s   172.16.1.22    k8sn02                          tier=frontend
frontend-jd4tt   1/1     Running   0          16s   172.16.2.150   k8sn03                          tier=frontend
frontend-w4tw2   1/1     Running   0          16s   172.16.1.21    k8sn02                          tier=frontend
  • Y ajouter des labels supplémentaires aux Pods créés,
$ kubectl label pods --selector=tier=frontend release=canary

$ kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE    LABELS
frontend-b2c29   1/1     Running   0          6m1s   release=canary,tier=frontend
frontend-jd4tt   1/1     Running   0          6m1s   release=canary,tier=frontend
frontend-w4tw2   1/1     Running   0          6m1s   release=canary,tier=frontend
  • Sinon, pour ajouter un label spécific par un pod,
$ kubectl label pods frontend-b2c29 environment=dev

$ kubectl label pods frontend-jd4tt environment=prod

$ kubectl label pods frontend-w4tw2 environment=prod

Je n’ai pas trouvé la méthode pour appliquer un label à plusieurs Pods à la fois. Si vous avez cette information, n’hésitez pas à la partager.

$ kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE    LABELS
frontend-b2c29   1/1     Running   0          9m8s   environment=dev,release=canary,tier=frontend
frontend-jd4tt   1/1     Running   0          9m8s   environment=prod,release=canary,tier=frontend
frontend-w4tw2   1/1     Running   0          9m8s   environment=prod,release=canary,tier=frontend

Par contre, rien à été modifié pour le réplicaset.

$ kubectl get rs --show-labels
NAME       DESIRED   CURRENT   READY   AGE   LABELS
frontend   3         3         3       12m   app=guestbook,tier=frontend

Pour supprimer un label, ajouter un signe négatif à la fin du nom du label à enlever.

$ kubectl label pods --selector=tier=frontend "release-"

$ kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE   LABELS
frontend-b2c29   1/1     Running   0          42m   environment=dev,tier=frontend
frontend-jd4tt   1/1     Running   0          42m   environment=prod,tier=frontend
frontend-w4tw2   1/1     Running   0          42m   environment=prod,tier=frontend

Créer ensuite un service, qui utilisera les labels proposés dans le fichier frontend.yaml,

$ kubectl expose rs frontend --port 80

$ kubectl get svc --show-labels
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE    LABELS
frontend     ClusterIP   10.97.33.196           80/TCP    13s    app=guestbook,tier=frontend
kubernetes   ClusterIP   10.96.0.1              443/TCP   162d   component=apiserver,provider=kubernetes

Supression des composants du lab en utilisant le label tier=frontend, qui est commun a tous ces composants.

$ kubectl delete services,rs,deployments -l tier=frontend

J’utiliserai une méthode plus simplifié de cette commande à la fin du chapitre.

Il n’est pas nécessaire à ce niveau de supprimer l’ensemble du lab puisque vous en aurez besoin pour la suite. Sinon, vous pouvez toujours refaire le lab depuis le début.

1.5 Sélecteurs

Un sélecteur est utilisé pour sélectionner des resources kubernetes, basées sur la valeur de leurs labels.

$ kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE   LABELS
frontend-8g474   1/1     Running   0          14m   environment=dev,tier=frontend
frontend-bzfj2   1/1     Running   0          14m   environment=prod,tier=frontend
frontend-fzfnd   1/1     Running   0          14m   environment=prod,tier=frontend

La méthode de sélection peut être basée sur

  • Comparaison entre valeurs et clés

Opération « = » (égalité):

$ kubectl get pod -l "environment=dev"
NAME             READY   STATUS    RESTARTS   AGE
frontend-8g474   1/1     Running   0          56m

Opération « != » (inégalité):

$ kubectl get pod -l "environment!=dev"
NAME             READY   STATUS    RESTARTS   AGE
frontend-bzfj2   1/1     Running   0          56m
frontend-fzfnd   1/1     Running   0          56m

Pour information, nous avons déjà eu recours à cette méthode pour retrouver tous les pods ayant pour label "tier=frontend" et d’effectuer quelques modification comme,

=> Ajout (release=canary) d’un label:

$ kubectl label pods -l "tier=frontend" release=canary

=> Suppression ("release-") d’un label:

$ kubectl label pods -l "tier=frontend" "release-"

 

Ajoutons un nouveau pod, auquel on associera le label environment=qa,

$ kubectl run nginx --image=nginx -l environment=qa

$ kubectl get pod --show-labels
NAME             READY   STATUS    RESTARTS   AGE   LABELS
frontend-8g474   1/1     Running   0          90m   environment=dev,tier=frontend
frontend-bzfj2   1/1     Running   0          90m   environment=prod,tier=frontend
frontend-fzfnd   1/1     Running   0          90m   environment=prod,tier=frontend
nginx            1/1     Running   0          8s    environment=qa
  • Filtrage des clés en fonction d’un ensemble de valeurs
$ kubectl get pod -l "environment in (dev,prod)" --show-labels
NAME             READY   STATUS    RESTARTS   AGE   LABELS
frontend-8g474   1/1     Running   0          67m   environment=dev,tier=frontend
frontend-bzfj2   1/1     Running   0          67m   environment=prod,tier=frontend
frontend-fzfnd   1/1     Running   0          67m   environment=prod,tier=frontend

$ kubectl get pod -l "environment notin (dev,prod)" --show-labels
NAME    READY   STATUS    RESTARTS   AGE   LABELS
nginx   1/1     Running   0          19m   environment=qa

La première expression permet de lister tous les pods dont la valeur du label est égale à dev ou prod.

Par contre, la deuxième expression ne liste que les pods dont la valeur du label est différent de dev et de prod.

II. NameSpace

2.1 Introduction au NameSpace (espace de nom)

Je vous ai fait une petite introduction sur les NameSpace dans le chap 3, sans vraiment rentrer dans les détails.

Voici ce qui est dit sur la page officielle de kubernetes en ce qui concerne les namespaces.

Kubernetes prend en charge plusieurs clusters virtuels présents sur le même cluster physique. Ces clusters virtuels sont appelés namespaces (espaces de nommage en français).
(…)
Les namespaces sont des groupes de noms. Ils fournissent un modèle d’isolation de nommage des ressources.
(…)
Il n’est pas nécessaire d’utiliser plusieurs namespaces juste pour séparer des ressources légèrement différentes, telles que les versions du même logiciel: utiliser les labels pour distinguer les ressources dans le même namespace.

Cas d’utilisation des namespaces, cette liste n’est pas exhaustive et risque d’être modifiée (voir le blog de kubernetes)

  • Séparer les différents espaces (Production, Développement, etc.)
  • Gérer les contrôles d’accès via RBAC, afin de n’authoriser que les utilisateurs avec les certaines permissions[1] pour accéder aux resources du cluster (rôle d’administrateur, etc.)
  • Assigner son propre cluster à chacun des client pour une demande bien précise. Par exemple,
    • Quota sur les resources par cluster (CPU, RAM, etc.)
    • Isolation[1] d’un projet
    • etc.

Kubernetes démarre initiallement avec quatre namespaces (voir les infos de la documentation officielle):

$ kubectl get namespaces
NAME              STATUS   AGE
default           Active   163d
kube-node-lease   Active   163d
kube-public       Active   163d
kube-system       Active   163d
  • default
    Le namespace par défaut
  • kube-system
    Le namespace pour les objets créés par Kubernetes lui-même
  • kube-public
    Ce namespace est créé automatiquement et est visible par tous les utilisateurs (y compris ceux qui ne sont pas authentifiés)
  • kube-node-lease
    Ce namespace contient les objets de bail associés à chaque nœud, ce qui améliore les performances des pulsations du nœud à mesure que le cluster évolue

2.2 Création d’un nouveau namespace

Avant de penser à créer votre namespace, je tiens à ajouter l’information suivante, toujours tirée de la page officielle de kubernetes:

Note: Évitez de créer des namespaces avec le préfixe kube-, car il est réservé aux namespaces système de Kubernetes.

Les namespaces peuvent être créés:

  • En ligne de commande,
    • Pour créer un namespace alpha-namespace avec pour label name=alpha,
$ kubectl create namespace alpha-namespace
$ kubectl label namespace alpha-namespace name=alpha
  • Avec un fichier YAML,
    • Récupérer le fichier de configuration du namespace créé par défaut
$ kubectl get ns default -o yaml > beta-namespace.yaml
    • Editer le contenu du fichier namespace-dev.yaml
    • Enfin, supprimer les informations inutiles, et n’oubliez pas d’ajouter un label pour ce namespace
$ vim beta-namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
  name: beta-namespace
  labels:
    name: beta

$ kubectl apply -f beta-namespace.yaml

Vérifier ensuite que les nouveaux namespaces ont bien été créé sans problème.

$ kubectl get ns --show-labels
NAME              STATUS   AGE    LABELS
alpha-namespace   Active   16m    name=alpha
beta-namespace    Active   10s    name=beta
default           Active   175d   <none>
kube-node-lease   Active   175d   <none>
kube-public       Active   175d   <none>
kube-system       Active   175d   <none>

2.3 Création de pods dans un namespace dédié

Par défaut, tous les nouveaux pods seront créés dans le namespace default.

Dans cette partie, je vais explicitement choisir mon namespace à utiliser pour déployer mes Pods.

  • Création d’un pods dans alpha-namespace
$ kubectl run nginx --image=nginx -l environment=qa -n alpha-namespace

$ kubectl get pod -A -l environment --show-labels
NAMESPACE         NAME             READY   STATUS    RESTARTS   AGE     LABELS
alpha-namespace   nginx            1/1     Running   0          5m50s   environment=qa
default           frontend-8g474   1/1     Running   0          167m    environment=dev,tier=frontend
default           frontend-bzfj2   1/1     Running   0          167m    environment=prod,tier=frontend
default           frontend-fzfnd   1/1     Running   0          167m    environment=prod,tier=frontend
default           nginx            1/1     Running   0          77m     environment=qa

L’option -A (–all-namespaces) nous permet d’afficher les pods de tous les namespaces, et qui ont pour label "environment=VALEUR".

  • Application d’un fichier YAML pour la création de Pods dans beta-namespace

Je vais réutiliser le fichier frontend.yaml,

$ kubectl create -f frontend.yaml -n beta-namespace

Sinon, ajouter le nom du namespace dans le fichier yaml initial, dans la partie metatada.

metadata:
  name: frontend
  namespace: app

Puis lancer la commande

$ kubectl create -f frontend.yaml

Liste des pods dans beta-namespace,

$ kubectl get pod -n beta-namespace --show-labels
NAME             READY   STATUS    RESTARTS   AGE    LABELS
frontend-7nwkw   1/1     Running   0          105s   tier=frontend
frontend-mzkqj   1/1     Running   0          105s   tier=frontend
frontend-snkwb   1/1     Running   0          105s   tier=frontend

2.4 Basculer entre les differents namespaces

Le basculement d’un namespace à un autre nous facilite l’utilisation de la commande kubectl, puisqu’il n’est plus nécessaire de préciser le nom du namespace dans lequel nous souhaitons travailler.

Ainsi, une fois dans le namespace choisi, vous pouvez omettre l’option --namespace=NAMESPACE à la fin de chaque commande.

La commande à utiliser quand le namespace est déjà créé, est la suivante:kubectl config set-context --current --namespace=NAMESPACE.

Ici, l’option --current fait référence au context courant, que vous pouvez afficher par,

$ kubectl config current-context
kubernetes-admin@kubernetes

Un context kubernetes est décrit comme un ensemble de paramètres d’accès utilisé par un compte utilisateur pour accéder à un cluster ou à un namespace. Je ne vais pas rentrer dans les détails, puisque nous n’allons pas modifier cette option. Si vous souhaitez le faire, jettez un coup d’oeil à la page de kubernetes.

$ kubectl config get-contexts 
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin 

$ kubectl config view
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: DATA+OMITTED
    server: https://192.168.0.200:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: REDACTED
    client-key-data: REDACTED 

Passer maintenant d’un namespace à un autre, en activant ce dernier comme le namespace par défaut.

$ kubectl config set-context --current --namespace=beta-namespace
Context "kubernetes-admin@kubernetes" modified.

$ kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
frontend-7nwkw   1/1     Running   0          144m
frontend-mzkqj   1/1     Running   0          144m
frontend-snkwb   1/1     Running   0          144m

$ kubectl config set-context --current --namespace=alpha-namespace
Context "kubernetes-admin@kubernetes" modified.

$ kubectl get pod
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          173m

Pour revenir au namespace initial, les deux options ci-dessous aboutissent au même résultat.

  • Remplacez le nom du NAMESPACE par « default »
  • Ne mettez rien après --namespace=
$ kubectl config set-context --current --namespace=
Context "kubernetes-admin@kubernetes" modified.

$ kubectl get pod
NAME             READY   STATUS    RESTARTS   AGE
frontend-8g474   1/1     Running   0          5h35m
frontend-bzfj2   1/1     Running   0          5h35m
frontend-fzfnd   1/1     Running   0          5h35m
nginx            1/1     Running   0          4h5m

2.5 Terminaison de pods

Appliqué à plusieurs namespaces, la terminaison de Pods et des différentes resources peut se faire, soit:

  • En basculant de le namespace concerné, et y faire le ménage
  • En ajoutant le nom du namspace à la fin de chaque commande kubectl
  • En supprimant le namespace

C’est cette dernière option que je vais choisir pour supprimer toutes les resources en dehors du namespace default, y compris les namespaces nouvellement créés.

$ kubectl delete ns alpha-namespace

$ kubectl delete ns beta-namespace

Pour supprimer tous les Pods du namespace default, qui est le dernier namespace actif du lab,

$ kubectl delete pod --all -n default
pod "frontend-8g474" deleted
pod "frontend-bzfj2" deleted
pod "frontend-fzfnd" deleted
pod "nginx" deleted

Sinon, vous pouvez utiliser les labels pour supprimer toutes les resources déployées dans ce lab.

$ kubectl apply -f frontend.yaml

$ kubectl get all -l tier=frontend
NAME                 READY   STATUS    RESTARTS   AGE
pod/frontend-fc7p9   1/1     Running   0          35s
pod/frontend-gh9wr   1/1     Running   0          35s
pod/frontend-jnkqt   1/1     Running   0          35s

NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/frontend   ClusterIP   10.96.168.101           80/TCP    6h54m

NAME                       DESIRED   CURRENT   READY   AGE
replicaset.apps/frontend   3         3         3       35s

$ kubectl delete all -l tier=frontend
pod "frontend-fc7p9" deleted
pod "frontend-gh9wr" deleted
pod "frontend-jnkqt" deleted
service "frontend" deleted
replicaset.apps "frontend" deleted

2.6 Network Namespace (netns)

Cette partie a déjà été traité dans le chap 9, dont je vais reprendre ici un extrait.

Chaque espace de nom, peut fournir à chacun des Pods toutes les resources liées au réseau.
Entre autre, chaque pod peut avoir,

  • Une adresse IP privée
  • Sa propre table de routage, avec ses règles de parefeux, etc.

Ce chapitre est maintenant terminé. J’espère vous avoir convaincu de l’utilité des labels!

Sources

9 Best Practices and Examples for Working with Kubernetes Labels
Labels and Selectors
Recommended Labels
Share a Cluster with Namespaces
Kubernetes Namespace
How to switch namespace in kubernetes

[k8s] Déploiement et Mise-à-jour des Pods – Partie 2

<<< Chap 14 | Sommaire | Chap 16 >>>

Chap 15. Déploiement et Mise-à-jour des Pods – Partie 2

IV. Introduction au contrôleur Daemonset

4.1 Contrôleur de type daemonset

Ce type de contrôleur a la particularité de ne déployer qu’un et un seul Pod par node.

Pour chaque ajout de nodes dans le cluster, un nouveau pod sera automatiquement créé par le contrôleur daemonset, et placé dans le node concerné.

A l’inverse, si on enlève un des nodes du cluster, le pod qui y était associé sera tout simplement détruit et ne peut être migré vers un autre node. D’où le principe d’un pod de type daemonset par node.

Vous pouvez de ce fait avoir plusieurs Pods créés par différents types de contrôleurs (deploy, daemonset, etc.) dans le même node.

4.2 Utilisation de Daemonset

Pour mettre en place notre réseau avec calico (voir chap 2) et les services du NFS (voir chap 11), nous avons fait appel au contrôleur daemonset. Dans ces deux cas, les Pods ont été créés au niveau du système dans l’espace de nom (namespace) kube-system.

$ kubectl get daemonset --all-namespaces
NAMESPACE     NAME              DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
default       nginx-daemonset   2         2         2       2            2           dsnode=true              2d14h
kube-system   canal             3         3         3       3            3           kubernetes.io/os=linux   98d
kube-system   csi-nfs-node      2         2         2       2            2           kubernetes.io/os=linux   62d
kube-system   kube-proxy        3         3         3       3            3           kubernetes.io/os=linux   98d

Il est fortement déconseillé de créér des Pods dans kube-system, sauf si vous savez exactement ce que vous faîtes.

A notre niveau, n’importe quel espace de nom autre que celui du système kube-system devrait faire l’affaire.

Daemonset est généralement utilisé comme,

Ces services devraient démarrer automatiquement pour chacun des nodes gérés par un contrôleur daemonset et, aussitôt que ces nodes rejoignent le cluster.

4.3 Fichier YAML

Dans le fichier YAML ci-dessous, j’ai placé # tolerations en mode commentaire. Je vous expliquerai un peu plus bas l’utilitée de cette option.

$ cat << EOF > daemon.yaml 
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: nginxds
  name: nginx-daemonset
spec:
  selector:
    matchLabels:
      app: nginxds
  template:
    metadata:
      labels:
        app: nginxds
    spec:
#      tolerations:
#      - key: node-role.kubernetes.io/master
#        effect: NoSchedule
      containers:
      - image: nginx
        name: nginx-daemonset
EOF

$ kubectl create -f daemon.yaml

Noter que le nombre de pod créé dépend du nombre de Workers. Dans mon cas, avec un Master (k8sn01) et deux workers (k8sn02 et k8sn03), je ne peux avoir que deux Pods max, un pour chaque Worker.

V. Choix du Node à associer aux services de Daemonset

5.1 Activation du Master

Par défaut, tous les workers en état de fonctionner seront automatiquement sélectionnés pour loger chacun des pods créés par daemonset, à raison de un Pod par Node.

Le cas de daemonset est particulier, puisque c’est le seul type de service qui autorise l’ajout du ou des masters, en plus des workers, comme node pour hébergers nos pods.

Pour ce faire, désactiver les commentaires du fichier daemon.yaml comme suit,

    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule

Appliquer ensuite les modifications.

 $ kubectl apply -f daemon.yaml

Un nouveau pod supplémentaire devrait avoir été créé pour le master.

5.2 Dé/selection de Nodes

La sélection, ou la désélection de nodes du serice daemonset passe par les labels.

L’idée est de tagguer uniquement les Nodes à utiliser pour contenir nos Pods.

Pour ajouter un label dsnode avec comme valeur true, aux nodes Master k8sn01et Worker k8sn03,

$ kubectl label node k8sn01 dsnode=true

$ kubectl label node k8sn03 dsnode=true

Liste des nodes avec le label dsnode,

$ kubectl get node -l dsnode
NAME     STATUS   ROLES                  AGE   VERSION
k8sn01   Ready    control-plane,master   98d   v1.21.0
k8sn03   Ready                     98d   v1.21.0

Modifier ensuite le fichier daemon.yaml afin d’y ajouter l’option de sélection de Nodes nodeSelector via les labels.

$ cat << EOF > daemon.yaml 
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: nginxds
  name: nginx-daemonset
spec:
  selector:
    matchLabels:
      app: nginxds
  template:
    metadata:
      labels:
        app: nginxds
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      nodeSelector:
        dsnode: "true"
      containers:
      - image: nginx
        name: nginx-daemonset
EOF

Cette modification a pour résultat de ne créer les pods qu’au niveau des Nodes ayant pour label dsnode="true".

$ kubectl apply -f daemon.yaml

$ kubectl get pod -o wide -l dsnode
NAME                    READY   STATUS    RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
nginx-daemonset-h6ghz   1/1     Running   0          7m52s   172.16.2.94   k8sn03   <none>           <none>
nginx-daemonset-rqxhq   1/1     Running   0          7m45s   172.16.0.7    k8sn01   <none>           <none>

Pour supprimer les labels au niveau des nodes une fois le lab terminé, utiliser la commande,

$ kubectl label node k8sn03 "dsnode-"
$ kubectl label node k8sn01 "dsnode-"

VI. Accès aux applications et répartition des charges

6.1 Connection de l’extérieur du cluster aux applications proposées par les Pods créés par Daemonset

Nous avons vu au chap 9 de cette série d’articles que, pour atteindre un Pod à partir d’un réseau en dehors du cluster, il faut passer par les Services.

$ kubectl get ds
NAME              DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
nginx-daemonset   3         3         3       3            3                     2d11h

$ kubectl expose ds nginx-daemonset
error: cannot expose a DaemonSet.apps

Malheureusement, un déploiement de type daemonset ne peut pas utiliser dobjet de type Service.

D’après la documentation, il faudrait passer par l’adresse IP du Node, associé au numéro de Port de l’application pour atteindre les services de nos conteneurs.

NodeIP and Known Port: Pods in the DaemonSet can use a hostPort, so that the pods are reachable via the node IPs. Clients know the list of node IPs somehow, and know the port by convention.

Ajouter donc un numéro du port à nos conteneurs.

$ cat daemon.yaml 
apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: nginxds
    dsnode: "true"
  name: nginx-daemonset
spec:
  selector:
    matchLabels:
      app: nginxds
  template:
    metadata:
      labels:
        app: nginxds
        dsnode: "true"
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      nodeSelector:
        dsnode: "true"
      containers:
      - image: nginx
        name: nginx-daemonset
        ports:
        - containerPort: 80
          hostPort: 80

Pour que les services de notre application Nginx soient accessible depuis l’extérieur, lier le port du conteneur avec celui du node hôte.

Ici j’ai choisi d’utiliser le port 80 pour une connection en HTTP vers le serveur Nginx (containerPort: 80), auquel je vais lier au port 80 des nodes (hostPort: 80) sélectionnés par le service de daemonset.

  • Noter que d’après les Best Practice, HostPort ne devrait pas être spécifié dans les fichiers YAML à moins que vous en avez vraiment besoin.
$ kubectl get pod -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP            NODE     NOMINATED NODE   READINESS GATES
nginx-daemonset-2vfp8   1/1     Running   0          95s   172.16.2.95   k8sn03              
nginx-daemonset-vjf5p   1/1     Running   0          79s   172.16.0.8    k8sn01     

Vous devriez maintenant, être en mesure d’accéder à la page d’acceuil du serveur Nginx, juste en tapant l’URL, ou l’adresse l’IP du node.

$ curl http://k8sn03
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

Pour rappel, j’utilise Pihole comme serveur de dns local pour la résolution du nom de domain.

$ nslookup k8sn03
Server:		127.0.0.53
Address:	127.0.0.53#53

Non-authoritative answer:
Name:	k8sn03.mylab.lan
Address: 192.168.0.202

6.2 Load balancer

L’inconvénient du système de déploiement avec daemonset est qu’il n’y a pas de méthode de répartition de charge (load balancer), puisque daemonset ne peut pas utiliser l’objet Service.

Cependant, rien ne nous empêche de créer manuellement un Service, qui aura pour objectif de regrouper tous les conteneurs avec les mêmes labels.

Dans notre cas, les pods qui nous intéressent sont ceux ayant pour label « app=nginxds« .

Préciser ensuite dans le fichier YAML de notre service, que le port 80 est celui à utiliser pour atteindre nos conteneurs.

$ cat << EOF > daemon-svc.yaml 
kind: Service
apiVersion: v1
metadata:
  name: nginx-svc
spec:
  selector:
    app: nginxds
  ports:
  - port: 80
EOF

$ kubectl apply -f daemon-svc.yaml

Vérification,

$ kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1              443/TCP   113d
nginx-svc    ClusterIP   10.108.38.43           80/TCP    23s

$ curl 10.108.38.43
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>< /code>< /pre>

J’ai omis le numéro de port puisque le port 80 est celui qui sera sélectionné par défaut pour accéder à un serveur web.

6.3 Màj des Pods et exemples d’utilisation

La méthode de mise-à-jour de Pods créés par un contrôleur de type daemonset se passe exactement de la même façon qu’avec deploy. Je vous renvoie à l’article précédent pour plus d’information.

Par exemple, pour modifier la version de notre serveur Nginx,

$ kubectl set image ds nginx-daemonset nginx-daemonset=nginx:1.9.1

Les options de kubectl set image pour les màj sont les suivantes.

kubectl set image daemonset <nom-du-daemonset> <nom-du-conteneur>=<image>:<version>

La nouvelle version de Nginx devrait apparaître dans les configurations YAML de notre daemonset.

$ kubectl describe ds nginx-daemonset | grep Image
    Image:        nginx:1.9.1

Pour annuler la dernière modification et revenir en arrière,

$ kubectl rollout undo ds nginx-daemonset

Sinon, vous pouvez passer par l’historique des màj, pour sélectionner et appliquer la version qui nous intéresse.

$ kubectl rollout history ds
daemonset.apps/nginx-daemonset 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>
4         <none>
5         kubectl set image ds nginx-daemonset nginx-daemonset=nginx:1.9.1 --record=true
$ kubectl rollout undo ds nginx-daemonset --to-revision=5

Sources

[kubernetes] DaemonSet
How to Add or Remove Labels to Nodes in Kubernetes