Introduction
\nLa conteneurisation d'applications Go avec Docker est la norme du développement moderne. Cependant, les Dockerfile de base sont souvent loin d'être idéaux : les images sont lourdes, la construction est lente, et en production, des problèmes surviennent avec la journalisation, les signaux et les healthchecks. Dans cet article, nous aborderons des techniques avancées : des builds multi-stage au réglage fin du réseau et des volumes. Vous apprendrez à rendre une image d'application Go minimale, sécurisée et prête pour la production.
\n\n1. Builds multi-stage : une image minimale en quelques secondes
\nGo se compile en un binaire statique, ce qui est idéal pour la conteneurisation. Cependant, si vous utilisez l'image complète golang:1.22 pour l'exécution, vous emportez le compilateur, le gestionnaire de paquets et les utilitaires système. La solution est le build multi-stage.
Dockerfile de base avec multi-stage
\n# Étape 1 : construction\nFROM golang:1.22-alpine AS builder\nWORKDIR /app\nCOPY go.mod go.sum ./\nRUN go mod download\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/main ./cmd/\n\n# Étape 2 : image minimale\nFROM alpine:3.19\nRUN apk add --no-cache ca-certificates tzdata\nCOPY --from=builder /app/main /usr/local/bin/main\nEXPOSE 8080\nENTRYPOINT ["/usr/local/bin/main"]\n\nPourquoi cela fonctionne-t-il ?
\n- \n
- Alpine — 5 Mo, ensemble minimal d'utilitaires. \n
CGO_ENABLED=0— désactive les dépendances C, le binaire devient statique. \n -ldflags="-s -w"— supprime les informations de débogage, réduisant la taille du binaire de 30 à 40 %. \n ca-certificatesettzdata— pour les requêtes HTTPS et l'heure correcte. \n
Résultat : image de 10-15 Mo au lieu de 800+ Mo. Construction en 15-20 secondes (avec mise en cache des dépendances).
\n\n2. Optimisation de la mise en cache et des couches
\nDocker met en cache les couches. Si vous copiez tout le code avant go mod download, toute modification dans les fichiers .go réinitialise le cache des dépendances. L'ordre correct :
COPY go.mod go.sum ./\nRUN go mod download\nCOPY . .\n\nUtilisez également .dockerignore :
.git\n*.md\ntest/\ncoverage/\n.env\nDockerfile\n\nCela exclut les fichiers inutiles du contexte de construction, accélérant le transfert au démon Docker.
\n\nAstuce avancée : mise en cache des modules
\nSi vous utilisez Go modules, vous pouvez monter le cache :
\nFROM golang:1.22-alpine AS builder\nWORKDIR /app\nRUN --mount=type=cache,target=/go/pkg/mod \\\n --mount=type=cache,target=/root/.cache/go-build \\\n go build -ldflags="-s -w" -o /app/main ./cmd/\n\nCela conserve les modules téléchargés et les artefacts de construction entre les exécutions de docker build.
3. Healthchecks, signaux et arrêt gracieux
\nDocker peut vérifier l'état de fonctionnement du conteneur. Ajoutez dans le Dockerfile :
\n\nHEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\\n CMD ["/usr/local/bin/main", "health"]\n\nDans le code Go, implémentez le point de terminaison /healthz :
package main\n\nimport (\n "net/http"\n "os"\n "os/signal"\n "syscall"\n)\n\nfunc main() {\n http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {\n w.WriteHeader(http.StatusOK)\n w.Write([]byte("ok"))\n })\n\n srv := &http.Server{Addr: ":8080"}\n\n // Arrêt gracieux\n go func() {\n sigint := make(chan os.Signal, 1)\n signal.Notify(sigint, syscall.SIGTERM, syscall.SIGINT)\n <-sigint\n srv.Shutdown(nil)\n }()\n\n srv.ListenAndServe()\n}\n\nImportant : l'application Go doit gérer correctement SIGTERM (envoyé par Docker lors de l'arrêt du conteneur). Sans arrêt gracieux, vous risquez de perdre des données ou de laisser des connexions ouvertes.