K3S + K3D = K8S, un nouveau match parfait pour le dev et les tests

13 min de lecture
K3S + K3D = K8S, un nouveau match parfait pour le dev et les tests

Travailler avec Kubernetes sur une machine locale quand on est Dev ou Ops n’est pas aussi simple qu’on pourrait le penser. Alors, comment créer facilement un cluster Kubernetes local qui réponde à ces besoins ? Chez SoKube nous utilisons intensivement k3d et k3s pour cela.

Il y a plus d’un an, j’avais présenté dans un précédent article ce qu’est k3d (avec k3s) et comment l’utiliser. Depuis, k3d a été entièrement réécrit. Les objectifs de cet article sont de montrer :

  • Ce que sont k3d/k3s
  • Les nouveautés de k3d v3
  • Comment créer un cluster Kubernetes simple sur votre machine locale
  • Comment créer un cluster Kubernetes multi serveurs (masters) et multi agents (workers) sur votre machine locale
  • Comment créer un cluster avec une version spécifique de Kubernetes
  • Comment remplacer le plugin CNI par défaut de k3s
  • Comment remplacer le contrôleur d’ingress par défaut de k3s
  • Comment utiliser un registre dédié pour télécharger les images avec k3s
  • Quelles sont les autres alternatives

k3s/k3d ?

k3s est une distribution Kubernetes pleinement conforme, très efficace et légère. k3d est un utilitaire conçu pour faire tourner facilement k3s dans Docker, il fournit une CLI simple pour créer, lancer, supprimer un cluster Kubernetes pleinement conforme avec 1 à n nœuds.

K3s inclut :

  • Flannel : un réseau overlay L2 très simple qui satisfait les exigences de Kubernetes. C’est un plugin CNI (Container Network Interface), au même titre que Calico, Romana, Weave-net. Flannel ne supporte pas les Network Policy de Kubernetes, mais il peut être remplacé par Calico (voir les sections suivantes).
  • CoreDNS : un serveur DNS flexible et extensible qui peut servir de DNS du cluster Kubernetes
  • Traefik est un reverse proxy HTTP et un load balancer moderne. Dans une section suivante, je montrerai également comment le remplacer soit par Traefik v2, soit par Nginx
  • Klipper Load Balancer : un load balancer de service qui utilise les ports disponibles de l’hôte.
  • SQLite3 : le backend de stockage utilisé par défaut (supporte également MySQL, Postgres et etcd3)
  • Containerd est un runtime de conteneurs comme Docker, sans la partie build d’image

Ces composants ont été choisis pour obtenir la distribution la plus légère possible. Mais comme nous le verrons plus loin dans cet article, k3s est une distribution modulaire dont les composants peuvent facilement être remplacés.

Récemment, k3s a rejoint la Cloud Native Computing Foundation (CNCF) au niveau sandbox en tant que première distribution Kubernetes (suscitant de nombreux débats sur le fait de savoir si k3s devrait plutôt être un sous-projet de Kubernetes).

Installation

L’installation est très simple et disponible via de nombreux installeurs : wget, curl, Homebrew, Aur, … et prend en charge tous les OS connus (linux, darwin, windows) ainsi que les architectures processeur (386, amd64) !

À noter que seul le client k3d doit être installé, il se chargera de créer un cluster k3s en utilisant la bonne image Docker.

Une fois installé, configurez l’autocomplétion avec votre shell préféré (bash, zsh, powershell), par exemple avec zsh :

k3d completion zsh > ~/.zsh/completions/_k3d
source .zshrc

Les nouveautés de k3d v3

En un an, l’équipe de k3d a fait un travail remarquable en réécrivant entièrement k3d v3. Il ne s’agit donc pas d’une simple montée de version majeure : de nouveaux concepts et structures ont été mis en place pour en faire un outil évolutif, doté de fonctionnalités très pratiques et intéressantes.

  • Nouvelle terminologie de k3d et k3s : pour être aussi inclusive que possible vis-à-vis de la communauté, les termes “Server” et “Agent” sont désormais utilisés pour désigner les nœuds “master” et “worker”.
  • Chaque cluster que vous créez génère désormais au moins 2 conteneurs : 1 load balancer et 1 nœud “server”. Le load balancer constitue le point d’accès à l’API Kubernetes, de sorte que même pour les clusters multi serveurs, un seul port API doit être exposé. Le load balancer se charge ensuite de proxifier vos requêtes vers le bon nœud server. (peut être désactivé avec le flag –no-lb)
  • Adoption de la syntaxe “NOM VERBE” : ce changement structurant facilite l’ajout de nouveaux noms (c’est à dire les objets gérés par k3d) et se rapproche d’autres CLI cloud-native (gcloud, awscli, azure cli, …), tout en offrant une hiérarchie de commandes plus claire.
  • Support des clusters multi serveurs (dqlite) avec rechargement à chaud de la configuration lors de l’ajout d’un nouveau nœud server au cluster
  • Gestion des nœuds indépendamment des clusters : k3d create/start/stop/delete node mynode
  • Autocomplétion shell via k3d completion [zsh | bash | psh | fish]
  • Support basique d’un système de plugins (k3d my-plugin)
  • …

Mon premier cluster k3d

Créons un cluster simple avec 1 loadbalancer et 1 nœud (avec le rôle de server et d’agent) nommé “dev” :

k3d cluster create dev --port 8080:80@loadbalancer --port 8443:443@loadbalancer

docker ps affichera les conteneurs sous-jacents créés par cette commande :

docker ps

K3d

Mapping des ports :

  • --port 8080:80@loadbalancer ajoute un mapping du port 8080 de l’hôte local vers le port 80 du loadbalancer, qui redirigera les requêtes vers le port 80 de tous les nœuds agents
  • --api-port 6443 : par défaut, aucun API-Port n’est exposé (pas de mapping de port hôte). Il sert à faire écouter l’API-Server de k3s sur le port 6443, ce port étant mappé sur le système hôte. Ainsi, le load balancer devient le point d’accès à l’API Kubernetes, de sorte que même pour les clusters multi serveurs, un seul port API doit être exposé. Le load balancer se charge ensuite de proxifier vos requêtes vers le nœud server approprié
  • -p "32000-32767:32000-32767@loadbalancer" : vous pouvez aussi exposer une plage de NodePort (si vous souhaitez vous passer du contrôleur d’Ingress).

Kubeconfig :

  • Par défaut, le contexte courant du kubeconfig par défaut bascule directement sur le contexte du nouveau cluster, de sorte que votre “~/.kube/config” est automatiquement mis à jour (vérifiez avec “kubectl config current-context”)
  • vous pouvez désactiver ce comportement avec le flag “--update-default-kubeconfig=false” : il faudra alors créer un fichier kubeconfig et exporter la variable KUBECONFIG :
    export KUBECONFIG=$(k3d kubeconfig write dev)
  • La suppression du cluster supprimera également l’entrée dans le fichier kubeconfig.
  • k3d fournit quelques commandes pour manipuler facilement le kubeconfig :
    # récupérer le kubeconfig du cluster dev
    k3d kubeconfig get dev
    
    # créer un fichier kubeconfig dans $HOME/.k3d/kubeconfig-dev.yaml k3d kubeconfig write dev
    
    # récupérer le kubeconfig d'un ou plusieurs cluster(s) et
    # le(s) fusionner dans un fichier dans $HOME/.k3d ou un autre fichier
    k3d kubeconfig merge ...

Cycle de vie :

  • Arrêter un cluster est très simple :
    k3d cluster stop dev
  • Puis le redémarrer et restaurer l’état du cluster tel qu’il était avant l’arrêt :
    k3d cluster start dev
  • Supprimer un cluster est tout aussi simple :
    k3d cluster delete dev

Test avec une simple application conteneur nginx

Une fois le cluster lancé, exécutez les commandes suivantes pour tester avec un simple conteneur nginx :

kubectl create deployment nginx --image=nginx
kubectl create service clusterip nginx --tcp=80:80
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx
  annotations:
    ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: nginx
          servicePort: 80
EOF

Pour tester : http://localhost:8080/

Un cluster Kubernetes multi serveurs et multi agents sur votre machine locale

À des fins de test, et pour se rapprocher au maximum d’un cluster Kubernetes de production, vous pouvez créer un cluster multi serveurs et/ou multi agents :

k3d cluster create test --port 8080:80@loadbalancer --port 8443:443@loadbalancer --api-port 6443 --servers 3 --agents 3

Récupérer la liste des nœuds :

> kubectl get nodes 
NAME                STATUS   ROLES    AGE   VERSION 
k3d-test-agent-1    Ready    <none>   20m   v1.18.2+k3s1 
k3d-test-agent-0    Ready    <none>   21m   v1.18.2+k3s1 
k3d-test-agent-2    Ready    <none>   20m   v1.18.2+k3s1 
k3d-test-server-2   Ready    master   21m   v1.18.2+k3s1 
k3d-test-server-0   Ready    master   21m   v1.18.2+k3s1 
k3d-test-server-1   Ready    master   21m   v1.18.2+k3s1

Une fois tous les nœuds démarrés, vous pouvez déployer la même application nginx à des fins de test. Passer l’application à 3 réplicas :

kubectl scale deployment nginx --replicas 3 

Les pods devraient se répartir sur les nœuds agents : K3d

Ce cluster contient 7 images Docker représentant le cluster de test complet (1 loadbalancer, 3 conteneurs pour les servers et 3 conteneurs pour les agents). À noter que les nœuds server font également tourner des workloads :

K3d

Une fois le cluster créé, il est également possible d’ajouter des nœuds :

k3d node create newserver --cluster test --role agent

Un cluster avec une version spécifique de Kubernetes

Il peut être très pratique de créer un cluster Kubernetes avec une version spécifique, que ce soit une version plus ancienne :

k3d cluster create test --port 8080:80@loadbalancer --port 8443:443@loadbalancer --image rancher/k3s:v1.17.13-k3s2

ou une version plus récente :

k3d cluster create test --port 8080:80@loadbalancer --port 8443:443@loadbalancer --image rancher/k3s:v1.19.3-k3s3

La liste des versions disponibles se trouve dans le dépôt Docker de k3s. Actuellement, la version la plus ancienne de ce dépôt est la V1.16.x.

Utiliser Calico à la place de Flannel comme plugin CNI

Flannel est un très bon plugin CNI, léger, mais il ne supporte pas les ressources Network Policy de Kubernetes (à noter que la NetworkPolicy sera appliquée sans erreur, mais aussi sans aucun effet) ! La modularité de k3s permet de remplacer le CNI par défaut par Calico. Pour déployer Calico, 2 fonctionnalités de k3s sont utilisées :

  • --k3s-server-arg '--flannel-backend=none' : retire Flannel de l’installation initiale de k3s.
  • Le déploiement automatique de manifests (“Auto-Deploying Manifests”), une fonctionnalité pratique de k3s : tout fichier trouvé dans /var/lib/rancher/k3s/server/manifests sera automatiquement déployé sur Kubernetes, de manière similaire à un kubectl apply

Vous devrez donc enregistrer localement le fichier de configuration calico.yaml, puis créer le cluster avec les arguments suivants :

k3d cluster create calico --k3s-server-arg '--flannel-backend=none' --volume "$(pwd)/calico.yaml:/var/lib/rancher/k3s/server/manifests/calico.yaml"

Test :

# créer 2 pods 'web' et 'test' 
kubectl run web --image nginx --labels app=web --expose --port 80
kubectl run test --image alpine -- sleep 3600

# vérifier que le pod "test" peut accéder au pod "web"
kubectl exec -it test -- wget -qO- --timeout=2 http://web 

Tout devrait fonctionner Ajoutons maintenant une NetworkPolicy qui refuse toutes les communications :

cat <<EOF | kubectl apply -f -
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: web-deny-all
spec:
  podSelector:
    matchLabels:
      app: web
  ingress: []
EOF

# vérifier que le pod "test" ne peut plus accéder au pod "web"
kubectl exec -it test -- wget -qO- --timeout=2 http://web 

Désormais, le pod “web” n’est plus accessible !

Changer le contrôleur d’Ingress par défaut

Par défaut, k3s utilise Traefik v1 comme contrôleur d’Ingress, mais il s’agit d’une ancienne version et Traefik v2 est sorti il y a plus d’un an avec de nombreuses fonctionnalités intéressantes, telles que le support TCP avec routage SNI et ports multi-protocoles, le déploiement Canary, le mirroring avec les Service Load Balancers, un nouveau Dashboard et une nouvelle WebUI… Il est prévu de remplacer par défaut ce contrôleur d’ingress par Traefik v2, mais là encore, la modularité de k3s permet dès à présent de remplacer le contrôleur d’Ingress par défaut grâce à :

  • --k3s-server-arg '--no-deploy=traefik' : pour retirer traefik v1 de l’installation de k3s
  • Le déploiement automatique de manifests, comme mentionné précédemment
  • Les opérateurs de charts Helm : k3s inclut un Helm Controller qui gère les charts Helm via une Custom Resource Definition (CRD) HelmChart

Remplacer par le contrôleur d’ingress Nginx :

Créez un fichier local “helm-ingress-nginx.yaml” :

# voir https://rancher.com/docs/k3s/latest/en/helm/
# voir https://github.com/kubernetes/ingress-nginx/tree/master/charts/ingress-nginx
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: ingress-controller-nginx
  namespace: kube-system
spec:
  repo: https://kubernetes.github.io/ingress-nginx
  chart: ingress-nginx
  version: 3.7.1
  targetNamespace: kube-system

puis créez le cluster avec le contrôleur d’Ingress Nginx

k3d cluster create nginx --k3s-server-arg '--no-deploy=traefik' --volume "$(pwd)/helm-ingress-nginx.yaml:/var/lib/rancher/k3s/server/manifests/helm-ingress-nginx.yaml"

Remplacer par le contrôleur traefik v2

Créez un fichier local ‘helm-ingress-traefik.yaml’ :

# voir https://rancher.com/docs/k3s/latest/en/helm/
# voir https://github.com/traefik/traefik-helm-chart
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
  name: ingress-controller-traefik
  namespace: kube-system
spec:
  repo: https://helm.traefik.io/traefik
  chart: traefik
  version: 9.8.0
  targetNamespace: kube-system

Puis créez le cluster avec le contrôleur d’Ingress Traefik v2

k3d cluster create traefik --k3s-server-arg '--no-deploy=traefik' --volume "$(pwd)/helm-ingress-traefik.yaml:/var/lib/rancher/k3s/server/manifests/helm-ingress-traefik.yaml"

Utiliser votre propre registre

Par défaut, k3s utilise DockerHub pour télécharger les images, mais il peut être très pratique d’utiliser son propre registre Docker

  • Pour les environnements “Air-gap” où aucun accès internet n’est autorisé
  • Pour se connecter à votre registre Docker d’entreprise (Harbor, Nexus, Artifactory, …)
  • Pour valider la manière de se connecter à un registre externe
  • Pour ceux qui disposent d’une connexion internet de mauvaise qualité (garder les images disponibles sur votre laptop).

Dans cet exemple, un registre très simple (celui de Docker) sera créé en tant que cache pull-through :

# il faudrait créer un volume et le monter dans /var/lib/registry
# mais par simplicité j'ai créé un conteneur sans configuration SSL
docker run -d --rm --name registry -p 5000:5000 -e REGISTRY_PROXY_REMOTEURL="https://registry-1.docker.io" registry:2

Créez un fichier local “registries.yaml” avec le contenu suivant :

# plus d'infos ici https://k3d.io/usage/guides/registries
# https://rancher.com/docs/k3s/latest/en/installation/private-registry/
mirrors:
  "docker.io":
    endpoint:
      - http://host.k3d.internal:5000
  "host.k3d.internal:5000":
    endpoint:
      - http://host.k3d.internal:5000
# L'authentification et le TLS peuvent être ajoutés
# configs:
#  "host.k3d.internal:5000":
#    auth:
#      username: myname
#      password: mypwd     
#    tls:
#        nous monterons "my-company-root.pem" 
#        dans le répertoire /etc/ssl/certs/.
#      ca_file: "/etc/ssl/certs/my-company-root.pem" 

host.k3d.internal : depuis la version v3.1.0, l’entrée host.k3d.internal est automatiquement injectée dans les conteneurs k3d (nœuds k3s) ainsi que dans la ConfigMap CoreDNS, ce qui permet d’accéder à votre système hôte depuis l’intérieur des nœuds k3s (qui tournent dans Docker) en la désignant simplement par host.k3d.internal.

Après la création d’un cluster k3d, vous pouvez vérifier cette configuration via :

kubectl -n kube-system get configmap coredns -o go-template={{.data.NodeHosts}}

Créez ensuite le cluster k3d en utilisant ce registre :

k3d cluster create test --volume "${PWD}/registries.yaml:/etc/rancher/k3s/registries.yaml" --port 8080:80@loadbalancer --port 8443:443@loadbalancer

Toutes les images (y compris celles utilisées en interne par k3d et k3s) seront téléchargées vers le registre Docker, vous pouvez vérifier le contenu de votre registre via

curl http://localhost:5000/v2/_catalog 

Le déploiement d’une image nginx sera lui aussi téléchargé via le registre Docker :

kubectl create deployment nginx --image=nginx

Utiliser “host.k3d.internal” s’avère très pratique dans d’autres situations, lorsque vous souhaitez accéder à un service présent sur votre machine, ou même à un service se trouvant dans un autre cluster k3d.

Alternatives

Il existe plusieurs façons et outils de travailler avec un cluster Kubernetes local, parmi lesquels :

  • K3d projet open source maintenu par Rancher
  • Docker for Desktop produit Docker gratuit
  • Minikube projet open source, Kubernetes SIGs (Special Interest Groups)
  • Kind projet open source, Kubernetes SIGs (Special Interest Groups)
  • MicroK8s projet open source maintenu par Canonical
  • k0s projet open source maintenu par Mirantis
  • CodeReady Container projet open source maintenu par RedHat

Aujourd’hui, ma préférence va à k3d, car il combine simplicité, extrême légèreté, modularité et fonctionnalité, tout en permettant de répondre à des besoins plus poussés. Mais les choses évoluent très vite, avec des fonctionnalités originales et innovantes. Et de nouveaux projets comme k0s semblent eux aussi très prometteurs.

Autres alternatives ou outils à considérer pour un usage en développement :

  • Skaffold gère le workflow de build, de push et de déploiement de votre application sur Kubernetes
  • Tye outil pour développeurs .Net qui facilite le développement, le test et le déploiement de microservices sur Kubernetes
  • Devspace déployez et développez des applications Kubernetes plus rapidement
  • Oketo une plateforme de développement Kubernetes dans le cloud

Conclusion

L’équipe Rancher a une nouvelle fois fait un excellent travail en réécrivant k3d, le rendant très simple, modulaire et efficace pour faire tourner plusieurs instances de clusters Kubernetes k3s, avec différentes topologies, sur une seule machine.

Les usages sont multiples et très adaptés au développement, au test et à la formation sur Kubernetes. k3s est une distribution de production sérieuse, qui répond également aux enjeux du edge computing et de l’IoT en proposant des architectures moins gourmandes en énergie et plus compactes, permettant d’évoluer vers une démarche GreenIT.

Par ailleurs, la distribution Kubernetes k3s est la première distribution Kubernetes de la Cloud Native Computing Foundation (CNCF). Son avenir est extrêmement prometteur, à suivre…

Pour aller plus loin, découvrez notre formation Kubernetes et l’accompagnement de nos experts Kubernetes.