Neuigkeiten von trion.
Immer gut informiert.

Kaniko ARM Image

Kaniko

Kaniko ist in Version 1.0 erschienen, damit ist es Zeit, sich das Werkzeug genauer anzusehen. Bereits in Mit Kaniko Docker Images ohne Daemon bauen und Mit Kaniko Docker Images in Kubernetes bauen wurde Kaniko vorgestellt. Auch wenn sich Kaniko weiterentwickelt hat - so braucht man keine root-Rechte mehr, um ein Image zu bauen - so gibt es leider noch kein ARM oder ARM64 Image.

In diesem Beitrag geht es also darum, wie auf Basis eines existierenden Dockerfile ein Build in Kubernetes durchgeführt werden kann. Dabei wird der Docker-in-Docker Ansatz gewählt, womit auch bei einer anderen Container-Runtime wie rkt oder CRI-O keine Probleme auftreten.

Das grundsätzliche Vorgehen gliedert sich in folgende Schritte:

  1. Bereitstellung von Docker-in-Docker als Container

  2. git-clone des Source Repositories

  3. Bauen des Images

  4. Pushen in eine Registry

Kubernetes stellt über das Pod-Konzept alles zur Verfügung, was für einen einfachen Build benötigt wird. Es gibt jedoch zwei Gründe, kein Pod zu nehmen, sondern auf einen Job zurückzugreifen:
Falls bei der Ausführung etwas in der Infrastrutkur schief geht, wird Kubernetes den Job auf einem anderen Knoten erneut anstarten, um sicherzustellen, dass der Job erfolgreich durchgeführt wird. Zum anderen kann ein Job automatisch die zugrundeliegenden Resourcen (den erzeugten Pod) abräumen, wenn die Ausführung erfolgreich war. (Diese API ist derzeit noch Beta und muss durch ein Feature-Toggle im Kubernetes Cluster aktiviert werden.)

Da in einem Pod mehrere Container gleichzeitig existieren, ist es sehr einfach sowohl einen Docker-Daemon als Container, als auch den eigentlichen Build in einem Container in einem gemeinsamen Pod auszuführen. Für das git-clone wird auf einen init-Container zurückgegriffen.

Als Container Registry wird eine in Kubernetes deployte Registry verwendet. Um zu demonstrieren, wie der Zugriff hier ohne TLS Absicherung erfolgen kann, ist der Schalter --insecure-registry gesetzt worden.

Die größte Herausforderung besteht darin, den eigentlich für 64bit Intel/AMD Plattformen konzipierten Build so zu patchen, dass dabei ein ARM kompatibles Image entsteht. Dazu wird das Dockerfile mit sed soweit bearbeitet, dass die Plattform für ARM passt, wie in Zeile 50 zu sehen ist.

So sieht der Kubernetes Build Job als YAML dann aus:

Beispiel für einen Kubernetes Build Job mit Docker-in-Docker (DinD)
---
apiVersion: batch/v1
kind: Job
metadata:
  name: kaniko-build
  labels:
    run: dind
    name: dind
spec:
  ttlSecondsAfterFinished: 10
  template:
    spec:
      initContainers:
        -
          name: git-clone
          image: alpine
          args:
            - /bin/sh
            - '-c'
            - 'apk add -U --no-cache git; git clone --depth=1 --single-branch https://github.com/GoogleContainerTools/kaniko.git /repo; echo "done"'
          volumeMounts:
            -
              name: git-repo
              mountPath: /repo
      containers:
        -
          image: 'docker:dind'
          name: dind
          args:
            - '--insecure-registry=registry.docker-registry:5000'
          workingDir: /repo
          securityContext:
            privileged: true
          volumeMounts:
            -
              name: cache-volume
              mountPath: /tmp
            -
              name: container-volume
              mountPath: /var/lib/docker
            -
              name: git-repo
              mountPath: /repo
        -
          image: 'docker:dind'
          name: docker
          args:
            - /bin/sh
            - '-c'
            - 'sed -i "20,21d" deploy/Dockerfile; sed -i "20iRUN export GOPATH=/usr/local; go get -u github.com/GoogleCloudPlatform/docker-credential-gcr; export GOPATH=/go;" deploy/Dockerfile; sed -i s@linux-amd64@linux-arm64@g deploy/Dockerfile; sed -i "23d" deploy/Dockerfile; sed -i "30d" deploy/Dockerfile; sed -i "30iCOPY --from=0 /go/bin/docker-credential-ecr-login /kaniko/docker-credential-ecr-login" deploy/Dockerfile; sed -i s@amd64@arm64@g Makefile; sed -i ''s@go get@go get -ldflags "-linkmode=external \"-extldflags=-static\""@g'' deploy/Dockerfile;  docker build -f deploy/Dockerfile -t registry.docker-registry:5000/kaniko/executor .;  docker images; docker push  registry.docker-registry:5000/kaniko/executor; echo "completed"'
          workingDir: /repo
          env:
            -
              name: DOCKER_HOST
              value: 'tcp://localhost:2375'
          volumeMounts:
            -
              name: cache-volume
              mountPath: /tmp
            -
              name: git-repo
              mountPath: /repo
      volumes:
        -
          name: container-volume
          emptyDir: {}
        -
          name: cache-volume
          emptyDir: {}
        -
          name: git-repo
          emptyDir: {}
      restartPolicy: Never

Die Ausführung kann über das Kubernetes Dashboard oder auch kubectl erfolgen.

Ausführung des Build Jobs
$ kubectl apply -f build.yaml
job.batch/kaniko-build created

Die Logdateien können ebenfalls mit kubectl abgefragt werden.

Ausgabe der Build Logs
$ kubectl get pods
NAME                 READY   STATUS     RESTARTS   AGE
kaniko-build-bstxt   0/2     Init:0/1   0          28s
$ kubectl logs kaniko-build-bstxt git-clone
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/aarch64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/aarch64/APKINDEX.tar.gz
(1/7) Installing ca-certificates (20190108-r0)
(2/7) Installing nghttp2-libs (1.35.1-r0)
(3/7) Installing libssh2 (1.8.0-r4)
(4/7) Installing libcurl (7.63.0-r0)
(5/7) Installing expat (2.2.6-r0)
(6/7) Installing pcre2 (10.32-r1)
(7/7) Installing git (2.20.1-r0)
Executing busybox-1.29.3-r10.trigger
Executing ca-certificates-20190108-r0.trigger
OK: 20 MiB in 21 packages
Cloning into '/repo'...
done
$ kubectl logs kaniko-build-bstxt docker
kubectl logs kaniko-build-hhpcf docker -f
Sending build context to Docker daemon  57.99MB
Step 1/21 : FROM golang:1.10
1.10: Pulling from library/golang
ed1ec7511bfa: Pulling fs layer
adb2bf3fc888: Pulling fs layer
795dadfe0a1e: Pulling fs layer
b3909cbce858: Pulling fs layer
7bf622939e59: Pulling fs layer
3280726fcda5: Pulling fs layer
5e93c19987c8: Pulling fs layer
b3909cbce858: Waiting
7bf622939e59: Waiting
3280726fcda5: Waiting
5e93c19987c8: Waiting
795dadfe0a1e: Verifying Checksum
795dadfe0a1e: Download complete
adb2bf3fc888: Verifying Checksum
adb2bf3fc888: Download complete
7bf622939e59: Verifying Checksum
7bf622939e59: Download complete
b3909cbce858: Verifying Checksum
b3909cbce858: Download complete
ed1ec7511bfa: Download complete
5e93c19987c8: Verifying Checksum
5e93c19987c8: Download complete
ed1ec7511bfa: Pull complete
3280726fcda5: Download complete
adb2bf3fc888: Pull complete
795dadfe0a1e: Pull complete
b3909cbce858: Pull complete
7bf622939e59: Pull complete
3280726fcda5: Pull complete
5e93c19987c8: Pull complete
Digest: sha256:5dd0c78e9f176d942cb6d521a9abfc0c1052f838205c3493ca1058f85f60a26c
Status: Downloaded newer image for golang:1.10
 ---> eb3d576e8e53
Step 2/21 : WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
 ---> Running in 7cdfc799ba9f
Removing intermediate container 7cdfc799ba9f
 ---> a9965aab5a18
Step 3/21 : RUN export GOPATH=/usr/local; go get -ldflags "-linkmode=external "-extldflags=-static"" -u github.com/GoogleCloudPlatform/docker-credential-gcr; export GOPATH=/go;
 ---> Running in 0e4878720e11
# github.com/GoogleCloudPlatform/docker-credential-gcr
/tmp/go-link-997980692/000002.o: In function `mygetgrouplist':
/workdir/go/src/os/user/getgrouplist_unix.go:15: warning: Using 'getgrouplist' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/tmp/go-link-997980692/000001.o: In function `mygetgrgid_r':
/workdir/go/src/os/user/cgo_lookup_unix.go:38: warning: Using 'getgrgid_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/tmp/go-link-997980692/000001.o: In function `mygetgrnam_r':
/workdir/go/src/os/user/cgo_lookup_unix.go:43: warning: Using 'getgrnam_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/tmp/go-link-997980692/000001.o: In function `mygetpwnam_r':
/workdir/go/src/os/user/cgo_lookup_unix.go:33: warning: Using 'getpwnam_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/tmp/go-link-997980692/000001.o: In function `mygetpwuid_r':
/workdir/go/src/os/user/cgo_lookup_unix.go:28: warning: Using 'getpwuid_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/tmp/go-link-997980692/000008.o: In function `_cgo_f7895c2c5a3a_C2func_getaddrinfo':
/tmp/go-build/cgo-gcc-prolog:46: warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
Removing intermediate container 0e4878720e11
 ---> ce7ac3857bf4
Step 4/21 : RUN go get -ldflags "-linkmode=external "-extldflags=-static"" -u github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
 ---> Running in 730f6508119f
# github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
/tmp/go-link-675467286/000004.o: In function `_cgo_f7895c2c5a3a_C2func_getaddrinfo':
/tmp/go-build/cgo-gcc-prolog:46: warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
Removing intermediate container 730f6508119f
 ---> 81027d21d1b8
Step 5/21 : COPY . .
 ---> f808ddf280ea
Step 6/21 : RUN make
 ---> Running in 4f4f46741951
GOARCH=arm64 GOOS=linux CGO_ENABLED=0 go build -ldflags '-extldflags "-static" -X .version=v0.7.0 -w -s  ' -o out/executor github.com/GoogleContainerTools/kaniko/cmd/executor
Removing intermediate container 4f4f46741951
 ---> e1c8f1895e95
Step 7/21 : FROM scratch
 --->
Step 8/21 : COPY --from=0 /go/src/github.com/GoogleContainerTools/kaniko/out/executor /kaniko/executor
 ---> 744a964af7db
Step 9/21 : COPY --from=0 /usr/local/bin/docker-credential-gcr /kaniko/docker-credential-gcr
 ---> 79235c4cb3cd
Step 10/21 : COPY --from=0 /go/bin/docker-credential-ecr-login /kaniko/docker-credential-ecr-login
 ---> 323a4aef318c
Step 11/21 : COPY files/ca-certificates.crt /kaniko/ssl/certs/
 ---> 16ae1dc75d5a
Step 12/21 : COPY files/config.json /kaniko/.docker/
 ---> 1f30715d9a5a
Step 13/21 : ENV HOME /root
 ---> Running in cf540316e35a
Removing intermediate container cf540316e35a
 ---> 429dac8e6456
Step 14/21 : ENV USER /root
 ---> Running in 5c467698a942
Removing intermediate container 5c467698a942
 ---> 892bdf0e7732
Step 15/21 : ENV PATH /usr/local/bin:/kaniko
 ---> Running in d67564654dab
Removing intermediate container d67564654dab
 ---> 3d2d377d561e
Step 16/21 : ENV SSL_CERT_DIR=/kaniko/ssl/certs
 ---> Running in b0c2a8332e21
Removing intermediate container b0c2a8332e21
 ---> 19d06ba79e5d
Step 17/21 : ENV DOCKER_CONFIG /kaniko/.docker/
 ---> Running in b8650dc8f683
Removing intermediate container b8650dc8f683
 ---> 8e8f8f799b65
Step 18/21 : ENV DOCKER_CREDENTIAL_GCR_CONFIG /kaniko/.config/gcloud/docker_credential_gcr_config.json
 ---> Running in 2a22a1de5f62
Removing intermediate container 2a22a1de5f62
 ---> 1f82a5a82c46
Step 19/21 : WORKDIR /workspace
 ---> Running in 3ec3afa4f6ce
Removing intermediate container 3ec3afa4f6ce
 ---> 3729042b500b
Step 20/21 : RUN ["docker-credential-gcr", "config", "--token-source=env"]
 ---> Running in 976b3ea8379f
Success: Token source(s) set.
Removing intermediate container 976b3ea8379f
 ---> cb7b6647a9b4
Step 21/21 : ENTRYPOINT ["/kaniko/executor"]
 ---> Running in a5b5334d86a2
Removing intermediate container a5b5334d86a2
 ---> 7fb1f7e0027d
Successfully built 7fb1f7e0027d
Successfully tagged registry.docker-registry:5000/kaniko/executor:latest
REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
registry.docker-registry:5000/kaniko/executor   latest              7fb1f7e0027d        1 second ago        51.7MB
<none>                                          <none>              e1c8f1895e95        25 seconds ago      871MB
golang                                          1.10                eb3d576e8e53        7 days ago          635MB
The push refers to repository [registry.docker-registry:5000/kaniko/executor]
8d424637be48: Preparing
a0a1c34efa68: Preparing
4e736a237f73: Preparing
f620da285b8f: Preparing
92249c608a34: Preparing
f3a681064861: Preparing
fe2343aeb32b: Preparing
f3a681064861: Waiting
fe2343aeb32b: Waiting
4e736a237f73: Pushed
a0a1c34efa68: Pushed
8d424637be48: Pushed
f620da285b8f: Pushed
f3a681064861: Pushed
92249c608a34: Pushed
fe2343aeb32b: Pushed
latest: digest: sha256:1929d65a810c0ba440d7a107bf8d8ce784069847b7c5592727ed324c49adce3b size: 1781
completed

Mit dem so gebauten Image kann Kaniko auch in ARM basierten Kubernetes Clustern eingesetzt werden. Wer den Eigenbau scheut, kann darauf hoffen, dass Google bald ein offizielles Image bereitstellt.




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!

Los geht's!

Bitte teilen Sie uns mit, wie wir Sie am besten erreichen können.