Klaster Kubernetes na maszynach wirtualnych Proxmox VE

Opis Kubernetes

Kubernetes to platforma orkiestracji kontenerów typu open source opracowana przez Google. Automatyzuje wdrażanie, skalowanie i zarządzanie aplikacjami w kontenerach. Celem Kubernetes jest uproszczenie zarządzania złożonymi, rozproszonymi systemami poprzez zapewnienie niezawodnej struktury do automatyzacji wdrażania, skalowania i operacji kontenerowych aplikacji w klastrach hostów. Dzięki Kubernetes możesz wdrażać i zarządzać kontenerami, które są lekkimi, izolowanymi środowiskami, które pakują aplikację wraz z jej zależnościami. Kubernetes zapewnia sposób na deklaratywne zdefiniowanie pożądanego stanu aplikacji i dba o koordynację wdrażania i skalowania kontenerów w celu spełnienia tego pożądanego stanu. Zapewnia, że kontenery są zawsze uruchomione, mogą być skalowane w górę lub w dół w zależności od zapotrzebowania i mogą przywracać działanie po awariach. Kubernetes wykorzystuje architekturę master-worker. Węzeł główny działa jako płaszczyzna kontrolna, zarządzając całym klastrem i podejmując decyzje dotyczące pożądanego stanu systemu. Węzły robocze, zwane również sługami lub maszynami roboczymi, są odpowiedzialne za uruchamianie kontenerów i wykonywanie zadań przydzielonych im przez mistrza.

Przydatne adresy

https://kubernetes.io/docs/setup/production-environment/container-runtimes/
https://kubernetes.io/docs/reference/kubectl/cheatsheet/
https://www.ibm.com/cloud/blog/8-kubernetes-tips-and-tricks
https://computingforgeeks.com/install-kubernetes-cluster-ubuntu-jammy/

Przygotowanie maszyn wirtualnych Proxmox VE na potrzeby klastra Kubernetes

Wykorzystanie gotowego szablonu maszyny wirtualnej z Ubuntu 22.04

Do szybkiego przygotowania 4 identycznych maszyn z systemem Ubuntu 22.04 wykorzystałem gotowy szablon Proxmox VE przygotowany wg. opisu na stronie https://itadmin.vblog.ovh/proxmox-ve-stworzenie-i-wykorzystanie-szablonu-systemu-ubuntu-22-04/. Klonujemy ten szablon ubuntu-2204-template do 4 maszyn wirtualnych wg. rozpiski w poniższej tabeli.

LPNazwaAdres ipvCPUvRAMvHDDOpis
1k8s-ctrl192.168.30.2024 GB16 GBMaster Node
2k8s-node-01192.168.30.2124 GB16 GBWorker Node 1
3k8s-node-02192.168.30.2224 GB16 GBWorker Node 2
4k8s-node-03192.168.30.2324 GB16 GBWorker Node 3

Ustawienie stałej adresacji sieciowej na każdej z maszyn wirtualnych w klastrze

Na każdej z maszyn po zalogowaniu ustawiamy odpowiednią adresację wg. wzoru w tabeli powyżej zmieniając jedynie opcję addresses od 192.168.30.20 do 192.168.30.23

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    version: 2
    ethernets:
        eth0:
            dhcp4: false
            dhcp6: false
            addresses: [192.168.30.20/24]
            routes:
            - to: default
              via: 192.168.30.1
            nameservers:
             addresses: [192.168.8.1,8.8.8.8]
sudo nano /etc/netplan/50-cloud-init.yaml
sudo netplan apply

Wyłączenie swapa na wszystkich maszynach wirtualnych wchodzących w skład klasta Kubernetes

Na wszystkich serwerach wchodzących w skład Kubernetes nie może być obecny swap.

ważne aby nie skasować danych !!!!

asdsdasdasdasdsadasd

Należy go usunąć jeśli istnieje w systemie np. korzystając z opisu na stronie https://itadmin.vblog.ovh/konfiguracja-i-konserwacja-systemu-linux-ubuntu/#Ustawienia_swap_w_systemie_Ubuntu

Instalacja i konfiguracja klastra Kubernetes na maszynach wirtualnych Proxmox VE

Instalacja usługi containerd (na wszystkich maszynach wirtualnych w klastrze)

sudo apt install containerd
# weryfikacja działania usługi
systemctl status containerd

Konfiguracja usługi containerd (na wszystkich maszynach wirtualnych w klastrze)

sudo mkdir /etc/containerd
linuser@k8s-ctrl:~$ containerd config default | sudo tee /etc/containerd/config.toml
sudo nano -c /etc/containerd/config.toml

szukamy odpowiedniej linii CTRL+W : runc.options (u mnie jest to linia nr 125) i zmieniamy wartość opcji SystemdCgroup z false na true

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
BinaryName = ""
CriuImagePath = ""
CriuPath = ""
CriuWorkPath = ""
IoGid = 0
IoUid = 0
NoNewKeyring = false
NoPivotRoot = false
Root = ""
ShimCgroup = ""
SystemdCgroup = true

Włączenie forwardingu dla ipv4 (na wszystkich maszynach wirtualnych w klastrze)

sudo nano -c /etc/sysctl.conf
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

Włączenie modułu do tworzenia mostków sieciowych  (na wszystkich maszynach wirtualnych w klastrze)

sudo nano -c /etc/modules-load.d/k8s.conf
br_netfilter
# restart wszystkich maszyn wirtualnych
sudo reboot

Dodanie repozytorium Kubernetes

sudo apt install curl apt-transport-https vim git -y
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg|sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/k8s.gpg
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 /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt install curl apt-transport-https vim git -y
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg|sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/k8s.gpg
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 /etc/apt/sources.list.d/kubernetes.list
sudo apt update

Instalacja oprogramowania Kubernetes (na wszystkich maszynach wirtualnych w klastrze)

sudo apt install kubelet kubeadm kubectl -y
sudo apt-mark hold kubelet kubeadm kubectl

Inicjalizacja klastra (tylko na maszynie k8s-ctrl)

sudo kubeadm init --control-plane-endpoint=192.168.30.20 --node-name k8s-ctrl --pod-network-cidr=10.244.0.0/16

Po dłuższej chwili otrzymamy potwierdzenie utworzenia klastra oraz zestaw komend potrzebnych do dołączenia kolejnych nodów do klastra (dobrze jest sobie zapisać to pliku tekstowym na przyszłość)

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

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

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join 192.168.30.20:6443 --token cdcrpn.edml213231264x \
        --discovery-token-ca-cert-hash sha256:77ef996c7af12312312312123213213123123ef806bb4ef \
        --control-plane

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.30.20:6443 --token cdcrpn.edmlts21312321312r64x \
        --discovery-token-ca-cert-hash sha256:77ef23231231231232312231232131a5897507ef806bb4ef

Dodanie praw dla lokalnego użytkownika do zarządzania klastrem (tylko na maszynie k8s-ctrl)

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

Sprawdzenie jakie pody obecnie występują na klastrze (tylko na maszynie k8s-ctrl)

kubectl get pods --all-namespaces
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-5d78c9869d-dcvsn           0/1     Pending   0          11m
kube-system   coredns-5d78c9869d-jf229           0/1     Pending   0          11m
kube-system   etcd-k8s-ctrl                      1/1     Running   0          11m
kube-system   kube-apiserver-k8s-ctrl            1/1     Running   0          11m
kube-system   kube-controller-manager-k8s-ctrl   1/1     Running   0          11m
kube-system   kube-proxy-kz6pn                   1/1     Running   0          11m
kube-system   kube-scheduler-k8s-ctrl            1/1     Running   0          11m
kubectl get pods --all-namespaces
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-5d78c9869d-dcvsn           0/1     Pending   0          11m
kube-system   coredns-5d78c9869d-jf229           0/1     Pending   0          11m
kube-system   etcd-k8s-ctrl                      1/1     Running   0          11m
kube-system   kube-apiserver-k8s-ctrl            1/1     Running   0          11m
kube-system   kube-controller-manager-k8s-ctrl   1/1     Running   0          11m
kube-system   kube-proxy-kz6pn                   1/1     Running   0          11m
kube-system   kube-scheduler-k8s-ctrl            1/1     Running   0          11m

Instalacja overlay network aby pody coredns zaczęły działać poprawnie

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

Ponowne sprawdzenie jakie pody obecnie występują na klastrze (tylko na maszynie k8s-ctrl)

kubectl get pods --all-namespaces
NAMESPACE      NAME                               READY   STATUS    RESTARTS   AGE
kube-flannel   kube-flannel-ds-qvcff              1/1     Running   0          17s
kube-system    coredns-5d78c9869d-dcvsn           1/1     Running   0          16m
kube-system    coredns-5d78c9869d-jf229           1/1     Running   0          16m
kube-system    etcd-k8s-ctrl                      1/1     Running   0          16m
kube-system    kube-apiserver-k8s-ctrl            1/1     Running   0          16m
kube-system    kube-controller-manager-k8s-ctrl   1/1     Running   0          16m
kube-system    kube-proxy-kz6pn                   1/1     Running   0          16m
kube-system    kube-scheduler-k8s-ctrl            1/1     Running   0          16m

Dodanie kolejnych nodów do klastra Kubernetes

Jeśli ktoś używa rozwiązania OpenShift może utworzyć link symboliczny o nazwie oc do polecenia kubectl aby ułatwić sobie pracę z klastrem

sudo ln -s /usr/bin/kubectl /usr/bin/oc
# sprawdzenie jakie obecnie występują nody w klastrze
linuser@k8s-ctrl:~$ oc get nodes
NAME       STATUS   ROLES           AGE   VERSION
k8s-ctrl   Ready    control-plane   21m   v1.27.3

Na wszystkich maszynach wirtualnych, które chcemy dołączyć jako kolejne nody do klastra uruchamiamy skopiowane wcześniej do notatnika polecenie sudo kubeadm join (…)

linuser@k8s-node-03:~$ sudo kubeadm join 192.168.30.20:6443 --token cdcrpn.edmltsgn9ciur64x \
        --discovery-token-ca-cert-hash sha256:77ef996c7aff2efd8ee02f12312135dea3e2392123a5897507ef806bb4ef

[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 -o yaml'
[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.

Sprawdzamy na maszynie k8s-ctrl czy wszystkie nody dołączyły się poprawnie do klastra

linuser@k8s-ctrl:~$ kubectl get nodes

NAME          STATUS   ROLES           AGE     VERSION
k8s-ctrl      Ready    control-plane   29m     v1.27.3
k8s-node-01   Ready    <none>          2m49s   v1.27.3
k8s-node-02   Ready    <none>          2m23s   v1.27.3
k8s-node-03   Ready    <none>          2m15s   v1.27.3

Aby w przyszłości dodać kolejne nody można się posłużyć poleceniem na maszynie k8s-ctrl

kubeadm token create --print-join-command

kubeadm join 192.168.30.20:6443 --token 8sg60g.djwvjubf5gg2qs6b --discovery-token-ca-cert-hash sha256:77ef996c7aff2efdsadsdsade239224c1a5897507ef806bb4ef

Podpowiadanie parametrów polecenia kubectl

source <(kubectl completion bash) # set up autocomplete in bash into the current shell, bash-completion package should be installed first.
echo "source <(kubectl completion bash)" >> ~/.bashrc # add autocomplete permanently to your bash shell.

Uruchomienie pierwszej aplikacji i weryfikacja działania klastra Kubernetes

Z poziomu konsoli maszyny k8s-ctrl

nano /home/linuser/nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: nginx-example
 labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: linuxserver/nginx
      ports:
        - containerPort: 80
          name: "nginx-http"
kubectl apply -f /home/linuser/nginx-pod.yaml

Sprawdzamy czy pod aplikacji nginx-http się uruchomił i na jakim nodzie

kubectl get pods -o wide

NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-example 1/1 Running 0 5m22s 10.244.3.2 k8s-node-03 <none> <none>

Sprawdzamy komunikację z maszyny k8s-ctrl narzedziem curl

curl 10.244.3.2

<html>
        <head>
            <title>Welcome to our server</title>
            <style>
            body{
                font-family: Helvetica, Arial, sans-serif;
            }
            (...)
            <div class="message">
                <h1>Welcome to our server</h1>
                <p>The website is currently being setup under this address.</p>
                <p>For help and support, please contact: <a href="[email protected]">[email protected]</a></p>
            </div>
        </body>
    </html>

Aby zawartość poda aplikacji nginx była widoczna na zewnętrz z przeglądarki www należy utworzyć service (rutę).

nano /home/linuser/nginx_pod_service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-example
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      nodePort: 30080
      targetPort: nginx-http
  selector:
    app: nginx
kubectl apply -f /home/linuser/nginx_pod_service.yaml

Weryfikujemy czy utworzył się service (ruta)

kubectl get service

NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.96.0.1      <none>        443/TCP        75m
nginx-example   NodePort    10.96.186.70   <none>        80:30080/TCP   12m

Możemy teraz wejść na stronę naszej aplikacji nginx używając dowolnego adresu ip nodów klastra i portu 30080 np. http://192.168.30.21:30080/