Docker und Go: Containerisierung von Anwendungen in Golang
In der modernen Entwicklung ist Containerisierung zum De-facto-Standard für die Bereitstellung und Skalierung von Anwendungen geworden. Docker und Go sind das ideale Paar für die Erstellung leichter, schneller und sicherer Microservices. Go wird zu einer statischen Binärdatei kompiliert, was es besonders attraktiv für die Containerisierung macht: Das endgültige Image kann nur wenige Megabyte wiegen. In diesem Artikel werden wir uns ansehen, wie man Go-Anwendungen richtig containerisiert, das Dockerfile optimiert und typische Fehler vermeidet.
Warum sind Docker und Go die ideale Kombination?
Go (Golang) wurde mit Blick auf moderne Cloud-Architekturen entwickelt. Seine wichtigsten Merkmale sind die statische Kompilierung, die integrierte Unterstützung für Nebenläufigkeit und das Fehlen externer Abhängigkeiten zur Laufzeit. Docker wiederum bietet eine isolierte Umgebung zum Ausführen von Anwendungen. Zusammen ermöglichen sie die Erstellung von Images, die:
- Minimalen Platz beanspruchen — Ein Go-Anwendungs-Image kann kleiner als 10 MB sein.
- Schnell starten — Eine statische Binärdatei benötigt weder einen Interpreter noch eine virtuelle Maschine.
- Leicht skalierbar sind — Ideal für die Orchestrierung in Kubernetes.
- Sicherer sind — Ein minimales Image reduziert die Angriffsfläche.
Erstellung einer einfachen Go-Anwendung für die Containerisierung
Zunächst schreiben wir eine einfache Webanwendung in Go, die wir containerisieren werden. Erstellen Sie die Datei main.go:
package main
import ( "fmt" "log" "net/http" "os")
func main() { port := os.Getenv("PORT") if port == "" { port = "8080" }
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go in Docker! 🐳") })
log.Printf("Server starting on port %s", port) log.Fatal(http.ListenAndServe(":"+port, nil))}Diese Anwendung lauscht auf HTTP-Anfragen und gibt eine Begrüßungsnachricht zurück. Der Port kann über die Umgebungsvariable PORT festgelegt werden.
Schreiben eines Dockerfiles für eine Go-Anwendung
Jetzt erstellen wir ein Dockerfile. Wir beginnen mit einem grundlegenden Ansatz und optimieren ihn dann.
Basis-Dockerfile (ohne Optimierung)
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./RUN go mod download
COPY . .RUN go build -o main .
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]Dieses Dockerfile verwendet einen Multi-Stage-Build — eines der besten Muster für Go. Die erste Stufe (builder) kompiliert die Anwendung, die zweite (finales Image) enthält nur die erstellte Binärdatei. Dieser Ansatz verhindert, dass der Compiler und die Go-Tools in das endgültige Image aufgenommen werden.
Optimiertes Dockerfile (Best Practices)
Um das Image noch kleiner und sicherer zu machen, verwenden wir scratch — ein leeres Image ohne Shell und Dienstprogramme:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./RUN go mod download
COPY . .
# CGO deaktivieren, Optimierungen aktivierenRUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main .
FROM scratch
WORKDIR /root/
COPY --from=builder /app/main .
# Root-Zertifikate für HTTPS hinzufügenCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
CMD ["./main"]Wichtige Optimierungen:
CGO_ENABLED=0— deaktiviert CGO, was die Größe der Binärdatei reduziert.-ldflags="-s -w"— entfernt die Symboltabelle und Debug-Informationen.scratch— minimales Image ohne überflüssige Schichten.- Kopieren von
ca-certificates.crt— notwendig für die Arbeit mit HTTPS.