Initieer een kubernetes cluster

In de vorige blog beschreef ik hoe ik de infrastructuur heb opgebouwd. Nu is het tijd voor kubernetes. Een van de mooie dingen van kubernetes is de eenvoud en de duidelijke uitleg wat je moet doen. Maar waar je begint, dat is niet altijd duidelijk. Het is precies het eerste woord wat ik intikte: initieer. Init dus, en dat doe je als root (of met sudo) op de masternode of control plane. In mijn geval de kub0 node, en we geven een pod-network-cidr mee:

kubeadm init --pod-network-cidr=10.244.0.0/16

Vervolgens begint een redelijk lang proces, afhankelijk van de snelheid van je internetverbinding en infrastructuur. Als je het voor het eerst start kan het nogal overweldigend overkomen. Wat er gebeurt in het kort is dat de juiste containers worden opgehaald, certificaten worden gegenereerd en geïnstalleerd en een paar pods worden gemaakt. Dat laatste is niet onbelangrijk:

[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"

Deze pods heb je minstens nodig om tot een werkend cluster te komen. Elke pod heeft zijn eigen functie. Uiteindelijk komt de melding:

Your Kubernetes control-plane has initialized successfully!

Vervolgens staan er een aantal stappen vermeld die je moet uitvoeren. Controleer vooraf even of je sudo werkt. Zo nee, voegt jezelf dan toe aan de groep ‘wheel’. Login als gewone gebruiker en voer uit:

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

Om te controleren of het gelukt is voer je een ‘find ${HOME}/.kube -ls‘ uit en controleer je of de rechten goed staan. Dat is het geval als de bestanden en directories op jouw naam staan.

In de config-file staan certificaten waarmee je jezelf kan authenticeren aan de API. Die config-file noemen we een kubeconfig bestand. Wil je er meer over lezen, ga dan naar https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/

Voordat nodes toegevoegd kunnen worden zal een podnetwerk moeten worden ingezet; gedeployed in goed nederlands. Er zijn meerdere addons voor podnetwerken beschikbaar. In de output van kubeadm staat een link waar de belangrijkste addons beschreven staan. https://kubernetes.io/docs/concepts/cluster-administration/addons/

Als je net begint en niet zo goed weet welke je moet kiezen, dan is Weave, Calico of Flannel vaak gebruikt. Maar als je specifieke eisen of wensen hebt, kan je ook een andere kiezen. Ik ga voor Weave. Om die te installeren moet een yaml-file worden toegepast (apply):

kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

serviceaccount/weave-net created
clusterrole.rbac.authorization.k8s.io/weave-net created
clusterrolebinding.rbac.authorization.k8s.io/weave-net created
role.rbac.authorization.k8s.io/weave-net created
rolebinding.rbac.authorization.k8s.io/weave-net created
daemonset.apps/weave-net created

Die wijsheid is te vinden op: https://www.weave.works/docs/net/latest/kubernetes/kube-addon/

Vervolgens zullen we de nodes moeten toevoegen aan het cluster. We loggen in op kub1, kub2 en kub3 (de andere doe ik later). De laatste regels van ‘kubeadm init‘ geven weer wat je moet doen. Er wordt gesproken over een token. Dat token is 24 uur geldig, dus we hebben 24 uur de tijd om met dit token nodes toe te voegen.

[root@kubx ~]# kubeadm join 192.168.2.40:6443 --token eochqn.0qvtlsm9ckwx06zj \
                                              --discovery-token-ca-cert-hash sha256:a47932f14acbc8e0e4f3ea008614348b1015fb7afbd2d52bbf368b1f908e5223
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

Hierna zijn de nodes gekoppeld. Als we nu gaan kijken wat de status is, zoals hierboven ook aangegeven is, zien we dat de status nog niet meteen “Ready” is. Geen zorgen, na enige tijd komt dat goed. Bedenk dat kubeadm als root moet worden uitgevoerd, maar kubectl als user.

[user@kub0 ~]$ kubectl get nodes
NAME                  STATUS     ROLES    AGE   VERSION
kub0.heirbaut.local   Ready      master   23h   v1.19.0
kub1.heirbaut.local   Ready      <none>   22h   v1.19.0
kub2.heirbaut.local   NotReady   <none>   8s    v1.19.0
kub3.heirbaut.local   NotReady   <none>   7s    v1.19.0
[user@kub0 ~]$ kubectl get nodes
NAME                  STATUS     ROLES    AGE   VERSION
kub0.heirbaut.local   Ready      master   23h   v1.19.0
kub1.heirbaut.local   Ready      <none>   22h   v1.19.0
kub2.heirbaut.local   NotReady   <none>   28s   v1.19.0
kub3.heirbaut.local   NotReady   <none>   27s   v1.19.0
[user@kub0 ~]$ kubectl get nodes
NAME                  STATUS   ROLES    AGE     VERSION
kub0.heirbaut.local   Ready    master   23h     v1.19.0
kub1.heirbaut.local   Ready    <none>   22h     v1.19.0
kub2.heirbaut.local   Ready    <none>   2m51s   v1.19.0
kub3.heirbaut.local   Ready    <none>   2m50s   v1.19.0

Het belangrijkste commando wat we gebruiken is kubectl. Het is een interface naar de API-server; in feite spreek je via kubectl alleen de API-server aan. Kubectl zelf doet verder niet zo veel, behalve de syntax controleren en nog een paar zaken. Maar het echte werk wordt door de API-server gedaan. Omdat kubectl veel commandline opties heeft, is het soms lastig die allemaal te onthouden. Command completion is dan erg handig en gelukkig levert kubernetes dat allemaal mee. Met het commando kubeadm completion bash wordt de output gegenereerd om te gebruiken voor command completion in bash. Het kan voor meerdere shells gegenereerd worden; zie de output van kubeadm completion --help. De snelste manier om het te gebruiken is om de command completion te laden als je inlogt. Daarvoor kan je het beste een bestand zetten in de directory /etc/bash_completion.d

kubeadm completion bash > /etc/bash_completion.d/k8s.bash

Daarna kan je het inladen door uit te loggen en weer in te loggen, of eenmalig dat script uit te voeren:

source /etc/bash_completion.d/k8s.bash

Het cluster is nu klaar. Alleen staat er nog niet veel op.

CKA als doel: bouw een testomgeving

In een IT wereld waarin alles zo snel verandert is het lastig om bij te blijven. Om op alle vlakken bij te blijven is te veel van het goede dus ligt de keuze voor de hand om iets te kiezen wat bij je past, en waar behoefte aan is in de markt. Jaren geleden werd al geroepen dat ‘alles in containers komt’. Een concept waarin ik wel geloof. Gelukkig kreeg ik de kans om iets met Docker te gaan doen. Kubernetes. Ik had er van gehoord; ik zag een klok, geen klepel en laat staan dat er muziek kwam. het concept wilde niet landen. Nu leer ik ook beter door dingen te doen. Moeilijke taal lezen met onbekende concepten en hypewoorden, daar kan ik niet de concentratie voor vinden.

Intussen ben ik goed bekend met Docker. Ik kan mijn eigen container bouwen en begrijp de meeste concepten intussen wel. Goeroe? In het land der blinden is eenoog koning. Dus ja, waar ik werk ben ik Koning Guru. Maar ik als zelfbenoemde Koning Eenoog moet wel verder en heb ik besloten me te gaan certificeren voor CKA: Certified Kubernetes Administrator. Niet alleen omdat de markt daarom vraagt, maar ook om mezelf uit te dagen. Als extra uitdaging ga ik het hier ook nog eens documenteren. Zo word ik Koning Goeroe met een visie; kijkend in de spiegel.

Ik moet toegeven dat ik een enorme aversie heb tegen certificeren. Voor mij betekent het niet veel. Een certificaat hebben betekent nog niet dat je je werk goed kan doen. Het betekent alleen dat je je zenuwen in bedwang had en kennis A had op moment B. Veel admin-taken die je doet, doe je hooguit een paar keer per jaar. En dan nog is het vaak met Ansible geautomatiseerd. Vervolgens moet je je na 2 of 3 jaar opnieuw certificeren. Waarom? Commercie! het zou beter zijn als je elke 5 jaar je rijbewijs opnieuw moest halen. Maar goed, CKA, dat wordt de uitdaging.

Mijn testomgeving

Mijn testomgeving is vrij simpel. Ik heb een 6 jaar oude PC (i7, 16Gb) als server met Centos 7 en een recente Intel Nuc (i5, 32Gb) met Debian Buster. Beide zijn voorzien van libvirt KVM waarop de VM’s met kubernetes draaien. Alle VM’s zijn uitgerust met 2Gb geheugen, 2 vCPU’s en Centos 7. De VM’s worden ge-pxeboot en via een kickstart bestand geinstalleerd. De VM’s worden met de hand voorzien van een vast IP-adres en een hostname, maar met dnsmasq moet het ook te doen zijn. Alles zit op hetzelfde netwerk. Op zich allemaal niet zo veel bijzonders, gewoon een testomgeving om te oefenen voor mijn examen CKA. Ik heb er voor gekozen om 9 nodes (kub0 t/m kub8) te gebruiken. Maar 3 was ook voldoende. Omdat ik een ansible script heb om de hosts te configureren maakt het niet uit of ik 3, 9 of 27 hosts wil gebruiken. Bovendien, als Koning Eenoog wil ik een groot rijk! Dus 9 nodes…

Om de nodes te configureren is er uitstekende documentatie te vinden op https://kubernetes.io/docs/home/ . Maar er zit ook een nadeel aan. Kubernetes ontwikkelt zich snel. Tegen de tijd dat je de documentatie leest, is het weer vernieuwd en veranderd. Maar door te lezen op ‘het internet’ vond ik uit dat mijn hosts de volgende wijzigingen moest hebben in de configuratie om als kubernetesnode te kunnen werken. Misschien heb jij wat extra wijzigingen nodig.

Kub0 wordt gebruikt als admin node (Control Plane). The rest zijn kubelets.

De Configuratie

De configuratie op de VM’s moet als volgt worden aangepast:

  • Installatie van de docker repository
  • Installatie van de Kubernetes repository
  • Installatie van kubeadm, docker, chrony, bash-completion en enkele extra packages als je wil.
  • Zorg dat docker en kubelet gestart worden op alle hosts. Chronyd moet ook worden gestart omdat alle nodes in tijd synchroon moeten lopen.
  • Ik wil geen firewall, dus firewalld zet ik uit. Misschien een minder goed idee voor een productieomgeving.
  • Kubernetes houdt niet van swap, dus disable swap en verwijder swap van /etc/fstab
  • Maak een daemon.json file
  • Herstart de docker daemon.

De daemon.json file die ik gebruik is als volgt:

{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}

Dit is omdat Centos 7 gebruik maakt van cgroups waarvan systemd de eigenaar is. Voor die tijd had docker zijn eigen cgroupfs-driver. Als je dit niet wijzigt zal docker niet opstarten. Wil je er meer over lezen: https://github.com/kubernetes/kubeadm/issues/1394

Hoef je maar drie hosts te configureren, dan red je je prima met linker en rechterhand. Een vierde wordt vervelend en de negende komt je de neus uit. Dus: automatiseren. In mijn Koninkrijk ben ik niet alleen de goeroe op K8S, maar ook op Ansible. Dus schreef ik een fantastisch script dat alle hosts voor mij configureert. Het is fantastisch omdat het goed genoeg werkt voor mij. Maar in een ander Koninkrijk zijn vast betere script. Er zit ook geen enkele logica in; het alleen een sequentiële uitvoering van een setje commando’s.

---
- hosts: kubernetes
  remote_user: root
  tasks:
  - name: Add repositories for Docker
    yum_repository:
      name: docker
      description: Official Docker repository
      baseurl: https://download.docker.com/linux/centos/7/$basearch/stable
      gpgcheck: yes
      gpgkey: https://download.docker.com/linux/centos/gpg

  - name: Add repositories for Kubernetes
    yum_repository:
      name: Kubernetes
      description: Official Kubernetes repository
      baseurl: https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
      gpgcheck: yes
      gpgkey: https://packages.cloud.google.com/yum/doc/yum-key.gpg
              https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg

  - name: Check if yum-utils and bash-completion are installed
    yum:
      name:
        - firewalld
        - bash-completion
        - yum-utils
        - docker-ce-18.06.3.ce-3.el7.x86_64
        - chrony
        - kubeadm
        - nfs-utils

  - name: Update to latest level
    yum:
      name=* state=latest

  - name: Make sure services are enabled/started
    service:
      name: "{{ item }}"
      enabled: yes
      state: started
    with_items:
      - docker
      - kubelet
      - chronyd

  - name: Make sure services are disabled/started
    service:
      name: "{{ item }}"
      enabled: no
      state: stopped
    with_items:
      - firewalld

  - name: Disable SWAP since kubernetes can't work with swap enabled (1/2)
    shell: |
      swapoff -a
#    when: kubernetes_installed is changed

  - name: Disable SWAP in fstab since kubernetes can't work with swap enabled (2/2)
    replace:
      path: /etc/fstab
      regexp: '^(.*?\sswap\s+swap.*)$'
      replace: '# \1'
#    when: kubernetes_installed is changed

  - name: Deploy Docker daemon.json.
    copy:
      src: files/daemon.json
      dest: /etc/docker/daemon.json


  - name: restart service docker, in all cases, also issue daemon-reload to pick up config changes
    systemd:
      state: restarted
      daemon_reload: yes
      name: docker

Maar het werkt!

Nu ben ik klaar om met het configureren van Kubernetes te starten.