Golang

Artigo 50 — Go na nuvem: deploy no Railway, Fly.io e Google Cloud Run Já leu

10 min de leitura

Artigo 50 — Go na nuvem: deploy no Railway, Fly.io e Google Cloud Run
Artigo 50 — Go na nuvem: deploy no Railway, Fly.io e Google Cloud Run Três plataformas, três filosofias O artigo anterior produziu um binário estático e uma

Artigo 50 — Go na nuvem: deploy no Railway, Fly.io e Google Cloud Run

Curso: Dominando Go em 1 Ano Prof. Ricardo Matos Módulo 9 — Deploy, Cloud e Carreira


Três plataformas, três filosofias

O artigo anterior produziu um binário estático e uma imagem Docker otimizada. Agora é hora de colocá-los em produção. Três plataformas dominam o mercado de deploy de aplicações Go containerizadas, cada uma com filosofia diferente:

Railway — zero configuração, deploy por push, ideal para projetos pessoais e startups que querem ir ao ar rapidamente sem pensar em infraestrutura.

Fly.io — infraestrutura global com controle fino, deploys na borda, banco de dados gerenciado, excelente para APIs que precisam de baixa latência em múltiplas regiões.

Google Cloud Run — serverless containerizado, escala para zero quando sem tráfego, integração nativa com o ecossistema GCP, ideal para cargas variáveis e aplicações corporativas.


Railway: do repositório ao ar em minutos

Railway detecta automaticamente projetos Go e os compila sem configuração adicional.

Deploy via CLI

# Instalar CLI
curl -fsSL https://railway.app/install.sh | sh

# Login
railway login

# Criar projeto
railway new taskapi

# Provisionar banco PostgreSQL
railway add --plugin postgresql

# Configurar variáveis de ambiente
railway variables set JWT_SECRET=$(openssl rand -hex 32)
railway variables set APP_ENV=producao
railway variables set LOG_LEVEL=info

# Ver variáveis (DATABASE_URL é criada automaticamente pelo plugin)
railway variables

# Deploy
railway up

# Verificar logs
railway logs --tail

# Status do deploy
railway status

Arquivo de configuração railway.toml

# railway.toml
[build]
builder = "dockerfile"
dockerfilePath = "Dockerfile"

[deploy]
healthcheckPath = "/saude"
healthcheckTimeout = 30
restartPolicyType = "on_failure"
restartPolicyMaxRetries = 3

[[deploy.environmentVariables]]
name = "PORT"
value = "$PORT"  # Railway injeta PORT automaticamente

Executando migrações no Railway

// cmd/api/main.go — executa migrações na inicialização
func executarMigracoes(databaseURL string) error {
    m, err := migrate.New(
        "file://migrations",
        databaseURL,
    )
    if err != nil {
        return fmt.Errorf("criar migrator: %w", err)
    }
    defer m.Close()

    if err := m.Up(); err != nil && err != migrate.ErrNoChange {
        return fmt.Errorf("executar migrações: %w", err)
    }

    version, dirty, _ := m.Version()
    slog.Info("migrações executadas", "versão", version, "dirty", dirty)
    return nil
}

GitHub Actions para Railway

# .github/workflows/deploy-railway.yml
name: Deploy Railway

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Deploy no Railway
        uses: bervProject/railway-deploy@main
        with:
          railway_token: ${{ secrets.RAILWAY_TOKEN }}
          service: taskapi

Fly.io: controle e distribuição global

Fly.io oferece VMs reais (Firecracker microVMs), não containers abstratos. Isso significa acesso a recursos como volumes persistentes, redes privadas e deploy em múltiplas regiões simultaneamente.

Configuração inicial

# Instalar flyctl
curl -L https://fly.io/install.sh | sh

# Login
fly auth login

# Criar e configurar a aplicação
fly launch \
    --name taskapi \
    --region gru \
    --no-deploy

# Banco PostgreSQL gerenciado pelo Fly
fly postgres create \
    --name taskapi-db \
    --region gru \
    --initial-cluster-size 1 \
    --vm-size shared-cpu-1x \
    --volume-size 10

# Associa o banco à aplicação
# Isso cria automaticamente DATABASE_URL nos secrets
fly postgres attach taskapi-db --app taskapi

# Segredos da aplicação
fly secrets set \
    JWT_SECRET="$(openssl rand -hex 32)" \
    APP_ENV="producao" \
    LOG_LEVEL="info" \
    --app taskapi

fly.toml completo

# fly.toml
app = "taskapi"
primary_region = "gru"

[build]
  dockerfile = "Dockerfile"
  [build.args]
    VERSAO = "1.0.0"

[env]
  PORT = "8080"
  APP_ENV = "producao"

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = true   # para máquinas sem tráfego
  auto_start_machines = true  # reinicia quando tráfego chega
  min_machines_running = 1    # sempre 1 instância mínima

  [http_service.concurrency]
    type = "requests"
    hard_limit = 1000
    soft_limit = 800

  [[http_service.checks]]
    grace_period = "10s"
    interval = "30s"
    method = "GET"
    path = "/saude"
    timeout = "5s"
    tls_skip_verify = false

[[vm]]
  memory = "256mb"
  cpu_kind = "shared"
  cpus = 1

# Deploy em múltiplas regiões
[[regions]]
  name = "gru"   # São Paulo
  count = 2

# Volume para dados persistentes (se necessário)
[[mounts]]
  source = "taskapi_data"
  destination = "/data"

Deploy e operações

# Primeiro deploy
fly deploy --app taskapi

# Deploy com estratégia rolling (sem downtime)
fly deploy --strategy rolling --app taskapi

# Escalar horizontalmente
fly scale count 3 --app taskapi

# Escalar verticalmente
fly scale vm shared-cpu-2x --memory 512 --app taskapi

# SSH na instância
fly ssh console --app taskapi

# Logs em tempo real
fly logs --app taskapi

# Monitoramento
fly status --app taskapi
fly checks list --app taskapi

# Executar comando ad-hoc (ex: migração manual)
fly ssh console --app taskapi -C "/taskapi migrate"

Deploy multi-região

# Adicionar região secundária
fly regions add iad   # Northern Virginia
fly regions add lhr   # London
fly scale count 1 --region iad
fly scale count 1 --region lhr

O Fly.io roteia automaticamente usuários para a região mais próxima, reduzindo latência globalmente.


Google Cloud Run: serverless com escala automática

Cloud Run executa containers de forma serverless — você paga apenas pelo tempo de CPU consumido durante requisições. Escala para zero automaticamente quando sem tráfego, e de zero para centenas de instâncias em segundos.

Configuração do GCP

# Instalar gcloud CLI
# https://cloud.google.com/sdk/docs/install

# Autenticar
gcloud auth login
gcloud auth configure-docker us-central1-docker.pkg.dev

# Criar projeto
gcloud projects create taskapi-prod --name="Task API Produção"
gcloud config set project taskapi-prod

# Habilitar APIs necessárias
gcloud services enable \
    run.googleapis.com \
    cloudbuild.googleapis.com \
    sqladmin.googleapis.com \
    secretmanager.googleapis.com \
    artifactregistry.googleapis.com

# Criar repositório de imagens
gcloud artifacts repositories create taskapi \
    --repository-format=docker \
    --location=us-central1 \
    --description="Task API Docker images"

Cloud SQL: PostgreSQL gerenciado

# Criar instância Cloud SQL
gcloud sql instances create taskapi-db \
    --database-version=POSTGRES_16 \
    --tier=db-f1-micro \
    --region=us-central1 \
    --storage-size=10GB \
    --storage-auto-increase \
    --backup-start-time=03:00 \
    --availability-type=zonal

# Criar banco e usuário
gcloud sql databases create taskapi --instance=taskapi-db
gcloud sql users create taskapi_user \
    --instance=taskapi-db \
    --password="$(openssl rand -hex 16)"

Secret Manager para variáveis sensíveis

# Armazenar segredos no Secret Manager
echo -n "sua-jwt-secret-aqui" | \
    gcloud secrets create jwt-secret --data-file=-

echo -n "postgres://taskapi_user:senha@/taskapi?host=/cloudsql/project:region:instance" | \
    gcloud secrets create database-url --data-file=-

# Criar service account para o Cloud Run
gcloud iam service-accounts create taskapi-sa \
    --display-name="Task API Service Account"

# Permissões para acessar os segredos
gcloud secrets add-iam-policy-binding jwt-secret \
    --member="serviceAccount:taskapi-sa@taskapi-prod.iam.gserviceaccount.com" \
    --role="roles/secretmanager.secretAccessor"

gcloud secrets add-iam-policy-binding database-url \
    --member="serviceAccount:taskapi-sa@taskapi-prod.iam.gserviceaccount.com" \
    --role="roles/secretmanager.secretAccessor"

# Permissão para conectar ao Cloud SQL
gcloud projects add-iam-policy-binding taskapi-prod \
    --member="serviceAccount:taskapi-sa@taskapi-prod.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

Build e deploy no Cloud Run

# Build via Cloud Build (sem Docker local)
gcloud builds submit \
    --tag us-central1-docker.pkg.dev/taskapi-prod/taskapi/api:latest \
    .

# Deploy no Cloud Run
gcloud run deploy taskapi \
    --image us-central1-docker.pkg.dev/taskapi-prod/taskapi/api:latest \
    --region us-central1 \
    --platform managed \
    --allow-unauthenticated \
    --service-account taskapi-sa@taskapi-prod.iam.gserviceaccount.com \
    --set-secrets="JWT_SECRET=jwt-secret:latest,DATABASE_URL=database-url:latest" \
    --add-cloudsql-instances taskapi-prod:us-central1:taskapi-db \
    --min-instances 0 \
    --max-instances 10 \
    --concurrency 1000 \
    --cpu 1 \
    --memory 256Mi \
    --timeout 30s \
    --port 8080

service.yaml: configuração declarativa

# cloudrun/service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: taskapi
  labels:
    cloud.googleapis.com/location: us-central1
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "0"
        autoscaling.knative.dev/maxScale: "10"
        run.googleapis.com/cloudsql-instances: "taskapi-prod:us-central1:taskapi-db"
        run.googleapis.com/startup-cpu-boost: "true"
    spec:
      serviceAccountName: taskapi-sa@taskapi-prod.iam.gserviceaccount.com
      containerConcurrency: 1000
      timeoutSeconds: 30
      containers:
        - image: us-central1-docker.pkg.dev/taskapi-prod/taskapi/api:latest
          ports:
            - containerPort: 8080
          env:
            - name: APP_ENV
              value: producao
            - name: JWT_SECRET
              valueFrom:
                secretKeyRef:
                  name: jwt-secret
                  key: latest
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: database-url
                  key: latest
          resources:
            limits:
              cpu: "1"
              memory: 256Mi
          livenessProbe:
            httpGet:
              path: /saude
            initialDelaySeconds: 10
            periodSeconds: 30
          startupProbe:
            httpGet:
              path: /saude
            failureThreshold: 3
            periodSeconds: 10
# Aplicar configuração declarativa
gcloud run services replace cloudrun/service.yaml --region us-central1

# Listar revisões
gcloud run revisions list --service taskapi --region us-central1

# Traffic splitting — deploy gradual
gcloud run services update-traffic taskapi \
    --region us-central1 \
    --to-revisions taskapi-00002-abc=10,taskapi-00001-xyz=90

# Logs
gcloud logging read \
    "resource.type=cloud_run_revision AND resource.labels.service_name=taskapi" \
    --limit 50 \
    --format json

GitHub Actions para Cloud Run

# .github/workflows/deploy-cloudrun.yml
name: Deploy Cloud Run

on:
  push:
    branches: [main]

env:
  PROJECT_ID: taskapi-prod
  REGION: us-central1
  SERVICE: taskapi
  IMAGE: us-central1-docker.pkg.dev/taskapi-prod/taskapi/api

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write

    steps:
      - uses: actions/checkout@v4

      - name: Autenticar no GCP
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
          service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}

      - name: Configurar gcloud
        uses: google-github-actions/setup-gcloud@v2

      - name: Configurar Docker
        run: gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev

      - name: Build e push
        run: |
          docker build \
            --build-arg VERSAO=${{ github.sha }} \
            --build-arg COMMIT=${{ github.sha }} \
            -t ${{ env.IMAGE }}:${{ github.sha }} \
            -t ${{ env.IMAGE }}:latest \
            .
          docker push ${{ env.IMAGE }}:${{ github.sha }}
          docker push ${{ env.IMAGE }}:latest

      - name: Deploy
        run: |
          gcloud run deploy ${{ env.SERVICE }} \
            --image ${{ env.IMAGE }}:${{ github.sha }} \
            --region ${{ env.REGION }} \
            --quiet

      - name: URL do serviço
        run: |
          gcloud run services describe ${{ env.SERVICE }} \
            --region ${{ env.REGION }} \
            --format "value(status.url)"

Comparativo das três plataformas

Aspecto Railway Fly.io Cloud Run
Configuração inicial Mínima Média Alta
Custo (baixo tráfego) ~$5/mês ~$5/mês ~$0 (escala p/ zero)
Controle de infraestrutura Baixo Alto Médio
Banco de dados gerenciado Sim Sim Cloud SQL
Multi-região Não Sim Sim
Escala para zero Não Opcional Sim
Cold start N/A Rápido (<100ms) Médio (<500ms)
SLA produção 99.5% 99.9% 99.95%
Melhor para Prototipagem APIs globais Escala variável

Saúde e observabilidade em produção

Independentemente da plataforma, um endpoint de saúde robusto é essencial:

type StatusSaude struct {
    Status   string            `json:"status"`
    Versao   string            `json:"versao"`
    Uptime   string            `json:"uptime"`
    Banco    string            `json:"banco"`
    Checks   map[string]string `json:"checks"`
}

var inicioApp = time.Now()

func handlerSaude(db *pgxpool.Pool) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx, cancelar := context.WithTimeout(r.Context(), 3*time.Second)
        defer cancelar()

        checks := make(map[string]string)
        status := "ok"

        // Verifica banco de dados
        if err := db.Ping(ctx); err != nil {
            checks["banco"] = "falha: " + err.Error()
            status = "degradado"
        } else {
            checks["banco"] = "ok"
        }

        httpStatus := http.StatusOK
        if status != "ok" {
            httpStatus = http.StatusServiceUnavailable
        }

        resposta := StatusSaude{
            Status: status,
            Versao: Versao,
            Uptime: time.Since(inicioApp).Round(time.Second).String(),
            Checks: checks,
        }

        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(httpStatus)
        json.NewEncoder(w).Encode(resposta)
    }
}

Resumo do que foi coberto

Este artigo apresentou deploy em nuvem com três plataformas: Railway com zero configuração via CLI e GitHub Actions, Fly.io com fly.toml detalhado, multi-região e operações avançadas, e Google Cloud Run com Secret Manager, Cloud SQL, service.yaml declarativo e pipeline CI/CD com Workload Identity Federation. O comparativo entre plataformas ajuda a escolher a mais adequada para cada cenário. O próximo artigo explora gRPC e Protocol Buffers.


Referências e leituras complementares

  • Railway Docs — Documentação oficial do Railway. https://docs.railway.app

  • Fly.io Docs — Documentação completa do Fly.io. https://fly.io/docs/

  • Cloud Run Docs — Documentação oficial do Google Cloud Run. https://cloud.google.com/run/docs

  • Cloud Run com Go — Guia específico para Go no Cloud Run. https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-go-service

  • Workload Identity Federation — Autenticação segura para GitHub Actions. https://cloud.google.com/blog/products/identity-security/enabling-keyless-authentication-from-github-actions

  • Twelve-Factor App — Metodologia de aplicações cloud-native. https://12factor.net


Próximo artigo: Artigo 51 — gRPC e Protocol Buffers: comunicação eficiente entre serviços**


you asked

Sim


claude response

Comentários

Mais em Golang

Artigo 29 — Roteamento avançado com Gorilla Mux ou Chi
Artigo 29 — Roteamento avançado com Gorilla Mux ou Chi

Artigo 29 — Roteamento avançado com Gorilla Mux ou Chi Quando a biblioteca pa...

Estudando e se aprofundando em Go
Estudando e se aprofundando em Go

Existe uma pergunta leg&iacute;tima que qualquer desenvolvedor deve fazer ant...

Introdução ao Go: história, filosofia e os criadores da linguagem
Introdução ao Go: história, filosofia e os criadores da linguagem

Talvez voc&ecirc; se pergunte:&nbsp;Por que mais uma linguagem de programa&cc...