GKE no GCP: Autopilot, Workload Identity e Cloud SQL Proxy: Do Básico ao Avançado Já leu

GKE Autopilot: Gerenciamento Automático de Clusters Kubernetes O Google Kubernetes Engine (GKE) Autopilot é um modo operacional que abstrai completamente a complexidade da gerência de nós Kubernetes. Diferentemente do modo Standard, onde você provisiona e gerencia nós manualmente, o Autopilot remove essa responsabilidade, permitindo que você se concentre apenas na implantação de workloads. O Google gerencia automaticamente o scaling, patches de segurança, atualizações e conformidade com boas práticas. Quando você cria um cluster Autopilot, o GCP automaticamente configura grupos de nós gerenciados, aplica políticas de segurança, ativa logging e monitoramento, e ajusta recursos conforme a demanda das suas aplicações. Isso significa menos overhead operacional, menos erros de configuração e maior conformidade com padrões de segurança. Para aplicações em produção, especialmente aquelas onde a equipe é pequena ou não possui especialização profunda em Kubernetes, o Autopilot é uma escolha extremamente prática. Criando um Cluster Autopilot Para criar um cluster Autopilot via gcloud CLI, execute: O comando acima cria um cluster gerenciado

GKE Autopilot: Gerenciamento Automático de Clusters Kubernetes

O Google Kubernetes Engine (GKE) Autopilot é um modo operacional que abstrai completamente a complexidade da gerência de nós Kubernetes. Diferentemente do modo Standard, onde você provisiona e gerencia nós manualmente, o Autopilot remove essa responsabilidade, permitindo que você se concentre apenas na implantação de workloads. O Google gerencia automaticamente o scaling, patches de segurança, atualizações e conformidade com boas práticas.

Quando você cria um cluster Autopilot, o GCP automaticamente configura grupos de nós gerenciados, aplica políticas de segurança, ativa logging e monitoramento, e ajusta recursos conforme a demanda das suas aplicações. Isso significa menos overhead operacional, menos erros de configuração e maior conformidade com padrões de segurança. Para aplicações em produção, especialmente aquelas onde a equipe é pequena ou não possui especialização profunda em Kubernetes, o Autopilot é uma escolha extremamente prática.

Criando um Cluster Autopilot

Para criar um cluster Autopilot via gcloud CLI, execute:

gcloud container clusters create meu-cluster-autopilot \
  --region us-central1 \
  --enable-autopilot \
  --enable-stackdriver-kubernetes \
  --addons HttpLoadBalancing,HttpsLoadBalancing

O comando acima cria um cluster gerenciado automaticamente na região us-central1. Os flags --enable-stackdriver-kubernetes e --addons ativam logging/monitoramento e controladores de ingress respectivamente. O Autopilot já vem com segurança em rede, RBAC (Role-Based Access Control) e Pod Security Standards habilitados por padrão.

Diferenças Críticas em Relação ao GKE Standard

No GKE Standard, você deve gerenciar Node Pools manualmente, decidir tipos de máquina, quantidades de nós e políticas de escalabilidade. O Autopilot decide isso automaticamente baseado nos requisitos de recursos dos seus Pods. Se um Pod necessita 2GB de memória e 0.5 CPUs, o Autopilot provisiona nós suficientes para acomodá-lo. Além disso, o Autopilot impõe restrições (como limites de recurso obrigatórios em containers) que forçam boas práticas desde o início.


Workload Identity: Autenticação Segura para Aplicações

O Workload Identity é um mecanismo de segurança que permite que containers rodando no GKE acessem APIs do Google Cloud (como Cloud SQL, Cloud Storage, Pub/Sub) sem armazenar credenciais (chaves JSON) dentro da imagem ou no filesystem do container. Funciona através de um mapeamento de confiança entre uma Service Account do Kubernetes (KSA) e uma Service Account do Google Cloud (GSA), usando tokens JWT gerados automaticamente.

Sem Workload Identity, você seria obrigado a injetar um arquivo de chave JSON na sua aplicação, o que aumenta significativamente o risco de vazamento de credenciais. Com Workload Identity, o controle é granular: você cria uma GSA com permissões específicas, mapeia uma KSA do Kubernetes para ela, e a aplicação automaticamente recebe um token de curta duração (com expiração de aproximadamente 1 hora). Esse modelo é mais seguro, mais fácil de auditar e elimina o risco de credenciais vazarem via repositórios Git.

Configurando Workload Identity Passo a Passo

Primeiro, certifique-se que o cluster tem Workload Identity habilitado. No Autopilot, isso vem ativado por padrão. Para verificar:

gcloud container clusters describe meu-cluster-autopilot \
  --region us-central1 \
  --format='value(workloadIdentityConfig.workloadPool)'

Você deve ver um output como projeto-id.svc.id.goog. Se não retornar nada, habilite com:

gcloud container clusters update meu-cluster-autopilot \
  --region us-central1 \
  --workload-pool=SEU-PROJETO-ID.svc.id.goog

Agora, crie uma Service Account do Google Cloud:

gcloud iam service-accounts create minha-app-sa \
  --display-name="Service Account da Minha App"

Conceda permissões à GSA. Se sua aplicação precisa ler do Cloud SQL, por exemplo:

gcloud projects add-iam-policy-binding SEU-PROJETO-ID \
  --member="serviceAccount:minha-app-sa@SEU-PROJETO-ID.iam.gserviceaccount.com" \
  --role="roles/cloudsql.client"

Crie uma Service Account do Kubernetes no seu namespace:

kubectl create serviceaccount minha-app-ksa -n meu-namespace

Estabeleça a relação de confiança entre KSA e GSA:

gcloud iam service-accounts add-iam-policy-binding \
  minha-app-sa@SEU-PROJETO-ID.iam.gserviceaccount.com \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:SEU-PROJETO-ID.svc.id.goog[meu-namespace/minha-app-ksa]"

Anotate a KSA com a email da GSA:

kubectl annotate serviceaccount minha-app-ksa \
  -n meu-namespace \
  iam.gke.io/gcp-service-account=minha-app-sa@SEU-PROJETO-ID.iam.gserviceaccount.com

Exemplo Prático: Pod Acessando Cloud Storage

Crie um Deployment que use a KSA anotada:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: storage-reader
  namespace: meu-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: storage-reader
  template:
    metadata:
      labels:
        app: storage-reader
    spec:
      serviceAccountName: minha-app-ksa
      containers:
      - name: app
        image: google/cloud-sdk:latest
        command:
        - /bin/bash
        - -c
        - |
          gcloud auth application-default print-access-token
          gsutil ls gs://meu-bucket
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"

Quando esse Pod inicia, o Workload Identity injetor (um webhook do Kubernetes) automaticamente:
1. Monta volumes contendo credenciais ephemeral
2. Configura variáveis de ambiente GOOGLE_APPLICATION_CREDENTIALS apontando para um arquivo de token
3. A aplicação usa gcloud auth application-default ou a biblioteca cliente Google Cloud, que automaticamente lê essas credenciais

O resultado: sua aplicação acessa Cloud Storage sem nenhuma chave JSON armazenada.


Cloud SQL Proxy: Conexão Segura e Simplificada ao Banco de Dados

O Cloud SQL Proxy é um cliente que estabelece uma conexão segura (via SSL/TLS mutuo) com uma instância Cloud SQL, criptografando toda a comunicação. Em vez de sua aplicação conectar diretamente ao IP do banco (potencialmente exposto na rede), o Proxy atua como intermediário, gerenciando autenticação, criptografia e rotação de certificados automaticamente.

Quando executado dentro do GKE com Workload Identity, o Cloud SQL Proxy não necessita de credenciais explícitas. Ele usa o token JWT fornecido pelo Workload Identity para se autenticar com o Cloud SQL API e obter certificados válidos. Isso elimina completamente a necessidade de senhas de banco de dados ou chaves de conexão armazenadas.

Implantando Cloud SQL Proxy no GKE

Você tem duas opções: rodar o Proxy em um sidecar container dentro do Pod, ou usar um proxy gerenciado que o GCP fornece. Demonstrarei o sidecar, que é mais transparente para a aplicação.

Primeiro, certifique-se que sua GSA tem permissão para acessar Cloud SQL:

gcloud projects add-iam-policy-binding SEU-PROJETO-ID \
  --member="serviceAccount:minha-app-sa@SEU-PROJETO-ID.iam.gserviceaccount.com" \
  --role="roles/cloudsql.client"

Agora, modifique o Deployment anterior para incluir o sidecar:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-com-database
  namespace: meu-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-com-database
  template:
    metadata:
      labels:
        app: app-com-database
    spec:
      serviceAccountName: minha-app-ksa
      containers:
      # Container principal da aplicação
      - name: app
        image: python:3.11-slim
        command:
        - /bin/bash
        - -c
        - |
          pip install psycopg2-binary
          python -c "
          import psycopg2
          conn = psycopg2.connect(
              host='127.0.0.1',
              port=5432,
              database='meudb',
              user='postgres',
              password='sua-senha'
          )
          cursor = conn.cursor()
          cursor.execute('SELECT version();')
          print(cursor.fetchone())
          cursor.close()
          conn.close()
          "
        env:
        - name: PGPASSWORD
          value: "sua-senha"
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"

      # Sidecar: Cloud SQL Proxy
      - name: cloud-sql-proxy
        image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.6.0
        args:
        - "PROJETO-ID:REGIAO:NOME-INSTANCIA"
        - "--port=5432"
        - "--max-connections=5"
        ports:
        - name: postgres
          containerPort: 5432
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"
        securityContext:
          runAsNonRoot: true
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL

Neste exemplo, o sidecar Cloud SQL Proxy usa Workload Identity automaticamente (não requer variáveis de ambiente extras) porque o Pod está rodando sob a KSA anotada. O container principal da aplicação conecta em localhost:5432, e toda a criptografia e autenticação é gerenciada pelo Proxy.

Alternativa: Cloud SQL Auth Proxy Gerenciado (Beta)

O GCP oferece um serviço gerenciado (ainda em beta em alguns casos) onde você não precisa incluir um sidecar. Isso simplifica ainda mais o Deployment:

apiVersion: v1
kind: Pod
metadata:
  name: app-simples
  namespace: meu-namespace
spec:
  serviceAccountName: minha-app-ksa
  containers:
  - name: app
    image: python:3.11-slim
    command:
    - python
    - -c
    - |
      import os
      print(f"Conectando via: {os.getenv('CLOUD_SQL_CONNECTION_NAME')}")
    env:
    - name: CLOUD_SQL_CONNECTION_NAME
      value: "PROJETO-ID:REGIAO:NOME-INSTANCIA"
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"
  automountServiceAccountToken: true

Com o serviço gerenciado, você ainda precisa configurar a conexão, mas o overhead operacional é menor.

Monitorando Conexões e Debugging

Para verificar se o Proxy está funcionando corretamente, acesse os logs:

kubectl logs -n meu-namespace deployment/app-com-database -c cloud-sql-proxy

Se a autenticação via Workload Identity falhar, você verá erros como "failed to retrieve token" ou "permission denied". Nesse caso, verifique:

  1. Se a GSA realmente tem role roles/cloudsql.client
  2. Se a anotação na KSA está correta
  3. Se o mapeamento de confiança foi criado com gcloud iam service-accounts add-iam-policy-binding

Para testar a conectividade manualmente dentro do Pod:

kubectl exec -it POD-NAME -c app -n meu-namespace -- /bin/bash
# Dentro do container
curl -v telnet://localhost:5432

Integrando os Três Componentes: Exemplo Completo

Agora vamos unir tudo em um exemplo real: um aplicativo Python que roda no GKE Autopilot, usa Workload Identity para autenticar no Cloud SQL, e se comunica com um banco de dados PostgreSQL através do Cloud SQL Proxy.

Pré-requisitos

  1. Um projeto GCP ativo
  2. Um cluster GKE Autopilot já criado (vimos como criar acima)
  3. Uma instância Cloud SQL PostgreSQL (crie com gcloud sql instances create)

Passo 1: Criar a Instância Cloud SQL

gcloud sql instances create meudb \
  --database-version=POSTGRES_15 \
  --tier=db-f1-micro \
  --region=us-central1 \
  --no-backup

Crie um banco de dados e um usuário:

gcloud sql databases create meudb --instance=meudb
gcloud sql users create app_user --instance=meudb --password=MinhaSenhaSegura123

Passo 2: Configurar Service Accounts e Workload Identity

# Criar GSA
gcloud iam service-accounts create gke-app-sa \
  --display-name="GKE App Service Account"

# Conceder permissões
gcloud projects add-iam-policy-binding SEU-PROJETO-ID \
  --member="serviceAccount:gke-app-sa@SEU-PROJETO-ID.iam.gserviceaccount.com" \
  --role="roles/cloudsql.client"

# Criar namespace
kubectl create namespace producao

# Criar KSA
kubectl create serviceaccount app-ksa -n producao

# Mapear KSA para GSA
gcloud iam service-accounts add-iam-policy-binding \
  gke-app-sa@SEU-PROJETO-ID.iam.gserviceaccount.com \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:SEU-PROJETO-ID.svc.id.goog[producao/app-ksa]"

# Anotar KSA
kubectl annotate serviceaccount app-ksa \
  -n producao \
  iam.gke.io/gcp-service-account=gke-app-sa@SEU-PROJETO-ID.iam.gserviceaccount.com

Passo 3: Criar a Aplicação

Crie um arquivo app.py:

import os
import psycopg2
from psycopg2.extras import RealDictCursor
import json

def get_connection():
    """Conecta ao Cloud SQL via localhost (Cloud SQL Proxy cuida da criptografia)"""
    conn = psycopg2.connect(
        host='127.0.0.1',
        port=5432,
        database='meudb',
        user='app_user',
        password=os.getenv('DB_PASSWORD', 'MinhaSenhaSegura123')
    )
    return conn

def create_table():
    """Cria tabela de exemplo se não existir"""
    try:
        conn = get_connection()
        with conn.cursor() as cur:
            cur.execute('''
                CREATE TABLE IF NOT EXISTS usuarios (
                    id SERIAL PRIMARY KEY,
                    nome VARCHAR(100),
                    email VARCHAR(100)
                )
            ''')
            conn.commit()
        print("Tabela criada ou já existe")
        conn.close()
    except Exception as e:
        print(f"Erro ao criar tabela: {e}")

def inserir_usuario(nome, email):
    """Insere um usuário"""
    try:
        conn = get_connection()
        with conn.cursor() as cur:
            cur.execute(
                'INSERT INTO usuarios (nome, email) VALUES (%s, %s) RETURNING id',
                (nome, email)
            )
            user_id = cur.fetchone()[0]
            conn.commit()
        print(f"Usuário inserido com ID: {user_id}")
        conn.close()
        return user_id
    except Exception as e:
        print(f"Erro ao inserir usuário: {e}")
        return None

def listar_usuarios():
    """Lista todos os usuários"""
    try:
        conn = get_connection()
        with conn.cursor(cursor_factory=RealDictCursor) as cur:
            cur.execute('SELECT * FROM usuarios')
            usuarios = cur.fetchall()
        conn.close()
        return usuarios
    except Exception as e:
        print(f"Erro ao listar usuários: {e}")
        return []

if __name__ == '__main__':
    print("Iniciando aplicação...")
    create_table()

    # Insere dois usuários
    inserir_usuario('João Silva', 'joao@example.com')
    inserir_usuario('Maria Santos', 'maria@example.com')

    # Lista usuários
    usuarios = listar_usuarios()
    print("Usuários no banco:")
    for user in usuarios:
        print(f"  - {user['nome']} ({user['email']})")

Crie um Dockerfile:

FROM python:3.11-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

CMD ["python", "app.py"]

Crie requirements.txt:

psycopg2-binary==2.9.9

Passo 4: Buildar e Fazer Push da Imagem

docker build -t gcr.io/SEU-PROJETO-ID/minha-app:1.0 .
docker push gcr.io/SEU-PROJETO-ID/minha-app:1.0

Passo 5: Criar o Deployment Completo

Salve como deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-db
  namespace: producao
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-db
  template:
    metadata:
      labels:
        app: app-db
    spec:
      serviceAccountName: app-ksa
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
      containers:
      - name: app
        image: gcr.io/SEU-PROJETO-ID/minha-app:1.0
        env:
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL

      - name: cloud-sql-proxy
        image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.6.0
        args:
        - "SEU-PROJETO-ID:us-central1:meudb"
        - "--port=5432"
        - "--max-connections=5"
        ports:
        - name: postgres
          containerPort: 5432
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "100m"
        securityContext:
          runAsNonRoot: true
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL

Passo 6: Criar Secret com Credenciais

kubectl create secret generic db-credentials \
  -n producao \
  --from-literal=password='MinhaSenhaSegura123'

Passo 7: Deploy e Verificação

kubectl apply -f deployment.yaml

# Aguarde alguns segundos
kubectl get pods -n producao

# Veja os logs
kubectl logs -n producao deployment/app-db -c app

Você deve ver um output mostrando que os usuários foram inseridos e listados com sucesso. Isso prova que a autenticação via Workload Identity, o Cloud SQL Proxy, e a aplicação estão todos funcionando juntos perfeitamente.

Para verificar os logs do Proxy especificamente:

kubectl logs -n producao deployment/app-db -c cloud-sql-proxy

Você verá mensagens confirmando que a autenticação foi bem-sucedida e as conexões estão abertas.


Conclusão

Neste artigo, cobrimos três componentes fundamentais para um deployment seguro e robusto no GCP:

  1. GKE Autopilot simplifica radicalmente a operação de Kubernetes ao gerenciar nós, scaling, patches e conformidade de segurança automaticamente. Você escreve Deployments, o Autopilot cuida do resto. Para times pequenos ou que querem focar em aplicação, não em infraestrutura, é a escolha ideal.

  2. Workload Identity elimina credenciais armazenadas, substituindo chaves JSON por tokens de curta duração gerenciados automaticamente. O mapeamento KSA→GSA é fino e auditável, tornando o ambiente significativamente mais seguro sem sacrificar a facilidade de uso.

  3. Cloud SQL Proxy, integrado com Workload Identity, fornece criptografia fim-a-fim transparente para o banco de dados. A aplicação conecta em localhost sem saber que há criptografia TLS mútuo, rotação de certificados e autenticação automática acontecendo nos bastidores.

Quando usados em conjunto — GKE Autopilot + Workload Identity + Cloud SQL Proxy — você obtém um stack extremamente seguro, altamente gerenciado e com overhead operacional mínimo. Nenhuma chave vaza em repositórios, nenhuma credencial precisa ser rodada manualmente, e nenhuma porta de banco fica exposta na rede.


Referências

  1. Google Cloud GKE Autopilot Documentation
  2. Workload Identity in GKE - Official Guide
  3. Cloud SQL Proxy for GKE - Setup Instructions
  4. GCP Best Practices for Security in Kubernetes
  5. PostgreSQL Client Libraries - Python psycopg2

Artigos relacionados