Gérer des clusters Kubernetes avec kubeadm

Familiarisez-vous avec l'architecture et les composants de Kubernetes. Découvrez comment exécuter des clusters Kubernetes sur des machines Linux à l'aide de kubeadm. Figer la version du cluster et effectuez des mises à niveau à tout moment sans interruption de service.

Gérer des clusters Kubernetes avec kubeadm
Photo par Photographie / Unsplash

Architecture des clusters Kubernetes

Vue d'ensemble

Architecture des clusters Kubernetes

Les composants des clusters Kubernetes peuvent être organisés en deux groupes :

Les composants du plan de contrôle et des nœuds de travail peuvent être déployés sur n’importe quel nœud du cluster Kubernetes, mais il est préférable de disposer de nœuds dédiés au plan de contrôle et aux nœuds de travail pour une gestion plus facile.

Composants des nœuds de travail

Sur les nœuds de travail, nous avons :

  • kubelet :

    • responsable de l'exécution des conteneurs des Pods
    • reçoit des instructions d'exécution à partir du plan de contrôle (où exécuter un conteneur de Pods spécifique, avec quelle configuration…)
    • transmet les statuts des pods au plan de contrôle via le composant kube-apiserver
  • kube-proxy (optionnel) :

    • responsable de la création et de la maintenance des règles réseau nécessaires aux communications avec les Pods, à l'intérieur ou depuis l'extérieur du cluster (atteindre les Pods à partir de services avec des IP externes...)
  • Logiciel d'exécution de conteneur (container runtime) :

    • un logiciel qui exécute des conteneurs (containerd, cri-o…)

Composants des nœuds du plan de contrôle

Sur les nœuds du plan de contrôle, nous avons :

  • kube-apiserver :

    • une API REST utilisée par kubectl, les nœuds de travail et d'autres composants externes pour communiquer avec le plan de contrôle
  • kube-scheduler :

    • décide sur quel nœud de travail un pod nouvellement créé doit s'exécuter
    • la décision de planification est basée sur des facteurs tels que :
      • besoins en ressources
      • spécifications d'affinité et d'anti-affinité
      • localité des données
      • taints et tolérations
  • etcd :

    • une base de données de stockage clés/valeurs pour le système Kubernetes
    • dans la plateforme Kubernetes, tout (Pods, Services…) est représenté comme un objet stocké dans cette base de données
  • kube-controller-manager :

    • exécute des processus de contrôleur qui surveillent en permanence certains des composants/objets Kubernetes
    • apporte les modifications nécessaires pour les amener à répondre aux états souhaités définis par les utilisateurs ou le système lui-même :
      • créer des comptes de service et des jetons pour les nouveaux espaces de noms
      • créer des pods pour exécuter des tâches (jobs)
      • créer des pods pour répondre au nombre de replicas des ReplicaSet
      • ...
    • chaque contrôleur est logiquement un processus unique, mais tous sont compilés dans un seul binaire et exécutés dans un seul processus
    • Voici quelques exemples de contrôleurs pour la mise à l'échelle automatique verticale et horizontale des nœuds et pods de cluster Kubernetes :
  • cloud-controller-manager :

    • regroupe les contrôleurs qui interagissent avec les API des fournisseurs de cloud

Configuration du plan de contrôle et des nœuds de travail

Ressources des nœuds de cluster Kubernetes

  • 1 nœud de plan de contrôle (2 CPU, 2 Go de RAM)
  • 2 nœuds de travail (2 CPU, 2 Go de RAM)
  • Ubuntu 22.04 LTS pour tous les nœuds

Prérequis

À exécuter sur les nœuds du plan de contrôle et nœuds de travail (copier/coller ok) :

# Disable swap. By default Kubelet won't start when swap is enabled
sudo swapoff -a && \

# To make kubelet start properly and make Kubernetes use swap, 
# have a look at this: 
# https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#swap-configuration

# Make sure the br_netfilter and overlay kernels 
# modules are automatically loaded at boot 
echo "overlay" | sudo tee /etc/modules-load.d/k8s.conf && \
echo "br_netfilter" | sudo tee -a /etc/modules-load.d/k8s.conf && \

# Load the overlay and br_netfilter kernels modules
sudo modprobe br_netfilter && \
sudo modprobe overlay && \

# To verify, run:
# lsmod | egrep "overlay|br_netfilter"

# Result:
# overlay               151552  0
# br_netfilter           32768  0
# bridge                311296  1 br_netfilter

# Make sure IPv4 packets forwarding (routing)
# is always enabled on the machine (even after reboot)
echo "net.ipv4.ip_forward = 1" | 
  sudo tee -a /etc/sysctl.d/k8s.conf && \

# Make preceding changes effective
sudo systemctl restart procps.service && \

# To verify, run:
# sysctl net.ipv4.ip_forward

# Result:
# net.ipv4.ip_forward = 1

# Install pre-requisite packages
sudo apt update && sudo apt install -y socat software-properties-common curl

Installer un logiciel d'exécution de conteneur

Nous devons installer un logiciel d'exécution de conteneur qui sera responsable de l'exécution de nos conteneurs de pods du clusters Kubernetes. Tout logiciel / environnement d'exécution de conteneur implémentant la norme d' Interface d'exécution de conteneur (CRI) de Kubernetes devrait fonctionner avec Kubernetes.

Nous utiliserons CRI-O comme logiciel / environnement d'exécution de conteneur. Pour obtenir une liste des versions disponibles, consultez Versions CRI-O.

Installons et configurons CRI-O sur les nœuds du plan de contrôle et les nœuds de travail (copier/coller ok) :

# Set the version of CRI-O to install
CRIO_VERSION=v1.29 && \
    
# Get required GPG key for verifying CRI-O packages signature
curl -fsSL https://pkgs.k8s.io/addons:/cri-o:/stable:/$CRIO_VERSION/deb/Release.key |
    sudo gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg && \

# Add CRI-O deb packages repository
echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://pkgs.k8s.io/addons:/cri-o:/stable:/$CRIO_VERSION/deb/ /" |
    sudo tee /etc/apt/sources.list.d/cri-o.list && \

# Install CRI-O
sudo apt update && sudo apt install -y cri-o && \

# Start CRI-O systemd service
sudo systemctl start crio.service

Notez que le pilote cgroups utilisé par l'environnement d'exécution de conteneur doit être le même que celui utilisé par kubelet que nous installerons ensuite. CRI-O utilise le pilote cgroups systemd par défaut. Pour afficher ce paramètre à partir de la configuration CRI-O, utilisez :

$ crio config | grep cgroup_manager

Pour modifier un paramètre de configuration, ajoutez un nouveau fichier de configuration à l'intérieur du répertoire /etc/crio/crio.conf.d. Utilisez crio config | less pour explorer les paramètres de configuration disponibles et leurs valeurs par défaut.

Installer kubelet, kubeadm et kubectl

Pour obtenir une liste des versions de Kubernetes disponibles, consultez Versions Kubernetes.

Nous allons installer kubelet, kubeadm et kubectl sur les nœuds du plan de contrôle et nœuds de travail (copier/coller ok) :

KUBERNETES_REPO_VERSION=v1.29 && \

# Get required GPG key for verifying Kubernetes packages signature
curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_REPO_VERSION/deb/Release.key |
    sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg && \

# Add Kubernetes deb packages repository
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBERNETES_REPO_VERSION/deb/ /" |
    sudo tee /etc/apt/sources.list.d/kubernetes.list && \

KUBERNETES_INSTALL_VERSION=1.29.9 && \
sudo apt update && sudo apt install -y kubelet=$KUBERNETES_INSTALL_VERSION* kubeadm=$KUBERNETES_INSTALL_VERSION* kubectl=$KUBERNETES_INSTALL_VERSION*

Vérifier:

$ apt policy kubelet kubeadm kubectl
kubelet:
  Installed: 1.29.9-1.1
  Candidate: 1.29.9-1.1
  Version table:
 *** 1.29.9-1.1 500
        500 https://pkgs.k8s.io/core:/stable:/v1.29/deb  Packages
        100 /var/lib/dpkg/status    
kubeadm:
  Installed: 1.29.9-1.1
  Candidate: 1.29.9-1.1
  Version table:
 *** 1.29.9-1.1 500
        500 https://pkgs.k8s.io/core:/stable:/v1.29/deb  Packages
        100 /var/lib/dpkg/status       
kubectl:
  Installed: 1.29.9-1.1
  Candidate: 1.29.9-1.1
  Version table:
 *** 1.29.9-1.1 500
        500 https://pkgs.k8s.io/core:/stable:/v1.29/deb  Packages
        100 /var/lib/dpkg/status

Étant donné que kubeadm gère kubelet en tant que service systemd, le pilote cgroups systemd est recommandé par rapport au pilote cgroupfs, utilisé par défaut par kubelet. À partir de la version 1.22, kubeadm définit le pilote cgroups kubelet par défaut sur systemd.

Le pilote cgroups utilisé par kubelet est systemd, le même pilote cgroups utilisé par notre logiciel / environnement d'exécution de conteneur CRI-O précédemment configuré, donc tout va bien.

Assurons-nous maintenant que nos packages d’installation Kubernetes ne sont pas mis à niveau automatiquement car nous souhaitons le faire manuellement.

$ sudo apt-mark hold kubelet kubeadm kubectl
kubelet set on hold.
kubeadm set on hold.
kubectl set on hold.

Initialiser le cluster sur le nœud du plan de contrôle

Mise en réseau des pods

Le CIDR réseau que nous allons utiliser pour les pods du cluster correspond au CIDR réseau par défaut du Plugin réseau que nous allons utiliser. En effet, les réseaux de pods Kubernetes sont fournis par modules complémentaires/plugins tiers et les nœuds du cluster ne seront pas prêts jusqu'à ce qu'un plugin réseau soit installé.

Notre runtime de conteneur CRI-O est fourni avec une configuration réseau par défaut, qui sera utilisée par le cluster Kubernetes, si nous n'installons pas de module complémentaire/plugin réseau spécifique. Cette configuration par défaut obligera nos pods Kubernetes à utiliser le sous-réseau suivant :

(k8s-control)$ cat /etc/cni/net.d/11-crio-ipv4-bridge.conflist | grep subnet
            [{ "subnet": "10.85.0.0/16" }]

Malheureusement, cette configuration par défaut ne convient pas aux clusters comportant plus d'un nœud. Pour notre cluster à 3 nœuds, nous devrons donc installer un plugin réseau prenant en charge les clusters multi-nœuds. Nous utiliserons Flanelle pour ce cas d'utilisation.

Pour éviter les problèmes avec Flannel, nous devons désactiver la configuration par défaut du plugin CNI de pont réseau fournie par CRI-O :

(k8s-control)$ cd /etc/cni/net.d/
(k8s-control)$ mv 11-crio-ipv4-bridge.conflist 11-crio-ipv4-bridge.conflist.disabled

Notes sur la haute disponibilité

Comme nous prévoyons d'ajouter davantage de nœuds de plan de contrôle à l'avenir (pour une haute disponibilité), nous utiliserons l'option « --control-plane-endpoint », indiquant l'adresse IP ou le nom de domaine du ou des équilibreurs de charge qui seront en amont des nœuds du plan de contrôle.

Pour l'instant, comme nous n'avons pas encore d'équilibreur(s) de charge ni de nœuds multiples pour le plan de contrôle, nous utiliserons, pour ce point de terminaison, un nom de domaine ('api-servers.k8s.local') pointant vers l'adresse IP de notre nœud de plan de contrôle unique.

Pour que les futurs nœuds du cluster résolvent correctement ce nom de domaine, ajoutez ce qui suit dans le fichier « /etc/hosts » sur chaque machine :

<control_plane_node_ip_address> api-servers.k8s.local

Après avoir ajouté davantage de nœuds de plan de contrôle à l'avenir, nous devrons simplement faire en sorte que ce nom de domaine pointe vers l'adresse IP du ou des équilibreurs de charge qui seront en amont des nœuds du plan de contrôle, afin d'atteindre les serveurs API du cluster.

Initialisation du cluster

Nous allons initialiser le cluster Kubernetes en utilisant le CIDR réseau Flannel par défaut ('10.244.0.0/16') pour les pods du cluster, puis déployer Flannel.

L'option '--cri-socket=container_runtime_unix_socket_path' peut également être utilisée dans le cas où plusieurs environnements d'exécution de conteneurs sont installés, pour indiquer celui qui doit être utilisé.

Utilisez la commande suivante pour initialiser le cluster :

(k8s-control)$ sudo kubeadm init --pod-network-cidr 10.244.0.0/16 --control-plane-endpoint api-servers.k8s.local --kubernetes-version v1.29.9
[init] Using Kubernetes version: v1.29.9
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'  
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [api-servers.k8s.local k8s-control kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 172.27.36.172]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-control localhost] and IPs [172.27.36.172 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-control localhost] and IPs [172.27.36.172 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "super-admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 7.001927 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-control as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node k8s-control as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule]
[bootstrap-token] Using token: 9rrzpt.b53urwsvxkrzqw4i
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join api-servers.k8s.local:6443 --token 9rrzpt.b53urwsvxkrzqw4i \
        --discovery-token-ca-cert-hash sha256:0e70caff86cbda4341e7072756835402dd79b9b9be0940dc81c6cb0fd8956f4a \
        --control-plane

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join api-servers.k8s.local:6443 --token 9rrzpt.b53urwsvxkrzqw4i \
        --discovery-token-ca-cert-hash sha256:0e70caff86cbda4341e7072756835402dd79b9b9be0940dc81c6cb0fd8956f4a

Créons maintenant le fichier kubeconfig requis pour s’authentifier et communiquer avec le cluster :

mkdir -p $HOME/.kube && \
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config && \
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Nous pouvons maintenant utiliser « kubectl » pour mettre à jour ou lire certaines des ressources du cluster :

(k8s-control)$ kubectl get nodes
NAME          STATUS      ROLES           AGE   VERSION
k8s-control   NotReady    control-plane   41s   v1.29.9

(k8s-control)$ kubectl get namespaces
NAME              STATUS   AGE
default           Active   46s
kube-node-lease   Active   46s
kube-public       Active   46s
kube-system       Active   46s

# The control plane components
(k8s-control)$ kubectl get pods -n kube-system
NAME                                  READY   STATUS    RESTARTS   AGE
coredns-76f75df574-sd4sj              0/1     Pending   0          52s
coredns-76f75df574-w7f54              0/1     Pending   0          52s
etcd-k8s-control                      1/1     Running   0          68s
kube-apiserver-k8s-control            1/1     Running   0          70s
kube-controller-manager-k8s-control   1/1     Running   0          64s
kube-proxy-slpcs                      1/1     Running   0          52s
kube-scheduler-k8s-control            1/1     Running   0          68s

Comme nous pouvons le voir, le nœud du plan de contrôle n'est pas prêt et les pods du module complémentaire coredns sont dans un état d'attente. C'est parce qu'il n'y a pour l'instant pas de plugin réseau installé.

Installons Flanelle:

(k8s-control)$ kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
namespace/kube-flannel created
serviceaccount/flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created

Vérifions que le nœud est maintenant prêt et que les pods coredns sont en cours d'exécution :

(k8s-control)$ kubectl get nodes
NAME          STATUS      ROLES           AGE    VERSION
k8s-control   Ready       control-plane   5m18s  v1.29.9

(k8s-control)$ kubectl get pods -n kube-system
NAME                                  READY   STATUS    RESTARTS   AGE
coredns-76f75df574-sd4sj              1/1     Running   0          5m25
coredns-76f75df574-w7f54              1/1     Running   0          5m23
(...)

Faire en sorte que les nœuds de travail rejoignent le cluster

Pour obtenir la commande « kubeadm join » qui doit être exécutée sur les nœuds de travail pour faire en sorte qu'ils rejoignent le cluster, exécutez la commande suivante sur le nœud du plan de contrôle :

(k8s-control)$ kubeadm token create --print-join-command

Ensuite, exécutez la commande join affichée, en utilisant sudo, sur les nœuds de travail pour faire en sorte qu'ils rejoignent le cluster Kubernetes :

(k8s-worker1)$ sudo kubeadm join api-servers.k8s.local:6443 --token nhunxq.1owm513wkij8as6e --discovery-token-ca-cert-hash sha256:0e70caff86cbda4341e7072756835402dd79b9b9be0940dc81c6cb0fd8956f4a
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

Nous avons également fait en sorte que « k8s-worker2 » rejoigne le cluster en utilisant la même commande join.
Vérifions que les nœuds de travail précédemment ajoutés font désormais partie du cluster en exécutant la commande suivante sur le nœud du plan de contrôle :

(k8s-control)$ kubectl get nodes
NAME          STATUS   ROLES           AGE    VERSION
k8s-control   Ready    control-plane   8m6s   v1.29.9
k8s-worker1   Ready    <none>          24s    v1.29.9
k8s-worker2   Ready    <none>          16s    v1.29.9

Nous avons ajouté avec succès des nœuds de travail au cluster.

Exécutons quelques pods et vérifions que le DNS, l'accès Internet et la communication entre les pods fonctionnent correctement :

(k8s-control)$ kubectl run nginx --image=nginx
pod/nginx created

(k8s-control)$ kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS        AGE     IP           NODE          NOMINATED NODE   READINESS GATES
nginx     1/1     Running   0               7s      10.244.1.2   k8s-worker1   <none>           <none>

(k8s-control)$ kubectl run -it busybox --image=busybox -- sh
If you don't see a command prompt, try pressing enter.
/ #
/ # cat /etc/resolv.conf
search default.svc.cluster.local svc.cluster.local cluster.local mshome.net
nameserver 10.96.0.10 # IP address of the coredns service
options ndots:5
/ #
/ # nslookup hackerstack.org # DNS resolution is working
Server:         10.96.0.10
Address:        10.96.0.10:53

Non-authoritative answer:
Name:   hackerstack.org
Address: 172.67.200.204
Name:   hackerstack.org
Address: 104.21.36.232
(...)
/ #
/ # nc -vz hackerstack.org 443 # internet access is working
hackerstack.org (104.21.36.232:443) open
/ #
/ # nc -vz 10.244.1.2 80 # communication with the nginx pod is working
10.244.1.2 (10.244.1.2:80) open

Très bien... les choses fonctionnent comme prévu. L'adresse IP '10.96.0.10' utilisée pour la résolution DNS par les conteneurs des pods correspond au service DNS du cluster Kubernetes dont les backends sont des pods coredns :

(k8s-control)$ kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   66m

(k8s-control)$ kubectl get pods -n kube-system
NAME                                  READY   STATUS    RESTARTS   AGE
coredns-76f75df574-sd4sj              1/1     Running   0          35m
(...)

Mettre à niveau le cluster

Contraintes de versions

Voici les contraintes de versions des composants des clusters Kubernetes par rapport à kubeadm :

  • Kubelet par rapport à kubeadm :
    • même version ou trois versions plus anciennes
    • Ex : kubeadm est à 1.31.x => kubelet à 1.31.x 1.30.x 1.29.x 1.28.x
  • Autres composants Kubernetes (kube-apiserver, kube-proxy, kube-controller-manager, kube-scheduler) par rapport à kubeadm :
    • même version ou une version plus ancienne
    • Ex : kubeadm à 1.31.x => composants Kubernetes à 1.31.x ou 1.30.x
    • La version cible des composants Kubernetes est spécifiée à l'aide de l'option kubeadm '--kubernetes-version'

Étapes de mise à niveau

Nous commençons par mettre à niveau le nœud du plan de contrôle, puis les nœuds de travail :

  • Mise à niveau du nœud du plan de contrôle :

    • drainer le nœud
    • mettre à niveau kubeadm
    • planifier la mise à niveau : kubeadm plan upgrade
    • appliquer la mise à niveau : kubeadm apply upgrade
    • mettre à niveau kubelet et kubectl
    • rendre le nœud disponible
  • Mise à niveau des nœuds de travail :

    • drainer le nœud (à partir du nœud du plan de contrôle)
    • mettre à niveau kubeadm
    • mettre à niveau la configuration de kubelet, kubelet et kubectl : kubeadm upgrade node
    • rendre le nœud disponible

Mise à niveau du nœud du plan de contrôle

Voici l’état des nœuds du cluster avant la mise à niveau :

(k8s-control)$ kubectl get nodes
NAME          STATUS   ROLES           AGE   VERSION
k8s-control   Ready    control-plane   31h   v1.29.9
k8s-worker1   Ready    <none>          31h   v1.29.9
k8s-worker2   Ready    <none>          31h   v1.29.9

Nous allons mettre à niveau le nœud du plan de contrôle vers la version 1.30.x de Kubernetes.

Ajouter le dépôt de paquets de la version cible de Kubernetes

Pour obtenir une liste des versions de Kubernetes disponibles, consultez Versions Kubernetes.

KUBERNETES_REPO_VERSION=v1.30 && \

# Get required GPG key for verifying Kubernetes packages signature
curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_REPO_VERSION/deb/Release.key |
    sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg && \

# Add Kubernetes deb packages repository
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBERNETES_REPO_VERSION/deb/ /" |
    sudo tee /etc/apt/sources.list.d/kubernetes.list
Drain du nœud et mise à niveau kubeadm
# Drain the node
(k8s-control)$ kubectl drain k8s-control --ignore-daemonsets
node/k8s-control cordoned
Warning: ignoring DaemonSet-managed Pods: kube-system/kube-proxy-mlzcd
node/k8s-control drained

# Verify
(k8s-control)$ kubectl get nodes
NAME          STATUS                     ROLES           AGE    VERSION
k8s-control   Ready,SchedulingDisabled   control-plane   2d7h   v1.29.9
k8s-worker1   Ready                      <none>          2d7h   v1.29.9
k8s-worker2   Ready                      <none>          23h    v1.29.9

# Upgrade kubeadm
KUBERNETES_INSTALL_VERSION=1.30.5 && \
sudo apt update && sudo apt install -y --allow-change-held-packages \
kubeadm=$KUBERNETES_INSTALL_VERSION*
Planifier et appliquer la mise à niveau

Procédez comme suit pour mettre à niveau les composants Kubernetes (kube-apiserver, kube-proxy, kube-controller-manager, kube-scheduler) vers la version 1.30.5 ainsi que etcd et CoreDNS vers leurs dernières versions.

  • Voir la planification de la mise à niveau
(k8s-control)$ sudo kubeadm upgrade plan v1.30.5
[preflight] Running pre-flight checks.
[upgrade/config] Reading configuration from the cluster...
[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[upgrade] Running cluster health checks
[upgrade] Fetching available versions to upgrade to
[upgrade/versions] Cluster version: 1.29.9
[upgrade/versions] kubeadm version: v1.30.5
[upgrade/versions] Target version: v1.30.5
[upgrade/versions] Latest version in the v1.29 series: v1.30.5

Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT   NODE          CURRENT   TARGET
kubelet     k8s-control   v1.29.9   v1.30.5
kubelet     k8s-worker1   v1.29.9   v1.30.5
kubelet     k8s-worker2   v1.29.9   v1.30.5

Upgrade to the latest version in the v1.29 series:

COMPONENT                 NODE          CURRENT    TARGET
kube-apiserver            k8s-control   v1.29.9    v1.30.5
kube-controller-manager   k8s-control   v1.29.9    v1.30.5
kube-scheduler            k8s-control   v1.29.9    v1.30.5
kube-proxy                              1.29.9     v1.30.5
CoreDNS                                 v1.11.1    v1.11.3
etcd                      k8s-control   3.5.15-0   3.5.15-0

You can now apply the upgrade by executing the following command:

        kubeadm upgrade apply v1.30.5

_____________________________________________________________________


The table below shows the current state of component configs as understood by this version of kubeadm.
Configs that have a "yes" mark in the "MANUAL UPGRADE REQUIRED" column require manual config upgrade or
resetting to kubeadm defaults before a successful upgrade can be performed. The version to manually
upgrade to is denoted in the "PREFERRED VERSION" column.

API GROUP                 CURRENT VERSION   PREFERRED VERSION   MANUAL UPGRADE REQUIRED
kubeproxy.config.k8s.io   v1alpha1          v1alpha1            no
kubelet.config.k8s.io     v1beta1           v1beta1             no
_____________________________________________________________________
  • Effectuer la mise à niveau
(k8s-control)$ sudo kubeadm upgrade apply v1.30.5
[preflight] Running pre-flight checks.
[upgrade/config] Reading configuration from the cluster...
[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[upgrade] Running cluster health checks
[upgrade/version] You have chosen to change the cluster version to "v1.30.5"
[upgrade/versions] Cluster version: v1.29.9
[upgrade/versions] kubeadm version: v1.30.5
[upgrade] Are you sure you want to proceed? [y/N]: y
[upgrade/prepull] Pulling images required for setting up a Kubernetes cluster
[upgrade/prepull] This might take a minute or two, depending on the speed of your internet connection
[upgrade/prepull] You can also perform this action in beforehand using 'kubeadm config images pull'
[upgrade/apply] Upgrading your Static Pod-hosted control plane to version "v1.30.5" (timeout: 5m0s)...
[upgrade/etcd] Upgrading to TLS for etcd
[upgrade/staticpods] Preparing for "etcd" upgrade
[upgrade/staticpods] Current and new manifests of etcd are equal, skipping upgrade
[upgrade/etcd] Waiting for etcd to become available
[upgrade/staticpods] Writing new Static Pod manifests to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests2733535561"
[upgrade/staticpods] Preparing for "kube-apiserver" upgrade
[upgrade/staticpods] Renewing apiserver certificate
[upgrade/staticpods] Renewing apiserver-kubelet-client certificate
[upgrade/staticpods] Renewing front-proxy-client certificate
[upgrade/staticpods] Renewing apiserver-etcd-client certificate
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-apiserver.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2024-09-28-08-07-04/kube-apiserver.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This can take up to 5m0s
[apiclient] Found 1 Pods for label selector component=kube-apiserver
[upgrade/staticpods] Component "kube-apiserver" upgraded successfully!
[upgrade/staticpods] Preparing for "kube-controller-manager" upgrade
[upgrade/staticpods] Renewing controller-manager.conf certificate
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-controller-manager.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2024-09-28-08-07-04/kube-controller-manager.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This can take up to 5m0s
[apiclient] Found 1 Pods for label selector component=kube-controller-manager
[upgrade/staticpods] Component "kube-controller-manager" upgraded successfully!
[upgrade/staticpods] Preparing for "kube-scheduler" upgrade
[upgrade/staticpods] Renewing scheduler.conf certificate
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-scheduler.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2024-09-28-08-07-04/kube-scheduler.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This can take up to 5m0s
[apiclient] Found 1 Pods for label selector component=kube-scheduler
[upgrade/staticpods] Component "kube-scheduler" upgraded successfully!
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster
[upgrade] Backing up kubelet config file to /etc/kubernetes/tmp/kubeadm-kubelet-config1684861658/config.yaml
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.30.5". Enjoy!

[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.
Mettre à jour kubelet et kubectl
# Installation
KUBERNETES_INSTALL_VERSION=1.30.5 && \
sudo apt update && sudo apt install -y --allow-change-held-packages \
kubelet=$KUBERNETES_INSTALL_VERSION* kubectl=$KUBERNETES_INSTALL_VERSION* && \

# Ensure new systemd unit files are loaded
sudo systemctl daemon-reload && \

# Restart kubelet
sudo systemctl restart kubelet
Finaliser la mise à niveau du nœud du plan de contrôle

Vérifions maintenant la version du plan de contrôle :

(k8s-control)$ kubectl get nodes
NAME          STATUS                     ROLES           AGE    VERSION
k8s-control   Ready,SchedulingDisabled   control-plane   2d7h   v1.30.5
k8s-worker1   Ready                      <none>          2d7h   v1.29.9
k8s-worker2   Ready                      <none>          23h    v1.29.9

Comme nous pouvons le voir à partir de la sortie de la commande précédente, le nœud du plan de contrôle « k8s-control » a été mis à niveau avec succès vers la version 1.30.5 de Kubernetes.

Nous pouvons maintenant rendre le nœud disponible pour mettre à jour son statut sur Prêt et nous assurer que kubeadm, kubelet et kubectl ne sont pas automatiquement mis à niveau :

kubectl uncordon k8s-control && \
sudo apt-mark hold kubeadm kubelet kubectl

Étapes de vérification finales :

(k8s-control)$ kubectl get nodes
NAME          STATUS   ROLES           AGE    VERSION
k8s-control   Ready    control-plane   2d7h   v1.30.5
k8s-worker1   Ready    <none>          2d7h   v1.29.9
k8s-worker2   Ready    <none>          24h    v1.29.9

(k8s-control)$ apt-mark showhold
kubeadm
kubectl
kubelet

Mise à niveau des nœuds de travail

Ajouter le dépôt de paquets pour la version future de Kubernetes

Pour obtenir une liste des versions de Kubernetes disponibles, consultez Versions Kubernetes.

KUBERNETES_REPO_VERSION=v1.30 && \

# Get required GPG key for verifying Kubernetes packages signature
curl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_REPO_VERSION/deb/Release.key |
    sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg && \

# Add Kubernetes deb packages repository
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBERNETES_REPO_VERSION/deb/ /" |
    sudo tee /etc/apt/sources.list.d/kubernetes.list
Drain du nœud de travail et mise à niveau kubeadm
# Drain the worker node from the control plane node
(k8s-control)$ kubectl drain k8s-worker1 --ignore-daemonsets --force
node/k8s-worker1 cordoned
Warning: ignoring DaemonSet-managed Pods: kube-system/kube-proxy-h7f2q; deleting Pods that declare no controller: default/busybox, default/nginx, default/ubuntu
evicting pod default/ubuntu
evicting pod default/busybox
evicting pod default/nginx
pod/nginx evicted
pod/busybox evicted
pod/ubuntu evicted
node/k8s-worker1 drained

# Upgrade kubeadm on the worker node
KUBERNETES_INSTALL_VERSION=1.30.5 && \
sudo apt update && sudo apt install -y --allow-change-held-packages \
kubeadm=$KUBERNETES_INSTALL_VERSION*
Mettre à jour la configuration de kubelet, kubelet et kubectl
  • Mettre à jour la configuration de kubelet
(k8s-worker1)$ sudo kubeadm upgrade node
[upgrade] Reading configuration from the cluster...
[upgrade] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[preflight] Running pre-flight checks
[preflight] Skipping prepull. Not a control plane node.
[upgrade] Skipping phase. Not a control plane node.
[upgrade] Backing up kubelet config file to /etc/kubernetes/tmp/kubeadm-kubelet-config1488552398/config.yaml
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[upgrade] The configuration for this node was successfully updated!
[upgrade] Now you should go ahead and upgrade the kubelet package using your package manager.
  • Mettre à jour kubelet et kubectl
KUBERNETES_INSTALL_VERSION=1.30.5 && \
sudo apt update && sudo apt install -y --allow-change-held-packages \
kubelet=$KUBERNETES_INSTALL_VERSION* kubectl=$KUBERNETES_INSTALL_VERSION* && \

# Ensure new systemd unit files are loaded
sudo systemctl daemon-reload && \

# Restart kubelet
sudo systemctl restart kubelet
Finaliser la mise à niveau du nœud de travail

Vérifions maintenant la version du nœud de travail :

(k8s-control)$ kubectl get nodes
NAME          STATUS                     ROLES           AGE     VERSION
k8s-control   Ready                      control-plane   2d10h   v1.30.5
k8s-worker1   Ready,SchedulingDisabled   <none>          2d10h   v1.30.5
k8s-worker2   Ready                      <none>          26h     v1.29.9

Comme nous pouvons le voir à partir de la sortie de la commande précédente, le nœud de travail « k8s-worker1 » a été mis à niveau avec succès vers la version 1.30.5 de Kubernetes.

Nous pouvons maintenant rendre le nœud disponible pour mettre à jour son statut sur Prêt et nous assurer que kubeadm, kubelet et kubectl ne sont pas automatiquement mis à niveau :

(k8s-control)$ kubectl uncordon k8s-worker1
node/k8s-worker1 uncordoned

(k8s-worker1)$ sudo apt-mark hold kubeadm kubelet kubectl
kubeadm set on hold.
kubelet set on hold.
kubectl set on hold.

Étapes de vérification finales :

(k8s-control)$ kubectl get nodes
NAME          STATUS   ROLES           AGE     VERSION
k8s-control   Ready    control-plane   2d10h   v1.30.5
k8s-worker1   Ready    <none>          2d10h   v1.30.5
k8s-worker2   Ready    <none>          27h     v1.29.9

(k8s-worker1)$ apt-mark showhold
kubeadm
kubectl
kubelet

Après avoir répété les étapes de mise à niveau du nœud de travail précédent sur le nœud de travail « k8s-worker2 » restant, notre cluster Kubernetes est entièrement mis à niveau :

(k8s-control)$ kubectl get nodes
NAME          STATUS   ROLES           AGE     VERSION
k8s-control   Ready    control-plane   2d23h   v1.30.5
k8s-worker1   Ready    <none>          2d23h   v1.30.5
k8s-worker2   Ready    <none>          39h     v1.30.5