Neuigkeiten von trion.
Immer gut informiert.

Kubernetes 1.13 auf ARM mit CRI-O und Arch Linux

In diesem Beitrag wurde die Installation von Kubernetes auf ODROID unter Arch Linux ARM beschrieben.

Hier geht es nun um das Update auf Kubernetes 1.13, auch in diesem Fall mit selbst übersetzten Paketen für Arch Linux. Bei anderen Distributionen sind ggf. bereits die Upstream Pakete verfügbar.

Wie schon in Kubernetes Upgrade Arch Linux ARM im Detail beschrieben, wird das Update in folgenden Schritten durchgeführt:

  1. Update der Container Runtime (falls erforderlich)

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

  3. Update der Kubernetes Pods auf den Master Nodes

  4. Aktualisierung der Kubernetes Binaries auf den Worker Nodes

Zum Build von Kubernetes 1.13

Da der Go Build von Kubernetes erhebliche Mengen Speicher zur Übersetzung und zum Linking benötigt, kann es auf Maschinen mit wenig Speicher erforderlich sein, einige Kniffe anzuwenden. Details dazu finden sich in diesem Beitrag sowie in dieser GitHub Issue: https://github.com/kubernetes/kubernetes/issues/70062

Build CRI-O und crictl

Zunächst werden aktuelle Versionen von CRI-O und der CRI-Tools (crictl) 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.13.0
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/crictl 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 runc

Auch runc ist in einer neueren Version verfügbar, analog wird ein Paket gebaut.

runc PKGBUILD für ARM
# Maintainer: Thomas Kruse <tk-kaal@trion.de>
# Contributor: Sébastien "Seblu" Luttringer

pkgname=runc
pkgver=1.0.0rc6+169+gccb5efd37
pkgrel=1
pkgdesc='CLI tool for managing OCI compliant containers'
arch=(x86_64 aarch64)
url='https://runc.io/'
license=(Apache)
depends=(glibc libseccomp)
makedepends=(git go go-md2man)
_commit=v1.0.0-rc6  # master
source=(git+https://github.com/opencontainers/runc.git#commit=$_commit)
md5sums=('SKIP')

pkgver() {
  cd runc
  git describe | sed 's/^v//;s/-//;s/-/+/g'
}

prepare() {
  mkdir -p src/github.com/opencontainers
  cp -r runc src/github.com/opencontainers/
}

build() {
  cd src/github.com/opencontainers/runc
  GOPATH="$srcdir" BUILDTAGS='seccomp' make runc man
}

package() {
  cd src/github.com/opencontainers/runc

  install -Dm755 runc "$pkgdir/usr/bin/runc"
  install -Dm644 contrib/completions/bash/runc \
    "$pkgdir/usr/share/bash-completion/completions/runc"

  install -d "$pkgdir/usr/share/man/man8"
  install -m644 man/man8/*.8 "$pkgdir/usr/share/man/man8"
}

Auch hier erfolgt der Build am einfachsten mit makepkg.

Build von Kubernetes 1.13 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>

# sha256sum kubernetes-1.13.2.tar.gz

pkgname=kubernetes
pkgver=1.13.2
_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=('8ccae08b6a02fcb4ceb153da0e5c80dc9f4fef9afe46f162518622f58bcf01d8'
            '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
}

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

Kubernetes Cluster Update

Zunächst wird die Container-Runtime aktualisiert, anschließend die Kubernetes Komponenten selbst.

Das Update des Kubernetes Clusters auf neue Kubernetes Komponenten erfolgt in Stufen: Zunächst wird der Master Node auf die neueste Kubernetes Version aktualisiert, danach die Worker.
Da das Beispiel aus lediglich einen einzelnen Master besteht, wird auch nur ein Knoten aktualisiert.

Update der Container Runtime

Update von CRI-O, crictl und runC
$ sudo systemctl stop kubelet
$ sudo systemctl stop crio
$ sudo pacman -U cri*.pkg.tar.xz
$ sudo pacman -U runc*.pkg.tar.xz
$ sudo systemctl start crio

Update Kubernetes Master Node

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

Update der Kubernetes Binaries auf Master Node
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.1", GitCommit:"4ed3216f3ec431b140b1d899130a69fc671678f4", GitTreeState:"archive", BuildDate:"2018-10-20T20:23:34Z", GoVersion:"go1.11.1", Compiler:"gc", Platform:"linux/arm64"}
$ sudo pacman -U kubernetes-*.tar.xz
$ kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"13", GitVersion:"v1.13.0", GitCommit:"ddf47ac13c1a9483ea035a79cd7c10005ff21a6d", GitTreeState:"archive", BuildDate:"2018-12-14T19:37:01Z", GoVersion:"go1.11.2", 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.
...

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

Upgrade von Kubernetes Master Node mit kubeadm
$ sudo kubeadm upgrade apply v1.13.0
...
[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.13.0". Enjoy!

Nun bleibt noch ein Upgrade eines evtl. genutzten CNI Netzwerkes. Bei Weave 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 vorher ggf. vorhandene Workload vom Master 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   127d   v1.13.0
c2-worker0   Ready    <none>   127d   v1.12.1
c2-worker1   Ready    <none>   127d   v1.12.1
c2-worker2   Ready    <none>   127d   v1.12.1

Update Kubernetes Worker Nodes

Die Worker Nodes werden einzeln aktualisiert, damit es zu keinen Einschränkungen der Verfügbarkeit des Kubernetes Clusters kommt. Dazu wird jeweils mit kubectl drain --ignore-daemonsets die Node evakuiert, aktualisiert und mit kubectl uncordon wieder aktiviert.

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.

Evakuierung der Kubernetes Workload von Worker Node
local:~$ NODE=c2-worker0
local:~$ kubectl drain $NODE --ignore-daemonsets --delete-local-data

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

Update von CRI-O und runC auf Worker Node
$ sudo systemctl stop kubelet; sudo systemctl stop crio
$ sudo pacman -U cri*.pkg.tar.xz; sudo pacman -U runc*.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-*-aarch64.pkg.tar.xz
...
$ sudo kubeadm upgrade node config --kubelet-version $(kubelet --version | cut -d ' ' -f 2)
$ sudo systemctl restart kubelet

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   127d   v1.13.0
c2-worker0   Ready    <none>   127d   v1.13.0
c2-worker1   Ready    <none>   127d   v1.13.0
c2-worker2   Ready    <none>   127d   v1.13.0

Das Upgrade des Kubernetes Clusters ist nun beendet. Alternativ zum Update des Clusters gibt es als Vorgehensweise, einen neuen Kubernetes Cluster mit der gewünschten Version zu erstellen und die Workload auf den Cluster umzuziehen. Außerhalb von Cloud-Umgebungen ist dazu jedoch entsprechend viel an Resourcen vorzuhalten, so dass das Vorgehen in der Praxis unpraktikabel sein kann.




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