Boas Práticas de ArgoCD: GitOps Contínuo, App of Apps e Sync Policies para Times Ágeis Já leu

GitOps e ArgoCD: Fundamentos GitOps é um paradigma operacional onde o Git se torna a fonte única da verdade (single source of truth) para toda a infraestrutura e aplicações. Ao invés de executar comandos imperativos para fazer deploy, você declara o estado desejado em um repositório Git, e ferramentas especializadas garantem que o cluster Kubernetes sempre esteja sincronizado com essa declaração. ArgoCD é a ferramenta mais madura e adotada para implementar GitOps em ambientes Kubernetes. A principal vantagem dessa abordagem é que toda mudança é auditável, reversível e reproducível. Você não depende mais de documentação externa ou da memória de quem fez o deploy — tudo está no Git. Além disso, qualquer pessoa pode propor mudanças via pull request, criando um fluxo colaborativo onde revisão de código antecede qualquer alteração em produção. ArgoCD observa continuamente o repositório Git e o estado atual do cluster, aplicando correções automáticas quando há divergências. Como ArgoCD Funciona ArgoCD funciona através de um controlador que

GitOps e ArgoCD: Fundamentos

GitOps é um paradigma operacional onde o Git se torna a fonte única da verdade (single source of truth) para toda a infraestrutura e aplicações. Ao invés de executar comandos imperativos para fazer deploy, você declara o estado desejado em um repositório Git, e ferramentas especializadas garantem que o cluster Kubernetes sempre esteja sincronizado com essa declaração. ArgoCD é a ferramenta mais madura e adotada para implementar GitOps em ambientes Kubernetes.

A principal vantagem dessa abordagem é que toda mudança é auditável, reversível e reproducível. Você não depende mais de documentação externa ou da memória de quem fez o deploy — tudo está no Git. Além disso, qualquer pessoa pode propor mudanças via pull request, criando um fluxo colaborativo onde revisão de código antecede qualquer alteração em produção. ArgoCD observa continuamente o repositório Git e o estado atual do cluster, aplicando correções automáticas quando há divergências.

Como ArgoCD Funciona

ArgoCD funciona através de um controlador que roda dentro do cluster e periodicamente verifica se o estado desejado (no Git) corresponde ao estado atual (no cluster). Quando há diferenças, ArgoCD pode sincronizar automaticamente ou alertar um usuário, dependendo da política configurada. O componente principal é a API do ArgoCD que gerencia Applications — objetos que definem qual repositório Git, branch, caminho e cluster devem ser sincronizados.

A arquitetura é simples mas poderosa: um servidor API expõe uma interface web e CLI, agentes de sincronização puxam as mudanças do Git periodicamente (não é push), e webhooks podem acelerar o processo quando há commits. Essa abordagem pull-based é mais segura que alternativas push-based porque o cluster nunca expõe credenciais para fora; apenas puxa informações do repositório.

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: guestbook
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: HEAD
    path: apps/guestbook
  destination:
    server: https://kubernetes.default.svc
    namespace: guestbook
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

Este exemplo define uma Application que aponta para um repositório Git, especifica o caminho dos manifestos Kubernetes, e declara que deve sincronizar automaticamente com prune (remover recursos não declarados) e selfHeal (corrigir alterações manuais).

App of Apps: Escalando GitOps para Múltiplos Ambientes

A medida que seus projetos crescem, gerenciar dezenas ou centenas de Applications manualmente fica insustentável. O padrão App of Apps resolve isso criando uma aplicação especial que, ao invés de gerenciar resources Kubernetes diretamente, gerencia outras Applications. Isso permite estruturar seu GitOps em camadas: uma Application raiz que aponta para um diretório contendo múltiplas Applications filhas.

Esse padrão é particularmente útil em cenários multi-ambiente ou multi-tenant. Você pode ter uma estrutura onde a Application raiz está no cluster principal, e Applications filhas representam cada ambiente (dev, staging, produção) ou cada time. Quando há mudanças na raiz, todas as Applications dependentes são afetadas, criando um efeito em cascata controlado.

# argocd/apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: HEAD
    path: argocd/apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
# argocd/apps/dev-env.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-environment
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: HEAD
    path: envs/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: dev
  syncPolicy:
    syncOptions:
    - CreateNamespace=true
# argocd/apps/prod-env.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prod-environment
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: HEAD
    path: envs/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: prod
  syncPolicy:
    automated:
      prune: false
      selfHeal: false
    syncOptions:
    - CreateNamespace=true

Observe que o ambiente de produção desabilita sincronização automática — isso é proposital. Mudanças sensíveis devem ser síncronizadas manualmente após aprovação humana, reduzindo risco de outages acidentais.

Estrutura de Diretórios Recomendada

Uma estrutura bem organizada é essencial para manter sanidade conforme o projeto cresce. A organização típica segue um padrão onde a raiz contém configurações do ArgoCD, e subdireórios representam ambientes ou aplicações. Cada aplicação pode ter seus próprios charts Helm ou manifestos Kustomize, facilitando reutilização e versionamento independente.

seu-repo/
├── argocd/
│   ├── apps/
│   │   ├── root-app.yaml
│   │   ├── dev-env.yaml
│   │   ├── prod-env.yaml
│   │   └── monitoring-env.yaml
│   └── projects/
│       ├── default.yaml
│       └── platform-team.yaml
├── envs/
│   ├── dev/
│   │   ├── kustomization.yaml
│   │   ├── deployment.yaml
│   │   └── service.yaml
│   ├── staging/
│   └── prod/
│       ├── kustomization.yaml
│       ├── deployment.yaml
│       └── service.yaml
└── apps/
    ├── backend/
    │   ├── Chart.yaml
    │   ├── values.yaml
    │   └── templates/
    ├── frontend/
    └── database/

Sync Policies: Controlando o Comportamento de Sincronização

A Sync Policy é o mecanismo que define como e quando o ArgoCD sincroniza o estado desejado com o cluster. Existem duas estratégias principais: sincronização automática ou manual. A sincronização automática aplica mudanças imediatamente quando detectadas no Git, enquanto a manual requer intervenção explícita. Ambas têm lugar em diferentes contextos, e a escolha depende do seu nível de confiança no pipeline e do impacto potencial de mudanças.

Além da automação base, existem várias opções que modificam comportamento: prune remove recursos que não estão mais declarados no Git, selfHeal corrige mudanças manuais aplicadas diretamente no cluster, e retry específica quantas tentativas fazer antes de desistir. Essas opções trabalham juntas para criar políticas sofisticadas.

Sincronização Automática vs Manual

# Estratégia 1: Sincronização Automática (recomendada para dev/staging)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: main
    path: apps/dev
  destination:
    server: https://kubernetes.default.svc
    namespace: dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allow:
        empty: false
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
# Estratégia 2: Sincronização Manual (recomendada para produção)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prod-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: main
    path: apps/prod
  destination:
    server: https://kubernetes.default.svc
    namespace: prod
  syncPolicy:
    syncOptions:
    - CreateNamespace=true
    - PrunePropagationPolicy=background
    - RespectIgnoreDifferences=true

No primeiro exemplo, a Application sincroniza imediatamente quando detecta mudanças, remove recursos órfãos e corrige alterações manuais. A política de retry garante que falhas temporárias não bloqueiem o processo. No segundo, não há automação — alguém deve manualmente trigger a sincronização após revisão, oferecendo segurança extra em produção.

Opções de Sincronização Avançadas

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: advanced-sync-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: HEAD
    path: apps/advanced
  destination:
    server: https://kubernetes.default.svc
    namespace: advanced
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allow:
        empty: false
    syncOptions:
    - CreateNamespace=true
    - PrunePropagationPolicy=foreground
    - PruneLast=true
    - SkipDryRunOnDeploy=false
    - RespectIgnoreDifferences=true
    - ApplyOutOfSyncOnly=false
    - Validate=true
  revisionHistoryLimit: 10

As opções importantes aqui são: CreateNamespace cria o namespace se não existir; PrunePropagationPolicy define como remover recursos (foreground aguarda dependências serem removidas primeiro); PruneLast garante que recursos sejam removidos após novos serem aplicados; SkipDryRunOnDeploy pula a validação dry-run para acelerar; RespectIgnoreDifferences ignora mudanças em campos que não devem triggerar sincronização (útil para status que o controlador modifica); ApplyOutOfSyncOnly aplica apenas mudanças, não o estado completo; Validate executa validação antes de aplicar.

Sincronização Parcial e Seletiva

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: selective-sync-app
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/seu-repo
    targetRevision: HEAD
    path: apps/selective
  destination:
    server: https://kubernetes.default.svc
    namespace: selective
  syncPolicy:
    syncOptions:
    - Validate=false
    - CreateNamespace=true
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/replicas
  - group: v1
    kind: ConfigMap
    name: app-config
    namespace: selective

O atributo ignoreDifferences permite que você ignore mudanças em certos campos. Neste exemplo, mudanças no número de replicas de Deployments não disparam uma sincronização (útil quando HPA modifica replicas dinamicamente), e qualquer mudança em um ConfigMap específico é ignorada.

Exemplo Prático: Configurando um Projeto Completo com App of Apps

Vamos consolidar tudo em um exemplo real: um projeto com múltiplos serviços, múltiplos ambientes, e sincronização configurada apropriadamente. Este exemplo demonstra os conceitos anteriores em ação.

Estrutura do Repositório

meu-projeto/
├── argocd/
│   ├── projects/
│   │   └── default.yaml
│   └── apps/
│       ├── root-app.yaml
│       ├── dev-cluster.yaml
│       ├── staging-cluster.yaml
│       └── prod-cluster.yaml
├── apps/
│   ├── backend/
│   │   ├── Chart.yaml
│   │   ├── values-dev.yaml
│   │   ├── values-prod.yaml
│   │   └── templates/
│   │       ├── deployment.yaml
│   │       ├── service.yaml
│   │       └── configmap.yaml
│   ├── frontend/
│   │   └── ...
│   └── worker/
│       └── ...
└── envs/
    ├── dev/
    │   └── kustomization.yaml
    ├── staging/
    │   └── kustomization.yaml
    └── prod/
        └── kustomization.yaml

Arquivo de Projeto (RBAC)

# argocd/projects/default.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: default
  namespace: argocd
spec:
  sourceRepos:
  - 'https://github.com/seu-usuario/meu-projeto'
  destinations:
  - namespace: 'dev'
    server: https://kubernetes.default.svc
  - namespace: 'staging'
    server: https://kubernetes.default.svc
  - namespace: 'prod'
    server: https://kubernetes.default.svc
  clusterResourceWhitelist:
  - group: ''
    kind: Namespace
  namespaceResourceBlacklist:
  - group: ''
    kind: ResourceQuota
  - group: ''
    kind: LimitRange

Application Raiz (App of Apps)

# argocd/apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root-app
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/meu-projeto
    targetRevision: main
    path: argocd/apps
    directory:
      recurse: true
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

Applications de Ambiente

# argocd/apps/dev-cluster.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dev-environment
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/meu-projeto
    targetRevision: main
    path: apps/backend
    helm:
      valuesFiles:
      - values-dev.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: dev
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true
  revisionHistoryLimit: 10
# argocd/apps/prod-cluster.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: prod-environment
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/seu-usuario/meu-projeto
    targetRevision: main
    path: apps/backend
    helm:
      valuesFiles:
      - values-prod.yaml
  destination:
    server: https://kubernetes.default.svc
    namespace: prod
  syncPolicy:
    # Sincronização manual para produção
    syncOptions:
    - CreateNamespace=true
    - Validate=true
  revisionHistoryLimit: 10

Helm Values para Configuração por Ambiente

# apps/backend/values-dev.yaml
replicaCount: 1
image:
  repository: seu-registry/backend
  tag: latest
  pullPolicy: Always
resources:
  requests:
    memory: "64Mi"
    cpu: "100m"
  limits:
    memory: "128Mi"
    cpu: "200m"
env:
  - name: ENVIRONMENT
    value: "dev"
  - name: LOG_LEVEL
    value: "debug"
# apps/backend/values-prod.yaml
replicaCount: 3
image:
  repository: seu-registry/backend
  tag: v1.2.3
  pullPolicy: IfNotPresent
resources:
  requests:
    memory: "256Mi"
    cpu: "500m"
  limits:
    memory: "512Mi"
    cpu: "1000m"
env:
  - name: ENVIRONMENT
    value: "prod"
  - name: LOG_LEVEL
    value: "warn"

Boas Práticas e Armadilhas Comuns

Implementar GitOps corretamente requer mais que apenas configurar ArgoCD — você precisa pensar em processos, segurança e escalabilidade. A primeira prática essencial é manter credenciais fora do Git. Use Sealed Secrets ou External Secrets Operator para armazenar senhas, tokens e chaves criptografadas no repositório. Nunca commite credenciais em texto plano.

A segunda prática é estabelecer um processo de revisão robusto. Como Git é agora sua fonte de verdade, pull requests se tornam portais de entrada críticos. Exija aprovações antes de merge, use automações para validar manifestos (kubeval, kube-score) e considere ambientes de teste automáticos antes de produção. A terceira é monitorar divergências entre Git e cluster — configure alertas no ArgoCD para casos onde sincronização falha repetidamente ou o cluster está out of sync.

Armadilhas comuns incluem usar sincronização automática em produção sem cuidado (crie barreiras de proteção como branches protegidos e CI/CD validações), confundir Application com AppProject (Applications gerenciam recursos, Projects oferecem RBAC), e não versionador manifestos corretamente (use tags Git ou branches para rastrear releases). Também é fácil negligenciar monitoramento de mudanças manuais — alguém editando um Deployment diretamente no cluster sem passar por Git quebra o contrato de GitOps.

Conclusão

ArgoCD implementa GitOps de forma elegante e madura, tornando Git a fonte única da verdade para infraestrutura Kubernetes. O padrão App of Apps escala essa abordagem para cenários complexos, permitindo estruturas hierárquicas onde uma Application raiz coordena múltiplas Applications filhas. Sync Policies são o mecanismo fino que controla como sincronização acontece, oferecendo automação total para ambientes de desenvolvimento e controle manual para produção, com inúmeras opções para comportamentos especializados.

O grande aprendizado aqui é que GitOps não é apenas um padrão técnico — é uma mudança cultural. Você está trocando imperativos manuais por declarativos versionados, auditáveis e colaborativos. Qualidade de código, revisão por pares e reversibilidade deixam de ser opcionais e se tornam automáticas. Entender esses conceitos profundamente, não apenas usar a ferramenta, permite você desenhar arquiteturas resilientes e escaláveis.

Referências


Artigos relacionados