Introdução
\nA conteinerização de aplicações Go com Docker é o padrão do desenvolvimento moderno. No entanto, Dockerfiles básicos muitas vezes estão longe do ideal: as imagens ficam pesadas, o build é lento e, em produção, surgem problemas com logging, sinais e healthcheck. Neste artigo, abordaremos técnicas avançadas: desde builds multi-stage até a configuração refinada de rede e volumes. Você aprenderá como tornar a imagem de uma aplicação Go mínima, segura e pronta para produção.
\n\n1. Builds multi-stage: imagem mínima em segundos
\nGo compila para um binário estático, o que é ideal para conteinerização. No entanto, se você usar a imagem completa golang:1.22 para execução, estará carregando o compilador, o gerenciador de pacotes e utilitários do sistema. A solução é o multi-stage build.
Dockerfile básico com multi-stage
\n# Stage 1: build\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# Stage 2: imagem mínima\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\nPor que isso funciona?
\n- \n
- Alpine — 5 MB, conjunto mínimo de utilitários. \n
CGO_ENABLED=0— desativa dependências C, o binário torna-se estático. \n -ldflags="-s -w"— remove informações de depuração, reduzindo o tamanho do binário em 30-40%. \n ca-certificatesetzdata— para requisições HTTPS e horário correto. \n
Resultado: imagem de 10-15 MB em vez de 800+ MB. Build — 15-20 segundos (com cache de dependências).
\n\n2. Otimização de cache e camadas
\nO Docker armazena em cache as camadas. Se você copiar todo o código antes de go mod download, qualquer alteração nos arquivos .go invalida o cache de dependências. A ordem correta é:
COPY go.mod go.sum ./\nRUN go mod download\nCOPY . .\n\nUse também .dockerignore:
.git\n*.md\ntest/\ncoverage/\n.env\nDockerfile\n\nIsso exclui arquivos desnecessários do contexto do build, acelerando a transferência para o daemon Docker.
\n\nTruque avançado: cache de módulos
\nSe você usa Go modules, pode montar o 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\nIsso preserva os módulos baixados e artefatos de build entre execuções de docker build.
3. Healthchecks, sinais e graceful shutdown
\nO Docker pode verificar a integridade do contêiner. Adicione no Dockerfile:
\n\nHEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\\n CMD ["/usr/local/bin/main", "health"]\n\nNo código Go, implemente o endpoint /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 // Graceful shutdown\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\nImportante: a aplicação Go deve tratar corretamente o SIGTERM (que o Docker envia ao parar o contêiner). Sem graceful shutdown, você corre o risco de perder dados ou deixar conexões abertas.