In einem der Letzten Artikel habe ich bereits gezeigt, wie man sich ein kleines Setup mit Minikube realisiert. Heute will ich euch zeigen, wie ein Kubernetes Cluster bestehend aus drei CentOS Maschinen erstellt wird.
Inhaltsverzeichnis
Für die Installation des Cluster habe ich die drei Maschinen mit einem aktuellen CentOS 7 installiert und statische IPs sowie Hostnamen auf den Systemen konfiguriert.
- k8s-master: 192.168.10.200
- k8s-node1: 192.168.10.201
- k8s-node2: 192.168.10.202
Installation von Kubernetes
Die folgenden Schritte müssen auf allen drei Maschinen durchgeführt werden.
/etc/hosts anpassen
Als erstes müssen wir sichergehen, dass die drei Systeme sich gegenseitig unter ihrem DNS Namen erreichen können. Dies kann man zum Beispiel dadurch lösen, dass man einen eigenen DNS Server mit Bind in seinem Netzwerk betreibt.
Will man das nicht oder will sich nur ein einfaches Testsetup aufbauen, können wir die drei Adresse auch einfach direkt auf den Systemen hinterlegen. Dazu öffnen wir mit einen Editor die Datei /etc/hosts und fügen die folgenden Zeilen ein:
1 2 3 | 192.168.10.200 k8s-master 192.168.10.201 k8s-node1 192.168.10.202 k8s-node2 |
Um das ganze zu testen, können wir von jedem System die jeweils anderen beiden versuchen mit einem Ping zu erreichen. Die Befehle für den k8s-master sehen dabei zum Beispiel wie folgt aus:
1 2 | ping k8s-node1 ping k8s-node2 |
SELinux konfigurieren
Da ich mich hier nicht weiter mit SELinux in diesem Tutorial auseinandersetzen will, schränke ich die Funktion von SELinux ein. Ich persönlich mache das, indem ich SELinux in den permissive Mode setzte. Falls man später dann doch mal SELinux aktivieren will macht das weniger Probleme. In einem Testsetup kann man es aber auch einfach auf disabled setzen.
Um die Einstellung entsprechend anzupassen, öffnen wir die Datei /etc/selinux/config mit einem Editor und ändern die Zeile SELINUX=enforcing auf die gewünschte Einstellung. Die Datei sieht bei mir dann wie folgt aus:
1 2 3 4 5 6 7 8 9 10 11 | # This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. SELINUX=permissive # SELINUXTYPE= can take one of three two values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted |
Anschließend muss das System einmal neugestartet werden.
br_netfilter Kernel Module aktivieren
Dieses Kernel-Modul muss aktiviert werden, damit die Pakete, welche die Network-Bridge durchlaufen, von iptables verarbeitet werden können und die Kubernetes Pods im gesamten Cluster ohne Probleme miteinander kommunizieren können.
Zum Aktivieren des Kernel Modules kann der folgende Befehl verwendet werden:
1 2 | modprobe br_netfilter echo '1' > /proc/sys/net/bridge/bridge-nf-call-iptables |
SWAP deaktivieren
Damit Kubernetes zur Laufzeit keine Probleme bekommt, müssen wir auf allen System den SWAP Speicher deaktivieren. Dazu kann der aktuelle Befehl verwendet werden:
1 | swapoff -a |
Damit der SWAP Speicher auch nach dem Neustart deaktiviert ist, sollte dieser in der fstab Datei auskommentiert werden. Dazu öffnet man die Datei /etc/fstab mit einem Editor und und kommentiert die Zeile mit SWAP aus. Der Eintrag sollte ungefähr so aussehen:
1 | #/dev/mapper/cl-swap swap swap defaults 0 0 |
Installation Docker CE
Bevor die Installation von Docker CE durchgeführt werden kann, müssen ein paar Pakete installiert werden. Dazu verwenden wir den folgenden Befehl:
1 | yum install -y yum-utils device-mapper-persistent-data lvm2 |
Anschließend fügt man das Docker Repository auf dem Systemen hinzu und installiert das Docker Paket
1 2 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce |
Nach der Installation wird Docker gestartet und dem Autostart hinzugefügt.
1 | systemctl start docker && systemctl enable docker |
Installation Kubernetes
Um nun die Komponenten für Kubernetes zu installieren, müssen wir als erstes das passende Repository auf den Systemen hinterlegen. Dafür können wir den folgenden Befehl in unser Terminal kopieren.
1 2 3 4 5 6 7 8 9 10 | cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg EOF |
Jetzt können die drei Pakete kubelet, kubeadm und kubectl installiert werden.
1 | yum install -y kubelet kubeadm kubectl |
Nun müssen wir noch kubelete starten und dem Autostart hinzufügen.
1 | systemctl start kubelet && systemctl enable kubelet |
Zum Testen ob Docker und kubelet starten, sollten die Systeme jetzt alle neugestartet werden.
cgroup Konfiguration
Für einen reibungslosen Betrieb des Cluster müssen wir sichergehen, dass Docker und kubelet dieselbe cgroup verwenden. Dafür müssen wir als erstes herausfinden, welche cgroup Docker verwendet. Dazu können wir den folgenden Befehl verwenden:
1 2 3 | docker info | grep -i cgroup Cgroup Driver: cgroupfs |
In der Ausgabe können wir sehen, das cgroupfs verwendet wird. Diese Einstellung setzen wir nun mit sed auch für Kubernetes.
1 | sed -i 's/cgroup-driver=systemd/cgroup-driver=cgroupfs/g' /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf |
Anschließend muss systemd und kubelet neugestartet werden.
1 2 | systemctl daemon-reload systemctl restart kubelet |
Kubernetes Cluster Konfiguration
Wir wollen nun das Cluster auf dem Master initialisieren (alle Befehle werden nun nur noch auf dem k8s-master ausgeführt). Dafür verwenden wir den Befehl kubeadm init. Dazu kommen noch zwei Parameter die ich vorher kurz erläutern will.
–apiserver-advertise-addres: Mit dieser Einstellung legen wir fest, unter welcher IP Adresse die Kubernetes API zur Verfügung gestellt werden soll. Normalerweise verwendet man dafür die IP Adresse des Masters.
–pod-network-cidr: Dieser Parameter dient zur Konfiguration des Pod Netzwerkes. Man gibt hier eine IP Adresse Range mit Subnetzmaske an.
Bei mir sieht der fertige Befehl wie folgt aus:
1 | kubeadm init --apiserver-advertise-address=192.168.10.200 --pod-network-cidr=10.13.0.0/16 |
Bei der Ausgabe sollte man sich die letzten Zeilen in eine Texteditor speichern, da man diese noch gebrauchen kann.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | kubeadm init --apiserver-advertise-address=192.168.10.200 --pod-network-cidr=10.13.0.0/16 W0728 10:12:10.030243 1493 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io] [init] Using Kubernetes version: v1.18.6 .... 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 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/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.10.200:6443 --token hb7xqv.neqox2ozk3qbtzxj \ --discovery-token-ca-cert-hash sha256:3b46bbd3da96870b7fc64d9b29500adc6b7e1a1b1fb5cfd0afa8f305cc7201f8 |
Wie am Ende beschrieben, legen wir uns jetzt eine .kube Konfiguration an. Dies sollte man als nicht root User machen.
1 2 3 | mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config |
Nun wird noch das flannel network auf dem Cluster deployed:
1 | kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml |
Jetzt müssen wir einige Minuten warten, da im Cluster u.A. Pods gestartet werden. Ist also genug Zeit für einen neuen Kaffee oder Tee.
Mit einem neuen Getränk auf dem Tisch prüfen wir als erstes mit dem Befehl kubectl get nodes ob der Master in der Node Übersicht angezeigt wird.
1 2 3 | kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready master 16m v1.18.6 |
Wenn bis jetzt alles geklappt hat, sollte der Status der k8s-master Node Ready sein.
Auch die ersten Pods sollten bereits laufen. Dies kann mit dem Befehl kubectl get pods –all-namespaces geprüft werden und sollte ungefähr so aussehen:
1 2 3 4 5 6 7 8 9 10 | kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-66bff467f8-rnpj7 1/1 Running 0 21m kube-system coredns-66bff467f8-tmmkz 1/1 Running 0 21m kube-system etcd-k8s-master 1/1 Running 1 21m kube-system kube-apiserver-k8s-master 1/1 Running 1 21m kube-system kube-controller-manager-k8s-master 1/1 Running 3 21m kube-system kube-flannel-ds-amd64-lkdwl 1/1 Running 1 14m kube-system kube-proxy-s698h 1/1 Running 1 21m kube-system kube-scheduler-k8s-master 1/1 Running 2 21m |
Damit ist das Kubernetes Cluster initialisiert. Als nächstes können die beiden Nodes in das Cluster aufgenommen werden.
Hinzufügen der Cluster Nodes
Das einbinden der anderen beiden Nodes ist relative einfach. Dazu verbinden wir uns auf k8s-node1 und k8s-node2 und führen den Befehl aus, den wir bei der Initialisierung des Clusters in der Textdatei gesichert haben.
Für k8s-node2 sieht das ganze bei mir zum Beispiel so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | kubeadm join 192.168.10.200:6443 --token hb7xqv.neqox2ozk3qbtzxj \ > --discovery-token-ca-cert-hash sha256:3b46bbd3da96870b7fc64d9b29500adc6b7e1a1b1fb5cfd0afa8f305cc7201f8 W0728 10:46:42.221942 4436 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set. [preflight] Running pre-flight checks [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/ [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] Downloading configuration for the kubelet from the "kubelet-config-1.18" ConfigMap in the kube-system namespace [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. |
Jetzt können wir noch mal die Cluster Nodes prüfen.
1 2 3 4 5 | kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready master 33m v1.18.6 k8s-node1 Ready <none> 80s v1.18.6 k8s-node2 Ready <none> 59s v1.18.6 |
Wie man auf dem Terminal nun sehen kann, sind k8s-node1 und k8s-node2 erfolgreich dem Cluster hinzugefügt.
Auch die Liste mit Pods ist länger geworden, da es einige Pods gibt, wo auf jeder Node eine Instanz laufen muss:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | kubectl get pods --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-66bff467f8-rnpj7 1/1 Running 0 33m kube-system coredns-66bff467f8-tmmkz 1/1 Running 0 33m kube-system etcd-k8s-master 1/1 Running 1 33m kube-system kube-apiserver-k8s-master 1/1 Running 1 33m kube-system kube-controller-manager-k8s-master 1/1 Running 3 33m kube-system kube-flannel-ds-amd64-lkdwl 1/1 Running 1 26m kube-system kube-flannel-ds-amd64-ppzsc 1/1 Running 2 2m10s kube-system kube-flannel-ds-amd64-wcc8b 1/1 Running 0 109s kube-system kube-proxy-dhrdl 1/1 Running 0 2m10s kube-system kube-proxy-k5w4z 1/1 Running 0 109s kube-system kube-proxy-s698h 1/1 Running 1 33m kube-system kube-scheduler-k8s-master 1/1 Running 2 33m |
Nun wollen wir im letzten Abschnitt noch einen ersten eigenen Pod deployen.
Testen mit dem nginx Pod
Nachdem das Cluster nun erfolgreich eingerichtet ist, soll natürlich auf dem Cluster auch eine Applikation laufen. Zum Testen bietet sich hier nginx an, weil es recht unkompliziert funktioniert.
Als erstes verbinden wir uns mit dem k8s-master. Hier legen wir nun ein nginx Deployment an.
1 2 | kubectl create deployment nginx --image=nginx deployment.apps/nginx created |
Die Details des Deployments können wir mit dem kubectl describe anzeigen lassen.
1 | kubectl describe deployment nginx |
Jetzt legen wir noch einen nginx Service an, damit die Applikation von außen aufgerufen werden kann.
1 | kubectl create service nodeport nginx --tcp=80:80 |
Lassen wir uns nun einen die Pods anzeigen, sehen wir das ein nginx Pod dazugekommen ist.
1 2 3 | kubectl get pods NAME READY STATUS RESTARTS AGE nginx-f89759699-s6vxx 1/1 Running 0 2m48s |
Ein Blick auf die Service Details zeigt uns, dass auch die Portweiterleitung erfolgreich eingerichtet wurde.
1 2 3 4 | kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 43m nginx NodePort 10.110.22.246 <none> 80:31553/TCP 76s |
Rufen wir nun das ganze im Browser auf, bekommen wir auch die Standardseite von nginx angezeigt. Für den Aufruf verwendet man die Kubernetes API Adresse (dafür haben wir die IP Adresse des Masters verwendet) und den Port, welchen wir mit kubectl get svc angezeigt bekommen (bei mir 31553).
Damit haben wir erfolgreich unser eigenes Kubernetes Cluster installiert.
Mich würde interessieren, welche Themen euch noch im Kontext zu Kubernetes interessieren. Hinterlasst mir doch ein kurzes Kommentar mit dem Thema. Dann kann ich mal schauen ob ich dazu auch einen Artikel hier auf dem Blog verfasse.
Ansonsten erstmal viel Spaß mit dem Cluster =)
Referenz: https://kubernetes.io/docs/home/
Techs meint
Vielen Dank für die umfassenden Informationen.