Table des matières

K3S : kubernetes ultra léger

Rappel : kubernetes, c'est quoi

Après l'avènement de docker, qui permettait de compiler des images d'os hébergeant une application spécifique et de la partager pour faire en sorte que n'importe qui, à partir de son client docker, puisse la tirer et l'exécuter, est arrivé docker-compose, puis kubernetes.

Pour cela, le système kubernetes est pilotable depuis un petit programme appelé kubectl, du moment que ce kubectl arrive à se connecter à la distribution kubernetes ( un fichier KUBECONFIG que lit kubectl permet de dire comment s'y connecter ).

Je connais 3 distribution kubernetes (d'ailleurs récemment cotoyées / utilisées chez maincare) :

  1. Openshift ( OC, client en CLI équivalent de kubectl )
  2. RKE
  3. K3s : (distribution kubernetes très légère)

Les distributions Openshift et RKE sont souvent installées avec une IHM web qui permet de faire de surveiller et gérer les composants directement dans un navigateur.

Et HELM, c'est quoi ? Par dessus kubernetes, une couche supplémentaire, HELM, un gestionnaire de packages, dont je parlerai un peu plus dans une autre doc, permet de d'automatiser l'installation d'applications (on parle de release) une ligne de commande Chaque application est décrite dans un chart et contient l'ensemble des ressources à déployer, dont les services tirant des images, des volumes, des configsMap, des secrets ), et l'articulation entre tous ces trucs.

Présentation du service k3s. Je l'ai installé sur l'hyper VM Ubuntu mon Windows 11 Pro

Rappel : notion kubernetes à maitriser

Cluster, namespaces, pod, service, deploiement, statefullset, ingress, secret, configMap

Voilà ce qu'on doit mettre dans son objet Deployment : selon les méthodes

- Methode1 : La ConfigMap est injectée comme variables d'environnement dans le conteneur désiré

   containers:
   - name: query        
     env:
      - name: GREETING_MESSAGE
        valueFrom:
          configMapKeyRef:
            name: query                 # Nom de la ConfigMap
            key: GREETING_MESSAGE       # La clé dans la ConfigMap

- Methode 2 : La ConfigMap est montée comme un fichier dans le système de fichiers du conteneur désiré

   containers:
   - name: query
     volumeMounts:
      - name: config-volume
        mountPath: /config          # Où le fichier sera monté
   volumes:
   - name: config-volume
     configMap:
       name: query                  # Nom de la Conf

- Methode 3 : La ConfigMap est passée comme un argument de la ligne de commande au conteneur

  apiVersion: apps/v1
  kind: Deployment
  spec:
    template:
      spec:
        containers:
        - name: query
          volumeMounts:                    # Étape 1 : Monter le volume
          - name: config-volume
            mountPath: /config
          args:                            # Étape 2 : Utiliser le fichier monté
          - "--spring.config.location=/config/application.yaml"
        volumes:                           # Étape 3 : Définir la source du volume
        - name: config-volume
          configMap:
            name: query                     # ← ICI la référence à la ConfigMap !

sachant la configMap query serait

  apiVersion: v1
  kind: ConfigMap
  metadata:
    name: query
    namespace: siakhooi-query
  data:
    application.yaml: |                    # ← La clé est un nom de fichier
      app:
        defaultGreetingMessage: Earth
  
    # On pourrait avoir plusieurs fichiers
    logback-spring.xml: |
      <configuration>
        <root level="INFO"/>
      </configuration>
    
    GREETING_MESSAGE: "Earth"
    LOG_LEVEL: "DEBUG"

On retrouve dans la configMap query les objets identifié par les clé GREETING_MESSAGE, LOG_LEVEL et application.yaml.

Attention :

  1. Role d'un Deployment/StatefulSet : Créer et gérer les pods c'est avec lui qu'on peut scaler ; le service gérera alors le loadbalancing entre les pods répliqués
  2. Role d'un Service : Exposer les pods en réseau

Contexte/cluster et namespace

kubectl fonctionne un peu comme une tête de lecture de disque. Il ne peut travailler qu'à un endroit : le contexte.

Le contexte est une information qui permet de se connecter à un un cluster et les paramètres qui définissent comment on y accède (url user et credential).

1 contexte pointe sur 1 cluster et 1 namespace ; en s'étant identifié avec 1 user

Avec kubectl, on définit l'ensemble des contextes avec lesquels il peut travailler dans un fichier qui s'appele KUBE_CONFIG. C'est dans ce fichier qu'on définit tous les contextes dont on a besoin.

k3s : service kubernetes léger

C'est une distrib légère de kubernetes qui s'appuie sur containerd (équivalent de docker) pour tirer les images docker, et les lancer dans un container. En fait, docker s'appuie aussi sur containerd, mais contient tout un outillage complémentaire.

Mise en place

J'ai installé kubernetes / helm sur

 - mon hyper VM Ubuntu ( dans mon windows 11 pro, mon pc xms-fixe )
 - mon xms-foxe-ubuntu

Installation

      curl -sfL https://get.k3s.io | sh -

Config

K3s utilise par défaut la config /etc/rancher/k3s/k3s.yaml pour tout les client

sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
export KUBECONFIG=~/.kube/config   --> à mettre dans .bashrc
sudo chmod 644 $KUBECONFIG
sudo chown $USER:$USER ~/.kube/config

kubectl.exe pour windows/powershell et k3s kubectl depuis sa VM

Attention : kubectl.exe est fourni par windows mais n'utilise absolument pas le $KUBECONFIG. Que ce soit pour le k3s qu'on installe sur l'Hyper VM, ou qu'on installerait sur une distribution Linux de sa WSL (WSL2)

'k3s kubectl' est la commande kubectl la plus judicieuse à utiliser puisqu'elle est livrée avec/dans k3s

J'ai fait un alias dans mon .bashrc pour ne pas avoir à taper k3s à chaque fois

Visiblement, sur mon hyper VM Ubuntu installée à partir d'un ISO 24 Server, j'ai déjà kubectl installé, donc je n'ai pas besoin de l'alias 'k3s kubectl'

K9s

C'est un client pseudo-graphique de kubernetes. Ca permet de consulter les pods, les namespaces etc …

* Installation :

sudo snap install k9s

* Utilisation :

Attention k9s n'est pas dans /snap/bin/k9s donc il est pas dans le $PATH

find /snap -name k9s -type f 2>/dev/null

Remarque : k9s s'appuie sur le dernier context et namespace sélectionné par kubectl.

kubernetes-dashboard

Présentation

C'est une IHM Web qui permet de piloter tous les namespaces du cluster sur lequel il est installé.

Installation

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml

Mon kubernetes-dashboard tourne bien. Il écoute en https sur 8443

      kubectl debug -it kubernetes-dashboard-6c7b75ffc-z6rlw -n kubernetes-dashboard --image=busybox --target=kubernetes-dashboard

=== Accéder à l'appli kubernetes-dashboard

Pour ouvrir une session ssh dessus,

kubectl exec -it kubernetes-dashboard-6c7b75ffc-z6rlw -n kubernetes-dashboard -- /bin/sh

La commande suivante permet de l'exposer sur un navigateur web à l'url suivante :

* url : http://127.0.0.1:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login * commande :

      kubectl proxy
      

ou

kubectl proxy --address='0.0.0.0' --port 8001 --accept-hosts='.*'
Créer un compte d'accès

Creer un ServiceAccount :

	
kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
EOF	
	

Creer un ClusterRoleBinding :

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
EOF

Remarque : chacune de ces 2 ressources, peuvent être créées aussi dans l'IHM de kubernetes-dashboard (si on y a accès), en cliquant sur le + en haut.

On a sécurisé l'accès, donc il faut un token d'accès ; Les tokens kubectl create token expirent. Pour obtenir un nouveau token :

kubectl -n kubernetes-dashboard create token admin-user

Pour mettre dans le clipboard

kubectl -n kubernetes-dashboard create token admin-user | xsel --clipboard --input

Je l'ai aliasé dans mon .bashrc

alias tokenkubernetesdashboard='k3s kubectl -n kubernetes-dashboard create token admin-user | xsel --clipboard --input'		
Remarque : Un fichier kubeconfig ne contient jamais un ServiceAccount en tant que tel. Il contient :
* un cluster
* un user
* un token
* un context

→ Le ServiceAccount fournit le token, et ce token est injecté dans le kubeconfig.

L'accès à l'IHM web de kubernetes-dashboard peut se faire :

kubectl -n kubernetes-dashboard create token admin-user

Adresse de l’API

kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'

Certificat CA

kubectl config view --raw --minify -o jsonpath='{.clusters[0].cluster.certificate-authority-data}'
nano ~/.kube/dashboard-admin.conf

Y mettre :

apiVersion: v1
kind: Config

clusters:
- name: k3s
  cluster:
	server: https://127.0.0.1:6443
	certificate-authority-data: <CA_BASE64>

users:
- name: dashboard-admin
  user:
	token: <TOKEN_DU_SERVICEACCOUNT>

contexts:
- name: dashboard-context
  context:
	cluster: k3s
	user: dashboard-admin
	namespace: dashboard

current-context: dashboard-context

Exposer l'appli kubernetes-dashboard

Il faut savoir sur quoi le service qu'on souhaite exposer écoute, pour ca on tape :

kubectl get svc -n kubernetes-dashboard

kubernetes-dashboard   ClusterIP   ...   443/TCP
dashboard-metrics-scraper (celui là on s'en fout)

On peut aussi voir le port d'écoute dans le dashboard, en éditant le service kubernetes-dashboard
  ports:
	- protocol: TCP
	  port: 443
	  targetPort: 8443
	  qui tappera sur le pod kubernetes-dashboard-... qui est déployé à partir du Deployment kubernetes-dashboard (qui tire l'image docker  kubernetesui/dashboard:v2.7.0 ) qui spécifie bien le port d'écoute 8443

puis on remplit le fichier kubernetes-dashboard-ingress.yaml

apiVersion: networking.k8s.io/v1 kind: Ingress metadata:

name: kubernetes-dashboard
namespace: kubernetes-dashboard
annotations:
  kubernetes.io/ingress.class: traefik
  traefik.ingress.kubernetes.io/service.serversscheme: https

spec:

rules:
- host: dashboard.local
  http:
    paths:
    - path: /
      pathType: Prefix
      backend:
        service:
          name: kubernetes-dashboard
          port:
            number: 443

Le ingress

Remarque : la ligne suivante du fichier permet de forcer traefik à parler au service kubernetes-dashboard en HTTPS

traefik.ingress.kubernetes.io/service.serversscheme: https

puis

kubectl apply -f kubernetes-dashboard-ingress.yaml

Accès au kubernetes-dashboard depuis Windows ou l'extérieur

Impossible d'accéder au dashboard depuis mon navigateur chrome sous W11 Pro. Mon serveur k3s est sous une hyper vm ubuntu 24 hébergée par mon w11 pro

Votre problème est classique : le dashboard Kubernetes tourne sur votre VM Ubuntu, et vous cherchez à y accéder depuis votre navigateur sur Windows 11. Voici comment résoudre cela en utilisant la méthode recommandée et sécurisée.

Solution S1 : kubectl port-forward

La méthode la plus simple et sécurisée est d'utiliser kubectl port-forward pour créer un tunnel entre votre machine Windows et le dashboard dans votre VM.

S1.E1 : S'assurer d'avoir kubectl sur son os W11pro ou l'installer
winget install -e --id Kubernetes.kubectl
S1.E2 : créer le fichier kube config pour pouvoir lier kubectl au service kubernetes distant

* E2.1 : récupérer le /etc/rancher/k3s/k3s.yaml du service k3s de la VM et le copier dans %USERPROFILE%\.kube\config

* E2.2 : remplacer localhost dans le fichier %USERPROFILE%\.kube\config par l'IP du service du PC qui héberge le service kubernetes (ou le nom DNS ou mDNS peut être)

 Exemples (dans le fichier %USERPROFILE%\.kube\config) : rqe : attention à l'indentation !!! 
        server: https://172.24.144.233:6443     # si l'ip de la VM est 172.24.144.233

ou

        server: https://xms-fixe-ubuntu:6443

Remarque :

on peut peut être remplacer par l'IP, qui change régulièrement par un host de la VM dans C:\Windows\System32\drivers\etc\hosts
ou encore mieux : normalement lors de l'installation de l'hyper VM, on fournit un nom d'ordinateur (moi par exemple : xms-fixe-vm-ubuntu), et quand vm est démarrée, depuis mon windows, je ping bien xms-fixe-vm-ubuntu ! 
S1.E3 : Créer le tunnel avec port-forward

Sur Windows, dans PowerShell ou CMD

kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard 8443:443

ou

kubectl -n kubernetes-dashboard port-forward --address 0.0.0.0 svc/kubernetes-dashboard 10443:443
kubectl -n kubernetes-dashboard port-forward --address 0.0.0.0 svc/kubernetes-dashboard [PORT WINDOWS]:[PORT INTERNE K3S]

pour aller plus loin, si on a plusieurs fichier kube config, pour préciser lequel on veut utiliser :

      kubectl --kubeconfig=/chemin/vers/fichier/kube_config port-forward -n kubernetes-dashboard service/kubernetes-dashboard 10443:443

Cette commande va créer un tunnel sécurisé de votre Windows (port 8443, windows écoute dessus) vers le service kubernetes-dashboard, qui à priori écoute sur le port 443 dans mon espace k3s.

L'accès au dashboard se fera alors à partir d'un webbrowser de windows à l'url : https://127.0.0.1:10443/#/service?namespace=kubernetes-dashboard

Attention !!! il faut bien comprendre qu'il ne s'agit pas du port 443 sur la VM, mais dans l'espace kubernetes, donc on ne le verra pas sur la VM avec un : sudo netstat -tlnp

Depuis la VM, “sudo netstat -tlnp” ne montrera que le port 6443 ouvert.

Effectivement, c'est biensur kubectl (le client kubernetes) qui fait le lien entre windows et k3s ; une fois que le lien est fait, le port 443 et le service svc/kubernetes-dashboard sont des éléments internes à kubernetes.

Le service kubernetes-dashboard dans votre cluster écoute sur le port 443 (HTTPS standard). C'est son port “interne” configuré dans le service Kubernetes. Dans l'IHM de kubernetes-dashboard, si on va voir la définition du service kubernetes-dashboard, on verra :

	ports:
	  - protocol: TCP
		port: 443          # Le port du SERVICE kubernetes-dashboard
		targetPort: 8443   # Le port du POD kubernetes-dashboard

Si on va voir la définition du pod dans l'IHM

	containers:
		- name: kubernetes-dashboard
		  image: kubernetesui/dashboard:v2.7.0
		  args:
			- '--auto-generate-certificates'
			- '--namespace=kubernetes-dashboard'
		  ports:
			- containerPort: 8443 # le container créé pour mettre en oeuvre le pod kubernetes-dashboard écoute bien sur 8443.
			  protocol: TCP				

Remarque : k3s expose ici 2 ports tcp :

  1. 1 pour le controle de kubectl ; en général c'est 6443 (dès fois 8443) ; on appelle ça le port de l'API Server
  2. 1 pour exposer le service
Remarque Pour éviter le port-forward

Solution S2 : NodePort fixe

Rédéfinir le service kubernetes-dashboard pour le forcer à écouter sur le port 32000 de la VM ( en plus du port 443 de k3s )

          cat <<EOF | kubectl apply -f -
          apiVersion: v1
          kind: Service
          metadata:
            name: kubernetes-dashboard
            namespace: kubernetes-dashboard
          spec:
            type: NodePort
            ports:
              - protocol: TCP
                port: 443
                targetPort: 8443
                nodePort: 32000
            selector:
              k8s-app: kubernetes-dashboard
          EOF

L'accès au dashboard se fera alors à partir d'un webbrowser de windows à l'url : https://xms-fixe-vm-ubuntu:32000/

Rqes et attention :

  1. Attention : pas de tabs devant les valeurs quand vous copiez/collez ces blocs !!! indentation très capricieuses !!!!
  2. Attention : ouvrir le port (nodePort) 32000 sur la vm ou désactiver ufw (parefeu)
  3. Attention : l'avantage de cette solution est qu'on peut FERMER LE TUNNEL qu'on avait créé solution S2 avec le port-forward et utiliser l'accès via le nodePort https://service_kubernetes_IP:32000/
  4. L ancien service kubernetes-dashboard était défini comme ça (on a en gros changé clusteIp en NodePort et ajouté une ligne nodePort à la fin du port ( je l'ai même fait manuellement dans l'IHM du dashboard après avoir appliqué la solution S2 )
          kind: Service
          apiVersion: v1
          metadata:
            name: kubernetes-dashboard
            namespace: kubernetes-dashboard
            uid: 4dc83b34-087b-4a7a-bfda-9a41929b9b5f
            resourceVersion: '1205'
            creationTimestamp: '2026-02-28T18:35:14Z'
            labels:
          	k8s-app: kubernetes-dashboard
          spec:
            ports:
          	- protocol: TCP
          	  port: 443
          	  targetPort: 8443
            selector:
          	k8s-app: kubernetes-dashboard
            clusterIP: 10.43.194.213
            clusterIPs:
          	- 10.43.194.213
            type: ClusterIP
            sessionAffinity: None
            ipFamilies:
          	- IPv4
            ipFamilyPolicy: SingleStack
            internalTrafficPolicy: Cluster

Pour vérifier :

kubectl get svc -n kubernetes-dashboard
NAME                        TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)         AGE
dashboard-metrics-scraper   ClusterIP   10.43.179.192   <none>        8000/TCP        19h
kubernetes-dashboard        NodePort    10.43.194.213   <none>        443:32000/TCP   19h	

Solution 3 : Utiliser un Ingress avec Traefik (Le plus propre)

Quelques commandes utiles

# Redémarrer une application suite à une modif de son deployment, d'une de ses configMap, secret, ou autre :

   kubectl rollout restart deployment my-query-query -n siakhooi-query

# Voir les infos des composants

   kubectl get ingressroute -A
   kubectl describe ingressroute ingress-route-dashboard -n kubernetes-dashboard
   kubectl get ingressroute ingress-route-dashboard -n kubernetes-dashboard -o yaml
   kubectl delete ingressroute ingress-route-dashboard -n kubernetes-dashboard			
   
   
   kubectl describe pod -n siakhooi-query -l app.kubernetes.io/instance=my-query | grep -A5 -E "Liveness|Readiness|Startup"

# Voir l'état de santé des pods d'un namespace :

   kubectl get pods -n siakhooi-query -w

# Voir les dernier events d'un pod :

   kubectl get events -n siakhooi-query --field-selector involvedObject.name=my-query-query-5d4fc694c6-pd2tt --watch

# Supprimer un replicatset

   kubectl delete replicaset my-query-query-56c4d6fdf7 -n siakhooi-query

# Voir les endpoints exposés par l'application (si Spring Boot Actuator est actif)

   kubectl exec -n siakhooi-query -it deployment/my-query-query -- curl -s http://localhost:8080/actuator/mappings | jq . 2>/dev/null || echo "Pas de actuator mappings"

# Voir tous les endpoints Actuator exposés

   curl http://192.168.1.54:32001/actuator

ou toujours pareil :

   kubectl exec -n siakhooi-query -it deployment/my-query-query -- curl -s http://localhost:8080/actuator

# Voir les logs

   kubectl logs -n siakhooi-query -l app.kubernetes.io/instance=my-query --tail=50

# Voir les variables d'environnement de l'application

   kubectl exec -n siakhooi-query -it deployment/my-query-query -- env | sort

Les logs (entre autre) permettent de mettre en évidence que le module actuator de springboot est activé. Ce module permet de mettre à disposition plein d'information de monitoring, sous la forme de endpoint dont le path commence par <appContextPath>/actuator/desInfos. D'ailleurs les probes de kubernetes ont été configurés pour aller consommer un endpoint défini par actuator : health !!!

A partir des logs, et de quelques commandes suivantes,

   curl http://192.168.1.54:32001/actuator/info
   curl http://192.168.1.54:32001/actuator/env | grep -i query
   curl http://192.168.1.54:32001/actuator/beans | grep -i query
   curl http://192.168.1.54:32001/actuator/mappings | jq '. | keys'
   sudo apt install jq
   curl http://192.168.1.54:32001/actuator/mappings | jq '. | keys'
   curl -s http://192.168.1.54:32001/actuator/mappings | grep -i query

on arrive à en déduire (avec l'aide de l'IA deepseek), que :

La release my-query tirée du chart siakhooi/query est une application Spring Boot qui permet d'exécuter des requêtes SQL via une API REST. Elle a trois contrôleurs principaux :

  1. QueryController - Pour exécuter des requêtes
  2. QueryConfigController - Pour configurer les requêtes
  3. DatasourceConfigController - Pour configurer les sources de données

Swagger : http://192.168.1.54:32001/swagger-ui/index.html#/query-config-controller/getQueryConfig

Remarque pour comprendre encore plus l'application :

Elle utilise spring.cloud.kubernetes.enabled:true

Ce truc de spring permet de se connecter à kubernetes pour lire et monter directement dans la JVM les configMaps, les volumes, etc ... donc les volumes, configMaps visibles dans kubernetes (via k9s, kubernetes-dashboard, ou via des describe en CLI) ne sont pas ceux utilisé par l'app ...
Mais ça permet de savoir ce que l'app utilise !!!

Cette commande :

   kubectl run -it --rm test-mysql \
     --image=mysql:8.0 \
     --restart=Never \
     -n siakhooi-query \
     -- mysql -h 10.42.0.18 \
             -P 3306 \
             -u fruituser \
             -puserpassword \
             --ssl-mode=DISABLED \
             -e "SHOW DATABASES;"

Attention : APPUYER 2 fois sur ENTER après la commande pour avoir la sortie standard …