GitOps dla Terraform: kontroler Flux TF, wykrywanie podnoszenia kosmicznego i dryfu
Wprowadź Terraform do paradygmatu GitOps: Flux Terraform Controller do uzgadniania kontynuuje od statusu repozytorium, Spacelift dla zaawansowanych zasad oraz RBAC i alertów dryf dla środowisk krytycznych.
GitOps i Terraform: dlaczego połączenie jest potężne
GitOps odmienił wdrażanie aplikacji Kubernetes: Git staje się źródłem prawdę mówiąc, sterownik w sposób ciągły godzi stan pożądany z rzeczywistym, każda zmiana przechodzi przez żądanie ściągnięcia. W roku 2026 obowiązuje ten sam paradygmat dla infrastruktury chmurowej zarządzanej za pomocą Terraform, z jedną zasadniczą różnicą w porównaniu z Tradycyjny CI/CD: zamiast wyzwalacza typu „wciśnij i zapomnij” masz: pojednanie kontynuuj który automatycznie wykrywa i koryguje dryfty.
Problem z tradycyjnymi przepływami pracy Terraform opartymi na GitHub Actions lub Atlantis e którym jestem reaktywny: Ktoś dokonuje ręcznej zmiany w konsoli AWS i nikt wie o tym do czasu uruchomienia następnego rurociągu. Dzięki GitOps dla Terraform każdy rozbieżność pomiędzy kodem HCL a stanem rzeczywistym staje się alarmem — lub zostaje skorygowana automatycznie w oparciu o skonfigurowaną politykę.
Czego się nauczysz
- Architektura GitOps dla IaC: model pull vs model push
- Flux Terraform Controller: instalacja, obiekt Terraform CRD i uzgadnianie
- Zarządzanie stanem Terraform z Kubernetes z backendem S3 i IRSA
- Spacelift: stosy, zasady Rego, RBAC i przepływy pracy zatwierdzające
- Wykrywanie dryfu: Alarm Slack/PagerDuty w przypadku nieautoryzowanych odchyleń
- Wzorzec dla środowisk krytycznych: automatyczna naprawa a ręczne zatwierdzanie
Model pull a model push dla IaC
Kluczową różnicą pomiędzy GitOps a tradycyjnym CI/CD jest model synchronizacji. w model pchania (GitHub Actions, Jenkins), potok uruchamia się przy każdym zatwierdzeniu i „wypycha” zmiany w infrastrukturze. w wyciągnąć model (czysty GitOps), agent działający w klastrze w sposób ciągły „pobiera” żądany stan z repozytorium i pogodzić. Ta różnica ma głębokie konsekwencje dla bezpieczeństwa i odporności:
# Push Model (GitHub Actions) — richiede credenziali cloud nella pipeline
# Il runner GitHub deve avere accesso outbound al cloud provider
# Problem: se il job fallisce a meta, lo state puo essere inconsistente
# Pull Model (Flux TF Controller) — l'agente vive dentro il cluster
# Solo il cluster Kubernetes ha le credenziali cloud (via IRSA o Workload Identity)
# Vantaggio: single point of trust, nessuna credenziale nelle GitHub Secrets
# Vantaggio: riconciliazione continua ogni N minuti, non solo su commit
# Confronto security:
# Push Model: GitHub runner --[credenziali]--> AWS/Azure/GCP
# Pull Model: Kubernetes pod -[IRSA/WI]--> AWS/Azure/GCP
# Git repository -[SSH/HTTPS]--> Flux controller (dentro cluster)
Kontroler Flux Terraform
Il Kontroler Flux Terraform (kontroler tf) i kontroler Kubernetes open source, które wprowadza Terraform do świata GitOps. To projekt społeczności Flux (Weaveworks + niezależny opiekun) który rozszerza Flux o możliwość uruchomienia planuje i stosuje Terraform jako natywne pętle uzgadniania Kubernetes.
Instalacja
# Prerequisiti: cluster Kubernetes + Flux installato
# Installa Flux sul cluster (se non presente)
flux install
# Installa il TF Controller tramite HelmRelease
cat <<'EOF' | kubectl apply -f -
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: tf-controller
namespace: flux-system
spec:
interval: 1h
url: https://weaveworks.github.io/tf-controller
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: tf-controller
namespace: flux-system
spec:
interval: 1h
chart:
spec:
chart: tf-controller
version: "0.16.x"
sourceRef:
kind: HelmRepository
name: tf-controller
namespace: flux-system
values:
replicaCount: 1
resources:
limits:
cpu: "1"
memory: 1Gi
requests:
cpu: 200m
memory: 512Mi
# Runner pods: eseguono il processo terraform effettivo
runner:
image:
tag: "v1.5.x-flux"
EOF
# Verifica installazione
kubectl get pods -n flux-system | grep tf-controller
# NAME READY STATUS RESTARTS
# tf-controller-6d8f9b4b5-xn7q2 1/1 Running 0
Konfiguracja GitRepository i Terraform CRD
Przepływ pracy opiera się na dwóch obiektach Kubernetes: a GitRepository co wskazuje
repozytorium z kodem HCL i obiektem Terraform (niestandardowy CRD), który definiuje
co pogodzić.
# 1. GitRepository: sorgente del codice HCL
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: infra-repo
namespace: flux-system
spec:
interval: 1m # Controlla il repo ogni minuto
url: https://github.com/myorg/terraform-infra
ref:
branch: main
secretRef:
name: github-ssh-key # Secret con chiave SSH o token
---
# 2. Terraform CRD: definisce il modulo da riconciliare
apiVersion: infra.contrib.fluxcd.io/v1alpha2
kind: Terraform
metadata:
name: aws-networking
namespace: flux-system
spec:
# Intervallo di riconciliazione
interval: 10m
# Sorgente HCL
sourceRef:
kind: GitRepository
name: infra-repo
path: ./environments/prod/networking # Path nel repo
# Approvazione automatica (auto-apply) o manuale
approvePlan: auto
# Gestione del drift: se lo stato reale differisce dal desired
# force: riconcilia automaticamente
# drift: solo alert, non corregge
enableInventory: true
# Backend per lo state (S3 con IRSA)
backendConfig:
customConfiguration: |
backend "s3" {
bucket = "myorg-terraform-state-prod"
key = "networking/terraform.tfstate"
region = "eu-west-1"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
# Variabili passate al modulo
vars:
- name: environment
value: prod
- name: aws_region
value: eu-west-1
# Variabili da Secret Kubernetes (per segreti)
varsFrom:
- kind: Secret
name: terraform-vars-prod
varsKeys:
- db_password
- api_key
IRSA dla dostępu AWS z Kubernetes
Najlepsze praktyki dotyczące uwierzytelniania AWS z Kubernetes e IRSA (Role IAM dla kont usług): Pod Terraform otrzymuje token JWT podpisany przez klaster wymieniany z tymczasowymi poświadczeniami AWS, bez żadnego klucza zakodowane na stałe w klastrze.
# Crea il Service Account con annotazione IRSA
kubectl create serviceaccount tf-runner -n flux-system
kubectl annotate serviceaccount tf-runner \
-n flux-system \
eks.amazonaws.com/role-arn=arn:aws:iam::123456789:role/TerraformRunnerRole
# IAM Role Trust Policy (da configurare su AWS):
# {
# "Version": "2012-10-17",
# "Statement": [{
# "Effect": "Allow",
# "Principal": {
# "Federated": "arn:aws:iam::123456789:oidc-provider/oidc.eks.eu-west-1.amazonaws.com/..."
# },
# "Action": "sts:AssumeRoleWithWebIdentity",
# "Condition": {
# "StringEquals": {
# "oidc.eks.eu-west-1.amazonaws.com/...:sub":
# "system:serviceaccount:flux-system:tf-runner"
# }
# }
# }]
# }
# Aggiorna il CRD Terraform per usare il Service Account
# Aggiungi nella spec:
# serviceAccountName: tf-runner
Wykrywanie dryfu i powiadomienia
Dryf ma miejsce wtedy, gdy faktyczny stan infrastruktury odbiega od stanu faktycznego
opisane w kodzie HCL — zwykle w przypadku ręcznych zmian w konsoli chmurowej.
Kontroler TF wykrywa dryft w każdym cyklu uzgadniania i raportuje go za pośrednictwem
niego Alert przez Flux.
# Alert Flux per notifiche Slack sul drift
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Provider
metadata:
name: slack-infra
namespace: flux-system
spec:
type: slack
channel: "#infra-alerts"
secretRef:
name: slack-webhook-url
---
apiVersion: notification.toolkit.fluxcd.io/v1beta2
kind: Alert
metadata:
name: terraform-drift-alert
namespace: flux-system
spec:
providerRef:
name: slack-infra
eventSeverity: warning
eventSources:
- kind: Terraform
name: "*" # Tutti gli oggetti Terraform
# Invia alert per questi eventi:
# - drift detected
# - reconciliation failed
# - plan pending approval
# Verificare lo stato di drift manualmente
kubectl get terraform -n flux-system
# NAME READY STATUS AGE
# aws-networking True Reconciliation succeeded 2h
# aws-database False Drift detected: 3 resources 15m
# Dettaglio del drift
kubectl describe terraform aws-database -n flux-system | grep -A 20 "Conditions:"
# Conditions:
# Last Transition Time: 2026-03-20T10:30:00Z
# Message: Drift detected: aws_db_instance.main (tags changed),
# aws_security_group.db (ingress rule added manually)
# Reason: TerraformOutputsWritten
# Status: False
# Type: Ready
Spacelift: GitOps Enterprise dla Terraform
Winda kosmiczna oraz przemyślaną platformę SaaS (z opcją samodzielnego hostowania). dla zespołów korzystających z Terraform w środowiskach korporacyjnych. W przeciwieństwie do kontrolera TF żyjąc w klastrze Kubernetes, Spacelift oferuje kompleksowy interfejs użytkownika i zaawansowane zasady wpisane Rego (ten sam język co OPA), szczegółowy RBAC e proces zatwierdzania z pełną ścieżką audytu.
Kluczowe koncepcje dotyczące transportu kosmicznego
# Struttura Spacelift
# Stack = equivalente di un workspace Terraform
# Ogni stack ha:
# - Source: GitHub/GitLab repository + branch + path
# - Runner image: immagine Docker con Terraform + provider
# - Environment variables: variabili e segreti
# - Policies: regole Rego applicate a plan/apply
# - Contexts: set di variabili condivisibili tra stack
# Creare uno stack via Spacelift API (Terraform provider spacelift):
resource "spacelift_stack" "networking_prod" {
name = "networking-prod"
repository = "terraform-infra"
branch = "main"
project_root = "environments/prod/networking"
# Auto-deploy su push al branch
autodeploy = false # Per prod: richiede approvazione manuale
# Terraform version
terraform_version = "1.9.x"
labels = ["team:platform", "env:prod", "tier:networking"]
}
resource "spacelift_context_attachment" "networking_prod" {
context_id = spacelift_context.aws_prod.id
stack_id = spacelift_stack.networking_prod.id
priority = 1
}
Polityka Rego w Spacelift
Polityki Rego są mocną stroną Spacelift: pozwalają na zdefiniowanie poręczy kompleksy, które są oceniane na każdym planie przed podjęciem decyzji o złożeniu wniosku o zatwierdzenie, zablokować lub zastosować automatycznie. I w zasadzie programowalna bramka.
# policy: require-approval-for-destructive-changes.rego
# Richiede approvazione umana se il plan contiene distruzioni
package spacelift
# Nega auto-apply se ci sono risorse da distruggere
deny[sprintf("Destroy richiede approvazione: %s", [resource])] {
change := input.terraform.resource_changes[_]
change.change.actions[_] == "delete"
resource := change.address
}
# Blocca completamente se piu di 5 risorse vengono distrutte
deny["Piu di 5 destroy in un singolo plan: richiede approvazione senior"] {
destroy_count := count([c |
c := input.terraform.resource_changes[_]
c.change.actions[_] == "delete"
])
destroy_count > 5
}
# Warn (non blocca) per modifiche ai security group
warn[sprintf("Security group modificato: %s", [resource])] {
change := input.terraform.resource_changes[_]
change.type == "aws_security_group"
change.change.actions[_] != "no-op"
resource := change.address
}
# policy: cost-control.rego
# Blocca istanze grandi in ambienti non-prod
package spacelift
expensive_instance_types := {
"m5.4xlarge", "m5.8xlarge", "m5.16xlarge",
"c5.4xlarge", "c5.9xlarge",
"r5.4xlarge", "r5.8xlarge"
}
deny[msg] {
# Leggi i tag dallo stack Spacelift
not contains(input.spacelift.stack.labels[_], "env:prod")
# Cerca istanze EC2 con instance_type costoso
change := input.terraform.resource_changes[_]
change.type == "aws_instance"
instance_type := change.change.after.instance_type
expensive_instance_types[instance_type]
msg := sprintf(
"Istanza %s di tipo %s non consentita in ambienti non-prod",
[change.address, instance_type]
)
}
Zatwierdzenie przepływu pracy Spacelift
# Spacelift approval workflow con notifiche Slack
# 1. Developer fa push al branch feature/add-rds
# 2. Spacelift crea automaticamente un preview run
# 3. La policy Rego valuta il plan: contiene 1 destroy (vecchio RDS)
# 4. Spacelift blocca l'auto-deploy e notifica Slack
# "Run #abc123 richiede approvazione: destroy aws_db_instance.old_db"
# 5. Senior engineer esamina il plan su Spacelift UI
# 6. Approva cliccando "Confirm" oppure aggiunge commento e rifiuta
# 7. Spacelift esegue l'apply o notifica il developer del blocco
# Via Spacelift CLI (spacectl):
spacectl stack run list --id networking-prod
# ID COMMIT STATE CREATED AT
# abc123 f3a8b91 PENDING_REVIEW 2026-03-20 10:30
# xyz789 a1c2d3e FINISHED 2026-03-19 14:22
spacectl run confirm --run abc123 --stack networking-prod
# Run abc123 confirmed, applying...
Zaawansowane wykrywanie dryfu: alarm i automatyczna naprawa
Wykrywanie dryfu nie wystarczy, jeśli nie towarzyszy mu jasna strategia reagowania. Istnieją trzy podejścia, każde z własnymi kompromisami:
# Approccio 1: Solo Alert (ambienti critici, audit trail necessario)
# Il drift viene rilevato e segnalato, ma non corretto automaticamente
# Uso: database di produzione, networking critico
# Approccio 2: Auto-Remediation per drift minore
# Modifiche ai tag, aggiornamenti di patch: correggi automaticamente
# Blocca e avvisa per modifiche strutturali
# Approccio 3: Full Auto-Apply (ambienti dev/staging)
# Qualsiasi drift viene corretto immediatamente dal controller
---
# Esempio Flux TF Controller: configurazione per approccio ibrido
apiVersion: infra.contrib.fluxcd.io/v1alpha2
kind: Terraform
metadata:
name: aws-networking-prod
namespace: flux-system
spec:
interval: 5m
approvePlan: "auto" # "auto" per ambienti non critici
# Plan runner: genera il piano ma NON lo applica
# L'apply richiede un secondo passaggio (manuale o automatico)
planOnly: false
# Dopo quanti drift consecutivi inviare un alert critico
# (configurato via Flux Alert con severita error)
retryInterval: 1m
timeout: 5m
# Script di scheduled drift check (alternativa leggera senza GitOps controller)
#!/bin/bash
# drift-check.sh — eseguito ogni ora via cron o GitHub Actions scheduled
set -euo pipefail
ENVIRONMENTS=("dev" "staging" "prod")
SLACK_WEBHOOK="${SLACK_DRIFT_WEBHOOK}"
for ENV in "${ENVIRONMENTS[@]}"; do
cd "/infra/environments/${ENV}"
# Inizializza senza output
terraform init -reconfigure -input=false -no-color > /dev/null 2>&1
# Esegui plan e cattura l'exit code
# 0 = no changes, 1 = error, 2 = changes detected (drift)
set +e
terraform plan -detailed-exitcode -no-color -out=/tmp/plan-${ENV} 2>&1
EXITCODE=$?
set -e
if [ $EXITCODE -eq 2 ]; then
CHANGES=$(terraform show -no-color /tmp/plan-${ENV} | \
grep -E "^\s+(#|~|\+|-)" | head -20)
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"text\": \"*DRIFT DETECTED* in environment: ${ENV}\n\`\`\`${CHANGES}\`\`\`\"
}"
echo "Drift alert sent for ${ENV}"
elif [ $EXITCODE -eq 0 ]; then
echo "${ENV}: no drift detected"
else
echo "ERROR: terraform plan failed for ${ENV}" >&2
exit 1
fi
done
Porównanie: kontroler TF, Spacelift i Atlantis
Kiedy używać jakiego narzędzia
- Kontroler strumienia TF: Zespół, który już korzysta z Flux/Argo dla Kubernetes, chce czystego i otwartego GitOps, zarządza infrastrukturą AWS za pomocą IRSA. Hostowane na własnym serwerze, bezpłatne, o średniej krzywej uczenia się.
- Winda kosmiczna: Zespół korporacyjny ze złożonymi wymaganiami RBAC, audyt szlak, proces zatwierdzania z wieloma osobami zatwierdzającymi, zaawansowane zasady Rego. Płatny SaaS, świetny UX, gotowe integracje (Slack, PagerDuty, Jira).
- Atlantyda: Zespół, który chce pozostać w paradygmacie opartym na PR bez Czysty GitOps. Zaplanuj/Zastosuj można komentować bezpośrednio w PR. Własny hosting, bezpłatny, bardzo dojrzały. Nie ma w nim natywnego, ciągłego pojednania.
- Chmura Terraform/Korporacja: Naturalny wybór, jeśli już występuje w ekosystemie HashiCorp, natywny język polityki Sentinel, integracja z Vault. Zobacz artykuł 10.
Najlepsze praktyki dotyczące GitOps IaC w produkcji
# Repository structure per GitOps Terraform
terraform-infra/
├── modules/ # Moduli riusabili (non riconciliati direttamente)
│ ├── networking/
│ ├── compute/
│ └── database/
├── environments/
│ ├── dev/
│ │ ├── networking/ # Stack separati per ogni layer
│ │ │ ├── main.tf
│ │ │ └── terraform.auto.tfvars
│ │ ├── compute/
│ │ └── database/
│ ├── staging/
│ └── prod/
│ ├── networking/ # Ogni ambiente ha il suo state isolato
│ ├── compute/
│ └── database/
├── flux/ # Manifesti Flux per i CRD Terraform
│ ├── dev/
│ │ ├── networking-tf.yaml
│ │ └── compute-tf.yaml
│ └── prod/
│ ├── networking-tf.yaml # approvePlan: "auto" o manuale
│ └── compute-tf.yaml
└── policies/ # Policy Rego (se Spacelift)
├── require-approval.rego
└── cost-control.rego
Anty-wzorzec: pojednanie zbyt agresywne
Ustawić interval: 1m con approvePlan: auto na środowiskach
produkcyjna i niebezpieczna: może to być zmiana, która nie została jeszcze połączona z główną
zastosowany przed przeglądem. Złota zasada: im bardziej krytyczne środowisko, tym dłużej
a odstęp jest bardziej rygorystyczny i proces zatwierdzania. W prod użyj interwału
powyżej 30 m i zawsze wymagają ręcznej zgody na zmiany konstrukcyjne.
Wnioski i dalsze kroki
GitOps dla Terraform reprezentuje dojrzałość infrastruktury jako kod: już nie potoki oparte na wyzwalaczach, ale ciągłe uzgadnianie, koniec z poświadczeniami w potokach ale rodzime tożsamości klastra, już nie „kto dokonał tej zmiany”, ale ścieżki audytu ukończone w Git. Kontroler Flux TF to idealny wybór dla zespołów natywnie korzystających z Kubernetes, podczas gdy Spacelift spełnia wymagania przedsiębiorstw dzięki silnikowi zasad Rego.
Kompletna seria: Terraform i IaC
- Artykuł 01 — Terraformuj od podstaw: HCL, dostawca i planuj-zastosuj-zniszcz
- Artykuł 02 — Projektowanie modułów Terraform wielokrotnego użytku
- Artykuł 03 — Stan Terraformu: Zdalny backend z S3/GCS
- Artykuł 04 — Terraform w CI/CD: GitHub Actions i Atlantis
- Artykuł 05 — Testowanie IaC: Test Terratestu i Terraformu
- Artykuł 06 — Bezpieczeństwo IaC: Checkov, Trivy i OPA
- Artykuł 07 — Terraform Multi-Cloud: AWS + Azure + GCP
- Artykuł 08 (ten) — GitOps dla Terraform: kontroler Flux TF, podnośnik kosmiczny i wykrywanie dryfu
- Artykuł 09 — Terraform vs Pulumi vs OpenTofu: Porównanie 2026
- Artykuł 10 — Wzorce Terraform Enterprise: przestrzeń robocza, strażnik i skalowanie zespołu







