Saltar al contenido
Todos los artículos

Compilar aplicaciones Go con Docker: una cadena de herramientas portable y hermética

por Ghazi Triki · 10 min de lectura

Compilar aplicaciones Go con Docker
En este artículo

Deje de instalar Go en su máquina. Sin contaminar GOPATH, sin “en mi máquina funciona” y sin desfase de versiones. Es el método exacto que usamos para reescribir los agentes Java de los informes BigBlueButton Learning Analytics y para crear el agente seguro Piler Go Agent. Así puede generar binarios estáticos utilizando solo Docker.

Por Ghazi Triki, RIADVICE

El problema de “basta con instalar Go”

Cuando gestionábamos agentes basados en Java para los informes BigBlueButton Learning Analytics, nos enfrentamos al clásico “infierno de dependencias”. Pasar a Go prometía simplicidad, pero solo si no repetíamos los mismos errores. Necesitábamos un proceso de compilación tan hermético como los binarios que producía.

Tarde o temprano, todos los equipos chocan con estos obstáculos:

  • Desfase de versiones. Ahmed usa la 1.24, Omar la 1.26 y el servidor de producción la 1.25. Un pequeño cambio en el compilador rompe la compilación, pero solo para Ahmed.
  • Máquinas contaminadas. GOPATH, GOMODCACHE y herramientas instaladas globalmente (staticcheck, gofumpt) que difieren de un puesto de trabajo a otro.
  • Incorporación difícil. Un ingeniero recién llegado pasa su primer día peleándose con variables de entorno y cadenas de herramientas C para cgo.

En RIADVICE valoramos la disciplina y el orden. Queríamos un proceso de compilación que lo reflejara: limpio, predecible y compartido.

La respuesta habitual es “use Docker”, pero la mayoría de los tutoriales solo enseñan a crear una imagen de contenedor. No explican cómo obtener un binario simple y enlazado estáticamente en su máquina, el artefacto que realmente necesita para un servicio systemd o un despliegue aislado de la red, sin instalar nunca el compilador de Go en local.

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

La imagen oficial golang es el compilador. Su código fuente se monta en /app. El binario se escribe en dist/myapp en su máquina. Cuando el comando termina, el contenedor se destruye (--rm) y su máquina queda tan limpia como antes.

Experiencia real: de BigBlueButton a Piler

No se trata solo de teoría. Hemos aplicado este método en dos proyectos críticos:

  1. Informes BigBlueButton Learning Analytics: reescribimos pesados agentes Java como binarios Go ligeros. Las compilaciones basadas en Docker nos permitieron apuntar exactamente al entorno Debian de los servidores BBB sin necesidad de tener ese entorno en nuestros portátiles.
  2. Piler Go Agent: para nuestra solución de archivado de correo electrónico, creamos un agente reforzado en materia de seguridad. El proceso de compilación tenía que ser tan seguro como el código. Al fijar el compilador en un SHA concreto, garantizamos que cada bit del binario estaba justificado.

Por qué es mejor que instalar Go

Aspectogo install en la máquinadocker run golang:...
Versión de GoLa última que haya instaladoFijada, idéntica para todos
GOPATH / cachéContamina ~/goCapas de contenedor herméticas
Cadena cgoRequiere configurar la máquinaIncluida en la imagen oficial
Paridad con la CI“En mi máquina funciona”Imagen idéntica, binario 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 ./...

El Makefile: la fuente de la disciplina

Usamos un Makefile para codificar los pasos de compilación. Observe que GO_IMAGE está fijada en una versión concreta. Nunca usamos latest.

Los detalles técnicos que importan

1. CGO_ENABLED=0 para binarios estáticos

En el agente Piler necesitábamos binarios capaces de ejecutarse en cualquier sistema, desde un Debian moderno hasta un servidor antiguo. Desactivar CGO garantiza que el binario contenga todo lo que necesita, sin dependencias dinámicas de las bibliotecas de la máquina.

2. -ldflags -X para la trazabilidad

Su binario debe poder indicarle exactamente de dónde procede. Inyectamos el commit de git y la fecha de compilación en el momento del enlazado. Esto fue vital para depurar los agentes de BigBlueButton en decenas de clústeres distintos.

3. Fijar la imagen del compilador

En nuestra cultura creemos en la coherencia. Fijar golang:1.26.5-trixie en lugar de usar latest significa que, si Ahmed compila el binario hoy y Omar lo compila dentro de seis meses, el resultado es idéntico bit a bit.

Conclusión

El compilador es una herramienta y, como cualquier herramienta, debe permanecer en su sitio. Al trasladar la cadena de herramientas de Go a Docker, hemos eliminado toda una categoría de errores del tipo “a mí me funciona” y hemos simplificado la incorporación de cada nuevo ingeniero que se une a RIADVICE.

Tanto si reescribe agentes Java como si crea una API segura para Piler, este método aporta la disciplina y la fiabilidad que exigen los sistemas en producción.


Ghazi Triki es el fundador de RIADVICE. Creamos integraciones seguras y de alto rendimiento para BigBlueButton, Piler y mucho más. Construyamos juntos algo extraordinario.

🔗 Obtenga golang-docker-boilerplate en GitHub