M05·05Deploy de Aplicações

CAPÍTULO 05

Deploy de Aplicações

Dockerize uma API Go com multi-stage build, crie um .dockerignore eficiente e orquestre com Docker Compose.

Por Thiago Souza12 min de leituraAtualizado em 2026-05

Hora de colocar a mão na massa. Vamos criar uma aplicação Go simples, dockerizar e rodar localmente.

Passo 1: Criar a aplicação Go

Crie uma pasta minha-app/ e dentro dela um arquivo main.go:

go
package main
 
import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "time"
)
 
type Response struct {
    Message   string    `json:"mensagem"`
    Hostname  string    `json:"hostname"`
    Timestamp time.Time `json:"timestamp"`
}
 
func handler(w http.ResponseWriter, r *http.Request) {
    hostname, _ := os.Hostname()
    resp := Response{
        Message:   "Olá do container!",
        Hostname:  hostname,
        Timestamp: time.Now(),
    }
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(resp)
}
 
func health(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    fmt.Fprintln(w, "OK")
}
 
func main() {
    porta := os.Getenv("PORT")
    if porta == "" {
        porta = "8080"
    }
 
    http.HandleFunc("/", handler)
    http.HandleFunc("/health", health)
 
    log.Printf("Servidor escutando na porta %s", porta)
    log.Fatal(http.ListenAndServe(":"+porta, nil))
}

E o go.mod:

bash
go mod init minha-app

Passo 2: Criar o Dockerfile (multi-stage)

Aqui um truque importante: multi-stage build. Compila numa imagem grande e copia só o binário pra uma imagem mínima.

dockerfile
# === Estágio 1: BUILD ===
FROM golang:1.26-alpine AS builder
 
WORKDIR /build
 
# Aproveitando cache de dependências
COPY go.mod go.sum* ./
RUN go mod download
 
# Copia o código e compila
COPY . .
 
# Build estático (não precisa de libs externas)
RUN CGO_ENABLED=0 GOOS=linux go build -o server .
 
# === Estágio 2: RUNTIME ===
FROM alpine:3.19
 
# Adiciona certificados (para HTTPS funcionar)
RUN apk --no-cache add ca-certificates
 
# Cria usuário não-root (segurança!)
RUN adduser -D -u 1000 appuser
USER appuser
 
WORKDIR /app
 
# Copia só o binário do estágio anterior
COPY --from=builder /build/server .
 
EXPOSE 8080
 
CMD ["./server"]
Por que multi-stage? A imagem final fica com ~15MB ao invés de ~700MB. Menos superfície de ataque, deploy mais rápido, menos uso de disco.

Passo 3: Criar .dockerignore

Para não copiar lixo pra dentro da imagem:

.git
.gitignore
README.md
*.md
.env
.vscode
.idea
node_modules
__pycache__
*.log

Passo 4: Build e run

bash
# Construir a imagem
docker build -t minha-app:v1 .
 
# Rodar
docker run -d -p 8080:8080 --name minha-app minha-app:v1
 
# Testar
curl http://localhost:8080
# {"mensagem":"Olá do container!","hostname":"a3b4c5d6","timestamp":"..."}
 
curl http://localhost:8080/health
# OK
 
# Ver logs
docker logs minha-app
 
# Entrar no container
docker exec -it minha-app sh
 
# Parar e limpar
docker stop minha-app
docker rm minha-app

Passo 5: Docker Compose (bônus)

Se sua aplicação tem múltiplos serviços (app + banco + cache), Docker Compose facilita demais.

docker-compose.yml:

yaml
services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - PORT=8080
      - DB_HOST=db
    depends_on:
      - db
    restart: unless-stopped
 
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=minha_app
    volumes:
      - db_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
 
volumes:
  db_data:
bash
docker compose up -d        # sobe tudo
docker compose logs -f app  # acompanha logs do app
docker compose down         # derruba tudo