Proxmox v6 : Cluster K8S kubeadm via Terraform & Cloud-Init

Proxmox v6 : Cluster K8S kubeadm via Terraform & Cloud-Init

Aujourd’hui, un peu de tout ! Terraform, Cloud-Init, Proxmox, Kubernetes, et un lab bien sympa !

Étant toujours dans mon apprentissage de Docker/K8s/Devops en gĂ©nĂ©ral, je me suis dit que monter un petit cluster Kubernetes sur un bon vieux Proxmox serait sympathique, d’autant plus que j’avais dĂ©jĂ  crĂ©Ă© un article traitant de k8s mais c’Ă©tait via Minikube, autant dire que c’Ă©tait trĂšs sommaire. Ici, malgrĂ© que je ne sois pas encore un pro, on va essayer d’avancer un peu plus loin et de se monter un “vrai” cluster 😉

Commençons donc directement !

I) Le lab en détails

Ici nous allons donc partir sur un cluster comprenant 3 hĂŽtes, un master et deux workers. PlutĂŽt que de crĂ©er Ă  la main et configurer nos 3 vms, je me suis dit que j’allais un peu automatiser ça.

On va donc, dans l’ordre :

  • CrĂ©er une template de machine virtuelle basĂ©e sur une image
    Cloud-Init de Debian 10 sur notre Proxmox ;
  • CrĂ©er un script Terraform pour dĂ©ployer X fois notre template, en configurant une IP et tout ce qui va bien Ă  chaque fois (installation des utilitaires de base, de docker, kubectl…) ;
  • Ensuite seulement nous mettrons en place notre cluster Kubernetes ;

II) Création du template via Cloud-Init

Ici pour la premiĂšre Ă©tape nous allons tĂ©lĂ©charger un disque Cloud-Init d’une Debian 10 prĂ©-configurĂ©e :

wget https://cdimage.debian.org/cdimage/openstack/current-10/debian-10-openstack-amd64.qcow2

Ah oui, juste ! Qu’est-ce que Cloud-Init avant toute chose… et bien je vais essayer de dĂ©crire cet outil assez grossiĂšrement, car je ferai sĂ»rement d’autres articles qui en parleront plus en dĂ©tails, mais retenez ceci :

Cloud Init est un package (un binaire) permettant de rĂ©aliser la configuration initiale d’un template de machine virtuelle ou de conteneur. Par exemple, une fois exĂ©cutĂ© il pourra changer le nom d’hĂŽte, l’adresse IP, installer tel ou tel paquet, importer telle clĂ© SSH, etc. Plus besoin donc de se connecter manuellement sur 100 VMs pour exĂ©cuter son script Bash fait maison…

Ensuite, il ne nous reste plus qu’Ă  crĂ©er notre VM qui servira de template en CLI, en lui greffant le disque virtuel Cloud-Init prĂ©cĂ©demment tĂ©lĂ©chargĂ© :

# Création d'une VM avec ID 1000 nommée debian-10-template, 2Go RAM, 1 proco, 2 coeurs
qm create 1000 -name debian-10-template -memory 2048 -net0 virtio,bridge=vmbr0 -cores 2 -sockets 1 -cpu cputype=kvm64 -kvm 1 -numa 1
# Import du disque
qm importdisk 1000 debian-10-openstack-amd64.qcow2 local-lvm
# Attachement du disque Ă  la VM
qm set 1000 -scsihw virtio-scsi-pci -virtio0 local-lvm:vm-1000-disk-0
# Ajout lecteur CD pour cloudinit
qm set 1000 -ide2 local-lvm:cloudinit
qm set 1000 -serial0 socket
# Boot sur disque principal
qm set 1000 -boot c -bootdisk virtio0
qm set 1000 -agent 1
qm set 1000 -hotplug disk,network,usb,memory,cpu
# 2 cores x1 sockets = 2 Vcpus
qm set 1000 -vcpus 2
qm set 1000 -vga qxl
# Copie clé SSH publique de l'hÎte proxmox-01
qm set 1000 --sshkey ~/.ssh/id_rsa.pub
# Passage en template
qm template 1000
# Passage taille du disque Ă  20Go et non plus 2Go
qm resize 1000 virtio0 +18G

N’Ă©tant pas un pro des commandes qm, j’avoue que certaines me sont encore un peu obscures, dĂ©solĂ© 😅

A partir de lĂ , on est bon !

Concernant la configuration IP, le user/password etc ce sera fait via notre script Terraform.

III) Installation et création du script Terraform

Ici je ne vais pas trop m’Ă©tendre sur ce qu’est Terraform et sur son installation complĂšte et dĂ©taillĂ©e, sachez simplement que ce produit vient aussi d’Hashicorp, la mĂȘme sociĂ©tĂ© qui propose Vagrant, et grosso modo :

  • Vagrant, permet de dĂ©ployer une infra mais orientĂ©e pour le dĂ©veloppement et non la production ;
  • Terraform, permet de dĂ©ployer une infra mais orientĂ©e vers le Cloud et la production ;

Ici c’est du on-premise mais Ă©tant donnĂ© que je n’avais jamais testĂ© Terraform auparavant, je me suis dit go for it 😛

Donc, pour l’installer :

sudo apt-get install unzip
wget https://releases.hashicorp.com/terraform/0.12.18/terraform_0.12.18_linux_amd64.zip
unzip terraform_0.12.25_linux_amd64.zip
sudo mv terraform /usr/local/bin/
terraform --version 

Vérifiez bien entendu la version à télécharger pour votre wget, et le tour est joué !

Ensuite, comme pour Vagrant, Terraform utilise des Providers pour dĂ©ployer ses vm/conteneurs. Ici, on va installer manuellement le provider Proxmox car par dĂ©faut il n’est pas inclus.

La premiĂšre Ă©tape est donc d’installer le langage Go (ici je suis sur une Ubuntu 20.04, Ă  vous d’adapter selon votre OS) :

sudo apt install golang

Et lĂ  on peut installer notre provider Proxmox (l’installation peut prendre plusieurs minutes) :

go get -v github.com/Telmate/terraform-provider-proxmox/cmd/terraform-provider-proxmox
go get -v github.com/Telmate/terraform-provider-proxmox/cmd/terraform-provisioner-proxmox
go install -v github.com/Telmate/terraform-provider-proxmox/cmd/terraform-provider-proxmox
go install -v github.com/Telmate/terraform-provider-proxmox/cmd/terraform-provisioner-proxmox
sudo cp terraform-provider-proxmox /usr/local/bin/
sudo cp terraform-provisioner-proxmox /usr/local/bin/

Il faudra simplement rĂ©aliser un terraform init pour “reload” le tout, et prendre en charge nos deux nouveaux plugins.

Bien, une fois fait on peut se placer dans un dossier prĂ©vu Ă  cet effet et crĂ©er notre fichier main.tf qui contiendra donc toutes les instructions pour dĂ©ployer nos VMs. Ici aussi, je ne vais pas dĂ©tailler l’entiĂ©retĂ© du script car ce serait trop long, le but de l’article est d’avant tout dĂ©ployer un cluster k8s je vous rappelle 😅

Soit, voici notre script :

# Script permettant de déployer X VMs sur hÎte Proxmox via Cloud-Init & Terraform
# Mairien Anthony - Notamax
# Mis Ă  jour le 20-05-20
# DĂ©finition du provider (ici proxmox-01)
provider "proxmox" {
    pm_api_url = "https://proxmoxIP:8006/api2/json"
    pm_user = "root@pam"
    pm_password = "SuperPaSSw0rD"
    # Laisser à "true" si certificat auto-signé
    pm_tls_insecure = "true"
}

# Création variable pour nombre de VMs à déployer (récupéré via l'argument -var 'nombre=X')
variable "nombre" {
  type = number
}

# Définition de la VM à créer
resource "proxmox_vm_qemu" "proxmox_vm" {
  count             = var.nombre
  name              = "vm-0${count.index}"
  # Nom du node sur lequel le déploiement aura lieu
  target_node       = "pve-01"
  clone             = "debian-10-template"
  full_clone        = true
  os_type           = "cloud-init"
  cores             = 2
  sockets           = "1"
  cpu               = "host"
  memory            = 2048
  scsihw            = "virtio-scsi-pci"
  bootdisk          = "scsi0"
disk {
    id              = 0
    size            = 20
    type            = "scsi"
    storage         = "local-lvm"
    storage_type    = "lvm"
    iothread        = true
  }
network {
    id              = 0
    model           = "virtio"
    bridge          = "vmbr0"
  }

# Configuration relative Ă  CloudInit
  # Clé SSH publique
  sshkeys =  <<EOF
  MaSuperCléSSH
  EOF

  # Setup de l'IP statique
  ipconfig0 = "ip=192.168.1.10${count.index + 1}/24,gw=192.168.1.1"
  ciuser = "it-anthony"
  cipassword = "SuperPaSSw0rD"

  # Déclaration du script de démarrage, en utilisant user it-anthony + clé SSH privée
  provisioner "file" {
    source      = "~/Documents/Terraform/startup.sh"
    destination = "/tmp/startup.sh"
      connection {
      type     = "ssh"
      user     = "it-anthony"
      private_key     = "${file("~/.ssh/id_rsa")}"
      host     = "${self.ssh_host}"
   }
  }
  # Exécution du script de démarrage
  provisioner "remote-exec" {
    inline = [
      "chmod +x /tmp/startup.sh",
      "/tmp/startup.sh",
    ]
    connection {
      type     = "ssh"
      user     = "it-anthony"
      private_key     = "${file("~/.ssh/id_rsa")}"
      host     = "${self.ssh_host}"
   }
  }
}

Je pense que le tout est assez bien commentĂ©, et au passage, car je ne pense pas l’avoir dit en introduction, tous les scripts prĂ©sentĂ©s ici sont d’ores et dĂ©jĂ  disponibles sur mon Github, histoire de vous faciliter la vie :

https://github.com/IT-Anthony/terraform-deploy-vms

Bien, avant de dĂ©ployer notre infrastructure il convient de crĂ©er un petit script bash permettant de faire les traditonnels apt-get update/apt-get update ainsi que l’installation de Docker :

#!/bin/bash
# startup.sh
# Mairien Anthony - Notamax
# Mis Ă  jour le 20-05-20
# Les "sleep" sont obligatoires sinon délais trop courts et plantage

# Mise à jour des dépÎts & paquets
sleep 30 && sudo apt-get update && sleep 10 && sudo apt-get upgrade -y && sleep 30 && sudo apt-get dist-upgrade -y && sleep 30

# Installation dépendances pour Docker
sudo apt -y install software-properties-common apt-transport-https ca-certificates curl gnupg2 software-properties-common && sleep 30

# Ajout du dépÎt Docker et de la clé
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"

# Ajout du dépÎt Kubernetes et de la clé puis installation de kubelet/kube-adm/kubenetes-cni
sudo curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update && sleep 10 && sudo apt-get install -y kubelet kubeadm kubernetes-cni

# Seconde mise à jour des dépÎts & installation Docker + paquets de base + démarrage docker au boot
sleep 30 && sudo apt-get update && sleep 10 && sudo apt -y install docker-ce docker-ce-cli htop vim qemu-guest-agent && sudo systemctl enable docker && sudo systemctl start docker

Je sais je sais, on se passera de commentaires sur ce script basique en bash… l’important est que ça fonctionne haha.

Ensuite pour dĂ©ployer notre infrastructure, il ne nous reste plus qu’Ă  exĂ©cuter un terraform apply avec comme argument la variable nombre ainsi que le nombre de VMs Ă  crĂ©er :

terraform apply -var 'nombre=3'

On patiente un peu, et le tour est joué !

Tadaaaa ! Nous avons donc déployé 3 machines virtuelles basées sur Debian 10 avec une installation automatique des mises à jour ainsi que de Docker ! Pas mal non ?

Mais bref, passons enfin Ă  ce cluster Kubernetes…

IV) Création du cluster Kubernetes

Ici nous allons donc utiliser kubeadm, qui est un utilitaire permettant de dĂ©ployer rapidement et facilement son cluster. La premiĂšre Ă©tape est donc d’installer Docker sur chaque noeud et dĂ©sactiver la mĂ©moire Swap (Ă©tant donnĂ© que nous avons utilisĂ© une image OpenStack ainsi qu’un script shell plus haut, tout ça est dĂ©jĂ  fait).

Il faut simplement Ă©diter manuellement le fichier /etc/cloud/cloud.cfg de nos 3 VMs pour passer en commentaire le paramĂštre update_etc_hosts, et ensuite changer le hostname via un rapide hostnamectl set-hostname (on aurait pu l’automatiser ça aussi, mais bon…).

Une fois fait, nous obtenons la chose suivante :

  • master-01, en 192.168.1.101/24 ;
  • worker-01, en 192.168.1.102/24 :
  • worker-02, en 192.168.1.103/24 ;

On se connecte donc sur le master pour commencer et démarrer notre installation :

sudo kubeadm init --apiserver-advertise-address=192.168.1.101 --node-name $HOSTNAME --pod-network-cidr=10.244.0.0/16

Cette commande va donc permettre de renseigner l’adresse IP de notre master, puis de lui comme nom “master-01” via la variable $HOSTNAME et enfin la partie pod-network permet de spĂ©cifier Ă  K8S le rĂ©seau Ă  utiliser pour la gestion des pods. Ici nous allons utiliser Flannel pour la gestion du rĂ©seau, il convient donc d’utiliser le range 10.244.0.0/16 spĂ©cifiquement.

On patiente un peu, puis nous allons ensuite crĂ©er un fichier de configuration que nous utiliserons via l’utilitaire kubectl, et qui permet de gĂ©rer le cluster (on notera d’ailleurs que les commandes sont donnĂ©es par l’installateur lui-mĂȘme).

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

On se doit ensuite de modifier les paramÚtres bridge pour permettre à K8S de correctement utiliser le réseau de la machine (à réaliser sur nos trois VMs) :

sudo sysctl net.bridge.bridge-nf-call-iptables=1

Et enfin on déploie notre fameux pod Flannel qui servira à la gestion du réseau, comme dit précédemment :

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Promis, la partie “config bien longue” est presque terminĂ©e ! Il faut simplement exĂ©cuter une derniĂšre petite commande pour modifier l’adressage rĂ©seau de Flannel et Ă©viter des petits soucis par la suite :

kubectl edit cm -n kube-system kube-flannel-cfg

Cette commande nous ouvre l’Ă©diteur Vim et nous permet de modifier prĂ©cisĂ©ment ce paramĂštre, changez-le en 10.10.0.0/16 :

net-conf.json: |
{
 "Network": "10.0.0.0/16",
 "Backend": {
   "Type": "vxlan"
 }
}

Une fois fait, on peut d’ores et dĂ©jĂ  se connecter sur notre worker-01 et worker-02 et les faire rejoindre le cluster :

sudo kubeadm join 192.168.1.101:6443 --token gunt7b.xs5qti51ysbgizxb \
    --discovery-token-ca-cert-hash sha256:ba1269a6f0e2f007f2f06a912b372e20df8ac9180dba3390f2b3293af8cfd0f2 

Bien entendu cette commande sera diffĂ©rente de votre cĂŽtĂ©, dĂ©jĂ  au niveau du token mais mĂȘme au niveau de l’IP du master.

Si vous dĂ©sirez rajouter un node aprĂšs que le token ait expirĂ©, car il n’a une durĂ©e de validitĂ© que de quelques minutes, vous pouvez exĂ©cuter cette commande :

kubeadm token create --print-join-command

Ensuite on peut rĂ©aliser le classique kubectl get nodes pour voir les membres du cluster, et mĂȘme rajouter un label “worker” Ă  nos deux workers justement via la commande kubectl lable node worker-01 node-role.kubernetes.io/worker=worker :

Bien, nos membres sont donc tous ready ! On va pouvoir dĂ©ployer un rapide conteneur Nginx puis nous l’exposerons en dehors du cluster pour vĂ©rifier que tout soit ok.

V) Conteneur Nginx pour tests et vérificaton

Pour crĂ©er notre conteneur Nginx et l’exposer en dehors du cluster, rien de plus simple !

kubectl create deployment web-01 --image=nginx

Ici on crĂ©er donc un deployment nommĂ© web-01 et basĂ© sur l’image Nginx.

On peut vĂ©rifier qu’il est bien en status Running via la commande kubectl get pods :

Niquel, on poursuit !

Ensuite on va crĂ©er un service de type NodePort et qui nous permettra donc de toucher le Nginx depuis l’extĂ©rieur du cluster :

kubectl create service nodeport web-01 --tcp=80:80

Ici le port 80 pointe sur le port 80 du conteneur, mais on aurait trĂšs bien pu prendre un port alĂ©atoire de l’hĂŽte, le principe est le mĂȘme.

Un petit kubectl get svc et on peut voir que notre service a bien un port-forwarding d’actif :

Et ensuite, si l’on se rend sur l’adresse IP du node sur lequel tourne notre conteneur, en rajoutant le port 32629 on devrait arriver sur notre serveur web Nginx :

It works ! D’ailleurs si vous aviez par exemple 200 nodes, il est bon de savoir sur quelle node notre pod a Ă©tĂ© dĂ©ployĂ©… pour cela on peut utiliser la commande kubectl describe pods web-01 :

On cherche ensuite la ligne Node: xxx et c’est tout bon.

VI) Conclusion

Et bien c’est dĂ©jĂ  la fin de cet article, qui aura Ă©tĂ© bien plus long Ă  rĂ©diger que ce que je ne pensais… de base je pensais simplement Ă  dĂ©ployer un petit cluster sur mon Proxmox comme dit en dĂ©but d’article, puis j’ai eut l’idĂ©e de vouloir “un peu automatiser” le tout, mais dĂ©couvrant tout juste Terraform/CloudInit et mĂȘme Kubernetes en gĂ©nĂ©ral, j’ai dĂ» faire face Ă  plusieurs galĂšres… bon le rĂ©sultat est bien lĂ , les scripts sont fonctionnels et le cluster aussi, mais il est clair que tout ceci peut ĂȘtre largement amĂ©liorĂ©, c’est avant tout un lab donc le but est bel et bien d’expĂ©rimenter, alors ne me jugez pas trop sĂ©vĂšrement haha 😅

J’espĂšre tout de mĂȘme vous avoir appris quelques bricoles, et je vous souhaite une bonne journĂ©e/soirĂ©e !

10 comments

comments user
antanof

Cool merci. Apparemment il n’est pas encore conseillĂ© d’utiliser le provider proxmox en prod. Pourquoi tu ne mets pas tes scripts et commandes directement dans le cloud-init.cfg ?

    comments user
    Mairien Anthony

    Merci Ă  toi d’avoir pris le temps de lire ! Pour la prod difficile de rĂ©pondre mĂȘme si tu as sĂ»rement raison, pour ma part j’ai trouvĂ© ça assez stable mais comme dit en dĂ©but/fin d’article c’Ă©tait plus une dĂ©couverte qu’autre chose.

    Concernant les scripts bonne question, je t’avoue que j’ai un peu fait au feeling car je dĂ©couvre tout juste CloudInit & Terraform 🙂

comments user
antanof

Je vais essayer de prendre le temps de tester ce provider. Pour cloud-init, si tu veux j’ai fait une conf automatisĂ©e que j’ai Ă©cris dans un article https://www.tekarena.fr/2020/installer-nextcloud-sur-fedora-servers-cloud-grace-a-terraform-et-ansible-part1
@+

    comments user
    Mairien Anthony

    Sympatoche antanof, je vais checker ça 😉

comments user
Seboss666

Hello,

J’ai eu un petit souci avec Proxmox 6.2 et le type de disque dĂ©clarĂ© dans le code, il faut remplacer le bootdisk et disk_type pour dire que c’est du virtio :

bootdisk = “virtio0”
disk {
id = 0
size = 40
type = “virtio”
storage = “local-lvm”
storage_type = “lvm”
iothread = true
}

En dehors de ça la machine est fonctionnelle en mĂȘme pas 2 minutes, c’est gĂ©nial merci. Perso je vais faire l’install de k3s avec Ansible ::)

    comments user
    Mairien Anthony

    Ah je ne savais pas pour Proxmox 6.2, je vais tester de mon cĂŽtĂ© tiens 😯

    Puis de rien, c’est vrai qu’Ansible est sacrĂ©ment puissant aussi comme outil, lĂ  je suis en mode un peu “touche-Ă -tout” donc je teste pas mal d’outils en vrac haha.

    P.S: Étant un grand lecteur de ton blog, ça me fait extrĂȘment plaisir de te voir apparaĂźtre ici, sur le mien ! Bonne continuation Ă  toi ! 😄

comments user
alex

Salut,

merci beaucoup pour le tuto. De mon cotĂ©, j’ai Ă©normĂ©ment galĂ©rĂ© sur la crĂ©ation du template de VM sur une base debian 10/proxmox 6.3.6. Voici comment j’ai du adapter le script pour qu’il fonctionne (modification sur nom du storage car ce n’Ă©tait pas le mĂȘme nom que le mien, modification de la ligne d’affectation du disque car elle ne fonctionnait pas, avancĂ©e de la partie resize sinon elle ne fonctionnait pas non plus) :
#!/bin/bash
vm_id=1000
storage_name=”local”

# CrĂ©ation d’une VM avec ID $vm_id nommĂ©e debian-10-template, 2Go RAM, 1 proco, 2 coeurs
qm create $vm_id -name debian-10-template -memory 4096 -net0 virtio,bridge=vmbr1 -cores 2 -sockets 1 -cpu cputype=kvm64 -kvm 1 -numa 1
# Import du disque
qm importdisk $vm_id debian-10-openstack-amd64.qcow2 $storage_name
# Attachement du disque Ă  la VM
qm set $vm_id -scsihw virtio-scsi-pci -virtio0 $storage_name:$vm_id/vm-$vm_id-disk-0.raw
# Passage taille du disque Ă  20Go et non plus 2Go
qm resize $vm_id virtio0 +18G
# Ajout lecteur CD pour cloudinit
qm set $vm_id -ide2 $storage_name:cloudinit
qm set $vm_id -serial0 socket
# Boot sur disque principal
qm set $vm_id -boot c -bootdisk virtio0
qm set $vm_id -agent 1
qm set $vm_id -hotplug disk,network,usb,memory,cpu
# 2 cores x1 sockets = 2 Vcpus
qm set $vm_id -vcpus 2
qm set $vm_id -vga qxl
# Copie clĂ© SSH publique de l’hĂŽte proxmox-01
qm set $vm_id –sshkey ~/.ssh/id_rsa.pub
# Passage en template
qm template $vm_id

    comments user
    Mairien Anthony

    Merci Ă  toi alex ! Je pense que je vais devoir remettre Ă  jour cet article, car ça a l’air d’avoir pas mal bougĂ© avec les derniĂšres versions…

    Bonne continuation Ă  toi !

comments user
junior

slt tres bon tuto,

j’aimerai savoir si tu connais un orchastration du genre cloudstack ou openstack qui peut utiliser les hypervisor proxmox ?

    comments user
    Mairien Anthony

    Oulah bonne question junior haha, je t’avoue que tout ce qui est conteneurs/orchestration j’ai un peu mis de cĂŽtĂ© depuis plusieurs mois dĂ» Ă  mon nouveau job, mais je vais essayer de m’y pencher un peu plus dans les mois qui viennent 😉

    Merci de ton commentaire tout de mĂȘme !

Laisser un commentaire

You May Have Missed