Neuigkeiten von trion.
Immer gut informiert.

Kubernetes Upgrade auf ARM mit CRI-O und Arch Linux

In diesem Beitrag wurde die Installation von Kubernetes auf ODROID unter Arch Linux ARM beschrieben. Nachdem Kubernetes 1.12 veröffentlich wurde, ist es an der Zeit, sich auch mit dem Thema Kubernetes Update zu beschäftigen.

Das grundsätzliche Vorgehen bei einem Upgrade von Kubernetes erfolgt in mehreren Schritten:

  1. Update der Kubernetes Binaries auf den Master Nodes (Control Plane)

  2. Update der Kubernetes Pods auf den Master Nodes

  3. Aktualisierung der Worker Nodes auf die neuesten Kubernetes Binaries

Kubernetes Upgrades werden jeweils zwischen Minor Versionen supported, d.h. ein Update von 1.10 zu 1.12 muss über den Zwischenschritt eines Updates auf Kubernetes 1.11 erfolgen. Das konkrete Vorgehen hängt von dem gewählten Verfahren zur Kubernetes Einrichtung ab. Da der Beispiel-Cluster mit kubeadm eingerichtet wurde, wird auch das Update von Kubernetes mit kubeadm upgrade durchgeführt.

Vorbereitungen - Kubernetes Build auf ODROID

Um Kubernetes aus dem Quellcode zu übersetzen, werden erhebliche Mengen an Hauptspeicher benötigt, um einen erfolgreichen Build zu erzeugen. Auf einem RaspberryPI und selbst auf einem ODROID ist zu wenig Speicher verbaut. Abhilfe schafft hier nur swap-Speicher als virtuelle Erweiterung des Hauptspeichers. Da Swap sehr langsam ist, bietet sich als Kompromiss an, zumindest teilweise auf zRAM zu setzen, um so mittels Kompression die Speicherausnutzung zu Lasten von höherer CPU-Last zu verbessern.

Verwendung von zRAM und Swapfile als Auslagerungspeicher
# swapoff -a
# zramctl --reset /dev/zram0
# zramctl --find --size 2000M
/dev/zram0
# mkswap /dev/zram0
# swapon /dev/zram0
Setting up swapspace version 1, size = 2 GiB (2097147904 bytes)
no label, UUID=29030557-c5d8-46d0-a2d4-92d0f0ce1e06
# fallocate -l 2500M /swapfile
# mkswap /swapfile
# swapon /swapfile
Setting up swapspace version 1, size = 2.5 GiB (2621435904 bytes)
no label, UUID=2c77581b-3938-4873-8150-4e47cd7f0685

Zusätzlich sollte das /tmp/ Verzeichnis, dass in der Regel mit tmpfs betrieben wird, ausgehängt werden: Die tmpfs Implementierung nutzt RAM bzw. Swap-Speicher als temporären Speicher für das Verzeichnis und geht somit auch zu Lasten des verfügbaren Speichers.

Update der Go Version

Nach dem Update der Go Version werden Flags nicht mehr in dem alten Format export GOFLAGS="-p 1" akzeptiert. Mit diesem Flag wird Go angewiesen, den Build nicht auf die Anzahl der vorhandenen CPU Kerne zu parallelisieren. Auch das ist ein Mittel, um den Hauptspeicherbedarf zu reduzieren. Das neue Format wäre export GOFLAGS="-p=1", jedoch gibt es dann einen Fehler bei der Ausführung der Kubernetes Tests:

Kubernetes Build Fehler mit GOFLAGS="-p=1"
go test: p flag may be set only once
==> ERROR: A failure occurred in build().
    Aborting...

Als einziger Weg, die Parallelisierung zu verhindern, bleibt daher aktuell der Hack, die CPU Kerne offline zu nehmen. Dank CPU Hotplug Support im Linux kernel, ist das auch relativ einfach zu machen.

Deaktivierung aller CPUs bis auf einen Kern
echo 0 > /sys/devices/system/cpu/cpu1/online
echo 0 > /sys/devices/system/cpu/cpu2/online
echo 0 > /sys/devices/system/cpu/cpu3/online

Nun kann ein Build aller Komponenten durchgeführt werden.

Build CRI-O und crictl

Zunächst werden aktuelle Versionen von CRI-O und der CRI-Tools gebaut. Als PKGBUILD für ARM können die folgenden Vorlagen dienen:

CRI-O Arch Linux ARM PKGBUILD
# Maintainer: Thomas Kruse <tk-kaal@trion.de>
# Contributor: Tony Lambiris <tony@criticalstack.com>

pkgname=cri-o
pkgver=1.11.7
pkgrel=1
pkgdesc='Open Container Initiative-based implementation of Kubernetes Container Runtime Interface'
arch=(x86_64 aarch64)
url='https://github.com/kubernetes-incubator/cri-o'
license=(Apache)
makedepends=(go go-md2man ostree)
backup=('etc/crio/crio.conf')
source=("git+https://github.com/kubernetes-incubator/cri-o")
sha256sums=('SKIP')

prepare() {
	cd "$srcdir/$pkgname"

	git checkout "v$pkgver"
	install -m755 -d "$srcdir/go/src/github.com/kubernetes-incubator"
	cp -a "$srcdir/$pkgname" "$srcdir/go/src/github.com/kubernetes-incubator/cri-o"
}

build() {
	cd "$srcdir/go/src/github.com/kubernetes-incubator/cri-o"

	export GOPATH="$srcdir/go"
	make -j1 binaries docs

	./bin/crio --selinux=true \
		--storage-driver=overlay \
		--conmon /usr/libexec/crio/conmon \
		--cni-plugin-dir /usr/libexec/cni \
		--default-mounts /run/secrets \
		--cgroup-manager=systemd config > crio.conf
}

package() {
	cd "$srcdir/go/src/github.com/kubernetes-incubator/cri-o"

	make install install.systemd PREFIX="$pkgdir/usr"

	# fix-up paths pointing to /usr/local to /usr
	sed -i --follow-symlinks -re 's|/usr/local|/usr|g' $pkgdir/usr/lib/systemd/system/*.service

	# install configs
	install -dm755 $pkgdir/etc/crio/
	install -Dm644 crio.conf $pkgdir/etc/crio/crio.conf
	install -Dm644 seccomp.json $pkgdir/etc/crio/seccomp.json
}
CRI-Tools Arch Linux ARM PKGBUILD
# Maintainer: Thomas Kruse <tk-kaal@trion.de>
# Maintainer: ProFfeSsoRr <evvsoft at gmail dot com>

pkgname=crictl-bin
pkgver=1.12.0
pkgrel=1
pkgdesc="CLI tool for Kubelet Container Runtime Interface (CRI)"
arch=(x86_64 aarch64)
url="https://github.com/kubernetes-incubator/cri-tools/blob/master/docs/crictl.md"
license=('Apache')
source=("https://github.com/kubernetes-incubator/cri-tools/releases/download/v${pkgver}/crictl-v${pkgver}-linux-arm64.tar.gz"
        "crictl.yaml")
sha256sums=('SKIP' 'SKIP')

package() {
  cd "$srcdir"
  install -Dm755 crictl "$pkgdir"/usr/bin/crictl
  install -Dm644 crictl.yaml "$pkgdir"/etc/crictl.yaml
}

Wie immer werden die Pakete am besten mit makepkg --clean --syncdeps gebaut. Nachdem die neuesten Versionen der Container Runtime CRI-O und zugehöriger Tools gebaut sind, können die Kubernetes Binaries gebaut werden.

Build von Kubernetes und kubeadm

Für kubelet und kubeadm wird das Paket kubernetes gebaut. Das benötigte PKGBUILD kann z.B. wie folgt aussehen:

Kubernetes Arch Linux ARM PKGBUILD
#Maintainer: Thomas Kruse <tk-kaal@trion.de>
#Contributor: Iwan Timmer <irtimmer@gmail.com>

pkgname=kubernetes
pkgver=1.12.1
_contribver=f4ce29dd35b68f538a5845d7e294bbf056d5d215
pkgrel=1
pkgdesc="Production-Grade Container Scheduling and Management"
depends=('glibc' 'bash')
makedepends=('go' 'rsync' 'go-bindata')
optdepends=('etcd: etcd cluster required to run Kubernetes')
arch=('x86_64' 'i686' 'armv7h' 'aarch64')
source=("$pkgname-$pkgver.tar.gz::https://dl.k8s.io/v$pkgver/kubernetes-src.tar.gz"
        "https://github.com/kubernetes/contrib/archive/$_contribver.tar.gz"
        "kubernetes.install")
noextract=("$pkgname-$pkgver.tar.gz")
url="http://kubernetes.io/"
license=("APACHE")
backup=('etc/kubernetes/apiserver'
        'etc/kubernetes/config'
        'etc/kubernetes/controller-manager'
        'etc/kubernetes/kubelet'
        'etc/kubernetes/proxy'
        'etc/kubernetes/scheduler')
install=kubernetes.install
sha256sums=('35fc8ba46c75f7f41560bb4b9f969ceecadfaa34924e2ff5808bee3bc497546b'
            '4bd2a2f4fc2a17b78dd53a8f7312760b4028d600d14006a3cdf5768b28b44b27'
            'fb6fce3ef4b793863286dafb5856ce28027427005d6c6fd44162844921ab714b')

prepare() {
    mkdir -p $srcdir/$pkgname-$pkgver
    tar -xf $srcdir/$pkgname-$pkgver.tar.gz -C $srcdir/$pkgname-$pkgver
}

build() {
    cd $srcdir/$pkgname-$pkgver

    make -j1
    hack/generate-docs.sh
}

package() {
    cd $srcdir/$pkgname-$pkgver

    [ "$CARCH" = 'i686' ] && _kubearch=386
    [ "$CARCH" = 'x86_64' ] && _kubearch=amd64
    [ "$CARCH" = 'aarch64' ] && _kubearch=arm64
    [ "$CARCH" = 'armv7h' ] && _kubearch=arm

    binaries=(apiextensions-apiserver cloud-controller-manager kube-proxy kube-apiserver kube-controller-manager kubelet kubeadm kubemark hyperkube kube-scheduler kubectl kubemark)
    for bin in "${binaries[@]}"; do
        install -Dm755 _output/local/bin/linux/$_kubearch/$bin $pkgdir/usr/bin/$bin
    done

    # install manpages
    install -d $pkgdir/usr/share/man/man1/
    install -pm 644 docs/man/man1/* $pkgdir/usr/share/man/man1

    # install the place the kubelet defaults to put volumes
    install -d $pkgdir/var/lib/kubelet

    cd $srcdir/contrib-$_contribver

    # install config files
    install -dm 755 $pkgdir/etc/kubernetes/
    install -m 644 -t $pkgdir/etc/kubernetes/ init/systemd/environ/*

    # install service files
    install -dm 755 $pkgdir/usr/lib/systemd/system
    install -m 644 -t $pkgdir/usr/lib/systemd/system init/systemd/*.service

    install -dm 755 $pkgdir/usr/lib/tmpfiles.d
    install -m 644 -t $pkgdir/usr/lib/tmpfiles.d init/systemd/tmpfiles.d/*.conf
}

Auch hier erfolgt der Build am einfachsten mit makepkg.

Build von Kubernetes auf Arch Linux ARM
$ makepkg -sc
==> Making package: kubernetes 1.12.0-1 (Sat Oct 20 13:26:27 2018)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> Retrieving sources...
  -> Found kubernetes-1.12.0.tar.gz
  -> Found f4ce29dd35b68f538a5845d7e294bbf056d5d215.tar.gz
  -> Found kubernetes.install
==> Validating source files with sha256sums...
    kubernetes-1.12.0.tar.gz ... Passed
    f4ce29dd35b68f538a5845d7e294bbf056d5d215.tar.gz ... Passed
    kubernetes.install ... Passed
==> Extracting sources...
  -> Extracting f4ce29dd35b68f538a5845d7e294bbf056d5d215.tar.gz with bsdtar
==> Starting prepare()...
==> Removing existing $pkgdir/ directory...
==> Starting build()...
+++ [1020 13:27:03] Building go targets for linux/arm64:
    ./vendor/k8s.io/code-generator/cmd/deepcopy-gen
+++ [1020 13:27:34] Building go targets for linux/arm64:
    ./vendor/k8s.io/code-generator/cmd/defaulter-gen
+++ [1020 13:27:58] Building go targets for linux/arm64:
    ./vendor/k8s.io/code-generator/cmd/conversion-gen
+++ [1020 13:28:28] Building go targets for linux/arm64:
    ./vendor/k8s.io/kube-openapi/cmd/openapi-gen
2018/10/20 13:29:04 Code for OpenAPI definitions generated
+++ [1020 13:29:04] Building go targets for linux/arm64:
    ./vendor/github.com/jteeuwen/go-bindata/go-bindata
+++ [1020 13:29:09] Building go targets for linux/arm64:
    cmd/kube-proxy
    cmd/kube-apiserver
    cmd/kube-controller-manager
    cmd/cloud-controller-manager
    cmd/kubelet
    cmd/kubeadm
    cmd/hyperkube
    cmd/kube-scheduler
    vendor/k8s.io/apiextensions-apiserver
    cluster/gce/gci/mounter
    cmd/kubectl
    cmd/gendocs
    cmd/genkubedocs
    cmd/genman
    cmd/genyaml
    cmd/genswaggertypedocs
    cmd/linkcheck
    vendor/github.com/onsi/ginkgo/ginkgo
    test/e2e/e2e.test
    cmd/kubemark
    vendor/github.com/onsi/ginkgo/ginkgo
    test/e2e_node/e2e_node.test
...

Die gebauten Pakete sollten auf alle Knoten verteilt werden oder über ein lokales Repository bereitgestellt werden.

Kubernetes Cluster Update

Das Update des Kubernetes Clusters auf eine neue Kubernetes Version erfolgt in zwei Stufen: Zunächst wird der Master Node auf die neueste Kubernetes Version aktualisiert, danach die Worker. Da kubeadm derzeit lediglich einen einzelnen Master unterstützt, wird auch nur ein Knoten aktualisiert.

CRI-O Update

Auf allen Nodes wird zunächst das aktualisierte CRI-O und crictl ausgerollt.

Update von CRI-O und crictl
$ sudo pacman -U cri*.pkg.tar.xz
loading packages...
resolving dependencies...
looking for conflicting packages...

Packages (2) cri-o-1.11.7-1  crictl-bin-1.12.0-1

Total Installed Size:  49.54 MiB
Net Upgrade Size:       4.22 MiB

:: Proceed with installation? [Y/n]
...

Master Node

Als erstes wird das neue Kubernetes Arch Paket installiert, danach die Version von kubeadm kontrolliert.

Update der Kubernetes Binaries auf Master Node
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.1", GitCommit:"b1b29978270dc22fecc592ac55d903350454310a", GitTreeState:"archive", BuildDate:"2018-07-28T11:57:20Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/arm64"}
$ sudo pacman -U kubernetes-1.12.1-1-aarch64.pkg.tar.xz
loading packages...
resolving dependencies...
looking for conflicting packages...

Packages (1) kubernetes-1.12.1-1

Total Installed Size:  820.89 MiB
Net Upgrade Size:       95.88 MiB

:: Proceed with installation? [Y/n]
...
: Running post-transaction hooks...
(1/3) Reloading system manager configuration...
(2/3) Creating temporary files...
(3/3) Arming ConditionNeedsUpdate...
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.1", GitCommit:"4ed3216f3ec431b140b1d899130a69fc671678f4", GitTreeState:"archive", BuildDate:"2018-10-20T12:23:34Z", GoVersion:"go1.11.1", Compiler:"gc", Platform:"linux/arm64"}

Durch kubeadm wird ein Upgrade von Kubernetes nativ unterstützt und erfordert wenige manuelle Eingriffe.

Kontrolle des Kubernetes Upgrade Plans von kubeadm
$ sudo kubeadm upgrade plan
[preflight] Running pre-flight checks.
[upgrade] Making sure the cluster is healthy:
[upgrade/config] Making sure the configuration is correct:
[upgrade/config] Reading configuration from the cluster...
[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[upgrade] Fetching available versions to upgrade to
[upgrade/versions] Cluster version: v1.11.1
[upgrade/versions] kubeadm version: v1.12.1
[upgrade/versions] Latest stable version: v1.12.1
[upgrade/versions] Latest version in the v1.11 series: v1.11.3

Components that must be upgraded manually after you have upgraded the control plane with 'kubeadm upgrade apply':
COMPONENT   CURRENT       AVAILABLE
Kubelet     4 x v1.11.1   v1.12.1

Upgrade to the latest stable version:

COMPONENT            CURRENT   AVAILABLE
API Server           v1.11.1   v1.12.1
Controller Manager   v1.11.1   v1.12.1
Scheduler            v1.11.1   v1.12.1
Kube Proxy           v1.11.1   v1.12.1
CoreDNS              1.2.2     1.2.2
Etcd                 3.2.18    3.2.24

You can now apply the upgrade by executing the following command:

	kubeadm upgrade apply v1.12.1
_____________________________________________________________________

Das eigentliche Upgrade der Control Plane wird dann durch kubeadm upgrade gestartet.

Folgende Schritte führt das Upgrade dabei durch:

  • Überprüfung des Kubernetes Cluster-Zustands

    • API Server ist erreichbar

    • Alle Nodes sind im Ready Zustand

    • Control-Plane ist betriebsbereit

  • Einhaltung der Upgrade-Policy: Lediglich Minor-Updates werden unterstützt

  • Prüfung, dass die Container-Images für die Control-Plane verfügbar sind oder gepullt werden können

  • Upgrade der Control-Plane Komponenten mit automatischem Rollback, falls Probleme auftreten

  • Aktualisierung der kube-dns und kube-proxy Manifeste und aller erforderlichen RBAC Rollen

  • Falls Zertifikate oder private Schlüsseln des API Servers in den nächsten 180 Tagen auslaufen werden diese aktualisiert, das bisherige Schlüsselmaterial gesichert

Upgrade von Kubernetes Master Node mit kubeadm
$ sudo kubeadm upgrade apply v1.12.1
[preflight] Running pre-flight checks.
[upgrade] Making sure the cluster is healthy:
[upgrade/config] Making sure the configuration is correct:
[upgrade/config] Reading configuration from the cluster...
[upgrade/config] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[upgrade/apply] Respecting the --cri-socket flag that is set with higher priority than the config file.
[upgrade/version] You have chosen to change the cluster version to "v1.12.1"
[upgrade/versions] Cluster version: v1.11.1
[upgrade/versions] kubeadm version: v1.12.1
[upgrade/confirm] Are you sure you want to proceed with the upgrade? [y/N]: y
[upgrade/prepull] Will prepull images for components [kube-apiserver kube-controller-manager kube-scheduler etcd]
[upgrade/prepull] Prepulling image for component etcd.
[upgrade/prepull] Prepulling image for component kube-apiserver.
[upgrade/prepull] Prepulling image for component kube-controller-manager.
[upgrade/prepull] Prepulling image for component kube-scheduler.
[apiclient] Found 1 Pods for label selector k8s-app=upgrade-prepull-kube-apiserver
[apiclient] Found 0 Pods for label selector k8s-app=upgrade-prepull-etcd
[apiclient] Found 0 Pods for label selector k8s-app=upgrade-prepull-kube-scheduler
[apiclient] Found 1 Pods for label selector k8s-app=upgrade-prepull-kube-controller-manager
[apiclient] Found 1 Pods for label selector k8s-app=upgrade-prepull-etcd
[apiclient] Found 1 Pods for label selector k8s-app=upgrade-prepull-kube-scheduler
[upgrade/prepull] Prepulled image for component kube-controller-manager.
[upgrade/prepull] Prepulled image for component kube-apiserver.
[upgrade/prepull] Prepulled image for component kube-scheduler.
[upgrade/prepull] Prepulled image for component etcd.
[upgrade/prepull] Successfully prepulled the images for all the control plane components
[upgrade/apply] Upgrading your Static Pod-hosted control plane to version "v1.12.1"...
Static pod: kube-apiserver-c2-master0 hash: fc768201867c1cdaeebbd9617ed4285c
Static pod: kube-controller-manager-c2-master0 hash: 6474abf9737348f996b44d724256fa9e
Static pod: kube-scheduler-c2-master0 hash: 6e1c1eb822c75df4cec74cac9992eea9
Static pod: etcd-c2-master0 hash: 24e2ce158856a08e7e23269fd3aa6740
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests507879547/etcd.yaml"
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/etcd.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2018-10-20-13-02-07/etcd.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s
Static pod: etcd-c2-master0 hash: 24e2ce158856a08e7e23269fd3aa6740
Static pod: etcd-c2-master0 hash: 24e2ce158856a08e7e23269fd3aa6740
Static pod: etcd-c2-master0 hash: 24e2ce158856a08e7e23269fd3aa6740
Static pod: etcd-c2-master0 hash: 18d7366d9b7791d910e690a889ab5233
[apiclient] Found 1 Pods for label selector component=etcd
[upgrade/staticpods] Component "etcd" upgraded successfully!
[upgrade/etcd] Waiting for etcd to become available
[util/etcd] Waiting 0s for initial delay
[util/etcd] Attempting to see if all cluster endpoints are available 1/10
[upgrade/staticpods] Writing new Static Pod manifests to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests507879547"
[controlplane] wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests507879547/kube-apiserver.yaml"
[controlplane] wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests507879547/kube-controller-manager.yaml"
[controlplane] wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests507879547/kube-scheduler.yaml"
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-apiserver.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2018-10-20-13-02-07/kube-apiserver.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s
Static pod: kube-apiserver-c2-master0 hash: fc768201867c1cdaeebbd9617ed4285c
Static pod: kube-apiserver-c2-master0 hash: fc768201867c1cdaeebbd9617ed4285c
Static pod: kube-apiserver-c2-master0 hash: fc768201867c1cdaeebbd9617ed4285c
...
[apiclient] Found 1 Pods for label selector component=kube-apiserver
[upgrade/staticpods] Component "kube-apiserver" upgraded successfully!
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-controller-manager.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2018-10-20-13-02-07/kube-controller-manager.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s
Static pod: kube-controller-manager-c2-master0 hash: 6474abf9737348f996b44d724256fa9e
Static pod: kube-controller-manager-c2-master0 hash: c5bc680d2460cb6417f3b079a50f53db
[apiclient] Found 1 Pods for label selector component=kube-controller-manager
[upgrade/staticpods] Component "kube-controller-manager" upgraded successfully!
[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-scheduler.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2018-10-20-13-02-07/kube-scheduler.yaml"
[upgrade/staticpods] Waiting for the kubelet to restart the component
[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s
Static pod: kube-scheduler-c2-master0 hash: 6e1c1eb822c75df4cec74cac9992eea9
Static pod: kube-scheduler-c2-master0 hash: 24b4b7b60678e898e3b4ff5fe89c6968
[apiclient] Found 1 Pods for label selector component=kube-scheduler
[upgrade/staticpods] Component "kube-scheduler" upgraded successfully!
[uploadconfig] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.12" in namespace kube-system with the configuration for the kubelets in the cluster
[kubelet] Downloading configuration for the kubelet from the "kubelet-config-1.12" ConfigMap in the kube-system namespace
[kubelet] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "c2-master0" as an annotation
[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
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.12.1". Enjoy!

[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven't already done so.

Nun bleibt noch ein Upgrade eines evtl. genutzten CNI Netzwerkes. Hat man sich für Weave entschieden, ist nicht viel zu tun: Das DaemonSet von Weave verwendet Rolling Updates - wird eine neue Kubernetes Version installiert, werden die Weave Net Pods automatisch der Reihe nach aktualisiert.

Damit das neue kubelet verwendet wird, muss der Prozess neu gestartet werden. Es empfiehlt sich ggf. vorhandene Workload vom Master vorher herunter zu nehmen.

Kubernetes Master Node drain und Upgrade von kubelet
$ NODE=c2-master0
$ kubectl drain $NODE --ignore-daemonsets
$ sudo systemctl restart kubelet
$ kubectl uncordon $NODE

Eine Überprüfung der Node Liste zeigt, dass der Master erfolgreich aktualisiert wurde.

Prüfung der Kubernetes Versionen nach Upgrade der Master Node
$ kubectl get nodes
NAME         STATUS   ROLES    AGE   VERSION
c2-master0   Ready    master   85d   v1.12.1
c2-worker0   Ready    <none>   85d   v1.11.1
c2-worker1   Ready    <none>   85d   v1.11.1
c2-worker2   Ready    <none>   85d   v1.11.1

Worker Nodes

Die Worker Nodes werden einzeln aktualisiert, damit es zu keinen Einschränkungen der Verfügbarkeit kommt. Dazu wird zunächst mittels kubectl drain die Arbeitslast evakuiert - Kubernetes stellt sicher, dass die Pods auf anderen Knoten des Clusters neu gestartet werden. Durch die Option --ignore-daemonsets werden Fehlermeldungen unterdrückt, die durch auf der Node aktive DaemonSet Objekte verursacht werden - da ein DaemonSet an eine Node gebunden ist, lässt sich diese nicht auf eine ander Node umziehen.

Werden Pods ausgeführt, die lokal Daten speichern, kann die Option --delete-local-data mit angegeben werden. Da lokaler Speicher nicht im Cluster an einem anderen Ort bereitgestellt werden kann, gehen diese Daten verloren, wenn die zugehörigen Pods auf einer anderen Node neu erzeugt werden.

Wichtig ist generell zu beachten: Wird mit einer harten Affinity oder Anti-Affinity gearbeitet, sollte stets eine reserve Node zur Verfügung stehen, damit die Pods tatsächlich evakuiert werden können.

Die mit local: angegebenen Schritte können auf einem beliebigen Host mit Zugriff auf den API Server durchgeführt werden, die weiteren Schritte werden jeweils auf den einzelnen Kubernetes Nodes durchgeführt.

Evakuierung der Kubernetes Workload von Worker Node
local:~$ NODE=c2-worker0
local:~$ kubectl drain $NODE --ignore-daemonsets --delete-local-data
node/c2-worker0 cordoned
WARNING: Ignoring DaemonSet-managed pods: kube-proxy-wj5bn, weave-net-58th2, rook-ceph-agent-mbhfd, rook-discover-fqbgc; Deleting pods with local storage: monitoring-grafana-57dd5f7b66-mmgp2
pod/rook-ceph-osd-prepare-c2-worker0-2pwbb evicted
pod/rook-ceph-mon6-r8h9r evicted
pod/monitoring-grafana-57dd5f7b66-mmgp2 evicted
pod/rook-ceph-osd-id-1-657ff7cdf6-djnjt evicted

Nun ist auf dem Worker auch der passende Zeitpunkt, um CRI-O zu aktualisieren.

Update von CRI-O auf Worker Node
$ sudo systemctl stop kubelet
$ sudo systemctl stop crio
$ sudo pacman -U cri*.pkg.tar.xz
$ sudo systemctl start crio

Auf den Worker Nodes muss zusätzlich die Konfiguration von kubelet aktualisiert werden, was ebenfalls durch kubeadm unterstützt wird.

Upgrade von kubelet und Anpassung der kubelet-Config
$ sudo pacman -U kubernetes-1.12.1-1-aarch64.pkg.tar.xz
...
$ sudo kubeadm upgrade node config --kubelet-version $(kubelet --version | cut -d ' ' -f 2)
$ sudo systemctl restart kubelet
$ systemctl status kubelet
* kubelet.service - Kubernetes Kubelet Server
   Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2018-10-20 13:35:48 UTC; 6s ago
     Docs: https://kubernetes.io/docs/concepts/overview/components/#kubelet
           https://kubernetes.io/docs/reference/generated/kubelet/
 Main PID: 28843 (kubelet)
    Tasks: 19 (limit: 2124)
   Memory: 85.1M
   CGroup: /system.slice/kubelet.service
local:~$ kubectl uncordon $NODE

Sind alle Nodes aktualisiert, sollten diese auch wieder als verfügbar gelistet werden:

Überprüfung der korrekten Funktion aller Kubernetes Nodes
$ kubectl get nodes
NAME         STATUS   ROLES    AGE   VERSION
c2-master0   Ready    master   85d   v1.12.1
c2-worker0   Ready    <none>   85d   v1.12.1
c2-worker1   Ready    <none>   85d   v1.12.1
c2-worker2   Ready    <none>   85d   v1.12.1

Der Kubernetes Cluster steht nun in der aktuellen Version zur Verfügung. Je nach Kubernetes Distribution und verwendetem Tooling sieht der Upgrade-Prozess natürlich anders aus.

Upgrade oder Phoenix

Eine alternative Vorgehensweise zu dem beschriebenen Upgrade des Kubernetes Clusters ist die Einrichtung eines separaten Clusters mit entsprechend neuer Kubernetes Version. Die Workload kann dann von dem bisherigen Cluster in den neuen Cluster umgezogen werden, der alte Cluster anschließend außer Betrieb genommen werden.

Im Prinzip ist das Vorgehen die Anwendung von blue-green-Deployment auf Cluster Ebene und erlaubt zum einen größere Versionssprünge, zum anderen ist ein Rollback jederzeit möglich. Der Aufwand und Resourcenbedarf ist bei diesem Vorgehen jedoch höher und dürft vor allem in Cloud-Umgebungen praktikabel sein, die dynamisch Resourcen zur Verfügung stellen. Gerade in Cloud Umgebungen werden jedoch in der Regel managed Kubernetes Umgebungen angeboten und stellen angesichts des mit Upgrades einhergehenden Aufwands eine attraktive Alternative dar.




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.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Zur Desktop Version des Artikels