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 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.
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
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.
Kustomize Controller: composição declarativa
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.
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
O path aponta para um diretório com kustomization.yaml. O campo prune: true significa que recursos deletados do Git também serão removidos do cluster. wait: true faz Flux aguardar deployments ficarem prontos antes de considerar a sincronização bem-sucedida.
Helm Controller: gerenciamento de charts
O Helm Controller gerencia releases Helm direto no cluster. Você não executa helm install manualmente — o controller observa e aplica. Isso funciona particularmente bem para dependências externas (databases, ingress controllers, monitoring stacks).
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
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 13.1.x.
Combinando Kustomize e Helm em uma estratégia real
Estrutura de projeto recomendada
Uma abordagem sólida separa concerns em diretórios. Helm para componentes reutilizáveis, Kustomize para customização ambiental. Vamos modelar um projeto real:
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
Exemplo prático: aplicação com dependências Helm
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:
# 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
Agora a dependência externa via Helm:
# 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: "${DB_PASSWORD}"
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
E a aplicação em Kustomize:
# 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
# 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: "db-postgresql-primary.data-layer.svc.cluster.local"
- name: DB_USER
value: "appuser"
- name: DB_NAME
value: "myapp"
Para production, você aumenta replicas e adiciona configurações:
# 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
# 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
Finalmente, a Kustomization que orquestra tudo:
# 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
Note o campo dependsOn: Flux aplica infraestrutura primeiro, depois aplicações. Isso previne deployments falharem por dependências indisponíveis.
Fluxo de trabalho GitOps com Flux
Sincronização e reconciliação
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.
# 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
Se uma HelmRelease falhar, o Flux não desiste. Ele continua tentando no intervalo especificado. Você pode ver erros com flux get helmrelease postgresql -n databases:
NAME READY MESSAGE
postgresql False Helm upgrade failed: error validating values
Segredos e sensibilidade
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:
# Criar segredo criptografado
echo "password: super-secreto" | sops -e /dev/stdin
# Resultado (encrypted)
password: ENC[AES256_GCM,data:h8k+...,iv:...,tag:...,type:str]
Flux detecta e descriptografa automaticamente se configurado:
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
Estratégia de branches e ambientes
Uma prática robusta usa branches para ambientes. GitOps puro significa cada branch = estado esperado de um ambiente:
# 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
Cada cluster aponta para seu branch via GitRepository:
# staging/flux-system.yaml
spec:
ref:
branch: staging
# production/flux-system.yaml
spec:
ref:
branch: main
Conclusão
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.