Go: Implantação do Go com o Docker
Última atualização: 2026-08-26
A compilação estática do Go torna essa linguagem ideal para implantações em contêineres — um arquivo binário mais uma imagem inicial resultam em uma imagem de produção de 12 MB.
Quando você precisa fazer a implantação de uma API em Go em um ambiente de produção, um Dockerfile bem projetado pode ajudar a otimizar o tamanho, reduzindo-o de uma imagem base de 1,2 GB para 12 MB.
1. Você aprenderá
- Compilação em várias etapas
- Escolha de uma imagem do Scratch, do Alpine ou do Distroless
CGO_ENABLED=0Compilação estática- Compilação cruzada entre plataformas
- Verificação de integridade do Docker
- Orquestração de múltiplos serviços com o Docker Compose
- Exemplo de arquivo YAML de implantação no K8s
2. A história real de um engenheiro de backend
(1) Pontos críticos: Uma imagem do Docker de 1,2 GB leva 5 minutos para ser implantada a cada vez
A API de comércio eletrônico do Bob está pronta para entrar em operação:
“Usei a abordagem mais prática: usei
FROM golang:1.22como imagem base, inseriCOPYo código-fonte nela e compilei dentro do contêiner. A imagem tem 1,2 GB e leva 5 minutos para ser baixada a cada implantação. O pipeline de CI/CD leva 15 minutos, desde o commit até a implantação. Meu chefe disse: ‘A implantação é muito lenta — leva 10 minutos para reverter a mudança.’”
# Bad approach: compile inside container, keep all build tools
FROM golang:1.22 # 800MB + compiler tools
WORKDIR /app
COPY . .
RUN go build -o server .
EXPOSE 8080
CMD ["./server"] # Image 1.2GB! Includes compiler, dependencies, toolchain
(2) Solução Go: Compilação em várias etapas
# Good approach: multi-stage build
# Stage 1: compile (use full Go image)
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server .
# Stage 2: run (use minimal image)
FROM scratch
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"] # Image 12MB! Only the binary
(3) Resultados: antes e depois da otimização
| Métrico | De estágio único (Go: 1,22) | Multiestágio (Scratch) | Melhoria |
|---|---|---|---|
| Tamanho da imagem | 1,2 GB | 12 MB | 100x |
| Pull de implantação | 5 minutos | 5 segundos | 60x |
| Riscos de segurança | Inclui compiladores e conjuntos de ferramentas | Apenas binários | Superfície de ataque mínima |
| Cache de compilação | ❌ Compilação completa a cada vez | ✅ Cache de dependências em camadas | — |
3. Melhores práticas para o Dockerfile
(1) ▶ Exemplo: Dockerfile para Go em múltiplas etapas
# ===== Stage 1: Build =====
FROM golang:1.22-alpine AS builder
# Set working directory
WORKDIR /app
# Copy dependency files first (leverage Docker cache)
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Static compilation
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .
# ===== Stage 2: Run =====
FROM scratch
# Copy binary from builder stage
COPY --from=builder /app/server /server
# If timezone files are needed
# COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# If SSL certificates are needed
# COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["/server", "-health"]
CMD ["/server"]
(2) ▶ Exemplo: versão alpina
# ===== Stage 1: Build =====
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server .
# ===== Stage 2: Run (Alpine) =====
FROM alpine:3.19
# Install runtime dependencies (if needed)
# RUN apk --no-cache add ca-certificates tzdata
COPY --from=builder /app/server /server
EXPOSE 8080
CMD ["/server"]
(3) Seleção de uma imagem base
| Imagem | Tamanho | Segurança | Casos de uso |
|---|---|---|---|
scratch |
0 MB | ✅ Superfície de ataque mínima | Compilado estaticamente em Go puro, sem dependências externas |
alpine |
5 MB | ⚠️ musl libc | Requer um shell, o curl, certificados, etc. |
distroless |
20 MB | ✅ Minimalista + Ferramentas | Requer certificado SSL e dados de fuso horário |
golang:alpine |
350 MB | ❌ Para desenvolvimento | Apenas para a fase de compilação |
golang:1.22 |
800 MB | ❌ | Nunca use em ambiente de produção |
-ldflags="-s -w" pode reduzir o tamanho do binário: -s remove a tabela de símbolos, e -w remove as informações de depuração DWARF. Isso pode reduzir o tamanho do seu binário em mais 30–40%, sem afetar seu funcionamento.
4. Compilação cruzada
(1) ▶ Exemplo: Script de compilação cruzada
# Makefile
APP=server
.PHONY: build-all
# Build for current platform
build:
CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/$(APP) .
# Cross-compile for multiple platforms
build-all:
# Linux amd64 (most common)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o bin/$(APP)-linux-amd64 .
# Linux arm64 (AWS Graviton / Apple M1)
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o bin/$(APP)-linux-arm64 .
# macOS
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="-s -w" -o bin/$(APP)-darwin-amd64 .
# Windows
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o bin/$(APP)-windows-amd64.exe
# Docker build
docker-build:
docker build -t myapp:latest .
(2) Impacto de CGO_ENABLED
| CGO_ENABLED | Vantagens | Desvantagens |
|---|---|---|
=0 |
Compilado estaticamente, roda em qualquer plataforma, imagem mínima | Não é possível usar bibliotecas C (como o driver C para o SQLite) |
=1 (padrão) |
Biblioteca C disponível | Requer o runtime de C; aumenta o tamanho da imagem |
mattn/go-sqlite3 (driver C), definir CGO_ENABLED=0 fará com que a compilação falhe. Solução: Use um driver SQLite puro para Go (como modernc.org/sqlite) ou defina CGO_ENABLED=1 com a imagem alpine (requer a instalação do gcc e do musl-dev).
5. Docker Compose para vários serviços
(1) ▶ Exemplo: API de comércio eletrônico + Banco de dados + Redis
# docker-compose.yml
version: '3.8'
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- DB_HOST=db
- DB_PORT=3306
- DB_USER=app
- DB_PASSWORD=secret
- DB_NAME=shop
- REDIS_ADDR=redis:6379
- GIN_MODE=release
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
healthcheck:
test: ["CMD", "/server", "-health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: shop
MYSQL_USER: app
MYSQL_PASSWORD: secret
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
volumes:
db_data:
6. Implantação no K8s
(1) ▶ Exemplo: Implantação mínima do K8s
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-api
labels:
app: go-api
spec:
replicas: 3
selector:
matchLabels:
app: go-api
template:
metadata:
labels:
app: go-api
spec:
containers:
- name: api
image: myregistry/go-api:latest
ports:
- containerPort: 8080
env:
- name: DB_HOST
value: "mysql-service"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: go-api-service
spec:
selector:
app: go-api
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
flowchart TD
subgraph Build ["Build Phase"]
SRC[Source code] --> DEP[go mod download]
DEP --> BUILD[go build]
BUILD --> BIN[Binary 15MB]
end
subgraph Container ["Container Phase"]
BIN --> CP[COPY to scratch]
CP --> IMG[Image 12MB]
end
subgraph Deploy ["Deploy Phase"]
IMG --> PUSH[Push to Registry]
PUSH --> K8S[K8s Deployment]
K8S --> POD[Pod 3 replicas]
end
Build --> Container --> Deploy
7. Exemplo completo: todo o processo de contêinerização de uma API de comércio eletrônico
(1) ▶ Exemplo: API completa com verificação de integridade
// cmd/server/main.go (complete API with health check)
package main
import (
"context"
"encoding/json"
"flag"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
healthFlag := flag.Bool("health", false, "run health check")
flag.Parse()
if *healthFlag {
// Health check mode: check if service is reachable
resp, err := http.Get("http://localhost:8080/health")
if err != nil {
os.Exit(1)
}
resp.Body.Close()
os.Exit(0)
}
mux := http.NewServeMux()
// Health check endpoint
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
})
mux.HandleFunc("GET /ready", func(w http.ResponseWriter, r *http.Request) {
// Check if dependencies like database are ready
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"ready": "true"})
})
// Business endpoint
mux.HandleFunc("GET /api/products", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"products": []string{"laptop", "mouse", "keyboard"},
})
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
// Graceful shutdown
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("Shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
server.Shutdown(ctx)
}()
log.Println("Service listening on :8080")
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
}
(2) Arquivo Dockerfile associado
# Dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Cache dependencies
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Static compilation + strip debug info
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# === Run stage ===
FROM scratch
# Copy binary
COPY --from=builder /app/server /server
# If SSL certificates are needed (accessing external HTTPS APIs)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["/server", "-health"]
CMD ["/server"]
(3) Arquivo .dockerignore
# .dockerignore
.git
.gitignore
*.md
bin/
tmp/
Dockerfile
.dockerignore
.dockerignore tem um impacto enorme na velocidade da compilação — ele exclui arquivos desnecessários, reduzindo o tamanho do contexto enviado ao daemon do Docker. Adicionar o diretório .git (que pode ter centenas de MB) pode acelerar significativamente as compilações.
❓ Perguntas Frequentes
P: Por que a compilação em múltiplas etapas reduz o tamanho da imagem? R: A primeira etapa (builder) usa a imagem completa do Go para compilar os binários, enquanto a segunda etapa (run) apenas copia os binários compilados para uma imagem base mínima. As ferramentas de compilação (compiladores, gerenciadores de dependências, código-fonte) não aparecem na imagem final — apenas os próprios binários.
P: A imagem “scratch” é segura? R: A imagem “scratch” é a mais segura — ela está completamente vazia, sem shell, bibliotecas ou ferramentas. Um invasor não pode executar nenhum comando dentro do contêiner. No entanto, se o seu programa precisar de um certificado SSL, dados de fuso horário ou um shell, você precisará copiar esses arquivos da etapa de compilação ou mudar para
alpine/distroless.
P: O que significa CGO_ENABLED=0? R: Por padrão, o Go usa vinculação dinâmica (CGO_ENABLED=1, utilizando o runtime C). Definir
CGO_ENABLED=0faz com que o Go gere um binário totalmente estático — que não depende de nenhuma biblioteca externa. Isso permite que o binário seja executado em qualquer sistema Linux, incluindoscratch.
P: Como faço para usar a compilação cruzada? R: Defina as variáveis de ambiente GOOS (SO de destino) e GOARCH (arquitetura de destino).
GOOS=linux GOARCH=amd64 go buildcompila um binário para Linux no macOS. O Go suporta quase todas as combinações de plataformas. A compilação cruzada é mais simples quandoCGO_ENABLED=0.
P: Qual é a diferença entre um check de integridade e um probe de prontidão? R: Um check de integridade verifica se um processo está em bom estado — caso contrário, o K8s reinicia o Pod. Um probe de prontidão verifica se um serviço está pronto para aceitar tráfego — caso não esteja pronto, o K8s remove o Pod do serviço. Utilize probes de prontidão durante a fase de inicialização e probes de atividade durante a fase de execução.
P: Qual é a relação entre o Docker e o K8s? Preciso aprender sobre o K8s? R: O Docker é um ambiente de execução de contêineres — ele empacota aplicativos em unidades padronizadas. O K8s é uma plataforma de orquestração de contêineres — ele gerencia a implantação, o dimensionamento e as verificações de integridade de vários contêineres. Projetos pequenos precisam apenas do Docker (ou do Docker Compose), enquanto projetos grandes exigem o K8s. Este curso aborda apenas exemplos de orquestração do K8s e não se aprofunda nos conceitos do K8s.
P: Qual é o tamanho típico das imagens do Docker em Go? R: Binários estáticos do Go + scratch ≈ 10–20 MB. Aplicativos em Python (baseados em python:3.12) ≈ 300 MB. Um aplicativo em Node.js (baseado em
node:22) ≈ 400 MB. Um aplicativo Java (baseado emamazoncorretto:21) ≈ 500 MB. As imagens do Go apresentam uma vantagem significativa em termos de tamanho.
📖 Resumo
- Compilação em várias etapas: compilação durante a etapa de “compilação”; imagem mínima durante a etapa de “execução”
- Imagens base: scratch (mínima) > distroless > alpine > golang:alpine
CGO_ENABLED=0: Compilação puramente estática; roda em qualquer plataforma-ldflags="-s -w": Remover tabelas de símbolos e informações de depuração- Compilação cruzada:
GOOS=linux GOARCH=arm64 go build HEALTHCHECK: Verificação de integridade nativa do Dockerdocker-compose: Orquestração de múltiplos serviços (API + banco de dados + Redis)- Implantação no K8s: Exemplo mínimo em YAML (probes de atividade e prontidão)
.dockerignore: Excluir arquivos desnecessários para acelerar a compilação
📝 Exercícios
-
Básico (Dificuldade ⭐): Escreva um Dockerfile (compilação em múltiplas etapas) para um serviço HTTP simples em Go. Compile a imagem e verifique se ela pode ser acessada usando
docker run -p 8080:8080. Usedocker imagespara verificar o tamanho da imagem. -
Avançado (Dificuldade ⭐⭐): Implementar a containerização full-stack de uma aplicação em Go com um banco de dados. Requisitos: (1) Três serviços: API em Go, MySQL e Redis; (2) orquestração via docker-compose.yml; (3) Implementar uma verificação de integridade para confirmar se o banco de dados e o Redis estão prontos antes de iniciar a API; (4) Usar
os.Getenvno código Go para ler as informações de conexão com o banco de dados; (5) Usar.dockerignorepara excluir arquivos desnecessários. -
Desafio (Dificuldade ⭐⭐⭐): Implemente um pipeline completo de CI/CD (implementação conceitual; não é necessária CI de fato). Requisitos: (1) O Makefile deve suportar
make build(compilação cruzada para linux/amd64 + linux/arm64),make docker-buildemake docker-プッシュ; (2) Compilações com Dockerfile em múltiplos estágios; (3) Otimização do cache de compilação para cada estágio; (4) K8sdeployment.yaml+service.yaml; (5) Inclui sondas de atividade e prontidão; (6) Limites de recursos (requestselimits).