GitOps & ArgoCD · MemphisLab Docs
MemphisLab Docs
k3s Platform

GitOps & ArgoCD

Como o ArgoCD sincroniza o cluster contra o Git e como o ApplicationSet gera um cliente por arquivo

O princípio GitOps

GitOps é uma aplicação específica do control loop (ver orchestration-fundamentals): em vez de alguém rodar kubectl apply na mão contra o cluster, um agente dentro do cluster observa um repositório Git e aplica sozinho qualquer mudança que aparecer nele. O Git vira a única fonte de verdade do estado desejado — kubectl apply direto continua tecnicamente possível, mas o próprio agente desfaz a mudança na próxima sincronização, porque ela diverge do que está no Git.

Duas consequências práticas disso: todo o histórico de mudança de infraestrutura é um histórico de commit (quem mudou o quê, quando, revisável em PR antes de acontecer), e o cluster nunca fica de fato inacessível para auditoria — mesmo sem acesso a kubectl, o estado desejado inteiro está legível no repositório.

ArgoCD: reconciliation + self-heal

O ArgoCD é o agente que faz esse control loop. Cada Application do ArgoCD aponta para um caminho num repositório Git e um destino (cluster + namespace); o ArgoCD compara periodicamente o manifest renderizado daquele caminho contra o que está de fato rodando, e sincroniza.

selfHeal: true é o que faz a reconciliação valer também contra mudança manual: se alguém rodar kubectl edit num Deployment gerenciado por uma Application com self-heal, o ArgoCD detecta a divergência e reverte para o que está no Git, não o contrário. É por isso que spec.replicas é omitido do Deployment quando o HPA está ativo (ver network-and-scaling) — sem isso, toda vez que o HPA escalasse o pod, o self-heal desfaria o scale-up na sincronização seguinte, porque o Git “diz” que é 1 réplica.

ApplicationSet: um Application por arquivo

Uma Application do ArgoCD é estática — um caminho, um destino. Esse cluster tem N clientes, cada um precisando da própria Application (próprio namespace, próprio values file), e o número de clientes cresce sem redeploy da plataforma. O ApplicationSet resolve isso: é um gerador de Applications, que lê algo (aqui, uma lista de arquivos no Git) e cria uma Application por item encontrado.

# argocd-apps/clients-appset.yaml
generators:
  - git:
      repoURL: https://github.com/finatto-devops/k3s-platform.git
      revision: HEAD
      files:
        - path: "clients/*.yaml"

template:
  metadata:
    name: '{{ .path.filename | trimSuffix ".yaml" }}'
  spec:
    sources:
      - repoURL: https://github.com/finatto-devops/k3s-platform.git
        path: charts/app
        helm:
          releaseName: '{{ .path.filename | trimSuffix ".yaml" }}'
          valueFiles:
            - $values/apps/{{ .platform.app }}.yaml
            - $values/{{ .path.path }}/{{ .path.filename }}
    destination:
      namespace: '{{ .path.filename | trimSuffix ".yaml" }}'

O generator git files varre clients/*.yaml a cada sincronização do ApplicationSet — criar um cliente novo é só adicionar um arquivo em clients/, sem tocar no ApplicationSet em si. {{ .path.filename | trimSuffix ".yaml" }} usa o nome do arquivo como nome da Application, nome do release Helm e nome do namespace — é por isso que clients/acme.yaml vira exatamente o namespace acme, sem mapeamento extra em lugar nenhum.

Cada Application gerada tem duas fontes de values combinadas: apps/{{ .platform.app }}.yaml (o que é comum à aplicação, ex. apps/checklist.yaml — mesmo repositório, mesma imagem, para todo cliente que roda checklist) e clients/<slug>.yaml (o que é específico deste cliente — domínio, bucket S3). O Helm mescla os dois: o segundo sobrescreve o primeiro campo a campo. Uma terceira source (sealed/, filtrada pelo nome do arquivo) traz os SealedSecret do cliente — ver secrets-encryption.

ignoreDifferences na definição do ApplicationSet é o que impede o self-heal de brigar com o HPA: /spec/replicas do Deployment é explicitamente excluído da comparação, então o ArgoCD para de tentar “corrigir” a contagem de réplicas que o HPA está ajustando.

Helm: templating por trás disso

O ArgoCD sincroniza manifests — objetos YAML finais, prontos para kubectl apply. charts/app não é esse YAML final: é um template Helm, com placeholders ({{ .Values.algumaCoisa }}) que o Helm resolve contra um arquivo values.yaml antes de virar manifest. Um único chart (charts/app, versão fixada em Chart.yaml) serve toda aplicação Laravel da plataforma — o que muda de app para app, e de cliente para cliente, são só os valores injetados, não o template.

É esse mecanismo que faz apps/checklist.yaml (comum à aplicação) e clients/acme.yaml (específico do cliente) virarem, juntos, um Deployment, um Service, um Ingress e um Cluster de Postgres completos — sem que ninguém escreva manifest à mão por cliente.