Kubernetes auf Raspberry PI
Sowohl für Trainings als auch eigene Experimente ist ein physischer Cluster oft anschaulicher, als virtuelle Maschinen. Als preiswerter Einstieg bieten sich bereits seit geraumer Zeit die Raspberry Pi Minicomputer an. Für rund 40 Euro pro Gerät erhält man einen Rechner mit 1 Gb RAM und vier ARM Kernen. Dazu wird noch eine USB Stromversorgung und MicroSD Speicherkarten benötigt, und schon kann es los gehen.
Als erstes muss ein Basisbetriebssystem für die Raspberry Pi Rechner ausgewählt und installiert werden.
Installation Raspbian für Kubernetes
Als Betriebssystem für den Raspberry Pi gibt es im Prinzip Arch Linux und Raspbian, eine Debian Variante, zur Auswahl. Am einfachsten ist der Einsatz von Raspbian, hier wird zunächst die aktuelle Version benötigt: https://downloads.raspberrypi.org/raspbian_lite_latest
Als nächstes muss das Image ausgepackt und auf die jeweiligen SD Karten für die Raspberry Pi Rechner geschrieben werden. Achtung: Wer sich hier irrt, kann versehentlich die eigene Festplatte überschreiben. Hier gilt es besonders vorsichtig vorzugehen, um Datenverlust zu vermeiden.
Zunächst prüft man, welche Devices dem eigenen Rechner zugeordnet sind:
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 477G 0 disk
├─sda1 8:1 0 512M 0 part /boot/efi
├─sda2 8:2 0 244M 0 part /boot
└─sda3 8:3 0 476,2G 0 part
sdb 8:16 0 3,7T 0 disk
└─sdb_crypt 253:4 0 3,7T 0 crypt /home
Nachdem eine SD Karte angeschlossen wurde, wird diese als zusätzliches Device erscheinen. Hier im Beispiel handelt es sich um "sdc".
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 477G 0 disk
├─sda1 8:1 0 512M 0 part /boot/efi
├─sda2 8:2 0 244M 0 part /boot
└─sda3 8:3 0 476,2G 0 part
sdb 8:16 0 3,7T 0 disk
└─sdb_crypt 253:4 0 3,7T 0 crypt /home
sdc 8:48 1 3,7G 0 disk
└─sdc1 8:49 1 3,7G 0 part /media/tkruse/3039-3366
$ unzip -p 2018-06-27-raspbian-stretch-lite.zip | sudo dd bs=4M of=/dev/sdc status=progress conv=fsync
...
924909568 bytes (925 MB, 882 MiB) copied, 6 s, 154 MB/s
...
0+28415 records in
0+28415 records out
1862270976 bytes (1,9 GB, 1,7 GiB) copied, 586,442 s, 3,2 MB/s
Nachdem das Filesystem erfolgreich geschrieben wurde, befinden sich auf der Speicherkarte zwei Partitionen.
Auf der /boot
Partition muss noch eine leere Datei mit dem Namen ssh
angelegt werden, damit auf dem Raspbian ein SSH Login möglich ist.
$ sudo mount /dev/sdc1 /mnt
$ sudo touch /mnt/ssh
$ sudo umount /mnt
Wenn die Rechner jetzt gebootet werden, sollten diese sich per DHCP IP Adressen beziehen und ein SSH Login möglich sein. Anschließend sind noch einige Einstellungen für Docker und Kubernetes vorzunehmen.
Raspbian Einrichtung für Docker und Kubernetes
Der Login auf die mit Raspbian installierten Raspberry Pi Rechner erfolgt mittels SSH als User pi
und dem Passwort raspberry
.
Zunächst muss Swap-Speicher deaktiviert werden, da Kubernetes ab Version 1.7 Fehler liefert, falls Swap Speicher aktiviert ist. Als Container-Runtime soll Docker zum Einsatz kommen, dazu werden die offiziellen Pakete von Docker Inc. verwendet. Damit die Rechner besser zu unterscheiden sind, empfiehlt es sich, auch den Hostname auf etwas eindeutiges zu setzen, zum Beispiel "master" und "worker-0" bzw. "worker-1".
$ ssh pi@10.23.15.186
pi@10.23.15.186's password:
pi@raspberrypi:~ $
#hostnamen setzen
$ sudo sed -i s/raspberrypi/rpi0-master/g /etc/hosts
$ sudo sed -i s/raspberrypi/rpi0-master/g /etc/hostname
# (auf worker) hostnamen setzen
$ sudo sed -i s/raspberrypi/rpi1-worker0/g /etc/hosts
$ sudo sed -i s/raspberrypi/rpi1-worker0/g /etc/hostname
#Swap deaktivieren
$ sudo dphys-swapfile swapoff && \
sudo dphys-swapfile uninstall && \
sudo update-rc.d dphys-swapfile remove
#Docker installieren
$ curl -sSL get.docker.com | sh && sudo usermod pi -aG docker
#Aktivierung der cgroup Kernelparameter
$ orig="$(head -n1 /boot/cmdline.txt) cgroup_enable=cpuset cgroup_enable=memory"
$ echo $orig | sudo tee /boot/cmdline.txt
$ sudo reboot
Nach dem Neustart können die Pakete für Kubernetes installiert werden.
Zum Einsatz kommt hier kubeadm
, da es das Setup deutlich einfacher macht, als alle Komponenten manuell zu installieren und zu konfigurieren.
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
OK
$ echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
$ sudo apt-get update -q
Hit:1 http://raspbian.raspberrypi.org/raspbian stretch InRelease
Hit:2 http://archive.raspberrypi.org/debian stretch InRelease
Hit:4 https://download.docker.com/linux/raspbian stretch InRelease
Get:3 https://packages.cloud.google.com/apt kubernetes-xenial InRelease [8993 B]
Get:5 https://packages.cloud.google.com/apt kubernetes-xenial/main armhf Packages [16.7 kB]
Fetched 25.6 kB in 2s (12.8 kB/s)
Reading package lists...
$ sudo apt-get install -qy kubeadm
Reading package lists...
Building dependency tree...
Reading state information...
The following additional packages will be installed:
cri-tools ebtables ethtool kubectl kubelet kubernetes-cni socat
The following NEW packages will be installed:
cri-tools ebtables ethtool kubeadm kubectl kubelet kubernetes-cni socat
0 upgraded, 8 newly installed, 0 to remove and 6 not upgraded.
Need to get 48.8 MB of archives.
After this operation, 313 MB of additional disk space will be used.
Nun sind alle Nodes vorbereitet und der eigentliche Clusteraufbau kann starten.
Kubernetes Master Einrichtung
Als erstes wird der Master eingerichtet. Master Knoten sind Teil der Kubernetes Control Plane und stellen die Kubernetes API zur Verfügung. Hier wird auch der etcd als Persistenz für den Clusterzustand betrieben.
Bei der Einrichtung von Kubernetes mit kubeadm wird durch den Schalter --token-ttl=0
ein dauerhaft gültiges Join-Token erzeugt.
In produktiven Umgebungen sollte das nicht verwendet werden, macht jedoch Experimente deutlich einfacher.
Verwendet man kubeadm
zum Setup, so wird selbst etcd als Docker Container betrieben.
Entsprechend kann der Pull der notwendigen Docker Images eine Weile dauern.
$ sudo kubeadm init --token-ttl=0
I0708 15:01:07.213044 863 feature_gate.go:230] feature gates: &{map[]}
[init] using Kubernetes version: v1.11.0
[preflight] running pre-flight checks
I0708 15:01:07.406001 863 kernel_validator.go:81] Validating kernel version
I0708 15:01:07.406453 863 kernel_validator.go:96] Validating kernel config
[WARNING SystemVerification]: docker version is greater than the most recently validated version. Docker version: 18.05.0-ce. Max validated version: 17.03
[preflight/images] Pulling images required for setting up a Kubernetes cluster
[preflight/images] This might take a minute or two, depending on the speed of your internet connection
[preflight/images] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[preflight] Activating the kubelet service
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [rpi0-master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 10.23.15.101]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Generated etcd/ca certificate and key.
[certificates] Generated etcd/server certificate and key.
[certificates] etcd/server serving cert is signed for DNS names [rpi0-master localhost] and IPs [127.0.0.1 ::1]
[certificates] Generated etcd/peer certificate and key.
[certificates] etcd/peer serving cert is signed for DNS names [rpi0-master localhost] and IPs [10.23.15.101 127.0.0.1 ::1]
[certificates] Generated etcd/healthcheck-client certificate and key.
[certificates] Generated apiserver-etcd-client certificate and key.
[certificates] valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/scheduler.conf"
[controlplane] wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests"
[init] this might take a minute or longer if the control plane images have to be pulled
[apiclient] All control plane components are healthy after 78.508000 seconds
[uploadconfig] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.11" in namespace kube-system with the configuration for the kubelets in the cluster
[markmaster] Marking the node rpi0-master as master by adding the label "node-role.kubernetes.io/master=''"
[markmaster] Marking the node rpi0-master as master by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "rpi0-master" as an annotation
[bootstraptoken] using token: mb2qy6.8ok4zzpbjeggvf2s
[bootstraptoken] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
Your Kubernetes master 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/
You can now join any number of machines by running the following on each node
as root:
kubeadm join 10.23.15.101:6443 --token bc3qy5.8afk4yyhasggvf2s --discovery-token-ca-cert-hash sha256:fe4a8a766f023077b08e752b8754808ea033845dd59be22adebec82b0ee74bdb
Falls beim Setup etwas schief geht, muss nicht der gesamte Rechner neu installiert werden:
Dazu kann mittels kubeadm reset
die gesamte Konfiguration zurückgerollt werden und ein erneuter Versuch gestartet werden.
Kubernetes Worker Einrichtung
Nachdem der Master bereitsteht, können dem Cluster weitere Knoten als Worker hinzugefügt werden.
$ sudo bash
# kubeadm join 10.23.15.101:6443 --token bc3qy5.8afk4yyhasggvf2s --discovery-token-ca-cert-hash sha256:fe4a8a766f023077b08e752b8754808ea033845dd59be22adebec82b0ee74bdb
[preflight] running pre-flight checks
[WARNING RequiredIPVSKernelModulesAvailable]: the IPVS proxier will not be used, because the following required kernel modules are not loaded: [ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh] or no builtin kernel ipvs support: map[ip_vs_wrr:{} ip_vs_sh:{} nf_conntrack_ipv4:{} ip_vs:{} ip_vs_rr:{}]
you can solve this problem with following methods:
1. Run 'modprobe -- ' to load missing kernel modules;
2. Provide the missing builtin kernel ipvs support
I0708 15:26:46.075527 2207 kernel_validator.go:81] Validating kernel version
I0708 15:26:46.076401 2207 kernel_validator.go:96] Validating kernel config
[WARNING SystemVerification]: docker version is greater than the most recently validated version. Docker version: 18.05.0-ce. Max validated version: 17.03
[discovery] Trying to connect to API Server "10.23.15.101:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://10.23.15.101:6443"
[discovery] Requesting info from "https://10.23.15.101:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "10.23.15.101:6443"
[discovery] Successfully established connection with API Server "10.23.15.101:6443"
[kubelet] Downloading configuration for the kubelet from the "kubelet-config-1.11" ConfigMap in the kube-system namespace
[kubelet] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[preflight] Activating the kubelet service
[tlsbootstrap] Waiting for the kubelet to perform the TLS Bootstrap...
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "vm-worker" as an annotation
This node has joined the cluster:
* Certificate signing request was sent to master and a response
was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the master to see this node join the cluster.
Kubernetes Overlay Network
Damit ein virtuelles Netzwerk für die Pods zur Verfügung steht, fehlt lediglich noch ein entsprechendes Overlay-Network. Auf dem Master kann dazu folgendes Kommando ausgeführt werden, um Weave als Container-Netzwerk zu installieren:
$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
serviceaccount/weave-net configured
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.extensions/weave-net created
Anschließend sollten auf dem Master alle Nodes zu sehen sein, und auch alle Pods healthy sein. Eine kurze Kontrolle stellt sicher, dass es dem neu geschaffenen Kubernetes Cluster gut geht:
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-78fcdf6894-9rdzp 1/1 Running 0 2d
kube-system coredns-78fcdf6894-llkpc 1/1 Running 0 2d
kube-system etcd-rpi0-master 1/1 Running 1 3m
kube-system kube-apiserver-rpi0-master 1/1 Running 1 3m
kube-system kube-controller-manager-rpi0-master 1/1 Running 0 3m
kube-system kube-proxy-5jxsf 1/1 Running 1 2d
kube-system kube-proxy-ds4xm 1/1 Running 0 5h
kube-system kube-proxy-n6429 1/1 Running 0 12h
kube-system kube-scheduler-rpi0-master 1/1 Running 1 3m
kube-system weave-net-4d4ft 2/2 Running 0 3m
kube-system weave-net-4rbvn 2/2 Running 0 3m
kube-system weave-net-ffkhx 2/2 Running 0 3m
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
rpi0-master Ready master 2d v1.11.0
rpi1-worker0 Ready <none> 6h v1.11.0
rpi2-worker1 Ready <none> 12h v1.11.0
Kubernetes Dashboard für ARM
Das Kubernetes Dashboard steht in einer speziellen Variante für die ARM Architektur zur Verfügung. Es gibt dabei zwei Varianten: Die - empfohlene - mit TLS Absicherung, und eine alternative Variante, die keine Security implementiert.
#empfohlen
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard-arm.yaml
# ohne TLS und Authentifizierung
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/alternative/kubernetes-dashboard-arm.yaml
Auf dem Master (oder dem lokalen Rechner) kann nun ein Proxy gestartet werden, der als Eingangstür zu allen Anwendungen im Cluster dient. Wird der Proxy auf dem Raspberry gestartet wird, soll dieser auf alle Netzwerkinterfaces binden und Requests aus dem lokalen Netz akzeptieren. Typischerweise wird ein Entwickler den Proxy bei sich lokal starten, dann wird natürlich kein Zugriff für das ganze Netz genehmigt.
$ kubectl proxy --address='0.0.0.0' --accept-hosts='^localhost$,^127\.0\.0\.1$,^10\.23\.15.*'
Starting to serve on [::]:8001
Für den Zugriff müssen dem Serviceaccount des Dashboard noch Rechte zur Clusterverwaltung eingeräumt werden:
$ kubectl create serviceaccount dashboard -n default
serviceaccount "dashboard" created
$ kubectl create clusterrolebinding dashboard-admin -n default \
--clusterrole=cluster-admin \
--serviceaccount=default:dashboard
clusterrolebinding "dashboard-admin" created
Wird die alternative Variante ohne TLS Zertifikate eingesetzt, so müssen dem nicht authentifizierten Kubernetes-Dashboard User noch die richtigen Rechte zur Cluster Administration eingeräumt werden. Hinweis: Dies Vorgehen ist lediglich zur Demonstration, nicht zum produktiven Betrieb zu empfehlen!
$ cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: kubernetes-dashboard
labels:
k8s-app: kubernetes-dashboard
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard
namespace: kube-system
EOF
Der Zugriff per Webbrowser kann dann so erfolgen: http://10.23.15.101:8001/api/v1/namespaces/kube-system/services/http:kubernetes-dashboard:/proxy/
Zu den Themen Kubernetes, Docker und Cloud Architektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:
Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.