Docker & Kubernetes

Boas Práticas de Flux CD: GitOps Alternativo com Kustomize e Helm Controller para Times Ágeis

11 min de leitura

Boas Práticas de Flux CD: GitOps Alternativo com Kustomize e Helm Controller para Times Ágeis

O que é Flux CD e por que GitOps? Flux CD é um operador Kubernetes que implementa o padrão GitOps de forma declarativa. Diferente de ferramentas como ArgoCD que centralizam a lógica em um painel, Flux distribui a responsabilidade através de controllers específicos rodando diretamente no seu cluster. A ideia central é simples: seu repositório Git é a fonte de verdade, e o cluster converge automaticamente para o estado desejado descrito lá. O padrão GitOps resolve um problema fundamental em DevOps: rastreabilidade completa de mudanças infraestruturais. Toda alteração passa por Git, traz histórico, permite rollback e code review. Flux, ao contrário de soluções monolíticas, adota uma arquitetura modular onde cada controller tem responsabilidade bem definida. Você instala apenas o que precisa, reduzindo overhead e complexidade. Arquitetura do Flux: Componentes Principais Source Controller e seus tipos de fonte O Source Controller é responsável por monitorar repositórios e buscar manifests. Ele não aplica nada — apenas sincroniza a fonte desejada. Flux suporta

<h2>O que é Flux CD e por que GitOps?</h2>

<p>Flux CD é um operador Kubernetes que implementa o padrão GitOps de forma declarativa. Diferente de ferramentas como ArgoCD que centralizam a lógica em um painel, Flux distribui a responsabilidade através de controllers específicos rodando diretamente no seu cluster. A ideia central é simples: seu repositório Git é a fonte de verdade, e o cluster converge automaticamente para o estado desejado descrito lá.</p>

<p>O padrão GitOps resolve um problema fundamental em DevOps: rastreabilidade completa de mudanças infraestruturais. Toda alteração passa por Git, traz histórico, permite rollback e code review. Flux, ao contrário de soluções monolíticas, adota uma arquitetura modular onde cada controller tem responsabilidade bem definida. Você instala apenas o que precisa, reduzindo overhead e complexidade.</p>

<h2>Arquitetura do Flux: Componentes Principais</h2>

<h3>Source Controller e seus tipos de fonte</h3>

<p>O Source Controller é responsável por monitorar repositórios e buscar manifests. Ele não aplica nada — apenas sincroniza a fonte desejada. Flux suporta múltiplas fontes: repositórios Git, buckets S3, registros Helm e OCI. Esse design permite usar a mesma ferramenta para cenários diversos sem criar abstrações artificiais.</p>

<pre><code class="language-yaml">apiVersion: source.toolkit.fluxcd.io/v1beta2

kind: GitRepository

metadata:

name: app-repo

namespace: flux-system

spec:

interval: 1m

url: https://github.com/seu-usuario/seu-repo.git

ref:

branch: main

secretRef:

name: git-credentials</code></pre>

<p>Este manifesto instrui o Source Controller a verificar o repositório a cada minuto. Se houver mudanças, ele as sincroniza localmente. Note que isso é puramente observação — nenhum recurso é criado ainda.</p>

<h3>Kustomize Controller: composição declarativa</h3>

<p>O Kustomize Controller aplica manifests gerados pelo Kustomize, uma ferramenta nativa do Kubernetes para template e patch de YAMLs. Diferente de Helm (que usa templates Go), Kustomize trabalha diretamente com manifests, o que mantém o resultado sempre válido no Kubernetes.</p>

<pre><code class="language-yaml">apiVersion: kustomize.toolkit.fluxcd.io/v1

kind: Kustomization

metadata:

name: app-deployment

namespace: flux-system

spec:

interval: 10m

sourceRef:

kind: GitRepository

name: app-repo

path: ./app/overlays/production

prune: true

wait: true</code></pre>

<p>O <code>path</code> aponta para um diretório com <code>kustomization.yaml</code>. O campo <code>prune: true</code> significa que recursos deletados do Git também serão removidos do cluster. <code>wait: true</code> faz Flux aguardar deployments ficarem prontos antes de considerar a sincronização bem-sucedida.</p>

<h3>Helm Controller: gerenciamento de charts</h3>

<p>O Helm Controller gerencia releases Helm direto no cluster. Você não executa <code>helm install</code> manualmente — o controller observa e aplica. Isso funciona particularmente bem para dependências externas (databases, ingress controllers, monitoring stacks).</p>

<pre><code class="language-yaml">apiVersion: source.toolkit.fluxcd.io/v1beta2

kind: HelmRepository

metadata:

name: bitnami

namespace: flux-system

spec:

interval: 1h

url: https://charts.bitnami.com/bitnami

---

apiVersion: helm.toolkit.fluxcd.io/v2beta1

kind: HelmRelease

metadata:

name: postgresql

namespace: databases

spec:

interval: 30m

chart:

spec:

chart: postgresql

version: 13.1.x

sourceRef:

kind: HelmRepository

name: bitnami

namespace: flux-system

values:

auth:

username: appuser

password: secure-password

persistence:

size: 10Gi</code></pre>

<p>Aqui temos duas fontes: uma apontando para o repositório Bitnami e outra descrevendo a release. O controller sincroniza automaticamente se o chart tiver novas versões compatíveis com <code>13.1.x</code>.</p>

<h2>Combinando Kustomize e Helm em uma estratégia real</h2>

<h3>Estrutura de projeto recomendada</h3>

<p>Uma abordagem sólida separa concerns em diretórios. Helm para componentes reutilizáveis, Kustomize para customização ambiental. Vamos modelar um projeto real:</p>

<pre><code>seu-repo/

├── clusters/

│ ├── staging/

│ │ └── flux-system.yaml

│ └── production/

│ └── flux-system.yaml

├── infrastructure/

│ ├── helm-repos.yaml

│ ├── ingress-controller/

│ │ └── helmrelease.yaml

│ └── cert-manager/

│ └── helmrelease.yaml

└── apps/

├── api-service/

│ ├── kustomization.yaml

│ ├── base/

│ │ ├── kustomization.yaml

│ │ ├── deployment.yaml

│ │ └── service.yaml

│ └── overlays/

│ ├── staging/

│ │ └── kustomization.yaml

│ └── production/

│ └── kustomization.yaml</code></pre>

<h3>Exemplo prático: aplicação com dependências Helm</h3>

<p>Suponha uma aplicação que depende de PostgreSQL (Helm) e precisa ser deployada em ambientes diferentes. Começamos com a fonte e repositório Helm:</p>

<pre><code class="language-yaml"># clusters/production/flux-system.yaml

apiVersion: source.toolkit.fluxcd.io/v1beta2

kind: GitRepository

metadata:

name: main-repo

namespace: flux-system

spec:

interval: 5m

url: https://github.com/seu-usuario/seu-app.git

ref:

branch: main

---

apiVersion: source.toolkit.fluxcd.io/v1beta2

kind: HelmRepository

metadata:

name: bitnami

namespace: flux-system

spec:

interval: 1h

url: https://charts.bitnami.com/bitnami</code></pre>

<p>Agora a dependência externa via Helm:</p>

<pre><code class="language-yaml"># infrastructure/database/helmrelease.yaml

apiVersion: helm.toolkit.fluxcd.io/v2beta1

kind: HelmRelease

metadata:

name: db

namespace: data-layer

spec:

interval: 30m

chart:

spec:

chart: postgresql

version: 13.1.5

sourceRef:

kind: HelmRepository

name: bitnami

namespace: flux-system

values:

auth:

username: appuser

password: &quot;${DB_PASSWORD}&quot;

database: myapp

persistence:

enabled: true

size: 20Gi

replica:

replicaCount: 2

postRenderers:

  • kustomize:

patchesStrategicMerge:

  • apiVersion: v1

kind: Service

metadata:

name: db-postgresql-primary

spec:

type: ClusterIP</code></pre>

<p>E a aplicação em Kustomize:</p>

<pre><code class="language-yaml"># apps/api-service/base/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1

kind: Kustomization

resources:

  • deployment.yaml
  • service.yaml

commonLabels:

app: api-service

managed-by: flux

replicas:

  • name: api-service

count: 1</code></pre>

<pre><code class="language-yaml"># apps/api-service/base/deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: api-service

spec:

selector:

matchLabels:

app: api-service

template:

metadata:

labels:

app: api-service

spec:

containers:

  • name: api

image: seu-registry/api-service:latest

ports:

  • containerPort: 8080

env:

  • name: DB_HOST

value: &quot;db-postgresql-primary.data-layer.svc.cluster.local&quot;

  • name: DB_USER

value: &quot;appuser&quot;

  • name: DB_NAME

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

<p>Para production, você aumenta replicas e adiciona configurações:</p>

<pre><code class="language-yaml"># apps/api-service/overlays/production/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1

kind: Kustomization

bases:

  • ../../base

replicas:

  • name: api-service

count: 3

patchesStrategicMerge:

  • deployment.yaml

resources:

  • hpa.yaml</code></pre>

<pre><code class="language-yaml"># apps/api-service/overlays/production/deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: api-service

spec:

template:

spec:

containers:

  • name: api

resources:

requests:

cpu: 500m

memory: 512Mi

limits:

cpu: 1000m

memory: 1Gi</code></pre>

<p>Finalmente, a Kustomization que orquestra tudo:</p>

<pre><code class="language-yaml"># clusters/production/app.yaml

apiVersion: kustomize.toolkit.fluxcd.io/v1

kind: Kustomization

metadata:

name: apps

namespace: flux-system

spec:

interval: 10m

sourceRef:

kind: GitRepository

name: main-repo

path: ./apps/api-service/overlays/production

prune: true

wait: true

dependsOn:

  • name: infrastructure</code></pre>

<p>Note o campo <code>dependsOn</code>: Flux aplica infraestrutura primeiro, depois aplicações. Isso previne deployments falharem por dependências indisponíveis.</p>

<h2>Fluxo de trabalho GitOps com Flux</h2>

<h3>Sincronização e reconciliação</h3>

<p>Quando você faz push em Git, o Source Controller detecta mudanças (dentro do intervalo configurado, ou imediatamente se configurar webhooks). Os controllers então entram em um loop de reconciliação: eles buscam o estado desejado em Git e o estado atual no cluster, executando ações para convergir.</p>

<pre><code class="language-bash"># Ver status das sincronizações

flux get kustomizations -A

flux get helmreleases -A

flux get sources git -A

Ver logs detalhados

flux logs --all-namespaces --follow

Forçar reconciliação imediatamente

flux reconcile kustomization app-deployment

flux reconcile source git app-repo</code></pre>

<p>Se uma HelmRelease falhar, o Flux não desiste. Ele continua tentando no intervalo especificado. Você pode ver erros com <code>flux get helmrelease postgresql -n databases</code>:</p>

<pre><code>NAME READY MESSAGE

postgresql False Helm upgrade failed: error validating values</code></pre>

<h3>Segredos e sensibilidade</h3>

<p>Dados sensíveis em Git violam segurança. Flux integra-se com SOPS (Secrets Operations) e sealed-secrets do Bitnami. Com SOPS, você criptografa valores com uma chave pública — só o cluster com a chave privada consegue descriptografar:</p>

<pre><code class="language-bash"># Criar segredo criptografado

echo &quot;password: super-secreto&quot; | sops -e /dev/stdin

Resultado (encrypted)

password: ENC[AES256_GCM,data:h8k+...,iv:...,tag:...,type:str]</code></pre>

<p>Flux detecta e descriptografa automaticamente se configurado:</p>

<pre><code class="language-yaml">apiVersion: kustomize.toolkit.fluxcd.io/v1

kind: Kustomization

metadata:

name: app-deployment

namespace: flux-system

spec:

interval: 10m

sourceRef:

kind: GitRepository

name: app-repo

path: ./app/overlays/production

decryption:

provider: sops

secretRef:

name: sops-keys</code></pre>

<h3>Estratégia de branches e ambientes</h3>

<p>Uma prática robusta usa branches para ambientes. GitOps puro significa cada branch = estado esperado de um ambiente:</p>

<pre><code class="language-bash"># Staging recebe commits direto

git push origin feature-x

Production requer pull request mergeado em main

git pull-request --branch=main

Clusters sincronizam branches diferentes

Staging: staging branch

Production: main branch</code></pre>

<p>Cada cluster aponta para seu branch via GitRepository:</p>

<pre><code class="language-yaml"># staging/flux-system.yaml

spec:

ref:

branch: staging

production/flux-system.yaml

spec:

ref:

branch: main</code></pre>

<h2>Conclusão</h2>

<p>Flux CD oferece uma arquitetura GitOps modular onde você compõe a solução desejada. A separação entre Source, Kustomize e Helm Controllers fornece flexibilidade: aplicações em Kustomize aproveitam versionamento semântico com Helm para infraestrutura crítica. O loop de reconciliação contínuo garante convergência automática, eliminando drift entre Git e cluster. Por fim, a integração com ferramentas como SOPS permite usar Git como single source of truth mesmo com dados sensíveis, mantendo auditoria completa de quem mudou o quê e quando.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://fluxcd.io/" target="_blank" rel="noopener noreferrer">Flux CD Documentação Oficial</a></li>

<li><a href="https://kustomize.io/" target="_blank" rel="noopener noreferrer">Kustomize - Kubernetes Customization</a></li>

<li><a href="https://helm.sh/" target="_blank" rel="noopener noreferrer">Helm Package Manager for Kubernetes</a></li>

<li><a href="https://martinfowler.com/articles/gitops.html" target="_blank" rel="noopener noreferrer">GitOps: What You Need to Know - Martin Fowler</a></li>

<li><a href="https://github.com/mozilla/sops" target="_blank" rel="noopener noreferrer">SOPS - Secrets Operations</a></li>

</ul>

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

Comentários

Mais em Docker & Kubernetes

Como Usar Helm Fundamentos: Charts, Values, Templates e Releases em Produção
Como Usar Helm Fundamentos: Charts, Values, Templates e Releases em Produção

Helm Fundamentos: Charts, Values, Templates e Releases Helm é o gerenciador d...

Dominando Pods em Kubernetes: Ciclo de Vida, Init Containers e Sidecar Pattern em Projetos Reais
Dominando Pods em Kubernetes: Ciclo de Vida, Init Containers e Sidecar Pattern em Projetos Reais

Entendendo Pods: A Unidade Fundamental do Kubernetes Um Pod é a menor unidade...

Boas Práticas de Imagens Docker: Layers, Union Filesystem e Como o Build Realmente Funciona para Times Ágeis
Boas Práticas de Imagens Docker: Layers, Union Filesystem e Como o Build Realmente Funciona para Times Ágeis

Introdução: O Que São Layers e Por Que Importam Docker revolucionou a forma c...