Vai al contenuto
Tutti gli articoli

Compilare applicazioni Go con Docker: una toolchain portabile ed ermetica

di Ghazi Triki · 9 min di lettura

Compilare applicazioni Go con Docker
In questo articolo

Smetta di installare Go sulla macchina host. Niente GOPATH inquinato, niente «sulla mia macchina funziona», nessuna deriva di versione. È lo schema esatto che abbiamo usato per riscrivere gli agenti Java dei report BigBlueButton Learning Analytics e per realizzare il Piler Go Agent sicuro. Ecco come distribuire binari statici usando soltanto Docker.

Di Ghazi Triki, RIADVICE

Il problema del «basta installare Go»

Quando gestivamo agenti basati su Java per i report BigBlueButton Learning Analytics, ci siamo scontrati con il classico «inferno delle dipendenze». Il passaggio a Go prometteva semplicità, ma solo a patto di non ripetere gli stessi errori. Ci serviva un processo di build ermetico quanto i binari che produceva.

Prima o poi ogni team va a sbattere contro questi ostacoli:

  • Deriva di versione. Ahmed usa la 1.24, Omar la 1.26 e il server di produzione la 1.25. Una piccola modifica del compilatore rompe la build, ma solo per Ahmed.
  • Host inquinati. GOPATH, GOMODCACHE e strumenti installati globalmente (staticcheck, gofumpt) che differiscono da una postazione all’altra.
  • Inserimento difficoltoso. Un nuovo ingegnere passa il primo giorno a lottare con variabili d’ambiente e toolchain C per cgo.

In RIADVICE diamo valore alla disciplina e all’ordine. Volevamo un processo di build che li rispecchiasse: pulito, prevedibile e condiviso.

La risposta abituale è «usate Docker», ma la maggior parte dei tutorial mostra soltanto come creare un’immagine di container. Non spiega come ottenere un semplice binario con link statico sulla macchina host, cioè l’artefatto che serve davvero per un servizio systemd o per un deployment air-gapped, senza mai installare il compilatore Go in locale.

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

L’immagine ufficiale golang è il compilatore. Il codice sorgente viene montato in bind su /app. Il binario viene scritto in dist/myapp sulla macchina host. Al termine del comando il container viene distrutto (--rm) e l’host resta pulito come prima.

Esperienza sul campo: da BigBlueButton a Piler

Non è solo teoria. Abbiamo applicato questo schema a due progetti critici:

  1. Report BigBlueButton Learning Analytics: abbiamo riscritto pesanti agenti Java sotto forma di binari Go leggeri. Le build basate su Docker ci hanno permesso di puntare esattamente all’ambiente Debian dei server BBB senza doverlo installare sui nostri portatili.
  2. Piler Go Agent: per la nostra soluzione di archiviazione delle e-mail abbiamo realizzato un agente con sicurezza rafforzata. Il processo di build doveva essere sicuro quanto il codice. Fissando il compilatore a uno SHA specifico, ci siamo assicurati che ogni bit del binario fosse tracciabile.

Perché è meglio che installare Go

Aspettogo install sull’hostdocker run golang:...
Versione di GoL’ultima che ha installatoFissata, identica per tutti
GOPATH / cacheInquina ~/goLayer del container ermetici
Toolchain cgoRichiede configurazione hostInclusa nell’immagine ufficiale
Parità con la CI«Sulla mia macchina funziona»Stessa immagine, stesso binario
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 ./...

Il Makefile, fonte di disciplina

Usiamo un Makefile per codificare le fasi di build. Si noti come GO_IMAGE sia fissata a una versione specifica. Non usiamo mai latest.

I dettagli tecnici che contano

1. CGO_ENABLED=0 per i binari statici

Per l’agente Piler ci servivano binari in grado di funzionare ovunque, da una Debian recente a un server datato. Disattivare CGO garantisce che il binario contenga tutto ciò di cui ha bisogno, senza dipendenze dinamiche dalle librerie dell’host.

2. -ldflags -X per la provenienza

Un binario deve poter dire esattamente da dove proviene. Iniettiamo il commit git e la data di build in fase di linking. È stato fondamentale per il debug degli agenti BigBlueButton su decine di cluster diversi.

3. Fissare l’immagine del compilatore

La nostra cultura si fonda sulla coerenza. Fissare golang:1.26.5-trixie anziché usare latest significa che, se Ahmed compila il binario oggi e Omar lo compila tra sei mesi, il risultato è identico bit per bit.

Conclusione

Il compilatore è uno strumento e, come ogni strumento, va tenuto al suo posto. Spostando la toolchain Go in Docker abbiamo eliminato un’intera categoria di bug del tipo «da me funziona» e semplificato l’inserimento di ogni nuovo ingegnere che entra in RIADVICE.

Che si tratti di riscrivere agenti Java o di costruire un’API sicura per Piler, questo schema offre la disciplina e l’affidabilità richieste dai sistemi in produzione.


Ghazi Triki è il fondatore di RIADVICE. Realizziamo integrazioni sicure e ad alte prestazioni per BigBlueButton, Piler e molto altro: costruiamo insieme qualcosa di grande.

🔗 Scarichi il golang-docker-boilerplate su GitHub

Condivida questo articolo

  • Disponibilità generale di ZcaleRight 1.0.0
    BigBlueButton · 6 min di lettura

    ZcaleRight 1.0.0: dieci server si gestiscono come uno solo

    ZcaleRight distribuisce le riunioni BigBlueButton in produzione dal 2019. Ora la versione 1.0.0 è in disponibilità generale: tutte le 69 funzionalità pubblicate apertamente, un manuale integrato nel prodotto, un accordo sui livelli di servizio e un sito dedicato.

  • Da sette anni a sette giorni
    Education · 5 min di lettura

    Da 7 anni a 7 giorni con l’IA agentica

    Se ha mai visto un foglio di calcolo COCOMO II prevedere 33 mesi di calendario per un progetto che il suo team ha consegnato in due sprint, conosce già il problema: i modelli di stima sono stati calibrati per un mondo che non esiste più.