Introdução: Por que Docker e Go são a dupla ideal?
Go (Golang) e Docker são duas ferramentas que mudaram a abordagem de desenvolvimento e implantação de serviços backend. Go compila em um único arquivo binário estático, e Docker permite empacotar esse binário em um ambiente isolado. Juntos, eles fornecem contêineres minimalistas, rápidos e seguros que podem ser executados em qualquer lugar: do laptop do desenvolvedor ao cluster de produção Kubernetes.
Neste artigo, percorreremos o caminho do zero até uma aplicação Go pronta para produção, empacotada em Docker. Você aprenderá como escrever um Dockerfile, otimizar o tamanho da imagem, usar construção multiestágio e executar contêineres com um serviço Go.
1. Fundamentos: Aplicação Go e um Dockerfile simples
Vamos começar com um servidor HTTP simples em Go. Criaremos o arquivo main.go:
package main
import ( "fmt" "net/http")
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go inside Docker!") })
fmt.Println("Server is running on :8080") http.ListenAndServe(":8080", nil)}Agora escreveremos um Dockerfile mínimo:
FROM golang:1.22-alpine AS builder
WORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN go build -o server .
FROM alpine:3.19RUN apk --no-cache add ca-certificatesWORKDIR /root/COPY --from=builder /app/server .EXPOSE 8080CMD ["./server"]Vamos construir a imagem e executar o contêiner:
docker build -t go-server .docker run -p 8080:8080 go-serverAgora abra o navegador e acesse http://localhost:8080 — você verá uma saudação da aplicação Go rodando dentro do contêiner.
Por que construção multiestágio?
No primeiro estágio, usamos a imagem oficial do Go com o conjunto completo de ferramentas para compilação. No segundo estágio, usamos um Alpine Linux mínimo, onde apenas o binário pronto é copiado. Isso permite obter uma imagem de cerca de 15-20 MB em vez de 500+ MB, se mantivéssemos a imagem completa do Go.
2. Otimização da imagem: minimizando tamanho e velocidade de construção
Uma imagem pequena não é apenas economia de espaço, mas também carregamento mais rápido e uma superfície de ataque menor. Vamos considerar as principais técnicas para aplicações Go.
2.1. Usando .dockerignore
Crie um arquivo .dockerignore na raiz do projeto:
.git*.mdDockerfile.dockerignorenode_modules/test/Isso evita a cópia de arquivos desnecessários para o contexto de construção e acelera o processo.
2.2. Cache de dependências
No nosso Dockerfile, primeiro copiamos apenas go.mod e go.sum, depois executamos go mod download. O Docker armazena em cache cada camada e, enquanto esses arquivos não mudarem, as dependências não serão recarregadas a cada alteração no código.
2.3. CGO e compilação estática
Go usa CGO por padrão para algumas operações (por exemplo, trabalho com rede através de chamadas de sistema). Para obter um binário totalmente estático sem dependências da libc, use as flags de compilação:
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o server .Isso garante que o binário funcionará em qualquer distribuição Linux, incluindo imagens mínimas como scratch.
2.4. Scratch — a imagem base mais leve
Se sua aplicação não precisar de shell, ping ou outras utilidades, use a imagem vazia scratch:
FROM scratchCOPY --from=builder /app/server /EXPOSE 8080CMD ["/server"]O tamanho dessa imagem será igual ao tamanho do próprio binário — geralmente menos de 10 MB.
3. Docker Compose: aplicação Go multicontêiner
Aplicações reais raramente consistem em a