Saltar para o conteúdo
Todos os artigos

Compilar aplicações Go com Docker: uma cadeia de ferramentas portátil e hermética

por Ghazi Triki · 10 min de leitura

Compilar aplicações Go com Docker
Neste artigo

Deixe de instalar o Go na sua máquina. Sem poluição do GOPATH, sem «funciona na minha máquina» e sem qualquer desvio de versões. Foi exatamente este o padrão que usámos para reescrever os agentes Java dos relatórios BigBlueButton Learning Analytics e para construir o Piler Go Agent seguro. Veja como distribuir binários estáticos usando apenas o Docker.

Por Ghazi Triki, RIADVICE

O problema do «basta instalar o Go»

Quando geríamos agentes em Java para os relatórios BigBlueButton Learning Analytics, enfrentávamos o clássico «inferno das dependências». A passagem para Go prometia simplicidade, mas só se não repetíssemos os mesmos erros. Precisávamos de um processo de compilação tão hermético como os binários que produzia.

Todas as equipas acabam por esbarrar nestes obstáculos:

  • Desvio de versões. O Ahmed está na 1.24, o Omar na 1.26 e o servidor de produção na 1.25. Uma pequena alteração no compilador parte a compilação, mas só para o Ahmed.
  • Máquinas poluídas. GOPATH, GOMODCACHE e ferramentas instaladas globalmente (staticcheck, gofumpt) que diferem de posto de trabalho para posto de trabalho.
  • Integração difícil. Um novo engenheiro passa o primeiro dia a lutar com variáveis de ambiente e cadeias de ferramentas C para o cgo.

Na RIADVICE, valorizamos a disciplina e a ordem. Queríamos um processo de compilação que o refletisse: limpo, previsível e partilhado.

A resposta habitual é «use o Docker», mas a maioria dos tutoriais só mostra como construir uma imagem de contentor. Não explicam como obter um binário simples, com ligação estática, na sua máquina, ou seja, o artefacto de que realmente precisa para um serviço systemd ou para uma implementação isolada da rede, sem nunca instalar localmente o compilador Go.

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

A imagem oficial golang é o compilador. O seu código-fonte é montado em /app. O binário é escrito em dist/myapp na sua máquina. Quando o comando termina, o contentor é destruído (--rm) e a sua máquina fica tão limpa como estava.

Experiência real: do BigBlueButton ao Piler

Não se trata apenas de teoria. Aplicámos este padrão a dois projetos críticos:

  1. Relatórios BigBlueButton Learning Analytics: reescrevemos agentes Java pesados como binários Go leves. As compilações com Docker permitiram-nos visar exatamente o ambiente Debian dos servidores BBB, sem precisarmos desse ambiente nos nossos portáteis.
  2. Piler Go Agent: para a nossa solução de arquivo de e-mail, construímos um agente com segurança reforçada. O processo de compilação tinha de ser tão seguro como o código. Ao fixar o compilador num SHA específico, garantimos que cada bit do binário estava justificado.

Porque é melhor do que instalar o Go

Aspetogo install na máquinadocker run golang:...
Versão do GoA última que instalouFixada, idêntica para todos
GOPATH / cachePolui ~/goCamadas de contentor herméticas
Cadeia de ferramentas cgoExige configuração da máquinaIncluída na imagem oficial
Paridade com a CI«Funciona na minha máquina»Imagem idêntica, binário idêntico
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 ./...

O Makefile: a fonte da disciplina

Usamos um Makefile para codificar as etapas de compilação. Repare que GO_IMAGE está fixada numa versão específica. Nunca usamos latest.

Os detalhes técnicos que contam

1. CGO_ENABLED=0 para binários estáticos

No agente Piler, precisávamos de binários capazes de correr em qualquer sistema, de um Debian recente a um servidor antigo. Desativar o CGO garante que o binário contém tudo aquilo de que precisa, sem dependências dinâmicas de bibliotecas da máquina.

2. -ldflags -X para a proveniência

O seu binário deve conseguir dizer-lhe exatamente de onde vem. Injetamos o commit git e a data de compilação no momento da ligação. Isto foi essencial para depurar os agentes BigBlueButton em dezenas de clusters diferentes.

3. Fixar a imagem do compilador

Na nossa cultura, acreditamos na consistência. Fixar golang:1.26.5-trixie em vez de usar latest significa que, se o Ahmed compilar o binário hoje e o Omar o compilar daqui a seis meses, o resultado é idêntico bit a bit.

Conclusão

O compilador é uma ferramenta e, como qualquer ferramenta, deve ficar no seu lugar. Ao passar a cadeia de ferramentas Go para o Docker, eliminámos toda uma categoria de erros do tipo «comigo funciona» e simplificámos a integração de cada novo engenheiro que entra na RIADVICE.

Quer esteja a reescrever agentes Java ou a construir uma API segura para o Piler, este padrão oferece a disciplina e a fiabilidade que os sistemas de produção exigem.


Ghazi Triki é o fundador da RIADVICE. Construímos integrações seguras e de alto desempenho para o BigBlueButton, o Piler e muito mais. Vamos construir algo extraordinário.

🔗 Obtenha o golang-docker-boilerplate no GitHub

Partilhar este artigo

  • Disponibilidade geral do ZcaleRight 1.0.0
    BigBlueButton · 6 min de leitura

    ZcaleRight 1.0.0: dez servidores geridos tão facilmente como um

    O ZcaleRight distribui reuniões BigBlueButton em produção desde 2019. Chega à versão 1.0.0 com disponibilidade geral: as 69 funcionalidades, todas publicadas abertamente, um manual integrado no produto, um acordo de nível de serviço e um site próprio.

  • De sete anos para sete dias
    Education · 6 min de leitura

    De 7 anos para 7 dias com IA agêntica

    Se alguma vez viu uma folha de cálculo COCOMO II prever 33 meses de calendário para um projeto que a sua equipa entregou em dois sprints, já conhece o problema: os modelos de estimativa foram calibrados para um mundo que já não existe.