Aller au contenu

Installer des clusters Kubernetes avec Kubeadm

Mise à jour :

logo kubernetes

Kubeadm est un outil conçu pour simplifier le déploiement de clusters Kubernetes sur vos serveurs. Il permet d’initialiser un cluster Kubernetes en quelques commandes seulement et assure la configuration de tous les composants nécessaires pour le faire fonctionner.

Introduction

Avec Kubeadm, vous pouvez créer des clusters vanilla avec une installation plus rapide et plus fiable. L’outil est extensible et adaptable, permettant aux administrateurs de personnaliser les configurations selon les besoins spécifiques de leur environnement. Kubeadm gère également la mise à jour et la configuration des clusters, ce qui simplifie grandement le processus d’administration continue.

L’objectif principal de ce guide est de fournir une vue d’ensemble complète de Kubeadm, en couvrant ses fonctionnalités principales, les concepts de base, les étapes d’installation et les meilleures pratiques pour installer et maintenir un cluster Kubernetes.

Historique de Kubeadm

Kubeadm a été introduit par la communauté Kubernetes pour répondre à la complexité croissante de l’installation et de la gestion des clusters Kubernetes. Avant Kubeadm, le déploiement de Kubernetes nécessitait de nombreuses étapes manuelles, une configuration complexe et une bonne compréhension des composants internes de Kubernetes. Cela rendait le processus non seulement fastidieux, mais aussi sujet aux erreurs, surtout pour les nouveaux utilisateurs.

L’outil Kubeadm a été lancé officiellement avec la version 1.6 de Kubernetes en 2017. Son objectif principal était de fournir une méthode simple, rapide et reproductible pour installer Kubernetes, sans pour autant compromettre la flexibilité et la robustesse des déploiements.

Avec chaque nouvelle version de Kubernetes, Kubeadm a été amélioré pour prendre en compte les changements de l’API de Kubernetes et en charge de nouvelles fonctionnalités et les changements de l’API de Kubernetes. Aujourd’hui, Kubeadm est largement utilisé dans les environnements de production, les environnements de test et par les développeurs souhaitant configurer des clusters Kubernetes sur leurs machines.

Fonctionnalités de Kubeadm

Kubeadm offre plusieurs fonctionnalités clés qui simplifient le déploiement et la gestion des clusters Kubernetes :

  • Initialisation du cluster : Configure automatiquement les composants critiques du plan de contrôle.
  • Ajout de nœuds : Génère des jetons d’adhésion sécurisés pour intégrer de nouveaux nœuds au cluster.
  • Gestion des mises à jour : Simplifie la mise à jour des composants du plan de contrôle et des nœuds de travail.
  • Gestion des certificats : Génère, renouvelle et distribue les certificats de sécurité nécessaires.
  • Configuration du réseau de pods : Supporte plusieurs plugins de réseau comme Calico et Flannel.
  • Support multi-plateforme : Compatible avec plusieurs systèmes d’exploitation et architectures matérielles.
  • Automatisation : S’intègre avec des outils comme Ansible, Terraform et des bash script pour automatiser les tâches d’administration.
  • Vérification de l’état du cluster : Fournit des outils pour diagnostiquer et vérifier l’état du cluster.

Prérequis

Avant de commencer avec Kubeadm, il est essentiel de s’assurer que votre environnement est correctement configuré pour supporter le déploiement et la gestion d’un cluster Kubernetes. Voici les prérequis nécessaires pour un déploiement réussi avec Kubeadm :

Infrastructure matérielle

Vous aurez besoin d’un ensemble de machines virtuelles ou physiques pour constituer les nœuds de votre cluster. Typiquement, un cluster Kubernetes comprend un nœud maître et plusieurs nœuds de travail. Voici les spécifications matérielles recommandées pour chaque type de nœud :

  • Nœud maître :

    • CPU : 2 cœurs minimum
    • Mémoire : 2 GB de RAM minimum
    • Stockage : 20 GB de disque
  • Nœud de travail :

    • CPU : 1 cœur minimum
    • Mémoire : 1 GB de RAM minimum
    • Stockage : 20 GB de disque

Dans mon cas, je vais utiliser des machines virtuelles instanciées sur le cloud Ouitscale. Je vais utiliser 3 maîtres et 2 workers.

Système d’exploitation

Kubeadm supporte plusieurs distributions Linux. Voici quelques exemples courants :

  • Ubuntu 22.04+
  • Rocky Linux 8+
  • Debian 11+

Il est recommandé de maintenir votre système d’exploitation à jour avec les dernières mises à jour de sécurité.

Logiciels nécessaires

Avant d’installer Kubeadm, certains logiciels doivent être installés et configurés sur chaque nœud :

  1. Un runtime de conteneurs parmi :
  • containerd : Un runtime de conteneur léger et rapide, maintenu par la CNCF. Il est souvent utilisé comme runtime par défaut pour Kubernetes.
  • CRI-O : Un runtime de conteneur optimisé pour Kubernetes, conforme à l’interface CRI (Container Runtime Interface). Il est conçu pour fonctionner avec les pods Kubernetes de manière efficace et sécurisée.
  • Docker : Docker utilise le runtime containerd.
  • gVisor : Un runtime de conteneur sécurisé développé par Google, qui offre une isolation renforcée des conteneurs.
  1. Kubeadm, Kubelet et Kubectl : Ces trois composants sont essentiels pour la gestion du cluster Kubernetes.

Configuration réseau

Kubernetes nécessite une configuration réseau correcte pour permettre la communication entre les nœuds. Assurez-vous que les ports suivants sont disponibles sur vos machines :

  • Port 6443 (kube-apiserver) sur le nœud maître
  • Ports 10250, 10251, 10252 (kubelet, scheduler, controller-manager) sur le nœud maître
  • Port 10250 (kubelet) sur les nœuds de travail
  • Port 179 (BGP) si vous l’utilisez

De plus, les nœuds doivent pouvoir communiquer entre eux sans restriction.

Accès utilisateur

Vous aurez besoin d’un accès root ou sudo sur toutes les machines pour exécuter les commandes nécessaires à l’installation et à la configuration de Kubernetes.

Synchronisation de l’heure

Il est indispensable que toutes les machines du cluster aient des horloges synchronisées. Utilisez un service comme NTP ou Chrony (Network Time Protocol) pour assurer la synchronisation de l’heure.

Création des machines sur le cloud Outscale

Ici, je vais suivre une architecture similaire à celle utilisée pour provisionner des machines sur le cloud Outscale avec Terraform, en l’adaptant pour déployer un cluster Kubernetes avec Kubeadm.

Avant de commencer, assurez-vous d’avoir installé les outils nécessaires comme Terraform , Ansible et d’avoir également vos identifiants d’accès pour Outscale.

Définissez un VPC (Virtual Private Cloud) et des sous-réseaux pour isoler vos ressources cloud. Créer les 5 machines dans le subnet privé.

Pour plus de détails, vous pouvez consulter l’article original sur le provisionnement de machines avec Terraform sur Outscale.

Installation des prérequis

Pour installer Kubeadm, ainsi que Kubelet et Kubectl, nous devons d’abord installer et configurer containerd comme runtime de conteneur. containerd est une alternative légère et performante à Docker, souvent utilisée dans les environnements Kubernetes.

Pour faciliter et accélérer leur déploiement quoi de mieux que d’utiliser Packer et de créer une OMI.

Voici le contenu du playbook Ansible :

provision_kube_nodes.yaml
---
- name: Add soft on kubernetes VM
become: true
hosts:
- kube_masters
- kube_workers
vars:
kubectl_version: "1.31.1-1.1"
kube_version_full: "1.31.1-1.1"
kube_version: "1.31"
sysctl_config:
net.ipv4.ip_forward: 1
net.bridge.bridge-nf-call-ip6tables: 1
net.bridge.bridge-nf-call-iptables: 1
tasks:
- name: Add Kubernetes GPG key | {{ inventory_hostname }}
ansible.builtin.get_url:
url: https://pkgs.k8s.io/core:/stable:/v{{ kube_version }}/deb/Release.key
dest: /etc/apt/keyrings/kubernetes-apt-keyring.asc
mode: '0644'
force: true
- name: Add Kubernetes repository | {{ inventory_hostname }}
ansible.builtin.apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.asc] https://pkgs.k8s.io/core:/stable:/v{{ kube_version }}/deb/ /"
state: present
update_cache: true
- name: Add Docker GPG key | {{ inventory_hostname }}
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
- name: Add Docker repository | {{ inventory_hostname }}
ansible.builtin.apt_repository:
repo: deb https://download.docker.com/linux/ubuntu jammy stable
state: present
update_cache: true
- name: Install packages | {{ inventory_hostname }}
ansible.builtin.package:
name:
- gnupg2
- software-properties-common
- apt-transport-https
- ca-certificates
- kubelet={{ kube_version_full }}
- kubeadm={{ kube_version_full }}
- kubectl={{ kubectl_version }}
- net-tools
- docker.io
state: present
- name: Hold version | {{ inventory_hostname }}
ansible.builtin.dpkg_selections:
name: "{{ item }}"
selection: hold
with_items:
- kubeadm
- kubelet
- kubectl
- name: Load modules overlay and br_netfilter
community.general.modprobe:
name: overlay
state: present
with_items:
- overlay
- br_netfilter
- name: Load modprobe
ansible.builtin.shell:
cmd: modprobe {{ item }}
with_items:
- overlay
- br_netfilter
- name: Configure systctl
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
sysctl_file: /etc/sysctl.d/kubernetes.conf
with_dict: '{{ sysctl_config }}'
- name: Configure crictl tools config
ansible.builtin.copy:
src: files/crictl.yaml
dest: /etc/
owner: root
group: root
mode: 0644
- name: Check swap State
ansible.builtin.stat:
path: /swap.img
register: swap_file_check
- name: Unmount swap | {{ inventory_hostname }}
ansible.builtin.shell:
cmd: swapoff -a
when: ansible_swaptotal_mb > 0
- name: Disable swap | {{ inventory_hostname }}
ansible.builtin.lineinfile:
path: /etc/fstab
regexp: '\sswap\s+sw\s+'
state: absent
when: swap_file_check.stat.exists
- name: Delete swap file
ansible.builtin.file:
path: /swap.img
state: absent
when: swap_file_check.stat.exists
- name: Create containerd folder
ansible.builtin.file:
name: /etc/containerd/
owner: root
group: root
mode: 0644
state: directory
- name: Delete Container default config
ansible.builtin.copy:
src: files/config.toml
dest: /etc/containerd/config.toml
owner: root
group: root
mode: 0644
notify: Restart containerd
handlers:
- name: Restart containerd
ansible.builtin.service:
name: containerd
state: restarted
- name: Install soft on controller
hosts: bastions
gather_facts: true
become: true
vars:
kubectl_version: "1.31.1-1.1"
kube_version_full: "1.31.1-1.1"
kube_version: "1.31"
base_ip_str: "10.240.0.1"
tasks:
- name: Add Kubernetes GPG key | {{ inventory_hostname }}
ansible.builtin.get_url:
url: https://pkgs.k8s.io/core:/stable:/v{{ kube_version }}/deb/Release.key
dest: /etc/apt/keyrings/kubernetes-apt-keyring.asc
mode: '0644'
force: true
- name: Add Kubernetes repository | {{ inventory_hostname }}
ansible.builtin.apt_repository:
repo: "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.asc] https://pkgs.k8s.io/core:/stable:/v{{ kube_version }}/deb/ /"
state: present
update_cache: true
- name: Install packages | {{ inventory_hostname }}
ansible.builtin.package:
name:
- kubectl
- haproxy
state: present
- name: Add haproxy config | {{ inventory_hostname }}
vars:
cplanes: "{{ groups['kube_masters'] }}"
workers: "{{ groups['kube_workers'] }}"
ansible.builtin.template:
src: haproxy.cfg.j2
dest: /etc/haproxy/haproxy.cfg
mode: 0644
- name: Start haproxy | {{ inventory_hostname }}
ansible.builtin.service:
name: haproxy
enabled: true
state: restarted
- name: Create folder .kube
ansible.builtin.file:
dest: /home/outscale/.kube
state: directory
owner: outscale
group: outscale
mode: 0700

Le contenu des templates :

files/config.toml
disabled_plugins = []
imports = []
oom_score = 0
plugin_dir = ""
required_plugins = []
root = "/var/lib/containerd"
state = "/run/containerd"
temp = ""
version = 2
[cgroup]
path = ""
[debug]
address = ""
format = ""
gid = 0
level = ""
uid = 0
[grpc]
address = "/run/containerd/containerd.sock"
gid = 0
max_recv_message_size = 16777216
max_send_message_size = 16777216
tcp_address = ""
tcp_tls_ca = ""
tcp_tls_cert = ""
tcp_tls_key = ""
uid = 0
[metrics]
address = ""
grpc_histogram = false
[plugins]
[plugins."io.containerd.gc.v1.scheduler"]
deletion_threshold = 0
mutation_threshold = 100
pause_threshold = 0.02
schedule_delay = "0s"
startup_delay = "100ms"
[plugins."io.containerd.grpc.v1.cri"]
device_ownership_from_security_context = false
disable_apparmor = false
disable_cgroup = false
disable_hugetlb_controller = true
disable_proc_mount = false
disable_tcp_service = true
enable_selinux = false
enable_tls_streaming = false
enable_unprivileged_icmp = false
enable_unprivileged_ports = false
ignore_image_defined_volumes = false
max_concurrent_downloads = 3
max_container_log_line_size = 16384
netns_mounts_under_state_dir = false
restrict_oom_score_adj = false
sandbox_image = "registry.k8s.io/pause:3.9"
selinux_category_range = 1024
stats_collect_period = 10
stream_idle_timeout = "4h0m0s"
stream_server_address = "127.0.0.1"
stream_server_port = "0"
systemd_cgroup = false
tolerate_missing_hugetlb_controller = true
unset_seccomp_profile = ""
[plugins."io.containerd.grpc.v1.cri".cni]
bin_dir = "/opt/cni/bin"
conf_dir = "/etc/cni/net.d"
conf_template = ""
ip_pref = ""
max_conf_num = 1
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "runc"
disable_snapshot_annotations = true
discard_unpacked_layers = false
ignore_rdt_not_enabled_errors = false
no_pivot = false
snapshotter = "overlayfs"
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = ""
[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime.options]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
BinaryName = ""
CriuImagePath = ""
CriuPath = ""
CriuWorkPath = ""
IoGid = 0
IoUid = 0
NoNewKeyring = false
NoPivotRoot = false
Root = ""
ShimCgroup = ""
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]
base_runtime_spec = ""
cni_conf_dir = ""
cni_max_conf_num = 0
container_annotations = []
pod_annotations = []
privileged_without_host_devices = false
runtime_engine = ""
runtime_path = ""
runtime_root = ""
runtime_type = ""
[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime.options]
[plugins."io.containerd.grpc.v1.cri".image_decryption]
key_model = "node"
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = ""
[plugins."io.containerd.grpc.v1.cri".registry.auths]
[plugins."io.containerd.grpc.v1.cri".registry.configs]
[plugins."io.containerd.grpc.v1.cri".registry.headers]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
tls_cert_file = ""
tls_key_file = ""
[plugins."io.containerd.internal.v1.opt"]
path = "/opt/containerd"
[plugins."io.containerd.internal.v1.restart"]
interval = "10s"
[plugins."io.containerd.internal.v1.tracing"]
sampling_ratio = 1.0
service_name = "containerd"
[plugins."io.containerd.metadata.v1.bolt"]
content_sharing_policy = "shared"
[plugins."io.containerd.monitor.v1.cgroups"]
no_prometheus = false
[plugins."io.containerd.runtime.v1.linux"]
no_shim = false
runtime = "runc"
runtime_root = ""
shim = "containerd-shim"
shim_debug = false
[plugins."io.containerd.runtime.v2.task"]
platforms = ["linux/amd64"]
sched_core = false
[plugins."io.containerd.service.v1.diff-service"]
default = ["walking"]
[plugins."io.containerd.service.v1.tasks-service"]
rdt_config_file = ""
[plugins."io.containerd.snapshotter.v1.aufs"]
root_path = ""
[plugins."io.containerd.snapshotter.v1.btrfs"]
root_path = ""
[plugins."io.containerd.snapshotter.v1.devmapper"]
async_remove = false
base_image_size = ""
discard_blocks = false
fs_options = ""
fs_type = ""
pool_name = ""
root_path = ""
[plugins."io.containerd.snapshotter.v1.native"]
root_path = ""
[plugins."io.containerd.snapshotter.v1.overlayfs"]
root_path = ""
upperdir_label = false
[plugins."io.containerd.snapshotter.v1.zfs"]
root_path = ""
[plugins."io.containerd.tracing.processor.v1.otlp"]
endpoint = ""
insecure = false
protocol = ""
[proxy_plugins]
[stream_processors]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
path = "ctd-decoder"
returns = "application/vnd.oci.image.layer.v1.tar"
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
path = "ctd-decoder"
returns = "application/vnd.oci.image.layer.v1.tar+gzip"
[timeouts]
"io.containerd.timeout.bolt.open" = "0s"
"io.containerd.timeout.shim.cleanup" = "5s"
"io.containerd.timeout.shim.load" = "5s"
"io.containerd.timeout.shim.shutdown" = "3s"
"io.containerd.timeout.task.state" = "2s"
[ttrpc]
address = ""
gid = 0
uid = 0
files/crictl.yml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 2
debug: false
pull-image-on-create: false

Créez votre inventaire statique en créant des groupes :

  • kube_masters
  • kube_workers

Personnellement, j’utilise un inveentaire dynamique qui créé ces groupes automatiquement :

Terminal window
ansible-inventory --graph
$ ansible-inventory --graph
@all:
|--@ungrouped:
|--@bastion:
| |--xxx.44.54.206
|--@kube_masters:
| |--10.0.2.16
| |--10.0.2.183
| |--10.0.2.221
|--@kube_workers:
| |--10.0.2.80
| |--10.0.2.147
| |--10.0.2.110
|--@moodle:
| |--10.0.2.91

Exécutez le playbook :

Terminal window
ansible-playbook provision_kube_nodes.yml

Utilisation d’haproxy comme Load Balancer

Je ne vais pas utiliser le service de Load Balancer Outscale. Je vais utiliser HAProxy pour cette tâche que je vais installer sur le bastion.

install_haproxy.yaml
---
- name: Create Corosync Pacemaker Cluster
hosts: bastions
gather_facts: true
tasks:
- name: Install packages
become: true
ansible.builtin.apt:
name:
- haproxy
state: present
- name: Copy haproxy config
become: true
ansible.builtin.copy:
src: haproxy.cfg
dest: /etc/haproxy/haproxy.cfg
mode: "0644"
owner: root
group: root
notify: Restart Service
handlers:
- name: Restart Service
become: true
ansible.builtin.service:
name: haproxy
state: restarted

Le template :

templates/haproxy.cfg.j2
listen stats
bind *:9000
mode http
stats enable
stats hide-version
stats uri /stats
stats refresh 30s
stats realm Haproxy\ Statistics
stats auth Admin:<votre mot de passe>
timeout client 100s
timeout server 100s
timeout connect 100s
global
daemon
maxconn 256
defaults
mode http
log global
option redispatch
retries 3
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend kubernetes-frontend
bind *:6443
option tcplog
mode tcp
default_backend kubernetes-backend
backend kubernetes-backend
mode tcp
balance roundrobin
option tcp-check
{% for host in cplanes %}
server kube_api{{loop.index}} {{ hostvars[host]["private_ip_address"] }}:6443 check
{% endfor %}

On peut lancer l’exécution du playbook :

Terminal window
ansible-playbook install_haproxy.yml

On se connecte au bastion et on vérifie que les ports sont en écoute :

Terminal window
ssh bastion
_ _ _
| |__ __ _ ___| |_(_) ___ _ __ __ _
| '_ \ / _` / __| __| |/ _ \| '_ \ _____ / _` |
| |_) | (_| \__ \ |_| | (_) | | | |_____| (_| |
|_.__/ \__,_|___/\__|_|\___/|_| |_| \__,_|
System info:
Hostname····: bastion-a.test.local
Distro······: Ubuntu 22.04.5 LTS
Kernel······: Linux 5.15.0-107-generic
Uptime······: up 2 days, 23 hours, 58 minutes
Load········: 0.00 (1m), 0.00 (5m), 0.00 (15m)
Processes···: 102 (root), 8 (user), 110 (total)
CPU·········: Intel Xeon Processor (Icelake) (2 vCPU)
Memory······: 1.0Gi used, 2.5Gi avail, 3.8Gi total
Disk usage:
/ 29% used out of 16G
[==============····································]
/boot/efi 6% used out of 110M
[===···············································]
outscale@bastion-a:~$ netstat -tlnp
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:6443 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:9000 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:8010 0.0.0.0:* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
tcp6 0 0 :::8010 :::* LISTEN -

C’est bon port le port 6443 est bien en écoute.

Initialisation d’un cluster manuellement

Sur le premier nœud maître, exécutez la commande suivante pour initialiser le cluster :

Terminal window
sudo kubeadm init --control-plane-endpoint <L'IP de votre bastion>:6443 --upload-cert --pod-network-cidr=192.168.0.0/16

Cette commande initialise un cluster Kubernetes et configure les composants du plan de contrôle.

Pour permettre à l’utilisateur non-root d’accéder au cluster, exécutez les commandes suivantes sur votre machine (dans mon cas c’est mon bastion) :

Terminal window
mkdir -p $HOME/.kube
sudo scp -i <MASTER_IP>:/etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Ajout de nœuds au cluster

Pour ajouter des nœuds masters supplémentaires au cluster, utilisez le jeton d’adhésion fourni par Kubeadm lors de l’initialisation. Exécutez cette commande sur chaque nœud master :

Terminal window
kubeadm join <L'IP de votre bastion>:6443 --token <TOKEN> --discovery-token-ca-cert-hash sha256:<HASH> --control-plane --certificate-key <HASH_CERT>" 192.168.0.0/16

Pour ajouter des nœuds workers supplémentaires au cluster, utilisez le jeton d’adhésion fourni par Kubeadm lors de l’initialisation. Exécutez cette commande sur chaque nœud worker :

Terminal window
sudo kubeadm join <L'IP de votre bastion>:6443 --token <TOKEN> --discovery-token-ca-cert-hash sha256:<HASH>

Remplacez <MASTER_IP>, <TOKEN> et <HASH> par les valeurs retournées par la commande kubeadm init.

Déploiement d’un réseau de pods

Le réseau de pods est essentiel pour permettre la communication entre les différents nœuds d’un cluster Kubernetes. Sans un réseau de pods configuré, les pods sur différents nœuds ne pourront pas communiquer entre eux. Il existe plusieurs solutions de réseaux de pods disponibles pour Kubernetes, comme Calico, Flannel, Weave Net et Canal. Ici, je vais utiliser Callico. Depuis le bastion :

Terminal window
kubectl create -f https://docs.projectcalico.org/manifests/calico.yaml

Vérification du cluster

Après avoir ajouté tous les nœuds, vous pouvez vérifier que votre cluster est opérationnel en utilisant la commande suivante depuis la machine bastion :

Terminal window
kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-a1.test.local Ready control-plane 2d15h v1.31.1
master-a2.test.local Ready control-plane 2d17h v1.31.1
master-a3.test.local Ready control-plane 2d17h v1.31.1
worker-a1.test.local Ready <none> 2d15h v1.31.1
worker-a2.test.local Ready <none> 2d15h v1.31.1
worker-a3.test.local Ready <none> 2d15h v1.31.1

Vous devriez voir une liste de tous les nœuds de votre cluster avec leur statut.

Vérifiez également que tous les pods du namespace kube-system tournent de manière normale (pas trop d’arrêt/relance) :

Terminal window
kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-6879d4fcdc-w9nfk 1/1 Terminating 0 2d15h
kube-system coredns-7c65d6cfc9-99fqw 1/1 Running 0 2d17h
kube-system coredns-7c65d6cfc9-qbr5v 1/1 Running 0 2d17h
kube-system etcd-master-a1.test.local 1/1 Running 0 2d15h
kube-system etcd-master-a2.test.local 1/1 Running 4 2d17h
kube-system etcd-master-a3.test.local 1/1 Running 0 2d17h
kube-system kube-apiserver-master-a1.test.local 1/1 Running 0 2d15h
kube-system kube-apiserver-master-a2.test.local 1/1 Running 4 2d17h
kube-system kube-apiserver-master-a3.test.local 1/1 Running 0 2d17h
kube-system kube-controller-manager-master-a1.test.local 1/1 Running 0 2d15h
kube-system kube-controller-manager-master-a2.test.local 1/1 Running 0 2d17h
kube-system kube-controller-manager-master-a3.test.local 1/1 Running 0 2d17h
kube-system kube-proxy-65tmz 1/1 Running 0 2d15h
kube-system kube-proxy-7pfxm 1/1 Running 0 2d15h
kube-system kube-proxy-dcm8v 1/1 Running 0 2d15h
kube-system kube-proxy-qg2zw 1/1 Running 0 2d17h
kube-system kube-proxy-ql45z 1/1 Running 0 2d15h
kube-system kube-proxy-sd429 1/1 Running 0 2d17h
kube-system kube-scheduler-master-a1.test.local 1/1 Running 0 2d15h
kube-system kube-scheduler-master-a2.test.local 1/1 Running 4 2d17h
kube-system kube-scheduler-master-a3.test.local 1/1 Running 0 2d17h

À partir de ce moment, vous pouvez commencer à déployer des applications sur votre cluster Kubernetes.

Mise à jour et gestion du cluster

Une gestion efficace et des mises à jour régulières sont essentielles pour maintenir un cluster Kubernetes sécurisé, performant et fiable. Kubeadm simplifie ces processus en fournissant des outils et des commandes pour gérer les mises à jour de Kubernetes et assurer une administration continue du cluster.

Mise à jour du plan de contrôle

La mise à jour du plan de contrôle est une étape critique lors de la mise à jour de votre cluster Kubernetes. Cela inclut la mise à jour de l’API Server, du Controller Manager et du Scheduler.

Avant de commencer, vérifiez la version actuelle de Kubernetes :

Terminal window
kubectl version
Client Version: v1.29.6
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.29.6

Contrôler ensuite les versions disponibles :

Terminal window
kubeadm | 1.31.1-1.1 | https://pkgs.k8s.io/core:/stable:/v1.31/deb Packages
kubeadm | 1.31.0-1.1 | https://pkgs.k8s.io/core:/stable:/v1.31/deb Packages

Il faut ensuite releaser le package kubeadm et kubelet :

Terminal window
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.31.x-y.y' && \
sudo apt-mark hold kubeadm

Kubeadm offre une commande pour planifier la mise à jour et vérifier les versions disponibles :

Terminal window
sudo kubeadm upgrade plan

Cette commande affiche les versions disponibles et vérifie la compatibilité avec les composants actuels du cluster.

Pour appliquer la mise à jour, utilisez la commande suivante en remplaçant vX.Y.Z par la version cible de Kubernetes :

Terminal window
sudo kubeadm upgrade apply vX.Y.Z

Cette commande met à jour les composants du plan de contrôle. Il faut procéder de la même manière pour kubelet.

Après l’exécution, redémarrez les composants pour appliquer les changements :

Terminal window
sudo systemctl restart kubelet

Vérifiez que les composants du plan de contrôle sont à jour :

Terminal window
kubectl get nodes
kubectl get pods -n kube-system

Il faudra répéter cette procédure sur les autres nœuds masters, mais au lieu d’utiliser la commande sudo kubeadm upgrade apply il faudra utiliser la commande suivante :

Terminal window
sudo kubeadm upgrade apply

Mise à jour des nœuds de travail (workers)

Après la mise à jour du plan de contrôle, mettez à jour les nœuds de travail pour assurer la compatibilité avec la nouvelle version du cluster.

Pour éviter les interruptions de service, drainez chaque nœud de travail avant la mise à jour. Le drainage évacue tous les pods en cours d’exécution sur le nœud :

Terminal window
kubectl drain <node-name> --ignore-daemonsets

Sur chaque nœud de travail, mettez à jour kubelet et kubectl :

Terminal window
sudo apt-get update
sudo apt-get install -y kubelet kubectl
sudo apt-mark hold kubelet kubectl

Redémarrez le service kubelet pour appliquer la mise à jour :

Terminal window
sudo systemctl restart kubelet

Une fois le nœud mis à jour, annulez le drainage pour rendre le nœud à nouveau disponible pour les pods :

Terminal window
kubectl uncordon <node-name>

Surveillance du cluster

La surveillance continue est essentielle pour assurer la santé du cluster. Utilisez des outils comme Prometheus et Grafana pour surveiller les performances et les métriques du cluster.

Sauvegarde et restauration

Assurez-vous d’avoir des stratégies de sauvegarde et de restauration en place pour protéger les données de votre cluster.

Sauvegarde des etcd

etcd stocke les données de l’état du cluster. Sauvegardez régulièrement les données d’etcd :

Terminal window
ETCDCTL_API=3 etcdctl snapshot save snapshot.db

Stockez ensuite la sauvegarde sur un bucket S3.

Restauration des etcd

Pour restaurer une sauvegarde, utilisez la commande suivante :

Terminal window
ETCDCTL_API=3 etcdctl snapshot restore snapshot.db

Rotation des certificats

Kubernetes utilise des certificats pour sécuriser la communication. Assurez-vous que les certificats sont renouvelés avant leur expiration.

Vérification des certificats

Vérifiez la validité des certificats avec la commande suivante :

Terminal window
kubectl get csr

Renouvellement des certificats

Renouvelez les certificats si nécessaire :

Terminal window
sudo kubeadm alpha certs renew all

Nettoyage du cluster

Maintenez votre cluster propre en supprimant régulièrement les ressources inutilisées.

Suppression des pods orphelins

Listez et supprimez les pods orphelins ou en échec :

Terminal window
kubectl get pods --all-namespaces --field-selector=status.phase=Failed
kubectl delete pod <pod-name> --namespace=<namespace>

En suivant ces pratiques, vous pouvez assurer une gestion efficace et des mises à jour régulières de votre cluster Kubernetes, garantissant ainsi sa stabilité et sa sécurité.

Conclusion

En conclusion, Kubeadm est un outil puissant et indispensable pour simplifier le déploiement et la gestion des clusters Kubernetes. Grâce à ses fonctionnalités robustes et à son intégration étroite avec Kubernetes, Kubeadm facilite la mise en place de clusters de production fiables et performants.

La gestion continue de votre cluster Kubernetes est essentielle pour garantir sa performance, sa sécurité et sa fiabilité. Les mises à jour régulières, la surveillance active, la sauvegarde des données et la rotation des certificats sont autant de pratiques essentielles pour maintenir un environnement Kubernetes optimal.

Plus d’infos

Pour approfondir vos connaissances et rester à jour avec les meilleures pratiques, je vous recommande de consulter les ressources suivantes :

  • Documentation officielle de Kubernetes : La documentation officielle est une source complète et à jour pour toutes les fonctionnalités et configurations de Kubernetes. Consulter la documentation
  • Projets GitHub : Les dépôts GitHub des projets Kubernetes et Kubeadm offrent un accès direct au code source et aux discussions de la communauté. Consulter le projet Kubeadm
  • Communauté Kubernetes : Participer aux forums, aux groupes de discussion et aux événements de la communauté Kubernetes peut vous fournir des informations précieuses et des opportunités de collaboration avec d’autres professionnels.