containerbau mit kaniko

Containerbau mit Kaniko

Die Automatisierung von Container-Imagebau-Prozessen startet in aller Regel mit dem direkten Portieren von docker build in eine CI-Pipeline. Dieser Befehl ist an den Docker Daemon des Hosts gerichtet, welcher als Socket in die Pipeline Sandbox gemounted wird.

Problematisch ist das vor allem, weil es erlaubt, aus der Pipeline heraus beliebige Prozesse auf dem Host zu starten, ggf. ohne weitere Isolation von ihrem Umfeld. Das macht die CI Pipeline zum Sicherheitsrisiko. Umgehen lässt sich dieses Problem auf mehreren Wegen, welche alle mit ihren Vor- und Nachteilen daherkommen:

  • Der Docker Daemon selbst l√§sst sich in einem Container ausf√ľhren („Docker in Docker“). Die Pipeline spricht dann nur mit einem containerisierten Docker Daemon, was mehr Isolation verspricht. Problem hierbei ist vor allem, dass der Daemon Container weitreichende Kernelprivilegien ben√∂tigt, um seine Arbeit (im Wesentlichen das Starten weiterer Prozesse) verrichten zu k√∂nnen. Solche privilegierten Container gelten ihrerseits selbst als Sicherheitsrisiko, da man aus ihnen ausbrechen k√∂nnte.
  • Durch das Ersetzen von Docker durch Buildah l√§sst sich auf den Docker Daemon vollst√§ndig verzichten. Der Buildah Container selbst muss aber trotzdem privilegiert laufen, um Prozesse in den zu bauenden Containern starten zu k√∂nnen.
  • Beliebige Abwandlungen beider vorheriger Varianten durch andere Tools oder Config (z.B. „rootless Docker in Docker“, Docker mit Userspace Remapping, …).

Beide Lösungen benötigen privilegierte Container, welche u.A. in Kubernetes nicht akzeptabel sind. Eine Lösung ohne dieses Problem bietet Kaniko. Kaniko lässt sich in Docker, wie auch in Kubernetes betreiben und bietet eine containerisierte Umgebung zum Imagebau, ohne weitreichende Kernel-Capabilities zu benötigen.

Das Konzept hinter Kaniko

Da ein Kaniko Container nicht privilegiert l√§uft, funktioniert der Image Bauprozess im Container etwas anders als in Docker. Wesentlicher Unterschied ist hier, dass alle Phasen des Baus (Aufbau des Basis Filesystems, isoliertes Ausf√ľhren von Befehlen, Snapshotting des Filesystems) im User- statt im Systemspace stattfinden. Von Anwendungsseite merkt man hiervon allerdings nichts.

Die einzigen Unterschiede in der Bedienung kommen daher, dass ein Kaniko Container zum einmaligen Gebrauch ‚Äď also zum Bau eines einzigen Images ‚Äď gedacht ist. Danach sollte im Idealfall ein neuer Kaniko Container gestartet werden, um wieder in einer neuen sauberen Umgebung zu starten, denn Kaniko l√∂scht das Filesystem alter Container nicht automatisch. Alternativ gibt es zum „Aufr√§umen“ im laufenden Kaniko mittlerweile aber auch eine M√∂glichkeit (siehe sp√§ter), um den Bau mehrerer Images in einem Container zu erm√∂glichen. Best Practice in alter Kubernetes-Manier bleibt aber weiterhin „eine Aufgabe ‚Äď ein Pod“.

Verwendung

Bauen und Pushen eines Container Images mit Kaniko

Im Gegensatz zu Docker, Buildah, etc. ist das¬†offizielle Kaniko Container Image komplett minimalistisch aufgebaut. Insbesondere beinhaltet es keine Shell, weswegen alle Vorbereitungen zum Containerbau (auch das Bereitstellen von Container Registry Credentials) au√üerhalb von Kaniko stattfinden m√ľssen. Ein typischer Build Ablauf zum Pushen in eine Docker Registry sieht daher so aus:

  • Erstellen einer Docker¬†config.json mit Credentials via

cat 
{
    "auths": {
        "$REGISTRY": {
            "auth": "$(echo -n $USER:$PASS | base64)"
        }
    }
}
EOF
  • Starten eines Kaniko Containers, der diese Config unter¬†/kaniko/.docker/config.json¬†mountet. Der Build Context, einschlie√ülich des Dockerfiles, muss ebenfalls unter¬†/workspace gemountet werden.
docker run -it --rm --name kaniko-build
           -v "$WORKSPACE":/workspace 
           -v dockerconfig.json:/kaniko/.docker/config.json:ro
           gcr.io/kaniko-project/executor:latest
           --dockerfile="${DOCKERFILE:-Dockerfile}" 
           --destination="$REGISTRY/$IMAGE:$TAG"
Der dabei ausgef√ľhrte Entrypoint ist der¬†/kaniko/executor.

Am Ende des Builds pusht Kaniko das Containerimage direkt in die mittels¬†--destination¬†angegebene Registry. Mehrfache Tags f√ľr das selbe Image, z.B. die Kombination Commit SHA, Commit Reference (Branch / Tag) und "latest", sind √ľber Merfachverwendung des¬†--destination¬†Flags m√∂glich.

Bau mehrerer Images im selben Kaniko Container

Nach dem Push endet der Kaniko-Executor Prozess und der Container stoppt. Im Container befinden sich jetzt noch √úberreste des erstellten Filesystems, ein erneutes Ausf√ľhren von¬†/kaniko/executor¬†kann (und wird!) fehlerhafte Images produzieren. Dementsprechend ist es empfehlenswert, f√ľr weitere Builds einen neuen, frischen Kaniko Container zu verwenden. Ist das keine Option, so erlaubt das Ausf√ľhren vom¬†/kaniko/executor¬†mit dem Flag¬†--cleanup¬†das anschlie√üende "Aufr√§umen" tempor√§rer Dateien:

docker run -it --rm --name kaniko-build
           -v "$WORKSPACE":/workspace 
           -v dockerconfig.json:/kaniko/.docker/config.json:ro
           gcr.io/kaniko-project/executor:latest
           --dockerfile="${DOCKERFILE:-Dockerfile}" 
           --destination="$REGISTRY/$IMAGE:$TAG" 
           --cleanup

Dadurch dauert der Build Prozess ein bisschen länger, der Container wird aber wiederverwendbar.

Um nun auch noch mehrere Aufrufe des /kaniko/executors in Folge (im selben Prozess) zu ermöglichen, bedarf es einer Shell. Diese ist zwar im minimalen Image nicht enthalten, in den "debug" Tags allerdings schon. Somit lässt sich dann im Kaniko Container mittels

docker run -it --rm --name kaniko-multi-build
           -v "$WORKSPACE":/workspace 
           -v dockerconfig.json:/kaniko/.docker/config.json:ro
           -v build.sh:/kaniko/build.sh
           --entrypoint /kaniko/build.sh
           gcr.io/kaniko-project/executor:debug
ein lokales Skript aufrufen, welches mehrere Images in Folge baut. Dieses sieht dann z.B. so aus:
build.sh
#!/bin/sh
/kaniko/executor --dockerfile="Dockerfile1" --destination="registry.io/image1:tag1" --cleanup
/kaniko/executor --dockerfile="Dockerfile2" --destination="registry.io/image2:tag2"

Verschiedene Build-Kontext-Ordner lassen sich dabei auch √ľber das¬†--context¬†Flag ausw√§hlen.

Best Practices

  • Kaniko muss zwar nicht als privilegierter Container laufen, Root-Rechte im Container (RunAsUser: 0) sind aber trotzdem im Laufe des Containerbaus n√∂tig, beispielsweise um Pakete zu installieren. Um dieses verbleibende Problem noch zu "entsch√§rfen", bedarf es beispielsweise¬†User-Namespace Remappings. Diese stehen leider unter Kubernetes (noch) nicht zur Verf√ľgung, weswegen f√ľr Kaniko ggf. bestehende Security Policies / Admission Rules aufgeweicht werden m√ľssen. Kaniko Pods sollten daher in Kubernetes immer noch nach M√∂glichkeit auf gesonderten Nodes laufen, die sie sich nicht mit anderen Workloads teilen.
  • Wie oben beschrieben ist es durch die "debug" Images m√∂glich, im selben Kaniko Container mehrere Images zu bauen. Trotzdem bleibt ein neuer, frischer Container f√ľr jedes Image weiterhin Best Practice. Grund daf√ľr ist die bessere Isolation der individuellen Build Prozesse.
  • Die Abwesenheit einer Shell im Image erfordert etwas umzudenken. Soll etwa eine Pipeline eine dynamische Anzahl an Images bauen (z.B. f√ľr die Automatisierte Wartung von Basisimages), so ist erst eine Imageliste au√üerhalb des Build Containers (in einem Vorbereitungsschritt) zu erstellen. Danach muss die Pipeline, ausgehend von dieser Liste, pro Image einen Kaniko Container starten. In GitLab l√§sst sich das beispielsweise mit einer¬†parallelen Job-Matrix oder, wenn die Anzahl der verschiedenen Image-Tags erst zur Laufzeit der Pipeline bekannt wird, mit dynamischen Child Pipelines realisieren. Diese Vorgehensweise hat neben der besseren Isolation auch noch einen weiteren Vorteil: Alle Build Prozesse laufen parallel ab, weswegen die Pipeline wesentlich schneller durchlaufen kann.

Sie ben√∂tigen Unterst√ľtzung oder Beratung bei der Umsetzung von IT-Automatisierungen oder Container-Plattformen? Wir beraten Sie gerne: info@atix.de

The following two tabs change content below.

Pascal Fries

Als IT Consultant f√ľr Cloud Native Technologien ber√§t Pascal Fries unsere Kunden in den Themen Infrastructure as Code und Continuous Deployment, insbesondere im Containerumfeld.

Neueste Artikel von Pascal Fries (alle ansehen)