Aller au contenu principal

Introduction à Kubernetes

Le premier billet de la formation kubernetes qui sera je l'espère une longue série. Ce premier billet permet de définir tous les concepts et objets principaux utilisés sur un cluster Kubernetes. Vous devez posséder au préalable des connaissances sur les conteneurs avant de vous attaquer à Kubernetes. Si certains mots ne sont pas clairs pour vous, vous pouvez aller faire un tour sur mon lexique Devops.

Introduction

Kubernetes, k8s, est un système Open Source qui exécute et coordonne des applications conteneurisées dans des clusters. Kubernetes gère le cycle complet de vie des applications et des services permettant ainsi de limiter le nombre de processus nécessaire au déploiement et à la mise à l'échelle des applications conteneurisées.

À l'origine, la plateforme Kubernetes a été conçue et développée par Google qui déployait depuis une dizaine d'années des conteneurs via une plateforme interne nommée Borg.

Architecture de Kubernetes

Dans un cluster Kubernetes, il existe deux rôles de machines : le(s) nœud(s) maîtres (master) et le(s) nœud(s) de travail (workers).

Le(s) nœud maître exécute(ent) le plan de contrôle Kubernetes, qui est responsable de la gestion des workers, prend les décisions de planification et met en œuvre des modifications pour conduire le cluster à l'état souhaité.

Les workers, comme leur nom l'indique sont chargés de gérer les applications via des pods.

Le Control Plane

Dans le graphique ci-dessus, nous voyons que le Control Plane Kubernetes est composé de plusieurs éléments. Chacun de ces éléments sont essentiels à la bonne santé d'un cluster Kubernetes. Si un de ces composants vient à dysfonctionner le cluster devient instable voir irrécupérable (etcd par exemple).

ETCD

ETCD est une base de données distribuée de type clé-valeur, qui a été développée en "Go", le langage de programmation de Google. Dans un cluster Kubernetes, ETCD est chargé de stocker la configuration et les informations nécessaires au fonctionnement du cluster, c'est-à-dire de tous ses composants : les nœuds, les pods, les configs, les secrets, les rôles, les comptes, etc.

L'API-Server

L'API Server est le composant central du cluster qui expose l'API REST de Kubernetes. C'est donc l'élément "frontal" du Control Plane de Kubernetes et c'est lui qui reçoit tous les appels et demandes, externes ou internes. C'est ce composant qui gère entre autre les appels via les commandes kubectl. Il est donc chargé :

  • De gérer les authentifications,
  • De valider les demandes reçues,
  • De gérer les données stockées dans la base de données ETCD,
  • De fournir les informations aux autres composants du cluster : kubelet, scheduller, ...

Le Scheduler

Le scheduler est chargé de la répartition des pods entre les différents workers nodes. Cette répartition se fait en fonction des ressources disponibles et des contraintes qui lui sont indiquées. Ces contraintes peuvent être des prérequis de type CPU et mémoire, des contraintes hardware, des affinités et anti-affinités.

Les nœuds sont filtrés en supprimant ceux qui ne répondent pas aux exigences du pod. Ensuite, les nœuds retenus sont classés par score, celui ayant obtenu le score le plus élevé est sélectionné.

Le scheduler indique au service kubelet du worker node sélectionné qu'il doit démarrer le pod.

Le Controller-Manager

Dans Kubernetes, le Controller Manager contient plusieurs controllers. Un controller est une boucle de contrôle qui surveille en permanence l'état d'un groupe d'objet qui lui est attribué. Il fera des demandes de modifications au serveur d'API, ou en direct si nécessaire, pour que l'état actuel de ses objets soit celui de l'état souhaité.

Parmi ces contrôleurs, on retrouve :

  • Deployment controller
  • StatefulSet controller
  • Node controller
  • Service controller
  • Endpoints controller
  • Namespace controller
  • PersistentVolume controller
  • ...

La description de ces objets est faite plus bas dans ce billet.

Cloud-Controller-Manager (optionnel)

Kubernetes peut fonctionner sur des clouds publics, privés et hybrides. Le Cloud-Controller-Manager permet de lier votre cluster à l'API de votre fournisseur de cloud. Il permet donc aux fournisseurs de cloud de publier des fonctionnalités à un rythme différent de celui du projet Kubernetes.

Composants des nœuds (nodes)

kubelet

Kubelet est un agent qui tourne sur tous les worker nodes du cluster Kubernetes. Kubelet examine les spécifications qui lui sont transmises par le scheduller et fait en sorte que les conteneurs définis avec ces spécifications tournent et soient en bonne santé.

kube-proxy

kube-proxy est un proxy réseau qui s'exécute sur chaque worker node du cluster et gère les règles réseau. Ces règles réseau permettent une communication réseau vers les Pods depuis des sessions réseau à l'intérieur ou à l'extérieur du cluster. kube-proxy utilise la couche de filtrage de paquets du système d'exploitation hôte s'il y en a une de disponible. Sinon, kube-proxy transmet le trafic lui-même.

Container Runtime

L'environnement d'exécution de conteneurs (Container Runtime) est le logiciel responsable de l'exécution des conteneurs. Le CRI est une interface de plug-in qui permet à kubelet d'utiliser différents environnements d'exécution de conteneurs, sans à avoir besoin de recompiler les composants du cluster. Kubernetes est compatible avec : Docker, ContainerD, cri-o, rktlet entre autre.

Les ressources Kubernetes

Ressources de base

NameSpaces

Dans Kubernetes, les namespaces ou espaces de noms fournissent un mécanisme pour isoler des groupes de ressources au sein d'un seul cluster. Les noms de ressources doivent être uniques au sein d'un même namespace, mais pas entre namespaces. La portée basée sur un namespace s'applique uniquement aux objets avec namespace (par exemple, déploiements, services, etc.) et non aux objets à l'échelle du cluster (par exemple, StorageClass, Nodes, PersistentVolumes, etc.)

Il n'est pas nécessaire d'utiliser plusieurs namespaces pour séparer des ressources légèrement différentes, telles que des versions différentes de la même application : utilisez plutôt les labels (étiquettes) pour distinguer les ressources au sein du même namespace.

Pods

Les pods sont les plus petites unités déployables que vous pouvez créer et gérer dans Kubernetes. Un Pod est un groupe d'un ou plusieurs conteneurs partageant des ressources réseau, de stockage et d'un ensemble d'espaces de noms qui s'exécutent sur les workers.

Un pod peut aussi contenir des conteneurs d'initialisation qui s'exécutent lors du démarrage de celui-ci.

Vous créerez rarement des pods directement dans Kubernetes! Vous les instancierez à l'aide de ressources de charge de travail telles que les déployments, les daemonsets ou les statefulets.

Services

Comme les pods sont des objets non permanents comment y accéder si leur IP changent ? C'est à ce niveau qu'interviennent les Services. Un service est une couche d'abstraction qui définit des règles permettant d'accéder à un ensemble logique de pods. Un Service permet donc aux pods de recevoir du trafic.

Pour cibler des pods un service va utiliser ce qu'on appelle un selector, qui va rechercher dans le cluster des objets possédant des paires clé/valeurs correspondantes à celles attendues.

Il existe différentes manières d'exposer un service :

  • ClusterIP (par défaut) : expose le Service sur une adresse IP interne au cluster
  • NodePort : expose le Service sur un port statique du node
  • Loadbalancer : expose le Service à l’extérieur en utilisant l’équilibreur de charge d’un fournisseur de cloud computing
  • ExternalName : Mappe le service à un nom externe (exemple : artefacts.robert.local)

Volumes

Par défaut les fichiers d'un pod sont éphémères, lorsqu'un conteneur plante, kubelet va le redémarrer, mais les fichiers du pod précédent sont perdus.

Pour ajouter de la persistance de données dans kubernetes, il est possible d'utiliser des volumes. Kubernetes prend en charge plusieurs types de stockage dont les principaux sont : hostPath, emptyDir, configMap, nfs, secret, local, PersistentVolumeClaim, ... La liste complète se trouve ici

Les volumes permettent également de partager des fichiers entre conteneurs.

Les Workloads

Un workload (charge de travail) est une application fonctionnant sur Kubernetes faisant appel à un ou à une série de pods. Un workload Kubernetes permet d'automatiser le processus de publication d'une application et de la rendre ainsi reproductible. Tout est géré par le back-end de Kubernetes sans intervention de votre part, sauf bien sûr sa déclaration.

Nous ne verrons ici les ressources standards de Kubernetes, mais il est possible d'en ajouter de nouveaux via ce qu'on appelle un CustomResourceDefinition (CRD).

Déployments

Un déploiement permet de décrire le cycle de vie d'une application sans état, en spécifiant en autre les images à utiliser, la façon dont les pods doivent être mis à jour ou mis à l'échelle (scaling)

StatefulSet

Contrairement à un déployment, un StatefulSet conserve une identité persistante pour chacun de ses pods. Les pods d'un StatefulSet sont créés à partir de la même spécification, mais ne sont pas interchangeables : chacun a un identifiant persistant qu'il conserve lors de toute reconstruction.

Par exemple si vous souhaitez utiliser des volumes de stockage pour assurer la persistance de vos données, un StatefulSet peut être la solution surtout si les volumes sont spécifiques à chaque pod.

DaemonSet

Le DaemonSet est utilisé pour le déploiement, comme son nom l'indique d'un daemon. Il va lancer une instance unique de pod sur chacun des workers nodes du cluster Kubernetes. En cas d'ajout d'un worker node, le controle plane de Kubernetes va programmer l'ajout d'un Pod pour ce DaemonSet sur ce nouveau noeud.

On l'utilise le plus souvent pour gérer du stockage, du monitoring ou de la journalisation de log.

Jobs et CronJobs

Les Jobs permettent d'exécuter des tâches ponctuelles voir des batchs plus complets. CronJob, en analogie avec les crons linux, permet de planifier à quels moments (minute, heure, jour du mois, mois et jour de la semaine) ces tâches doivent s'exécuter.

Extensions de Kubernetes

Kubernetes est hautement configurable et extensible via des plugins sur une grande partie des composants du cluster. Il est donc possible d'étendre au niveau de l'infrastructure du cluster :

  • Les ressources de type réseau
  • Les ressources de type volume
  • D'intégrer de nouveaux devices
  • D'intégrer, voir de remplacer le scheduller par un autre.

Un autre moyen d'étendre les fonctionnalités d'un cluster Kubernetes est d'ajouter des points d'entrée à l'API. Cela permet d'intégrer des nouvelles méthodes d'authentification, d'autorisations et de contrôle d'admission.

Administration d'un cluster Kubernetes : le client officiel kubectl

Kubectl est l'outil officiel en ligne de commande pour administrer des clusters Kubernetes. Il permet de gérer l'ensemble des objets définis sur un cluster Kubernetes.

Installation de kubectl

Pour installer kubectl je vous conseille d'utiliser asdf qui permet d'installer une très grande partie des outils permettant de gérer des clusters kubernetes.

asdf plugin add kubectl
asdf install kubectl latest
asdf global kubectl latest

kubectl version --output=yaml
clientVersion:
  buildDate: "2023-04-14T13:21:19Z"
  compiler: gc
  gitCommit: 4c9411232e10168d7b050c49a1b59f6df9d7ea4b
  gitTreeState: clean
  gitVersion: v1.27.1
  goVersion: go1.20.3
  major: "1"
  minor: "27"
  platform: linux/amd64
kustomizeVersion: v5.0.1
serverVersion:
  buildDate: "2023-03-27T22:23:17Z"
  compiler: gc
  gitCommit: 01ea3ff27be0b04f945179171cec5a8e11a14f7b
  gitTreeState: clean
  gitVersion: v1.26.3+k3s1
  goVersion: go1.19.7
  major: "1"
  minor: "26"
  platform: linux/amd64

Syntaxe de kubectl

Sa syntaxe est la suivante :

kubectl [commande] [TYPE] [NOM] [flags]
  • commande : Indique l'opération que vous désirez exécuter sur une ou plusieurs ressources, par exemple create, get, describe, delete.
  • TYPE : Indique le type de ressources que vous voulez manipuler
  • NOM : Indique le nom de la ressource (sensible à la casse)
  • flags : flags optionnels

Mode de fonctionnement de Kubectl

Il existe deux modes de fonctionnement de la cli kubectl. Le mode impératif et le mode déclaratif.

Le mode déclaratif utilisera principalement la commande apply et fera appel à un fichier manifest écrit en YAML décrivant un ou plusieurs ressources à créer. Si la ressource n'existe pas, elle sera créée, si elle existe kubernetes adaptera les ressources en fonction des modifications éventuellement apportées.

Le mode impératif permet de créer une ressource directement sans utiliser forcément un fichier manifest. Si une ressource existe la commande kubectl create .. retournera une erreur.

Le mode impératif utilise essentiellement les commandes create, delete et replace.

L'autocomplétion

Kubectl est fourni avec de l'autocomplétion, c'est pratique et ça permet d'aller très vite dans la création des commandes kubectl :

sudo apt install bash-completion
kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null

Redémarrer votre shell et tapez kubectl suivi de tabulation. Vous ferez par exemple pour changer de namespace, on verra la création plus tard, il suffit de taper kubectl -n tab et la liste de tous les namespaces existant sur votre cluster s'affichera.

kubectl -n
default          kube-node-lease  kube-public      kube-system
kubectl config set-context --current --namespace=kube-system
Context "kubernetes-admin@kubernetes" modified.

Pour aller encore plus on peut utiliser un alias :

alias k=kubectl
complete -F __start_kubectl k
k tab
annotate       apply          autoscale      completion

Pour le rendre permanent ajouter ces deux lignes à la fin de votre fichier .bashrc

Obtenir de la documentation

Pour obtenir de l'aide sur la syntaxe de kubectl, il suffit de taper classiquement l'option --help :

kubectl --help
# ou pour une commande
kubectl run pod --help

De plus, pour créer des ressources, vous pourriez chercher la documentation sur le site Kubernetes, mais il est utile de savoir que Kubectl intègre une documentation complète des ressources qu'il est possible de créer dans un cluster Kubernetes : nodes, pod, service, deployment, nodes

Exemple sur les pods :

kubectl explain pods
KIND:     Pod
VERSION:  v1

DESCRIPTION:
     Pod is a collection of containers that can run on a host. This resource is
     created by clients and scheduled onto hosts.

FIELDS:
   apiVersion   <string>
     APIVersion defines the versioned schema of this representation of an
     object. Servers should convert recognized schemas to the latest internal
     value, and may reject unrecognized values. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

   kind <string>
     Kind is a string value representing the REST resource this object
     represents. Servers may infer this from the endpoint the client submits
     requests to. Cannot be updated. In CamelCase. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

   metadata     <Object>
     Standard object's metadata. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

   spec <Object>
     Specification of the desired behavior of the pod. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

   status       <Object>
     Most recently observed status of the pod. This data may not be up to date.
     Populated by the system. Read-only. More info:
     https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status

Pour obtenir la documentation d'un sous-objet, il suffit d'ajouter un point plus le nom de l'objet :

Exemple pods.spec.affinity :

kubectl explain pods.spec.affinity
kubectl explain pods.spec.affinity
KIND:     Pod
VERSION:  v1

RESOURCE: affinity <Object>

DESCRIPTION:
     If specified, the pod's scheduling constraints

     Affinity is a group of affinity scheduling rules.

FIELDS:
   nodeAffinity <Object>
     Describes node affinity scheduling rules for the pod.

   podAffinity  <Object>
     Describes pod affinity scheduling rules (e.g. co-locate this pod in the
     same node, zone, etc. as some other pod(s)).

   podAntiAffinity      <Object>
     Describes pod anti-affinity scheduling rules (e.g. avoid putting this pod
     in the same node, zone, etc. as some other pod(s)).

Pour afficher toutes les informations d'objet et de ces sous-objet,s on utilise l'option --recursive. Attention c'est violent !

Pour trouver la liste des ressources pouvant être déclarées, avec leur kind et versions d'api respectives, il suffit d'utiliser la commande api-ressources :

kubectl api-resources
NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
bindings                                       v1                                     true         Binding
componentstatuses                 cs           v1                                     false        ComponentStatus
configmaps                        cm           v1                                     true         ConfigMap
endpoints                         ep           v1                                     true         Endpoints
events                            ev           v1                                     true         Event
limitranges                       limits       v1                                     true         LimitRange
namespaces                        ns           v1                                     false        Namespace
nodes                             no           v1                                     false        Node
persistentvolumeclaims            pvc          v1                                     true         PersistentVolumeClaim
persistentvolumes                 pv           v1                                     false        PersistentVolume
pods                              po           v1                                     true         Pod
....

Ce sont ces informations qui seront déclarées au début d'un manifest.

Gros avantage la documentation est à jour et correspond à celle de votre cluster. Bien sûr, la version de Kubectl doit correspondre à celle du cluster. Pour le vérifier rien de plus simple :

kubectl version
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.1", GitCommit:"86ec240af8cbd1b60bcc4c03c20da9b98005b92e", GitTreeState:"clean", BuildDate:"2021-12-16T11:41:01Z", GoVersion:"go1.17.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.5", GitCommit:"5c99e2ac2ff9a3c549d9ca665e7bc05a3e18f07e", GitTreeState:"clean", BuildDate:"2021-12-16T08:32:32Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"linux/amd64"}

Mince ce n'est pas mon cas ! Allez, on fait la mise à jour de kubectl (on pourrait faire l'upgrade du cluster plutôt) :

apt-cache madison kubectl |grep 1.22.5
apt install kubectl=1.22.5-00
kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.5", GitCommit:"5c99e2ac2ff9a3c549d9ca665e7bc05a3e18f07e", GitTreeState:"clean", BuildDate:"2021-12-16T08:38:33Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.5", GitCommit:"5c99e2ac2ff9a3c549d9ca665e7bc05a3e18f07e", GitTreeState:"clean", BuildDate:"2021-12-16T08:32:32Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"linux/amd64"}

kubectl get

La commande get permet de rechercher et d'afficher la configuration d'un objet. Regardons un exemple avec un pod du namespace kube-system.

apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubeadm.kubernetes.io/etcd.advertise-client-urls: https://192.168.121.82:2379
    kubernetes.io/config.hash: 413446f10a47cd29048961fc9e411de6
    kubernetes.io/config.mirror: 413446f10a47cd29048961fc9e411de6
    kubernetes.io/config.seen: "2022-01-10T09:31:44.361934226Z"
    kubernetes.io/config.source: file
    seccomp.security.alpha.kubernetes.io/pod: runtime/default
  creationTimestamp: "2022-01-10T09:31:44Z"
  labels:
    component: etcd
    tier: control-plane
  name: etcd-master1
kubectl get pod -A
kubectl -n kube-system get pod etcd-master1 -o yaml

La commande nous retourne toutes les informations du pod ETCD.

Déployer un cluster Kubernetes

Maintenant que nous avons mis en place tous ces concepts passons à l'installation d'un cluster Kubernetes. Je vous propose de suivre ce tutoriel montrant comment l'installer avec kubeadm sur des VM provisionnées avec vagrant. Ou si vous êtes à court de ressources matérielles avec kind.

Plus d'informations

Comme dit en au début du billet, ce billet fait partie d'une série. Je vous invite donc à revenir de temps en temps, car je risque de le compléter comme je l'ai déjà fait avec Ansible. Parmi les sujets prévus : la création d'applications, les bonnes pratiques, une aide à la résolution d'incident, une sélection d'outils, etc.

Voici la liste des billets existants (certains sont en cours de réécriture, Eh oui, je progresse et les outils évoluent également) :

Vous pouvez également regarder du côté de mon Home Lab Devops ou j'utilise k3s pour :

Source Principal

Plus d'infos

Livres Gratuits

Livres

Sites