Aller au contenu

Déployer un cluster K8s avec Kubespray

logo kubernetes

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 :

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

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

Terminal window
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 :

Terminal window
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 :

Terminal window
sudo footloose create

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

Terminal window
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 :

Terminal window
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.

Terminal window
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 :

Terminal window
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 :

Terminal window
ansible -m ping -i node1, all
node1 | SUCCESS => {
"changed": false,
"ping": "pong"
}

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

Terminal window
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:

Terminal window
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.

Terminal window
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 :

Terminal window
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:

Terminal window
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 :

Terminal window
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 :

Terminal window
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 :

Terminal window
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 :

Terminal window
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 :

Terminal window
kubectl create ns test
namespace/test created
kubens test
Context "ignite" modified.
Active namespace is "test".
kubectl create deployment nginx --image=nginx

kubespray ignite

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 !

Terminal window
sudo footloose delete

Plus d’infos sur kubespray.

A la prochaine !