Docker & Kubernetes

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

15 min de leitura

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

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

<h2>GitOps e ArgoCD: Fundamentos</h2>

<p>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.</p>

<p>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.</p>

<h3>Como ArgoCD Funciona</h3>

<p>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.</p>

<p>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.</p>

<pre><code class="language-yaml">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</code></pre>

<p>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).</p>

<h2>App of Apps: Escalando GitOps para Múltiplos Ambientes</h2>

<p>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.</p>

<p>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.</p>

<pre><code class="language-yaml"># 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</code></pre>

<pre><code class="language-yaml"># 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</code></pre>

<pre><code class="language-yaml"># 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</code></pre>

<p>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.</p>

<h3>Estrutura de Diretórios Recomendada</h3>

<p>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.</p>

<pre><code>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/</code></pre>

<h2>Sync Policies: Controlando o Comportamento de Sincronização</h2>

<p>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.</p>

<p>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.</p>

<h3>Sincronização Automática vs Manual</h3>

<pre><code class="language-yaml"># 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</code></pre>

<pre><code class="language-yaml"># 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</code></pre>

<p>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.</p>

<h3>Opções de Sincronização Avançadas</h3>

<pre><code class="language-yaml">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</code></pre>

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

<h3>Sincronização Parcial e Seletiva</h3>

<pre><code class="language-yaml">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</code></pre>

<p>O atributo <code>ignoreDifferences</code> 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.</p>

<h2>Exemplo Prático: Configurando um Projeto Completo com App of Apps</h2>

<p>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.</p>

<h3>Estrutura do Repositório</h3>

<pre><code>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</code></pre>

<h3>Arquivo de Projeto (RBAC)</h3>

<pre><code class="language-yaml"># argocd/projects/default.yaml

apiVersion: argoproj.io/v1alpha1

kind: AppProject

metadata:

name: default

namespace: argocd

spec:

sourceRepos:

  • &#039;https://github.com/seu-usuario/meu-projeto&#039;

destinations:

  • namespace: &#039;dev&#039;

server: https://kubernetes.default.svc

  • namespace: &#039;staging&#039;

server: https://kubernetes.default.svc

  • namespace: &#039;prod&#039;

server: https://kubernetes.default.svc

clusterResourceWhitelist:

  • group: &#039;&#039;

kind: Namespace

namespaceResourceBlacklist:

  • group: &#039;&#039;

kind: ResourceQuota

  • group: &#039;&#039;

kind: LimitRange</code></pre>

<h3>Application Raiz (App of Apps)</h3>

<pre><code class="language-yaml"># 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</code></pre>

<h3>Applications de Ambiente</h3>

<pre><code class="language-yaml"># 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</code></pre>

<pre><code class="language-yaml"># 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</code></pre>

<h3>Helm Values para Configuração por Ambiente</h3>

<pre><code class="language-yaml"># apps/backend/values-dev.yaml

replicaCount: 1

image:

repository: seu-registry/backend

tag: latest

pullPolicy: Always

resources:

requests:

memory: &quot;64Mi&quot;

cpu: &quot;100m&quot;

limits:

memory: &quot;128Mi&quot;

cpu: &quot;200m&quot;

env:

  • name: ENVIRONMENT

value: &quot;dev&quot;

  • name: LOG_LEVEL

value: &quot;debug&quot;</code></pre>

<pre><code class="language-yaml"># apps/backend/values-prod.yaml

replicaCount: 3

image:

repository: seu-registry/backend

tag: v1.2.3

pullPolicy: IfNotPresent

resources:

requests:

memory: &quot;256Mi&quot;

cpu: &quot;500m&quot;

limits:

memory: &quot;512Mi&quot;

cpu: &quot;1000m&quot;

env:

  • name: ENVIRONMENT

value: &quot;prod&quot;

  • name: LOG_LEVEL

value: &quot;warn&quot;</code></pre>

<h2>Boas Práticas e Armadilhas Comuns</h2>

<p>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.</p>

<p>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.</p>

<p>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.</p>

<h2>Conclusão</h2>

<p>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.</p>

<p>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.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://argo-cd.readthedocs.io/" target="_blank" rel="noopener noreferrer">ArgoCD Official Documentation</a> — Documentação oficial completa do ArgoCD</li>

<li><a href="https://www.cncf.io/blog/2021/10/15/what-is-gitops-really/" target="_blank" rel="noopener noreferrer">GitOps: What You Need to Know - CloudNative Computing Foundation</a> — Artigo sobre princípios de GitOps</li>

<li><a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/" target="_blank" rel="noopener noreferrer">Argo CD App of Apps Pattern</a> — Documentação específica sobre o padrão App of Apps</li>

<li><a href="https://github.com/kelseyhightower/kubernetes-the-hard-way" target="_blank" rel="noopener noreferrer">Kubernetes the Hard Way - Kelsey Hightower</a> — Base sólida para entender infraestrutura Kubernetes</li>

<li><a href="https://argo-cd.readthedocs.io/en/stable/user-guide/auto_sync/" target="_blank" rel="noopener noreferrer">ArgoCD Sync Policies Reference</a> — Detalhes técnicos sobre políticas de sincronização</li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Docker & Kubernetes

Boas Práticas de Containers vs VMs: Namespaces, Cgroups e o que o Docker Realmente Faz para Times Ágeis
Boas Práticas de Containers vs VMs: Namespaces, Cgroups e o que o Docker Realmente Faz para Times Ágeis

Introdução: O Problema das Máquinas Virtuais Quando começamos a trabalhar com...

EKS na AWS: Provisionamento, Node Groups e Add-ons Gerenciados na Prática
EKS na AWS: Provisionamento, Node Groups e Add-ons Gerenciados na Prática

Introdução ao EKS: Contexto e Arquitetura O Amazon EKS (Elastic Kubernetes Se...

Distributed Tracing em Kubernetes: Jaeger e OpenTelemetry Collector na Prática
Distributed Tracing em Kubernetes: Jaeger e OpenTelemetry Collector na Prática

O que é Distributed Tracing e por que você precisa disso em Kubernetes Distri...