Neuigkeiten von trion.
Immer gut informiert.

Kubernetes auf Arch Linux ARM

Auch wenn Arch Linux meist topaktuelle Pakete hat, so ist dies bei Projekten wie Kubernetes auf der ARM Architektur nicht der Fall. Auch im AUR, also dem von der Community gepflegten Bereich, finden sich für ARM 64 bit keine Kubernetes Pakete. Es ist jedoch relativ einfach, sich auf Basis des Quellcodes von Kubernetes einen eigenen Build zu erzeugen. Damit sind sowohl für ARM 32bit als auch ARM 64bit eigene Arch Linux Kubernetes Pakete in überschaubarer Zeit und mit nur wenigen Handgriffen gebaut.

Wie das geht, wird im folgenden Beitrag erklärt.

Zunächst wird ein PKGBUILD benötigt. Das ist die Beschreibung, mit der ein Arch Linux Paket gebaut wird. Arch Linux bringt dafür ein eigenes Werkzeug mit, makepkg. Als Vorlage für das Kubernetes-PKGBUILD kann das PKGBUILD aus dem AUR Paket dienen. Nachdem die fehlenden Plattformen ergänzt wurden, sieht ein Kubernetes Version 1.11 PKGBUILD wie folgt aus:

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

pkgname=kubernetes
pkgver=1.11.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=('e597a3a73f4c4933e9fb145d398adfc4e245e4465bbea50b0e55c78d2b0e70ef'
            '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-aggregator 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
}

Bei der eigentlichen Installation wird noch ein Systemuser angelegt und ein Verzeichnis, in dem kubelet seine Daten ablegen kann. Dazu wird ein kubernetes.install Script verwendet, das im folgenden zu sehen ist.

kubernetes.install Datei für Kubernetes auf Arch Linux ARM
post_install() {
    getent group kube >/dev/null || groupadd -r kube
    getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin -c "Kubernetes user" kube
    chown -R kube:kube /var/lib/kubelet
}

post_upgrade() {
    post_install
}

Nun kann der eigentliche Build des Kubernetes ARM Paketes erfolgen. Dies geschieht mit makepkg -s, allerdings gibt es noch eine Sache zu beachten:

Für den Build werden erhebliche Mengen Hauptspeicher benötigt, sodass eine Maschine mit 2 GB zu wenig ist. Abhilfe kann da zram schaffen. Mit zram kann im Hauptspeicher ein Swap-Bereich angelegt werden, dessen Inhalt komprimiert ist. Auf diese Weise kann der Hauptspeicher effizienter genutzt werden, die CPU wird jedoch stärker belastet. Bei 2GB echtem RAM und einem Kompressionsverhältnis von 1:2 bis 1:3 sollten entsprechend auch 2 GB zram Speicher nutzbar sein.

Aktivierung von zram Auslagerungsspeicher
$ sudo modprobe zram
$ sudo swapoff -a
$ sudo zramctl --reset /dev/zram0
$ sudo zramctl --find --size 2000M
$ sudo mkswap /dev/zram0
$ sudo swapon /dev/zram0
$ free  -m
       total   used   free  shared  buff/cache   available
Mem:   1974     324    848       0         800        1628
Swap:  2047       0   2047

Falls das noch nicht reicht, kann zusätzlich eine Auslagerungsdatei angelegt werden, um darüber weiteren Swap Speicher bereitzustellen. Das geht natürlich mit entsprechenden Geschwindigkeitseinbußen einher, da so ein Swap Speicher auf dem relativ langsamen eMMC, SD oder SSD Sekundärspeicher angesiedelt ist.

Aktivierung von 1GB Swapspeicher in einer Datei
$ sudo fallocate -l 1000M /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile

Wichtig zu wissen ist, dass der Build von Kubernetes standardmäßig parallel mit allen CPU-Kernen abläuft. Damit multipliziert sich auch der Hauptspeicherbedarf mit der Anzahl paralleler Buildprozesse. Gerade bei sowieso knappem Speicher lohnt es sich also, die Parallelität einzuschränken.

Kubernetes ist in der Sprache go (golang) entwickelt und entsprechend muss die go-Option gesetzt werden, nicht alle Prozessoren zur Parallelisierung des Builds einzusetzen. Dies geschieht mit der Option "-p 1". Der Kubernetes Buildprozess hat für solche Parameter die Umgebungsvariable GOFLAGS definiert, darin kann dann -p 1 hinterlegt werden.

Kubernetes als nicht-paralleler Build um Speicher zu sparen
[tkruse@c2-master0 kubernetes]$ export GOFLAGS="-p 1"
[tkruse@c2-master0 kubernetes]$ makepkg -si
==> Making package: kubernetes 1.11.0-1 (Sun Jul 22 13:50:37 2018)
==> Checking runtime dependencies...
==> Checking buildtime dependencies...
==> Installing missing dependencies...
resolving dependencies...
looking for conflicting packages...

Packages (2) go-bindata-3.4.0-1  rsync-3.1.3-1

Total Download Size:   0.80 MiB
Total Installed Size:  2.91 MiB

:: Proceed with installation? [Y/n]
:: Retrieving packages...
 rsync-3.1.3-1-aarch64                                   276.4 KiB  4.50M/s 00:00 [##############################################] 100%
 go-bindata-3.4.0-1-aarch64                              541.7 KiB  5.29M/s 00:00 [##############################################] 100%
(2/2) checking keys in keyring                                                    [##############################################] 100%
(2/2) checking package integrity                                                  [##############################################] 100%
(2/2) loading package files                                                       [##############################################] 100%
(2/2) checking for file conflicts                                                 [##############################################] 100%
(2/2) checking available disk space                                               [##############################################] 100%
:: Processing package changes...
(1/2) installing rsync                                                            [##############################################] 100%
(2/2) installing go-bindata                                                       [##############################################] 100%
:: Running post-transaction hooks...
(1/2) Reloading system manager configuration...
(2/2) Arming ConditionNeedsUpdate...
==> Retrieving sources...
  -> Downloading kubernetes-1.11.0.tar.gz...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   161  100   161    0     0    288      0 --:--:-- --:--:-- --:--:--   288
100 23.2M  100 23.2M    0     0  6513k      0  0:00:03  0:00:03 --:--:-- 8400k
  -> Downloading f4ce29dd35b68f538a5845d7e294bbf056d5d215.tar.gz...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   160    0   160    0     0    305      0 --:--:-- --:--:-- --:--:--   304
100 15.9M    0 15.9M    0     0  3095k      0 --:--:--  0:00:05 --:--:-- 4278k
  -> Found kubernetes.install
==> Validating source files with sha256sums...
    kubernetes-1.11.0.tar.gz ... Passed
    f4ce29dd35b68f538a5845d7e294bbf056d5d215.tar.gz ... Passed
    kubernetes.install ... Passed
==> Extracting sources...
  -> Extracting f4ce29dd35b68f538a5845d7e294bbf056d5d215.tar.gz with bsdtar
==> Starting prepare()...
==> Starting build()...
+++ [0722 13:51:46] Building go targets for linux/arm64:
    ./vendor/k8s.io/code-generator/cmd/deepcopy-gen
+++ [0722 13:52:17] Building go targets for linux/arm64:
    ./vendor/k8s.io/code-generator/cmd/defaulter-gen
+++ [0722 13:52:38] Building go targets for linux/arm64:
    ./vendor/k8s.io/code-generator/cmd/conversion-gen
+++ [0722 13:53:02] Building go targets for linux/arm64:
    ./vendor/k8s.io/code-generator/cmd/openapi-gen
+++ [0722 13:53:33] Building go targets for linux/arm64:
    ./vendor/github.com/jteeuwen/go-bindata/go-bindata
+++ [0722 13:53:38] 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/kube-aggregator
    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
make: Entering directory '/home/tkruse/kubernetes/src/kubernetes-1.11.0'
make[1]: Entering directory '/home/tkruse/kubernetes/src/kubernetes-1.11.0'
make[1]: Leaving directory '/home/tkruse/kubernetes/src/kubernetes-1.11.0'
+++ [0722 16:35:53] Building go targets for linux/arm64:
    cmd/gendocs
    cmd/genkubedocs
    cmd/genman
    cmd/genyaml
make: Leaving directory '/home/tkruse/kubernetes/src/kubernetes-1.11.0'
==> Entering fakeroot environment...
==> Starting package()...
==> Tidying install...
  -> Removing libtool files...
  -> Purging unwanted files...
  -> Removing static library files...
  -> Stripping unneeded symbols from binaries and libraries...
  -> Compressing man and info pages...
==> Checking for packaging issues...
==> WARNING: Package contains reference to $srcdir
usr/bin/kubemark
usr/bin/kubectl
usr/bin/apiextensions-apiserver
usr/bin/kube-aggregator
usr/bin/cloud-controller-manager
usr/bin/kubeadm
usr/bin/kube-controller-manager
usr/bin/hyperkube
usr/bin/kubelet
usr/bin/kube-proxy
usr/bin/kube-scheduler
usr/bin/kube-apiserver
==> Creating package "kubernetes"...
  -> Generating .PKGINFO file...
  -> Generating .BUILDINFO file...
  -> Adding install file...
  -> Generating .MTREE file...
  -> Compressing package...
=> Leaving fakeroot environment.
==> Finished making: kubernetes 1.11.0-1 (Sun Jul 22 17:03:01 2018)
==> Installing package kubernetes with pacman -U...
loading packages...
resolving dependencies...
looking for conflicting packages...

Packages (1) kubernetes-1.11.0-1

Total Installed Size:  725.18 MiB

:: Proceed with installation? [Y/n]
(1/1) checking keys in keyring                                                     [################################################] 100%
(1/1) checking package integrity                                                   [################################################] 100%
(1/1) loading package files                                                        [################################################] 100%
(1/1) checking for file conflicts                                                  [################################################] 100%
(1/1) checking available disk space                                                [################################################] 100%
:: Processing package changes...
(1/1) installing kubernetes                                                        [################################################] 100%
Optional dependencies for kubernetes
    etcd: etcd cluster required to run Kubernetes
:: Running post-transaction hooks...
(1/3) Reloading system manager configuration...
(2/3) Creating temporary files...
(3/3) Arming ConditionNeedsUpdate...

Als Buildergebnis wird die Datei kubernetes-1.11.0-1-aarch64.pkg.tar.xz erzeugt. Diese kann nun lokal installiert werden, oder auf andere Knoten verteilt und dort installiert werden.

Installation von Kubernetes mit pacman auf Arch Linux ARM
$ sudo pacman -U kubernetes-1.11.0-1-aarch64.pkg.tar.xz
$ sudo pacman -U socat ethtool ebtables




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