Skip to main content

Container-Images einfach erstellt mit Ko

Artikel von
feature building go images

10. Oktober 2022

0 Min. Lesezeit

In einem früheren Artikel habe ich beschrieben, wie — und warum — Sie das Tool Jib der Google Open Source Group zum Erstellen von Container-Images für Ihre Java-Anwendung nutzen können. Jib erstellt schlanke, JVM-basierte, OCI-konforme Images, die Best Practices entsprechen – ganz ohne Container-Runtime wie Docker. Außerdem müssen Sie damit keine Dockerfiles schreiben und verwalten. Was aber, wenn Sie Go-Anwendungen entwickeln? Dafür gibt es ein weiteres Open-Source-Tool für Go, das ähnlich funktioniert: Ko.

Hinweis: Während der Erstellung dieses Artikels wurde das Ko-Projekt aus einem Google gehörenden GitHub-Repository in eine eigene Organisation auf oberster Ebene namens „ko-build“ migriert. Wir haben den Wortlaut dieses Beitrags entsprechend angepasst. Verweise auf „Google Ko“ finden sich jedoch möglicherweise noch eine Weile online und in Modulnamen. Sie können sicher sein: Es handelt sich um dasselbe Projekt. Herzlichen Glückwunsch an das Ko-Projekt zu diesem Erfolg, der die Migration ermöglicht hat!

In diesem Artikel erfahren Sie, wie Sie mit Ko Container-Images ohne Dockerfiles erstellen, SBOMs generieren und Kubernetes integrieren.

Was ist Ko?

Ko ist ein einzelnes Binärprogramm und Kommandozeilentool, das in Ihrem Entwicklungsprozess dort eingesetzt werden kann, wo Sie heute den go-Compiler ausführen. Neben dem Kompilieren Ihrer Anwendung erstellt es ein extrem schlankes Container-Image, in dem Ihre Anwendung installiert ist. Wie Jib überträgt Ko das Image je nach Konfiguration und Ausführung in eine Registry oder legt es im lokalen Docker-Image-Cache ab.

Ko bietet außerdem einige praktische Funktionen rund um die Erstellung einer Software-Stückliste (SBOM) sowie die Kubernetes-Integration, die iterative Entwicklungs- und Bereitstellungsprozesse erheblich vereinfachen.

Welche Probleme soll Ko lösen?

Viele Programmiererinnen und Programmierer sind unabhängig von der verwendeten Sprache noch neu beim Erstellen von Containern und haben häufig Fragen zum Erstellen von Images, zum Beispiel:

  • Welches Basis-Image sollte ich verwenden, und entspricht es den Richtlinien meiner Organisation?

  • Wie kombiniere ich Befehle am besten, um überflüssige Image-Layer zu vermeiden?

  • Wie viel von meiner Anwendung muss ich in das Image kopieren, damit sie ausgeführt werden kann?

  • Welche Tools muss ich beherrschen, um das Image zu erstellen? (Docker? Buildah? BuildKit?)

  • Muss ich gemäß den Anforderungen meiner Organisation bestimmte Standardannotationen einfügen?

Anwendungsarchitektinnen und -architekten sowie Teamleitungen möchten außerdem dafür sorgen, dass ihre Teams Governance-Vorgaben und Standards einfach einhalten können. Einheitliche Vorgehensweisen aufrechtzuerhalten, kann jedoch schwierig sein, wenn jedes Team eigene, organisch gewachsene Dockerfile-Muster hat.

Auch Sicherheitsteams achten genau darauf, was in ein Image gelangt und welche Tools bei seiner Erstellung zum Einsatz kommen. Wird beispielsweise die Docker-Engine – oder der docker.sock des Host-Computers – für einen CI-Build-Node zugänglich gemacht, können Build-Umgebungen auf diesen Nodes erhöhte Zugriffsrechte erhalten.

„Der [Docker-]Daemon … kann weit mehr als Images erstellen und mit Registries interagieren. Ohne zusätzliche Sicherheitstools kann jeder Benutzer, der auf diesem Computer einen Docker-Build auslösen kann, auch einen Docker-Run ausführen, um jeden beliebigen Befehl auf dem Computer auszuführen … Er kann also nicht nur beliebige Befehle ausführen, sondern es ist auch schwierig nachzuverfolgen, wer für eine schädliche Aktion verantwortlich war, wenn er diese Berechtigung dafür nutzt.“

— Container Security von Liz Rice, Kapitel 6

Andererseits kann die Einführung neuer Build-Tools komplex sein und den Lernaufwand für die Verantwortlichen der Build-Systeme erhöhen. Ko soll all diese Herausforderungen angehen und zugleich Entwicklerinnen und Entwicklern eine bessere Nutzererfahrung bieten.

Images mit und ohne Ko erstellen

Um zu zeigen, wie Ko die oben genannten Herausforderungen angeht, sehen wir uns zunächst an, wie Ko im Vergleich zu herkömmlichen Go- und Docker-Build-Schritten abschneidet.

Images ohne Ko erstellen

In diesem Beispiel erstellen wir die klassische Go-Tutorial-Anwendung: eine „Hello World“-Web-App.

1. Erstellen Sie in einem leeren Verzeichnis die folgende Datei mit dem Namen hello.go:

package main

import (
"fmt"
"net/http"
)

func main() {
http.HandleFunc("/", HelloWebServer)
http.ListenAndServe(":8080", nil)
}

func HelloWebServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

2. Wir erstellen die Binärdatei direkt in unserem Dockerfile – das ist ein gängiges Muster:

FROM golang AS build
WORKDIR /go/src
COPY . .
RUN GOOS=linux go build -ldflags "-linkmode external -extldflags -static" -a hello.go

FROM gcr.io/distroless/static:nonroot
USER nonroot:nonroot
COPY --from=build /go/src/hello .
EXPOSE 8080
CMD ["./hello"]

Dies ist ein Multi-Stage-Build. Die erste Stage mit dem Namen build basiert auf dem offiziellen golang-Image. In dieser Stage kopieren wir den Inhalt unserer Anwendung (derzeit nur die Datei hello.go) in das Image unter /go/src und führen einfach das Tool go build mit den Parametern aus, die zum Erstellen einer statischen Binärdatei erforderlich sind.

In der zweiten Stage verwenden wir das Basis-Image static:nonroot des Google-Distroless-Projekts, legen nonroot als Standardbenutzer fest, kopieren die Binärdatei hello aus der Stage build in das Stammverzeichnis, hinterlegen Metadaten zum freizugebenden Port und legen fest, dass beim Start des Containers standardmäßig ./hello ausgeführt wird.

Warum zwei Stages?

Es ist üblich, Ihre Anwendung im Dockerfile zu erstellen. So stellen Sie sicher, dass sowohl auf dem Rechner einer Entwicklerin oder eines Entwicklers als auch bei automatisierten Builds die richtige Compiler-Version verwendet wird. Ein häufiger Fehler ist jedoch, dasselbe Image bereitzustellen, in dem der Build stattgefunden hat. Das ist ein Sicherheitsrisiko, da das bereitgestellte Image dann nicht nur unnötige Tools wie den Compiler, sondern auch den Quellcode Ihrer Anwendung enthält. Kann ein Angreifer eine Schwachstelle ausnutzen oder eine Kopie Ihres Images erhalten, stehen ihm viele Informationen und Tools zur Verfügung, um seinen Angriff auszuweiten. Mit dem Basis-Image distroless/static:nonroot erhalten wir ein äußerst minimales Dateisystem mit einem Nicht-Root-Standardbenutzer.

3. Nachdem unser Dockerfile fertig ist, können wir mit dem Befehl docker build ein lokal zwischengespeichertes Image erstellen und mit einem Tag versehen:

$ docker build -t localhost:5000/hello-go:1.2.3 .
[+] Building 5.6s (12/12) FINISHED
 => [internal] load build definition from Dockerfile
 => => transferring dockerfile: 298B
 => [internal] load .dockerignore
 => => transferring context: 73B
 => [internal] load metadata for gcr.io/distroless/static:nonroot
 => [internal] load metadata for docker.io/library/golang:latest
 => [stage-1 1/2] FROM gcr.io/distroless/static:nonroot
 => CACHED [build 1/4] FROM docker.io/library/golang
 => [internal] load build context
 => => transferring context: 5.15kB
 => [build 2/4] WORKDIR /go/src
 => [build 3/4] COPY . .
 => [build 4/4] RUN GOOS=linux go build -ldflags "-linkmode external -extldflags -static" -a hello.go
 => [stage-1 2/2] COPY --from=build /go/src/hello .
 => exporting to image
 => => exporting layers
 => => writing image sha256:eb9735e61e1dec63bd557dd5c61d8789733f2f4456a0d01687816bd0d135a7bc
 => => naming to localhost:5000/hello-go:1.2.3

$ docker images
REPOSITORY               TAG    IMAGE ID      CREATED         SIZE
localhost:5000/hello-go  1.2.3  eb9735e61e1d  11 minutes ago  9.23MB

Hinweis: Ihre Ausgabe kann etwas anders aussehen – je nachdem, welche Docker-Version Sie verwenden und ob BuildKit-Erweiterungen aktiviert sind.

4. Damit andere dieses Image verwenden können, müssen wir es in ein Registry-Repository übertragen. In diesem Beispiel betreibe ich auf meinem Rechner ein lokales Repository auf Port 5000, und zwar über das registry:2-Image von DockerHub. (Deshalb habe ich dem Image das Präfix localhost:5000/ gegeben.)

Mit Docker lässt sich ein Image ganz einfach über den Befehl docker push in eine Registry übertragen:

$ docker push localhost:5000/hello-go:1.2.3
The push refers to repository [localhost:5000/hello-go]
0ba424468cb9: Pushed
ca623f32e759: Pushed
1.2.3: digest: sha256:ddcabff499d90fdf6850ef0b7addb33db7def…

Hinweis: Bei einer verwalteten Registry müsste ich mich zuerst mit docker login anmelden.

5. Zum Ausführen unseres Images können wir schließlich docker run oder ein entsprechendes kubectl-Deployment verwenden. Der Einfachheit halber nutzen wir die erste Möglichkeit:

$ docker run --rm -d -p 8080:8080 localhost:5000/hello-go:1.2.3
Unable to find image 'localhost:5000/hello-go:1.2.3' locally
1.2.3: Pulling from hello-go
45e68f4d0d8c: Already exists
ce1a03145a01: Already exists
Digest: sha256:ddcabff499d90fdf6850ef0b7addb33db7defe4669f9af1079894e84ad407199
Status: Downloaded newer image for localhost:5000/hello-go:1.2.3
2ef3c3dc0363ad10e9bf21baa7e78c63ca4df904bcba1fdab3af7732fce3857d

Anschließend können wir die Anwendung mit einem einfachen curl-Befehl testen:

$ curl http://localhost:8080/Patch
Hello, Patch!

Die gerade ausgeführten Schritte könnten visualisiert etwa so aussehen:

Diagramm mit dem Go- und Docker-Workflow: vom Go-Build und Dockerfile über das Docker-Image und den Push in die Registry bis zum Container- oder Kubernetes-Start.

Gehen wir nun zum Anfang zurück und sehen uns an, wie das mit Ko funktioniert.

Images mit Ko erstellen

1. Wir beginnen mit derselben Datei hello.go in einem leeren Verzeichnis:

package main

import (
"fmt"
"net/http"
)

func main() {
http.HandleFunc("/", HelloWebServer)
http.ListenAndServe(":8080", nil)
}

func HelloWebServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

2. Als Nächstes speichern wir die Registry für unser Image in einer Umgebungsvariable und führen ko build aus:

$ export KO_DOCKER_REPO=localhost:5000
$ ko build hello.go
2022/09/19 14:12:37 No matching credentials were found, falling back on anonymous
2022/09/19 14:12:40 Using base gcr.io/distroless/static:nonroot@sha256:2a9e2b4fa771d31fe3346a873be845bfc2159695b9f90ca08e950497006ccc2e for hello.go
2022/09/19 14:12:40 Building hello.go for linux/amd64
2022/09/19 14:12:44 Publishing localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest
2022/09/19 14:12:44 existing blob: sha256:2952e4f69ebf4bea5cc557f73626f95649cb546424fd998481ba690a08d9db7f
2022/09/19 14:12:44 existing blob: sha256:a706af0bb599ee120bd57c0e6abca55f66fd714f9e74706d9c97a583fc79d37e
2022/09/19 14:12:44 localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:sha256-3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c.sbom: digest: sha256:7d444debc3cd2d5545e88606dc529fa7ed90b1bb581ff8ac30b0f475b68d4ec0 size: 367
2022/09/19 14:12:44 Published SBOM localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:sha256-3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c.sbom
2022/09/19 14:12:44 existing blob: sha256:5ec5232d47ab0ad088792a191b672fe6ec27db63b19daebd7322ad64a2cd8676
2022/09/19 14:12:44 existing blob: sha256:250c06f7c38e52dc77e5c7586c3e40280dc7ff9bb9007c396e06d96736cf8542
2022/09/19 14:12:45 pushed blob: sha256:2d23903e55394a021ba4936cfad8ccbec6998413164416fcae4f6bf888665fce
2022/09/19 14:12:45 pushed blob: sha256:1cd0595314a53d179ddaf68761c9f40c4d9d1bcd3f692d1c005938dac2993db6
2022/09/19 14:12:45 localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest: digest: sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c size: 750
2022/09/19 14:12:45 Published localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7@sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c
localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7@sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c

Dieser Befehl:

  • kompilierte unsere Anwendung zu einer statischen Binärdatei,

  • packte sie in ein korrekt aufgebautes Image und

  • übertrug es in meine Registry.

Für all das waren weder ein Dockerfile noch eine Container-Runtime erforderlich. Außerdem sehen Sie in der Ausgabe Hinweise darauf, dass eine SBOM erstellt und veröffentlicht wurde. Darauf kommen wir später zurück.

Hinweis:Der Image-Tag enthält hier einen MD5-Hash, der standardmäßig aus dem Importpfad Ihrer Go-Anwendung gebildet wird. Da wir in diesem Beispiel kein vollständiges Go-Modul haben, wird der Hash einfach aus hello.go abgeleitet. Weitere Informationen zu Tags finden Sie in der Ko-Dokumentation.

3. Nun führen wir das Image mit docker run aus:

$ docker run --rm -d -p 8080:8080 localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest
Unable to find image 'localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest' locally
latest: Pulling from hello.go-7a204cfb24536a350234d9132276cae7
1cd0595314a5: Pull complete
250c06f7c38e: Pull complete
5ec5232d47ab: Pull complete
Digest: sha256:3925979ac92afb8cb89fc80438097873daf067195e4d0b9d2fd6f55d6201355c
Status: Downloaded newer image for localhost:5000/hello.go-7a204cfb24536a350234d9132276cae7:latest
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
70877dcd53d23c66873e8e1e5a2092b46740e699b214a545e386113e5e098d7c

… und testen es mit curl:

curl http://localhost:8080/ko
Hello, ko!

Die Ko-Pipeline könnte visualisiert etwa so aussehen:

Diagramm: KO Build sendet Images an eine Registry. Anschließend starten Docker oder Kubernetes einen Container und ziehen das Image dabei automatisch.

Indem Ko die aufwendige Image-Erstellung und das Übertragen übernimmt, konnten wir die Anzahl der erforderlichen Schritte, die Abhängigkeit von einer Container-Runtime zur Build-Zeit sowie den mit Dockerfiles verbundenen Aufwand für Fachwissen und Wartung reduzieren.

Sinnvolle Standardeinstellungen, deterministische Anpassungen

Ko-Nutzerinnen und -Nutzer profitieren von den gemeinsam definierten Best Practices der Open-Source-Community. Was aber, wenn Ihre Organisation eigene Standards hat, die davon abweichen? Hier helfen die Kommandozeilenoptionen von Ko und/oder die Konfigurationsdatei ko.yaml.

Benutzerdefiniertes Basis-Image

Angenommen, Ihr Unternehmen schreibt ein bestimmtes Image als Basis für alle Go-Anwendungen vor. Fügen Sie einfach in einer Datei ko.yaml im Stammverzeichnis die Zeile defaultBaseImage: ein und geben Sie dort den gewünschten Image-Namen an.

defaultBaseImage: repo.mycorp.com/myteam/corp-approved-scratch:220923

Go-Compiler-Flags

Um die Go-Compiler-Option -ldflags wie in unserem ursprünglichen Beispiel ausdrücklich anzugeben, fügen Sie der Datei ko.yaml einfach einen Eintrag builds: mit den passenden Einstellungen hinzu, wie in der Ko-Dokumentation beschrieben.

builds:
 - id: hello
   dir: .
   main: hello.go
   env:
     - GOOS=linux
     - CGO_ENABLED=0
   ldflags:
     - -extldflags "-static"
     - -linkmode external

Docker-Labels

Docker-Labels – auch OCI-Annotationen genannt – ermöglichen es, einem Image Metadaten hinzuzufügen. Diese können beispielsweise auf das Quell-Repository, den CI-Build, der das Image erstellt hat, oder andere Daten verweisen, die Ihr Team hinterlegen möchte. Weitere Informationen zu Image-Labels und ihrer Verwendung finden Sie in meinem Blogbeitrag Docker-Labels: Wie und wann Sie sie verwenden sollten.

Ko unterstützt das Hinzufügen von Labels über das Kommandozeilen-Flag --image-label:

$ ko build hello.go --image-label foo=bar -L –image-label org.opencontainers.image.source=https://repo.mycorp.com/superteam/hello
…
ko.local/hello.go-7a204cfb24536a350234d9132276cae7:acf1dce794131205d488ed8fe4818866ebf509a61f4cf60aa07463e2b054d97d

$ docker image inspect ko.local/hello.go-7a204cfb24536a350234d9132276cae7:latest --format '{{json .Config.Labels}}'
{"foo":"bar","org.opencontainers.image.source":"https://repo.mycorp.com/superteam/hello"}

Hinweis: Zum Zeitpunkt der Veröffentlichung dieses Artikels lassen sich Image-Labels noch nicht in der Konfigurationsdatei .ko.yaml festlegen. Für diese Funktion gibt es jedoch bereits ein offenes Issue.

Weitere interessante Ko-Funktionen

SBOMs für Images

Wie oben zu sehen, erstellt Ko automatisch eine SBOM für Ihr neues Container-Image und überträgt sie. Standardmäßig wird das SPDX-Format verwendet. Sie können aber auch CycloneDX wählen, indem Sie in der Kommandozeile das Flag --sbom=cyclonedx übergeben. Mit --sbom=none lässt sich die Funktion deaktivieren. Diese und weitere Konfigurationsdetails finden Sie in der Ko-Dokumentation.

Eine ausführliche Erläuterung von SBOMs und ihrer Bedeutung für eine sichere Supply Chain würde den Rahmen dieses Artikels sprengen. Wenn Sie mehr erfahren möchten, lesen Sie SBOMs für die Sicherheit von Open-Source-Supply-Chains erstellen.

Kubernetes-Integration

Wenn Sie Ihr Image in einer Kubernetes-Clusterumgebung testen müssen, kennen Sie wahrscheinlich den Aufwand eines sich wiederholenden Ablaufs mit Schritten wie diesen:

  • Mein Image erstellen

  • Es in meine Registry übertragen (oder anderweitig in den Kubernetes-Cluster laden)

  • Mein Deployment-YAML mit dem neuen Image-Tag aktualisieren

  • Mit kubectl erneut bereitstellen

Ko vereinfacht das Ganze, indem es all diese Schritte mit einem einzigen Befehl automatisiert: ko apply. Ersetzen Sie im Deployment-YAML den Image-Tag durch einen speziell formatierten ko://-Tag. Mit ko apply werden dann automatisch alle oben genannten Schritte ausgeführt und die Bereitstellung erfolgt in dem Cluster, der im aktuellen Kontext Ihrer kubectl-Konfiguration festgelegt ist.

Stellen Sie sich zum Beispiel vor, Sie haben eine Sandbox oder einen lokal ausgeführten Kubernetes-Cluster und möchten Ihre Arbeit schrittweise bereitstellen und testen. Hier ist ein Deployment-Manifest, mit dem sich unsere „hello“-Anwendung dort bereitstellen lässt:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: hello-server
spec:
 selector:
   matchLabels:
     run: hello-server
 replicas: 2
 template:
   metadata:
     labels:
       run: hello-server
   spec:
     containers:
       - name: hello-server
         imagePullPolicy: IfNotPresent
         image: ko://github.com/myteam/hello
         ports:
           - containerPort: 8080
             name: http

Sie sehen, dass die Zeile image: den Eintrag ko:// enthält, gefolgt vom Pfad zum Go-Modul unserer Anwendung auf GitHub.

Nun führen wir einfach ko apply aus und übergeben die YAML-Datei mit dem Flag -f, wie wir es auch mit kubectl tun würden:

$ ko apply -f myfile.yaml
2022/09/27 14:35:51 Using base gcr.io/distroless/static:nonroot@sha256:2a9e2b4fa771d31fe3346a873be845bfc2159695b9f90ca08e950497006ccc2e for github.com/myteam/hello
2022/09/27 14:35:51 Building github.myteam/hello for linux/amd64
…
2022/09/27 14:35:56 Published myteam/golang-ea0a77f5cbe6ba6aea599ad83048ae7b@sha256:bd7eb1052cf5b8664890f23683930f07423aba0089150bb818a53e76ef2ebf68
deployment.apps/hello-server created

$ k get pods
NAME                                READY   STATUS    RESTARTS   AGE
pod/hello-server-5b5cc95db4-gc9rn   1/1     Running   0          10s
pod/hello-server-5b5cc95db4-rhwnp   1/1     Running   0          10s

Jetzt können wir unsere Anwendung testen, eine Änderung vornehmen und ko apply -f myfile.yaml beliebig oft erneut ausführen. Ko übernimmt dabei die ganze Arbeit für Sie!

Wie zu erwarten, gibt es weitere Kubernetes-Befehle, die Sie wahrscheinlich benötigen, zum Beispiel:

  • delete, um Ihre Deployments zu entfernen, und

  • resolve, um YAML zu generieren, das Sie an kubectl oder andere Tools übergeben können.

Die vollständige Dokumentation finden Sie unter https://github.com/ko-build/ko#kubernetes-integration.

Herausforderungen bei der Verwendung von Ko

Wir haben uns mit den Gründen befasst, warum Sie Ko in Ihre Workflows integrieren möchten. Bevor Sie loslegen, sprechen wir jedoch über einige Herausforderungen, die bei der Verwendung von Ko auftreten können.

Plattformübergreifende Aspekte

Wenn Sie auf einem Rechner ohne Linux entwickeln, kann das Fehlen einer Container-Runtime die Arbeit in mancher Hinsicht erschweren. Mit einem containerbasierten Build-Image können Sie sowohl die Build-Tools als auch das Betriebssystem samt Bibliotheken in einem gemeinsamen Basis-Build-Image bündeln. So spielt es praktisch keine Rolle mehr, ob die Plattform Ihrer Workstation der Ihrer Produktionsumgebung entspricht.

Ein Beispiel: Der Go- und Docker-Teil des obigen Beispiels funktioniert auf so gut wie jeder Plattform. Denn unabhängig davon, wo Sie den Build erstellen, stellt das Basis-Image golang die erforderlichen Debian-basierten glibc-Bibliotheken bereit, mit denen der Go-Builder die statische Binärdatei erstellt. Wenn Sie hingegen beispielsweise versuchen, das Ko-Beispiel auf einem MacOS-Rechner mit denselben ldflags-Optionen auszuführen, erhalten Sie Fehlermeldungen zu fehlenden Bibliotheken, die für die statische Verknüpfung erforderlich sind.

Der Grund dafür ist, dass MacOS/Darwin nicht die erforderlichen Bibliotheken für die Cross-Kompilierung einer statischen Linux-Binärdatei enthält – zumindest zum Zeitpunkt, an dem ich dies schreibe. Ohne Apple zu sehr herausgreifen zu wollen: Ähnliche Probleme können auch bei unterschiedlichen CPU-Architekturen sowie auf Linux- und Windows-WLS-Workstations auftreten.

Verlust von Fachkenntnissen

Die Fähigkeiten und das Wissen, die für die Erstellung und Pflege hochwertiger Container-Images erforderlich sind, sind wertvoll. Wenn Tools den Bedarf an diesen Kenntnissen abstrahieren oder beseitigen, kann es schwieriger werden, Probleme zu beheben, wenn etwas schiefgeht. Je weniger Teammitglieder Container-Technologien verstehen, desto stärker ist das Team bei der Fehlersuche und Wartung zur Laufzeit auf die wenigen Personen mit entsprechender Erfahrung angewiesen. Im Extremfall kann das zu Burnout, Fluktuation und/oder Ausfällen führen.

Nachlässigkeit bei der Sicherheit

Wenn die Erstellung von Container-Images vollständig automatisiert wird und nicht mehr bei den Entwicklern liegt, kann die Einhaltung von Prozessen wie dem Scannen von Images auf Schwachstellen leiden. Schwachstellen in nicht aktualisierten Images, Paketen, Bibliotheken usw. können unbemerkt eindringen und Ihre Anwendung Angriffen aussetzen. Stellen Sie daher sicher, dass solche Scans weiterhin auf andere automatisierte Weise durchgeführt werden, zum Beispiel durch:

  • Build-Skripte oder Makefiles

  • Git-Hooks

  • CI-Build-Schritte

Falls Sie noch keine Image-Scans durchführen: Snyk bietet kostenlose Scans von Container-Images, die Schwachstellen darin finden und einfache, umsetzbare Empfehlungen zu ihrer Behebung liefern.

Container-Images vereinfachen

Ko und andere alternative Tools zur Image-Erstellung können die Erstellung von Container-Images für Ihre Go-Anwendungen erheblich vereinfachen und Ihren Teams helfen, Images effizienter und einheitlicher zu erstellen. Besonders Ihre CI-Tools profitieren davon: Sie müssen Ihrer Build-Umgebung keine Container-Runtime mehr bereitstellen und reduzieren so Angriffsvektoren, die durch privilegierte Zugriffe in Ihrem SDLC entstehen. Unabhängig davon, ob Sie ein Build-Tool wie Ko verwenden: Stellen Sie sicher, dass Ihre Teams mit Container-Technologien und den Sicherheitsrisiken der Prozesse und Tools vertraut sind, mit denen sie ihre Images erstellen.

Starten Sie mit Capture the Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Herausforderungen lösen.