Installer kubernetes on premise est un petit défi même si certaines solutions sont plus simples que d’autres. J’ai pu tester différentes options comme kubadm, rancher ou en core kubespray mais jamais “the hard way”. Cette dernière méthode est certainement la meilleure pour mieux appréhender la constitution d’un cluster. Certes, cette méthode est formatrice mais peut effrayer notamment faire peur si on veut installer un cluster kubernetes pour de la production. Si vous souhaitez vous former à kubernetes rendez-vous sur cette playlist.
Je vous propose de découvrir une méthode intermédiaire mais d’y ajouter le nécessaire pour rendre votre cluster hautement disponible. Et vous allez voir que contrairement à ce que l’on pense ce n’est pas si compliqué. Je vous invite après cette installation de prendre du recul et d’identifier les points critiques pour être bien à l’aise avec ces éléments. Car le jour où votre cluster kube dysfonctionne en production et on premise, vous pouvez transpirer pas mal pour débuguer car généralement vous n’avez pas mis qu’une seule application dessus.
Tout le code ci-dessous est présent dans mon dépôt officiel.
Mais je vous invite à découvrir les vidéos dans cette playlist youtube :
De quoi avons nous besoin côté machines virtuelles ?
Eh oui on veut tester un environnement de production mais rien ne vous empêches de vous faire la main sur votre laptop ou de simples machines virtuelles. Personnellement, j’utilise souvent virtualbox et je fais le provisioning de manière automatisé avec vagrant. Le Vagrantfile est donc ce qui va le mieux décrire notre infrastructure. J’ai volontairement évité de factoriser les boucles de manière à rendre cela plus lisisble.
Notre infrastructure sera composée :
- 1 machine de déploiement pour jouer kubespray (ansible) on va pas salir notre laptop non plus et en prod on ne déploie pas de son laptop.
- 2 haproxy : ils serviront pour loadbalaner l’api kubernetes pour les commandes kubectl vers nos master (port 6443) et également pour loadblancer sur les noeuds worker les flux http/https (80/443)
- 2 masters : il vaudrait mieux 3 mais notre but est de faire de simples tests tout de même (en ajoute run n’est juste que rajouter une ligne dans le ansible)
- 1 node : notre objectif n’est pas d’héberger mais de tester un cluster kube et son environnement haute disponibilité.
Ce qui donne :
Vagrant.configure(2) do |config| common = <<-SHELL if ! grep -q deploykub /etc/hosts; then sudo echo "192.168.7.120 kdeploykub" >> /etc/hosts ;fi if ! grep -q node01 /etc/hosts; then sudo echo "192.168.7.121 kmaster01" >> /etc/hosts ;fi if ! grep -q node02 /etc/hosts; then sudo echo "192.168.7.122 kmaster02" >> /etc/hosts ;fi if ! grep -q node03 /etc/hosts; then sudo echo "192.168.7.123 knode01" >> /etc/hosts ;fi if ! grep -q node04 /etc/hosts; then sudo echo "192.168.7.124 haproxy01" >> /etc/hosts ;fi if ! grep -q node05 /etc/hosts; then sudo echo "192.168.7.125 haproxy02" >> /etc/hosts ;fi sudo yum -y install vim tree net-tools telnet git python3-pip sshpass sudo setsebool -P haproxy_connect_any=1 sudo echo "autocmd filetype yaml setlocal ai ts=2 sw=2 et" > /home/vagrant/.vimrc sed -i 's/ChallengeResponseAuthentication no/ChallengeResponseAuthentication yes/g' /etc/ssh/sshd_config sudo systemctl restart sshd SHELL config.vm.box = "centos/7" config.vm.box_url = "centos/7" config.vm.define "kdeploykub" do |kdeploykub| kdeploykub.vm.hostname = "kdeploykub" kdeploykub.vm.network "private_network", ip: "192.168.7.120" kdeploykub.vm.provider "virtualbox" do |v| v.customize [ "modifyvm", :id, "--cpus", "1" ] v.customize [ "modifyvm", :id, "--memory", "512" ] v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] v.customize ["modifyvm", :id, "--natdnsproxy1", "on"] v.customize ["modifyvm", :id, "--name", "kdeploykub"] end config.vm.provision :shell, :inline => common end config.vm.define "kmaster01" do |kmaster01| kmaster01.vm.hostname = "kmaster01" kmaster01.vm.network "private_network", ip: "192.168.7.121" kmaster01.vm.provider "virtualbox" do |v| v.customize [ "modifyvm", :id, "--cpus", "2" ] v.customize [ "modifyvm", :id, "--memory", "2048" ] v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] v.customize ["modifyvm", :id, "--natdnsproxy1", "on"] v.customize ["modifyvm", :id, "--name", "kmaster01"] end config.vm.provision :shell, :inline => common end config.vm.define "kmaster02" do |kmaster02| kmaster02.vm.hostname = "kmaster02" kmaster02.vm.network "private_network", ip: "192.168.7.122" kmaster02.vm.provider "virtualbox" do |v| v.customize [ "modifyvm", :id, "--cpus", "2" ] v.customize [ "modifyvm", :id, "--memory", "2048" ] v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] v.customize ["modifyvm", :id, "--natdnsproxy1", "on"] v.customize ["modifyvm", :id, "--name", "kmaster02"] end config.vm.provision :shell, :inline => common end config.vm.define "knode01" do |knode01| knode01.vm.hostname = "knode01" knode01.vm.network "private_network", ip: "192.168.7.123" knode01.vm.provider "virtualbox" do |v| v.customize [ "modifyvm", :id, "--cpus", "2" ] v.customize [ "modifyvm", :id, "--memory", "2048" ] v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] v.customize ["modifyvm", :id, "--natdnsproxy1", "on"] v.customize ["modifyvm", :id, "--name", "knode01"] end config.vm.provision :shell, :inline => common end config.vm.define "haproxy01" do |haproxy01| haproxy01.vm.hostname = "haproxy01" haproxy01.vm.network "private_network", ip: "192.168.7.124" haproxy01.vm.provider "virtualbox" do |v| v.customize [ "modifyvm", :id, "--cpus", "1" ] v.customize [ "modifyvm", :id, "--memory", "512" ] v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] v.customize ["modifyvm", :id, "--natdnsproxy1", "on"] v.customize ["modifyvm", :id, "--name", "haproxy01"] end config.vm.provision :shell, :inline => common end config.vm.define "haproxy02" do |haproxy02| haproxy02.vm.hostname = "haproxy02" haproxy02.vm.network "private_network", ip: "192.168.7.125" haproxy02.vm.provider "virtualbox" do |v| v.customize [ "modifyvm", :id, "--cpus", "1" ] v.customize [ "modifyvm", :id, "--memory", "512" ] v.customize ["modifyvm", :id, "--natdnshostresolver1", "on"] v.customize ["modifyvm", :id, "--natdnsproxy1", "on"] v.customize ["modifyvm", :id, "--name", "haproxy02"] end config.vm.provision :shell, :inline => common end end
Ou encore ce Vagrantfile
Commençons par le loadbalancer externe haproxy et sa vip gérée par keepalived
Attention avant de lancer le ansible kubespray, il est nécessaire que votre haproxy et votre VIP soient mis en place.
Donc première chose on installe les deux haproxy et keepalived :
sudo apt install - y haproxy keepalived
et ensuite on édite la configuration de haproxy
global log 127.0.0.1 local2 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats defaults mode http log global option httplog option dontlognull option http-server-close option forwardfor except 127.0.0.0/8 option redispatch retries 3 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout http-keep-alive 10s timeout check 10s maxconn 3000 listen stats bind *:9000 stats enable stats uri /stats stats refresh 2s stats auth xavki:password listen kubernetes-apiserver-https bind *:6443 mode tcp option log-health-checks timeout client 3h timeout server 3h server master1 192.168.7.121:6443 check check-ssl verify none inter 10000 server master2 192.168.7.122:6443 check check-ssl verify none inter 10000 balance roundrobin
Bien sûr derrière je vous laisse reload le service systemd de haproxy.
Ensuite ajoutons la configuration de keepalived :
vrrp_script reload_haproxy { script "/usr/bin/killall -0 haproxy" interval 1 } vrrp_instance VI_1 { virtual_router_id 100 state MASTER priority 100 # interval de check advert_int 1 # interface de synchro entre les LB lvs_sync_daemon_interface eth1 interface eth1 # authentification entre les 2 machines LB authentication { auth_type PASS auth_pass secret } # vip virtual_ipaddress { 192.168.7.130/32 brd 192.168.7.255 scope global } track_script { reload_haproxy } }
Dans cette configuration on utilise une VIP qui switchera en fonction du service haproxy et du fait qu’il soit fonctionnel au vue de systemd ou non. Cette VIP est la :
192.168.7.130
Par défaut cette VIP sera portée par la machine haproxy1. Si celui-ci tombe cette adresse va être installée sur haproxy2 grâce à keepalived.
Vérifiez que les deux services (haproxy et keepalived) soient bien démarrés sur les 2 machines haproxy. Activez le redémarrage au reboot avec le systemctl enable.
Maintenant configurons kubespray
Kubespray c’est du ansible. donc première chose vous devez l’installer sur la machine de déploiement et installer également pip :
sudo apt install ansible python3-pip
Ensuite clonez le dépôt de kubespray :
git clone https://github.com/kubernetes-sigs/kubespray
rendez-vous dans le nouveau répertoire puis :
sudo pip3 install -r requirements.txt
Ensuite on prépare le ansible on va recopier le modéèle d’inventaire :
cp -rfp inventory/sample inventory/mycluster
Puis on modifie cet inventory :
# ## Configure 'ip' variable to bind kubernetes services on a # ## different ip than the default iface # ## We should set etcd_member_name for etcd cluster. The node that is not a etcd member do not need to set the value, or can set the empty string value. [all] kmaster01 ansible_host=192.168.7.121 ip=192.168.7.121 etcd_member_name=etcd1 kmaster02 ansible_host=192.168.7.122 ip=192.168.7.122 etcd_member_name=etcd2 knode01 ansible_host=192.168.7.123 ip=192.168.7.123 etcd_member_name=etcd3 # ## configure a bastion host if your nodes are not directly reachable # bastion ansible_host=x.x.x.x ansible_user=some_user [kube-master] kmaster01 kmaster02 [etcd] kmaster01 kmaster02 knode01 [kube-node] knode01 [calico-rr] [k8s-cluster:children] kube-master kube-node calico-rr
On y a donc déclaré nos nodes master et notre worker.
Particularité ETCD
Particularité pour notre test, nous allons faire ce qu’il ne faut pas faire c’est à dire héberger les services etcd sur les master et surtout sur 1 worker. Pourquoi cela ? Etcd nécessite un nombre impair de noeuds pour son bon fonctionnement. Donc on pourrait en avoir 1 ou 3. Mais si on veut faire des tests de coupures de noeuds il est préférable d’en avoir 3. J’ai donc salement choisi d’en ajouter un sur le worker.
En production il serait recommandé d’externaliser les etcd et ne pas les avoir au sein des machines kubernetes (plus y ajouter des backups etc).
Dernière configuration ansible
Maintenant il nous faut éditer le fichier du group all :
vim inventory/mykub/group_vars/all/all.yml
comme en modifiant ces lignes
## External LB example config apiserver_loadbalancer_domain_name: "elb.kub" loadbalancer_apiserver: address: 192.168.7.130 port: 6443
On vient donc de préciser que la VIP est notre point d’entrée du loadbalancer et également que votre cluster répondra sur le nom de domaine elb.kub (vous pourrez modifier votre /etc/hosts).
Maintenant c’est partie on peut jouer notre ansible en tant que user vagrant et on utilisera le password “vagrant”… un standard sur vagrant.
ansible-playbook -i inventory/my-cluster/inventory.ini -u vagrant -k -b cluster.yml
Attendez plusieurs dizaines de minutes suivant votre connexion et le nombre de machines et votre cluster sera up à la fin.
Première connexion avec kubectl
Dernière tâche pour accéder à notre cluster, il nous faut récupérer le certificat du compte d’administrateur de notre cluster kubernetes.
Copiez sur un des master le fichier
cat /etc/kubernetes/admin.conf
Puis créez et collez le contenu sur la machine devant accéder au cluster :
mkdir -p ~/.kube vim ~/.kube/config
Enfin installons kubectl sur cette même machine :
sudo apt-get update && sudo apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl
Maintenant testons notre cluster :
kubectl cluster-info kubectl get nodes
Test de la haute disponibilité
Si tout est bien fonctionnel…
Coupez un haproxy soit en coupant le service avec un systemctl stop soit en arrêtant plus ou moins brutalement la machine.
Testez à nouveau vos commandes kubectl.
Ensuite coupez un master et procédez au même test.
Alors ça marche ?
Je vous invite à découvrir la playlist complète pour découvrir kubernetes et d’autres outils liés ici.