Faire de votre cluster Kubeadm, un vrai simulateur accessible à distance

Faire de votre cluster Kubeadm, un vrai simulateur accessible à distance

Introduction

Dans l'article précédent, je vous ai présenté comment créer de toute pièce un cluster Kubeadm avec Vagrant et Ansible. Il est maintenant question de rendre accessible ce cluster vers l'extérieur, par exemple, si vous souhaitez évaluer une personne à distance sur ses connaissances sur Kubernetes ou tout simplement pour vous, afin d'avoir accès à votre cluster quel que soit votre lieu.

Dans cet article, le but n'est pas de reprendre ce qui a été fait dans kubeadm-automation, mais bien de le compléter à travers deux playbooks Ansible qui permettent d'en faire un "vrai" simulateur à la manière de killer.sh (mais sans interface graphique).

Tout d'abord, je vous présenterai comment rendre votre cluster Kubeadm accessible à distance de manière sécurisée à travers son API kubeapi-server sans ouvrir le moindre flux vers l'extérieur, assez pratique quand on travaille en local sur sa machine, par exemple. Et pour finir, je vous montrerai comment alimenter votre cluster de scripts qui permettent d'en faire un vrai simulateur.

L'accès à distance

Pour de qui est de rendre accessible votre cluster Kubeadm fraichement déployé, il est nécessaire d'utiliser un outil qui permet de créer un tunnel du localhost de notre machine master afin de l'exposer sur internet via une url générée par l'outil.

J'ai choisi la solution ngrok qui répond à ce besoin. Vous pouvez utiliser l'outil dans sa version gratuite pour réaliser les étapes de cet article, il n'y a aucun problème. Vous aurez tout de même besoin d'un compte à créer au préalable.

Une fois, votre compte créé, il faudra récupérer l'url de votre binaire qui est affichée ici ainsi que le token d'authentification une fois connecté.

Pour exposer notre kubeapi-server, il est d'abord primordial avant toute chose de créer un proxy sur notre localhost vers cette API. Ce qui permettra par la suite, de créer notre tunnel avec ngrok en prenant le port de ce proxy.

Notre playbook sera composé de plusieurs étapes :

  • Téléchargement du binaire pour ngrok ;
  • Vérifier si le proxy vers le kubeapi-server s'exécute, si ce n'est pas le cas, le démarrer ;
  • Vérifier que le ngrok s'exécute, si ce n'est pas le cas, l'initialiser et démarrer le tunnel vers le proxy du kubeapi-server qui est sur le port 8081 par défaut ;
  • Récupérer les informations de ngrok : l'url publique sur laquelle notre tunnel est accessible ;
  • Générer la configuration Kubernetes pour accéder à notre cluster via les informations de ngrok.

Tout est bon pour vous ? Je vous présente le playbook Ansible qui va réaliser ces tâches :

---
- hosts: master
  gather_facts: yes
  vars:
    ngrok_download_url: # L'url de votre binaire ngrok
    ngrok_authtoken: # Votre token d'authentification
    ngrok_api: http://localhost:4040/api/tunnels
  tasks:
  # Téléchargement du binaire pour ngrok
  - name: Download ngrok
    unarchive:
      src: "{{ ngrok_download_url }}"
      dest: /usr/local/bin
      remote_src: yes
    become: yes

  # Vérifier si le proxy vers le ``kubeapi-server`` s'exécute
  - name: Check if proxy is starting
    shell:  ps aux | grep "kubectl proxy" | grep -v grep
    register: check_proxy
    changed_when: false
    ignore_errors: yes

  # Si ce n'est pas le cas, le démarrer
  - name: Start proxy
    shell: nohup kubectl proxy --disable-filter=true >/dev/null 2>&1 &
    when: check_proxy.stdout_lines | length == 0

  # Vérifier que le ngrok s'exécute
  - name: Check if ngrok is starting
    shell:  ps aux | grep "ngrok http 8001" | grep -v grep
    register: check_ngrok
    changed_when: false
    ignore_errors: yes

  # Si ce n'est pas le cas, l'initialiser
  - name: Init ngrok
    shell: ngrok authtoken {{ ngrok_authtoken }}
    when: check_ngrok.stdout_lines | length == 0

  # Et démarrer le tunnel vers le proxy du kubeapi-server qui est sur le port 8081 par défaut
  - name: Start ngrok
    shell: nohup ngrok http 8001 >/dev/null 2>&1 &
    when: check_ngrok.stdout_lines | length == 0

  # Récupérer les informations de ngrok : l'url publique sur laquelle notre tunnel est accessible
  - name: Get ngrok informations
    uri:
      url: "{{ ngrok_api }}"
      return_content: yes
    register: ngrok_api
    retries: 3
    delay: 5
    until: ngrok_api.json.tunnels[0].public_url is defined

  # Générer la configuration Kubernetes pour accéder à notre cluster via les informations de ngrok
  - name: Get kubeconfig from master
    fetch:
      src: /home/vagrant/.kube/config
      dest: kubeconfig_remote
      flat: yes

  # Remplacement de l'url avec celle générée par ngrok
  - name: Replace localhost by ngrok url
    replace:
      path: kubeconfig_remote
      regexp: 'https://10.0.0.10:6443'
      replace: "{{ ngrok_api.json.tunnels[0].public_url }}"
    delegate_to: 127.0.0.1
    become: no

  # Ajout de "insecure-skip-tls-verify: true" car l'url générée par ngrok n'est pas celle qui est autorisée via le certificat CA de Kubernetes
  - name: Replace localhost by ngrok url
    replace:
      path: kubeconfig_remote
      regexp: 'certificate-authority-data(.*)'
      replace: 'insecure-skip-tls-verify: true'
    delegate_to: 127.0.0.1
    become: no

Je vous recommande fortement de créer ce playbook dans le répertoire de votre dépôt de code kubeadm-automation pour avoir accès facilement à l'inventaire de Vagrant.

Ce qui permet de l'exécuter avec la commande suivante (configure_remote_access.ymlest le nom de mon fichier playbook) :

ansible-playbook configure_remote_access.yml -i .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory

Comme vous pouvez le remarquer, le playbook génère un fichier à la racine de votre dépôt de code, qui se nomme kubeconfig_remote et qui contient votre configuration pour piloter votre cluster avec kubectl via une url publique (*.ngrok.io).

Pour l'utiliser :

export KUBECONFIG=kubeconfig_remote

Et vous pouvez ensuite faire vos commandes kubectl favorites :

$ kubectl get nodes -o wide
NAME          STATUS   ROLES                  AGE     VERSION    INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
k8s-master    Ready    control-plane,master   9m14s   v1.20.11   10.0.0.10     <none>        Ubuntu 20.04.2 LTS   5.4.0-80-generic   containerd://1.5.5
k8s-worker1   Ready    <none>                 7m32s   v1.20.11   10.0.0.11     <none>        Ubuntu 20.04.2 LTS   5.4.0-80-generic   containerd://1.5.5

Et voilà, vous avez votre cluster Kubeadm accessible à distance !

Faire de votre cluster, un simulateur !

Pour faire de votre cluster Kubeadm un vrai simulateur, il faut avoir une sorte de script qui donne un énoncé et qui est capable de vérifier si l'ensemble de l'énoncé a été réalisé correctement.

On peut donc imaginer un script (pourquoi pas shell ?) qui vient vérifier différents points à travers l'exécution de commandes kubectl sur les objets à créer.

Je vous propose un exemple de script assez court que j'ai moi-même réalisé pour arriver vers ce but-là. Bien sûr, vous pouvez l'adapter en fonction de vos besoins.

Script commons.sh :

RED='\033[0;31m' # Rouge
GREEN='\033[0;32m' # Vert
NC='\033[0m' # Sans couleur

# Le but de cette fonction est d'afficher ce que va vérifier notre commande "kubectl" et de l'exécuter pour savoir si le retour de cette commande renvoie quelque chose (OK) ou non (KO).
check_context() {
  context=$1
  command=$(bash -c "$2" 2>/dev/null)

  if [[ ! -z "$command" ]]; then
    echo -e "${context} : ${GREEN}OK${NC}"
  else
    echo -e "${context} : ${RED}KO${NC}" 
  fi
}

Script check_ex1.sh :

#!/bin/bash

source ./commons.sh

check_context "Le namespace ex1 a été créé" "kubectl get ns | grep ex1"
check_context "Le déploiement test-k8s a été créé" "kubectl -n ex1 get deploy | grep test-k8s"
check_context "Le déploiement possède 3 replicas" "kubectl -n ex1 get deploy test-k8s -o=jsonpath='{.spec.replicas}' | grep 3"
check_context "Le déploiement a comme image nginx" "kubectl -n ex1 get deploy test-k8s -o=jsonpath='{.spec.template.spec.containers[0].image}' | grep nginx"

Le but de l'exercice est de créer un déploiement test-k8s dans le namespace ex1 qui utilise l'image nginx et qui possède 3 replicas.

Si on exécute le script avant :

$ ./check_ex1.sh
Le namespace ex1 a été créé : KO
Le déploiement test-k8s a été créé : KO
Le déploiement possède 3 replicas : KO
Le déploiement a comme image nginx : KO

Et après avoir réalisé l'exercice :

Le namespace ex1 a été créé : OK
Le déploiement test-k8s a été créé : OK
Le déploiement possède 3 replicas : OK
Le déploiement a comme image nginx : OK

Pour finir, vous pouvez très bien mettre ces exercices sur votre cluster avec le playbook ci-dessous :

- hosts: master
  gather_facts: yes
  become: yes
  tasks:
  - name: Copy exercices
    copy:
      src: ../k8s-simulator/ # Dossier contenant nos exercices
      dest: "/home/{{ ansible_user }}/k8s-simulator"
      mode: 0755

Le but de cette démarche est d'enrichir au fur et à mesure les exercices et d'ajouter plusieurs vérifications afin de proposer une bibliothèque assez complète de challenge !

Conclusion

Vous avez maintenant un cluster Kubeadm qui ressemble à un vrai simulateur et où l'API de Kubernetes est accessible vers l'extérieur.

Personnellement, j'utilise cela pour tester les potentiels candidats de l'entreprise dans laquelle je suis, SFEIR, afin de vérifier leurs connaissances sur la création de Pod, Deployment, Service, etc. Ce qui correspond à un niveau de certification plutôt proche de la CKAD.

Si on souhaite aller plus loin et tester des compétences d'administration ou de mise en place de la sécurité, un accès ssh est indispensable afin de réaliser des modifications au niveau des composants clés de Kubernetes comme par exemple le scheduler.