Aller au contenu principal

Déployer un cluster K8s avec Kubespray

· 9 minutes de lecture
Stéphane ROBERT
Consultant DevOps

Pour des besoins professionnels, je dois monter des clusters Kubernetes afin d'héberger des API. Pour cela, je fais le choix d'utiliser KubeSpray. KubeSpray est un framework qui permet de provisionner des clusters Kubernetes sur du bare-metal, mais aussi chez la plupart des clouders, en utilisant Ansible. Pour tester le tout, je vais utiliser Ignite que j'ai présenté récemment, auquel je vais ajouter footloose.

Provisionnement des VM avec Footloose et Ignite

Pour créer rapidement mon cluster de micro-VM je vais utiliser footloose un autre projet de WeaveWorks qui permet de créer rapidement une série de machines. Je vais créer un cluster kubernetes composés de 3 masters et 2 workers à base de Centos 8.5.

Pour installer footloose je vous renvoie à mon billet sur ignite

Création de l'image CENTOS 8.5

Je vais mettre à jour l'image et l'injecter dans Ignite. Mon Dockerfile :

FROM weaveworks/ignite-centos:8

RUN dnf upgrade -y

On lance le build :

docker build -t weaveworks/ignite-centos:8.5 .

On récupère la version et on tague l'image teste rapidement :

docker run --rm -it build -t weaveworks/ignite-centos:8.5 cat /etc/redhat-release
CentOS Linux release 8.5.2111

docker tag weaveworks/ignite-centos:8.5 weaveworks/ignite-centos:8.5.2111

On l'importe dans Ignite :

sudo ignite image import weaveworks/ignite-centos:8.5.2111 --runtime docker

INFO[0000] Starting image import...
INFO[0004] Imported OCI image "weaveworks/ignite-centos:8.5.2111" (526.4 MB) to base image with UID "cdc78b581b43346e"
INFO[0004] Created image with ID "cdc78b581b43346e" and name "weaveworks/ignite-centos:8.5.2111"

Maintenant créons nos 5 VM avec Footloose.

Création du cluster de VM avec Footloose

Footloose permet d'utiliser ignite comme backend pour provisionner des micro-VM. Je vais donc créer un cluster de 5 VM avec 1 CPU / 1.6 GB (le minimum accepté par kubespray) et 5 GO de disque chacune.

Déclarons notre cluster avec ce fichier :

cluster:
  name: cluster
  privateKey: cluster-key
machines:
- count: 5
  spec:
    image: weaveworks/ignite-centos:8.5.2111
    name: node%d
    portMappings:
    - containerPort: 22
    # This is by default "docker". However, just set this to "ignite" and it'll work with Ignite :)
    backend: ignite
    # Optional configuration parameters for ignite:
    ignite:
      cpus: 1
      memory: 1600MB
      diskSize: 5GB
      kernel: "weaveworks/ignite-kernel:5.10.51"

Lançons le provisionnement en lançant la commande footloose create depuis le répertoire où se trouve le fichier footloose.yaml :

sudo footloose create

Il est possible de créer le cluster en indiquant le nom du fichier avec l'option -c :

sudo footloose create -c mon-cluster.yml

INFO[0000] Creating machine: cluster-node0 ...
INFO[0002] Creating machine: cluster-node1 ...
INFO[0004] Creating machine: cluster-node2 ...
INFO[0006] Creating machine: cluster-node3 ...
INFO[0009] Creating machine: cluster-node4 ...

On vérifie qu'elles sont bien présentes sous Ignite :

sudo ignite ps
VM ID                   IMAGE                           KERNEL                           SIZE     CPUS    MEMORY          CREATED STATUS  IPS             PORTS                   NAME
054f0119bc87ca0c        weaveworks/ignite-centos:8.5    weaveworks/ignite-kernel:5.10.51 5.0 GB   1       1024.0 MB       52m ago Up 52m  10.61.0.50      0.0.0.0:44567->22/tcp   cluster-node3
3498ae67c7f695cd        weaveworks/ignite-centos:8.5    weaveworks/ignite-kernel:5.10.51 5.0 GB   1       1024.0 MB       52m ago Up 52m  10.61.0.51      0.0.0.0:43621->22/tcp   cluster-node4
714f445ea643ebad        weaveworks/ignite-centos:8.5    weaveworks/ignite-kernel:5.10.51 5.0 GB   1       1024.0 MB       52m ago Up 52m  10.61.0.47      0.0.0.0:45371->22/tcp   cluster-node0
d6cf7a17be0368e5        weaveworks/ignite-centos:8.5    weaveworks/ignite-kernel:5.10.51 5.0 GB   1       1024.0 MB       52m ago Up 52m  10.61.0.49      0.0.0.0:33309->22/tcp   cluster-node2
f1fcde290a9f3f09        weaveworks/ignite-centos:8.5    weaveworks/ignite-kernel:5.10.51 5.0 GB   1       1024.0 MB       52m ago Up 52m  10.61.0.48      0.0.0.0:35075->22/tcp   cluster-node1

Provisionnement d'un cluster avec Kubepsray

Construction de l'environnement

Dans un premier temps, nous allons récupérer le projet et installer les requirements.

git clone https://github.com/kubernetes-incubator/kubespray.git
cd kubespray
pyenv virtualenv 3.9.7 kubespray
pyenv local kubespray

pip install -r requirements.txt
Looking in indexes: https://python:****@artefacts.sa-cim.local/repository/pypi-all/simple/
Collecting ansible==3.4.0
  Downloading https://artefacts.sa-cim.local/repository/pypi-all/packages/ansible/3.4.0/ansible-3.4.0.tar.gz (31.9 MB)
     |████████████████████████████████| 31.9 MB 3.4 MB/s


Installing collected packages: pycparser, six, pyparsing, MarkupSafe, cffi, PyYAML, packaging, jinja2, cryptography, ansible-base, ruamel.yaml.clib, ruamel.yaml, pbr, netaddr, jmespath, ansible
    Running setup.py install for ansible-base ... done
    Running setup.py install for ansible ... done
Successfully installed MarkupSafe-1.1.1 PyYAML-6.0 ansible-3.4.0 ansible-base-2.10.15 cffi-1.15.0 cryptography-2.8 jinja2-2.11.3 jmespath-0.9.5 netaddr-0.7.19 packaging-21.3 pbr-5.4.4 pycparser-2.21 pyparsing-3.0.6 ruamel.yaml-0.16.10 ruamel.yaml.clib-0.2.4 six-1.16.0

Éditons le fichier ansible.cfg pour l'adapter à notre besoin. On ajoute simplement la localisation de la clé SSH et le remote_user (les deux paramètres avant [inventory]) :

[ssh_connection]
pipelining=True
ssh_args = -o ControlMaster=auto -o ControlPersist=30m -o ConnectionAttempts=100 -o UserKnownHostsFile=/dev/null
#control_path = ~/.ssh/ansible-%%r@%%h:%%p
[defaults]
# https://github.com/ansible/ansible/issues/56930 (to ignore group names with - and .)
force_valid_group_names = ignore
host_key_checking=False
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /tmp
fact_caching_timeout = 7200
stdout_callback = default
display_skipped_hosts = no
library = ./library
callback_whitelist = profile_tasks
roles_path = roles:$VIRTUAL_ENV/usr/local/share/kubespray/roles:$VIRTUAL_ENV/usr/local/share/ansible/roles:/usr/share/kubespray/roles
deprecation_warnings=False
inventory_ignore_extensions = ~, .orig, .bak, .ini, .cfg, .retry, .pyc, .pyo, .creds, .gpg
private_key_file = ../cluster-key
remote_user = root
[inventory]
ignore_patterns = artifacts, credentials

Création de l'inventaire et modification du paramétrage

On va utiliser les outils de KubeSpray pour générer l'inventaire :

cp -rfp inventory/sample inventory/ignite
declare -a IPS=(10.61.0.47 10.61.0.48 10.61.0.49 10.61.0.50 10.61.0.51)
CONFIG_FILE=inventory/ignite/hosts.yml python3 contrib/inventory_builder/inventory.py ${IPS[@]}

Vous devriez vous retrouver avec ce fichier inventory/ignite/hosts.yml

    node1:
      ansible_host: 10.61.0.47
      ip: 10.61.0.47
      access_ip: 10.61.0.47
    node2:
      ansible_host: 10.61.0.48
      ip: 10.61.0.48
      access_ip: 10.61.0.48
    node3:
      ansible_host: 10.61.0.49
      ip: 10.61.0.49
      access_ip: 10.61.0.49
    node4:
      ansible_host: 10.61.0.50
      ip: 10.61.0.50
      access_ip: 10.61.0.50
    node5:
      ansible_host: 10.61.0.51
      ip: 10.61.0.51
      access_ip: 10.61.0.51
  children:
    kube_control_plane:
      hosts:
        node1:
        node2:
    kube_node:
      hosts:
        node1:
        node2:
        node3:
        node4:
        node5:
    etcd:
      hosts:
        node1:
        node2:
        node3:
    k8s_cluster:
      children:
        kube_control_plane:
        kube_node:
    calico_rr:
      hosts: {}

Testons :

ansible -m ping -i node1, all

node1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Ensuite pour Centos il faut modifier le paramétrage de Calico :

sed -i -r 's/^(calico_iptables_backend:).*/\1 "Auto"/g' roles/network_plugin/calico/defaults/main.yml

Dans le paramétrage du cluster j'ai désactivé aussi la mise à jour du DNS:

sed -i -r 's/^(dns_mode:).*/\1 none/g' inventory/ignite/group_vars/k8s_cluster/k8s-cluster.yml

Installation du cluster Kubernetes

Tout est prêt on peut passer à l'installation du cluster.

ansible-playbook -i inventory/ignite cluster.yml
Wednesday 15 December 2021  11:48:41 +0000 (0:00:00.131)       0:25:18.294 ****
===============================================================================
kubernetes/preinstall : Install packages requirements --------------------------------------------------------------------------------- 141.77s
download : download_container | Download image if required ----------------------------------------------------------------------------- 48.32s
network_plugin/calico : Start Calico resources ----------------------------------------------------------------------------------------- 44.53s
bootstrap-os : Assign inventory name to unconfigured hostnames (non-CoreOS, non-Flatcar, Suse and ClearLinux, non-Fedora) -------------- 41.05s
download : download_file | Download item ----------------------------------------------------------------------------------------------- 34.31s
kubernetes/kubeadm : Join to cluster --------------------------------------------------------------------------------------------------- 32.23s
kubernetes/control-plane : kubeadm | Initialize first master --------------------------------------------------------------------------- 29.68s
download : download_container | Download image if required ----------------------------------------------------------------------------- 28.06s
policy_controller/calico : Start of Calico kube controllers ---------------------------------------------------------------------------- 25.77s
kubernetes/control-plane : Joining control plane node to the cluster. ------------------------------------------------------------------ 24.47s
download : download_container | Download image if required ----------------------------------------------------------------------------- 24.26s
bootstrap-os : Gather host facts to get ansible_distribution_version ansible_distribution_major_version -------------------------------- 23.03s
bootstrap-os : Gather host facts to get ansible_os_family ------------------------------------------------------------------------------ 22.87s
policy_controller/calico : Create calico-kube-controllers manifests -------------------------------------------------------------------- 22.09s
download : download_container | Download image if required ----------------------------------------------------------------------------- 21.43s
Gather necessary facts (hardware) ------------------------------------------------------------------------------------------------------ 20.98s
etcd : reload etcd --------------------------------------------------------------------------------------------------------------------- 20.96s
download : download_file | Download item ----------------------------------------------------------------------------------------------- 20.88s
Gather necessary facts (network) ------------------------------------------------------------------------------------------------------- 20.77s
Gather minimal facts ------------------------------------------------------------------------------------------------------------------- 20.67s

Au bout de 25 minutes notre cluster est prêt. Récupérons sa configuration.

Créons les entrées dans /etc/hosts

Pour accéder plus facilement aux noeuds un petit quick win Ansible pour ajouter les hosts de l'inventaire dans /etc/hosts :

---
- hosts: k8s_cluster
  gather_facts: true
  become: true


  tasks:
    - name: update /etc/hosts file
      vars:
        ansible_python_interpreter: "/usr/bin/python"
      blockinfile:
        path: /etc/hosts
        marker: "# {mark} Node {{ inventory_hostname }}"
        block: "{{ ansible_facts.default_ipv4.address }} {{ inventory_hostname }}"
      delegate_to: localhost

Si vous détruisez votre cluster il faudra le rejouer pour mettre les bonnes IP !

On lance le playbook :

cd ..
ansible-playbook -i kubespray/inventory/ignite create-etc-hosts.yml

cat /etc/hosts

# BEGIN Node node1
10.61.0.62 node1
# END Node node1
# BEGIN Node node2
10.61.0.63 node2
# END Node node2
# BEGIN Node node5
10.61.0.66 node5
# END Node node5
# BEGIN Node node4
10.61.0.65 node4
# END Node node4
# BEGIN Node node3
10.61.0.64 node3
# END Node node3

Configuration de kubectl

Afin de pouvoir utiliser les commandes kubectl depuis notre poste, on va récupérer la configuration du cluster et la déposer dans le répertoire .kube:

ssh -i cluster-key root@node1 cat /etc/kubernetes/admin.conf > ~/.kube/config-ignite

Récupérer l'adresse de node1 pour remplacer 127.0.0.1 dans la config :

awk '$2 ~ /node1/ {print $0}' /etc/hosts
10.61.0.62 node1

Editez le fichier ~/.kube/config-ignite et remplacer 127.0.0.1 par cette adresse. On peut aussi changer le nom du context par ignite :

    server: https://10.61.0.62:6443
  name: cluster.local
contexts:
- context:
    cluster: cluster.local
    user: kubernetes-admin
  name: ignite

Utilisons les contexts :

KUBECONFIG=~/.kube/config-test:~/.kube/config-ignite
kubectl config get-contexts

CURRENT   NAME      CLUSTER         AUTHINFO           NAMESPACE
*         default   default         default
          ignite    cluster.local   kubernetes-admin

Pour changer de contexte utilisons kubectx :

asdf plugin add kubectx
asdf install kubectx latest
asdf global kubectx latest
kubectx ignite
Switched to context "ignite".

On vérifie qu'on accède au cluster :

kubectl get pod -A
NAMESPACE     NAME                                      READY   STATUS    RESTARTS       AGE
kube-system   calico-kube-controllers-5788f6558-c9mv9   1/1     Running   2 (166m ago)   166m
kube-system   calico-node-bvcqd                         1/1     Running   0              168m
kube-system   calico-node-lnw87                         1/1     Running   2 (167m ago)   168m
kube-system   calico-node-nfcj4                         1/1     Running   2 (167m ago)   168m
kube-system   calico-node-ptfjj                         1/1     Running   3 (167m ago)   168m
kube-system   calico-node-wzqb9                         1/1     Running   2 (167m ago)   168m
kube-system   kube-apiserver-node1                      1/1     Running   0              171m
kube-system   kube-apiserver-node2                      1/1     Running   0              170m
kube-system   kube-controller-manager-node1             1/1     Running   3 (98m ago)    171m
kube-system   kube-controller-manager-node2             1/1     Running   3 (53m ago)    170m
kube-system   kube-proxy-2shqj                          1/1     Running   0              168m
kube-system   kube-proxy-4t96f                          1/1     Running   0              169m
kube-system   kube-proxy-fnt65                          1/1     Running   0              169m
kube-system   kube-proxy-hxjks                          1/1     Running   0              168m
kube-system   kube-proxy-kwmmr                          1/1     Running   0              169m
kube-system   kube-scheduler-node1                      1/1     Running   3 (98m ago)    171m
kube-system   kube-scheduler-node2                      1/1     Running   3              170m
kube-system   nginx-proxy-node3                         1/1     Running   0              169m
kube-system   nginx-proxy-node4                         1/1     Running   0              169m
kube-system   nginx-proxy-node5                         1/1     Running   0              169m

Un petit test :

kubectl create ns test
namespace/test created

kubens test
Context "ignite" modified.
Active namespace is "test".

kubectl create deployment nginx --image=nginx

Cela confirme que ignite est très bon remplaçant à vagrant. La création des nodes s'est faite en moins d'une minute. Donc à nous la destruction et reconstruction sans perdre de temps. Ça confirme aussi que KubeSpray peut répondre à mon besoin.

Allez on nettoie tout !

sudo footloose delete

Plus d'infos sur kubespray.

A la prochaine !