Aller au contenu
Tous les articles

Compiler des applications Go avec Docker : une chaîne d’outils portable et hermétique

par Ghazi Triki · 10 min de lecture

Compiler des applications Go avec Docker
Dans cet article

Cessez d’installer Go sur votre machine hôte. Aucune pollution du GOPATH, plus de « ça marche sur ma machine » et aucune dérive de version. C’est exactement la méthode que nous avons appliquée pour réécrire les agents Java des rapports BigBlueButton Learning Analytics et pour développer l’agent Go sécurisé de Piler. Voici comment livrer des binaires statiques en utilisant uniquement Docker.

Par Ghazi Triki, RIADVICE

Le problème du « il suffit d’installer Go »

Lorsque nous gérions des agents Java pour les rapports BigBlueButton Learning Analytics, nous étions confrontés au classique « enfer des dépendances ». Passer à Go promettait la simplicité, à condition de ne pas reproduire les mêmes erreurs. Il nous fallait un processus de compilation aussi hermétique que les binaires qu’il produisait.

Toute équipe finit par se heurter aux mêmes obstacles :

  • La dérive des versions. Ahmed utilise la 1.24, Omar la 1.26 et le serveur de production la 1.25. Une légère modification du compilateur casse la compilation, mais uniquement chez Ahmed.
  • Des machines hôtes polluées. GOPATH, GOMODCACHE et des outils installés globalement (staticcheck, gofumpt) qui diffèrent d’un poste de travail à l’autre.
  • Une intégration laborieuse. Un nouvel ingénieur passe sa première journée à se débattre avec des variables d’environnement et des chaînes d’outils C pour cgo.

Chez RIADVICE, nous sommes attachés à la rigueur et à l’ordre. Nous voulions un processus de compilation à leur image : propre, prévisible et partagé.

La réponse habituelle est « utilisez Docker », mais la plupart des tutoriels se contentent de montrer comment construire une image de conteneur. Ils n’expliquent pas comment obtenir un simple binaire lié statiquement sur votre machine hôte, c’est-à-dire l’artefact dont vous avez réellement besoin pour un service systemd ou un déploiement en environnement isolé, sans jamais installer le compilateur Go en local.

Shell
docker run --rm -v "$(pwd):/app" -w /app golang:1.26.5-trixie \
    go build -o dist/myapp ./cmd/myapp

L’image officielle golang fait office de compilateur. Vos sources sont montées dans /app. Le binaire est écrit dans dist/myapp sur votre machine hôte. Une fois la commande terminée, le conteneur est détruit (--rm), et votre machine reste aussi propre qu’auparavant.

Retour d’expérience : de BigBlueButton à Piler

Il ne s’agit pas d’une simple théorie. Nous avons appliqué cette méthode à deux projets critiques :

  1. Rapports BigBlueButton Learning Analytics : nous avons réécrit de lourds agents Java sous forme de binaires Go légers. La compilation dans Docker nous a permis de cibler exactement l’environnement Debian des serveurs BBB, sans avoir besoin de cet environnement sur nos ordinateurs portables.
  2. Agent Go de Piler : pour notre solution d’archivage des e-mails, nous avons développé un agent à la sécurité renforcée. Le processus de compilation devait être aussi sûr que le code. En épinglant le compilateur sur un SHA précis, nous avons garanti la traçabilité de chaque bit du binaire.

Pourquoi c’est mieux que d’installer Go

Critèrego install sur l’hôtedocker run golang:...
Version de GoLa dernière que vous avez installéeÉpinglée, identique pour tous
GOPATH / cachePollue ~/goCouches de conteneur hermétiques
Chaîne d’outils cgoConfiguration de l’hôte requiseFournie dans l’image officielle
Parité avec la CI« Ça marche sur ma machine »Image identique, binaire identique
Makefile
BINARY := mini-api
DIST := dist

# Version metadata derived from git
VERSION    := $(shell tmp=$$(git describe --tags --always --dirty 2>/dev/null) && echo "$${tmp#v}" || echo dev)
COMMIT     := $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
BRANCH     := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)
BUILD_DATE := $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
LDFLAGS := -X example.com/mini-api/internal/version.Version=$(VERSION) \
           -X example.com/mini-api/internal/version.Commit=$(COMMIT) \
           -X example.com/mini-api/internal/version.Branch=$(BRANCH) \
           -X example.com/mini-api/internal/version.BuildDate=$(BUILD_DATE)

# Pinned toolchain image
GO_IMAGE := golang:1.26.5-trixie

# Run any command inside the Go toolchain container
define run_in_docker
    docker run --rm -v "$(PWD):/app" -w /app $(GO_IMAGE)
endef

.PHONY: build test lint

build:
    @mkdir -p $(DIST)
    $(call run_in_docker) bash -c 'CGO_ENABLED=0 go build -buildvcs=false -ldflags="$(LDFLAGS)" -o $(DIST)/$(BINARY) ./cmd/$(BINARY)'
    @echo "Built $(DIST)/$(BINARY) (version $(VERSION))"

test:
    $(call run_in_docker) go test -race ./...

lint:
    $(call run_in_docker) go vet ./...

Le Makefile, garant de la rigueur

Nous utilisons un Makefile pour formaliser les étapes de compilation. Notez que GO_IMAGE est épinglée sur une version précise. Nous n’utilisons jamais latest.

Les détails techniques qui comptent

1. CGO_ENABLED=0 pour des binaires statiques

Pour l’agent Piler, il nous fallait des binaires capables de fonctionner sur n’importe quel système, d’une Debian récente à un serveur ancien. Désactiver CGO garantit que le binaire contient tout ce dont il a besoin, sans aucune dépendance dynamique vis-à-vis des bibliothèques de l’hôte.

2. -ldflags -X pour la traçabilité

Votre binaire doit pouvoir indiquer précisément d’où il provient. Nous injectons le commit git et la date de compilation au moment de l’édition de liens. Cela s’est révélé essentiel pour déboguer les agents BigBlueButton sur des dizaines de clusters différents.

3. Épingler l’image du compilateur

Notre culture repose sur la cohérence. Épingler golang:1.26.5-trixie plutôt que d’utiliser latest signifie que si Ahmed compile le binaire aujourd’hui et Omar dans six mois, le résultat est identique au bit près.

Conclusion

Le compilateur est un outil et, comme tout outil, il doit rester à sa place. En déplaçant la chaîne d’outils Go dans Docker, nous avons éliminé toute une catégorie de bogues du type « chez moi, ça marche » et simplifié l’intégration de chaque nouvel ingénieur qui rejoint RIADVICE.

Que vous réécriviez des agents Java ou que vous développiez une API sécurisée pour Piler, cette méthode apporte la rigueur et la fiabilité qu’exigent les systèmes de production.


Ghazi Triki est le fondateur de RIADVICE. Nous concevons des intégrations sécurisées et performantes pour BigBlueButton, Piler et bien d’autres : construisons ensemble quelque chose de remarquable.

🔗 Obtenir golang-docker-boilerplate sur GitHub

Partager cet article