GitOps ve Docker — ArgoCD, Flux Temelleri
CI/CD pipeline'ımız artık test, build, push ve deploy adımlarını otomatikleştiriyor. Ama deploy kısmında hâlâ "CI pipeline'ı Kubernetes'e push ediyor" yaklaşımını kullanıyoruz. Bu derste çok daha zarif bir alternatif göreceğiz: GitOps — deployment'ları Git üzerinden yönetmek. Bu yaklaşım, deploy yapmayı bir Git commit'i kadar basit hale getiriyor.
Bir evin termostatını düşün. "22°C olsun" diyorsun (desired state). Termostat sürekli oda sıcaklığını ölçer (current state). Eğer sıcaklık düşerse kalorifer yakar, yükselirse durdurur. Sen sıcaklığı ayarlamak için kaloriferle uğraşmıyorsun — sadece istediğin durumu söylüyorsun, termostat gerisini hallediyor.
GitOps tam bu mantıkla çalışır. Altyapının istenen durumunu Git'te tanımlarsın (YAML dosyaları). Bir araç (ArgoCD veya Flux) sürekli Git ile cluster'ı karşılaştırır. Fark varsa otomatik düzeltir. Deployment yapmak = Git'e commit atmak. Rollback yapmak = Git'te revert etmek. Her şey Git'te, her şey takip edilebilir, her şey tekrarlanabilir.
GitOps Nedir?
Temel Prensipler
Declarative: Tüm altyapı YAML/JSON ile tanımlı (imperative komutlar değil)
Versioned: Her değişiklik Git'te — tam geçmiş, audit trail
Automated: Git'teki değişiklikler otomatik olarak uygulanır
Self-healing: Cluster'daki drift otomatik düzeltilir
Geleneksel CI/CD vs GitOps
=== Geleneksel CI/CD (Push Model) ===
Developer → Git Push → CI Build → CI Deploy → Cluster
↑
CI cluster'a erişir (push)
=== GitOps (Pull Model) ===
Developer → Git Push → CI Build → Registry Push
↓
Git Repo ← Agent (pull) → Cluster
(desired) (ArgoCD/Flux) (current)| Özellik | Geleneksel CI/CD | GitOps |
|---|---|---|
| Deploy nasıl? | CI pipeline cluster'a push eder | Agent cluster'dan Git'i çeker |
| Cluster erişimi | CI'ın cluster credential'ları var | Sadece agent'ın erişimi var |
| Drift detection | Yok — elle kontrol | Otomatik — sürekli reconciliation |
| Rollback | Pipeline'ı tekrar çalıştır | Git revert |
| Audit trail | CI log'ları | Git history |
| Güvenlik | CI'a cluster yetkisi vermek riskli | Agent kendi cluster'ında çalışır |
💡 İpucu: GitOps, CI/CD'nin yerini almaz — onu tamamlar. CI pipeline hala build + test + push yapar. GitOps, deploy kısmını devralır.
GitOps Repo Yapısı
GitOps'ta genellikle iki repo kullanılır:
1. Application Repo (kaynak kod)
└── Dockerfile, source code, CI pipeline
2. GitOps Repo (deployment tanımları)
└── Kubernetes manifests, Helm values, Kustomize overlaysGitOps Repo Yapısı
gitops-repo/
├── apps/
│ ├── myapp/
│ │ ├── base/
│ │ │ ├── kustomization.yaml
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ └── configmap.yaml
│ │ └── overlays/
│ │ ├── staging/
│ │ │ ├── kustomization.yaml
│ │ │ └── patch-replicas.yaml
│ │ └── production/
│ │ ├── kustomization.yaml
│ │ ├── patch-replicas.yaml
│ │ └── patch-resources.yaml
│ └── monitoring/
│ ├── prometheus/
│ └── grafana/
├── infrastructure/
│ ├── cert-manager/
│ ├── ingress-nginx/
│ └── sealed-secrets/
└── README.mdArgoCD — Kubernetes Native GitOps
ArgoCD, Kubernetes için en popüler GitOps aracıdır. Güzel bir UI, RBAC, multi-cluster desteği ve SSO entegrasyonu sunar.
ArgoCD Kurulumu
# Namespace oluştur
kubectl create namespace argocd
# ArgoCD'yi kur
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# ArgoCD CLI kur (macOS)
brew install argocd
# Admin şifresini al
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
# UI'ya erişim (port-forward)
kubectl port-forward svc/argocd-server -n argocd 8080:443
# https://localhost:8080 → admin / <şifre>
# CLI ile login
argocd login localhost:8080 --username admin --password <şifre> --insecureArgoCD Application Tanımlama
# argocd/myapp-staging.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-staging
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-repo.git
targetRevision: main
path: apps/myapp/overlays/staging
destination:
server: https://kubernetes.default.svc
namespace: staging
syncPolicy:
automated:
prune: true # Git'ten silinen kaynakları cluster'dan da sil
selfHeal: true # Manuel değişiklikleri geri al (drift correction)
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m# argocd/myapp-production.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-production
namespace: argocd
annotations:
# Slack notification
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: deployments
notifications.argoproj.io/subscribe.on-sync-failed.slack: deployments
spec:
project: default
source:
repoURL: https://github.com/myorg/gitops-repo.git
targetRevision: main
path: apps/myapp/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- RespectIgnoreDifferences=trueArgoCD CLI Kullanımı
# Application listesi
argocd app list
# NAME CLUSTER STATUS HEALTH
# myapp-staging https://kubernetes.default.svc Synced Healthy
# myapp-production https://kubernetes.default.svc Synced Healthy
# Sync durumu
argocd app get myapp-staging
# Manuel sync (automated kapalıysa)
argocd app sync myapp-production
# Sync geçmişi
argocd app history myapp-production
# Rollback (belirli revision'a)
argocd app rollback myapp-production 5
# Diff — Git ile cluster arasındaki fark
argocd app diff myapp-stagingArgoCD Mimarisi
┌─────────────────────────────────────────────────────────┐
│ ArgoCD │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ API Server │ │ Repo Server │ │Application │ │
│ │ │ │ │ │ Controller │ │
│ │ UI + CLI │ │ Git clone │ │ │ │
│ │ endpoint │ │ & render │ │ Reconcile │ │
│ └──────────────┘ └──────────────┘ │ loop │ │
│ │ (3 dakika) │ │
│ └────────────┘ │
│ │ │
│ ┌─────────────┐ │ │
│ │ Git Repo │←──── poll ────┘ │
│ │ (desired) │ │
│ └─────────────┘ │
│ ↕ compare │
│ ┌─────────────┐ │
│ │ Cluster │ │
│ │ (current) │←──── sync ─────────── │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────┘Flux — Lightweight GitOps
Flux, CNCF projesi olarak Kubernetes'e daha "native" bir yaklaşım sunar. ArgoCD'den daha hafif, UI yerine CLI odaklıdır.
Flux Kurulumu
# Flux CLI kur
brew install fluxcd/tap/flux
# Cluster'a Flux kur
flux bootstrap github \
--owner=myorg \
--repository=gitops-repo \
--branch=main \
--path=clusters/production \
--personal
# Bu komut:
# 1. gitops-repo'da clusters/production dizinini oluşturur
# 2. Flux bileşenlerini cluster'a kurar
# 3. Flux'ı repo ile senkronize ederFlux Kaynak Tanımlama
# clusters/production/myapp-source.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: myapp
namespace: flux-system
spec:
interval: 1m # Her dakika Git'i kontrol et
url: https://github.com/myorg/gitops-repo
ref:
branch: main
secretRef:
name: github-credentials # Private repo için
# clusters/production/myapp-kustomization.yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: myapp
namespace: flux-system
spec:
interval: 5m
path: ./apps/myapp/overlays/production
prune: true # Git'ten silinen kaynakları kaldır
sourceRef:
kind: GitRepository
name: myapp
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: api
namespace: production
timeout: 3mFlux ile Helm Release
# clusters/production/myapp-helmrelease.yaml
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: myapp-charts
namespace: flux-system
spec:
interval: 30m
url: https://charts.example.com
apiVersion: helm.toolkit.fluxcd.io/v2beta2
kind: HelmRelease
metadata:
name: myapp
namespace: production
spec:
interval: 5m
chart:
spec:
chart: myapp
version: "1.x" # Semver range — otomatik minor/patch update
sourceRef:
kind: HelmRepository
name: myapp-charts
namespace: flux-system
values:
replicaCount: 3
image:
repository: ghcr.io/myorg/myapp
tag: v1.2.3
resources:
limits:
memory: 512Mi
cpu: "1"Flux Image Automation
Flux, yeni image tag'leri algılayıp GitOps repo'sunu otomatik güncelleyebilir:
# Image repository izleme
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: myapp
namespace: flux-system
spec:
image: ghcr.io/myorg/myapp
interval: 5m
# Hangi tag'leri kabul et?
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: myapp
namespace: flux-system
spec:
imageRepositoryRef:
name: myapp
policy:
semver:
range: "1.x" # 1.x.x tag'lerini kabul et
# Git'i otomatik güncelle
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
name: myapp
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: myapp
git:
checkout:
ref:
branch: main
commit:
author:
email: flux@example.com
name: Flux
messageTemplate: "chore: update myapp to {{range .Updated.Images}}{{println .}}{{end}}"
push:
branch: main
update:
path: ./apps/myapp
strategy: Setters# deployment.yaml'da marker ekle
spec:
containers:
- name: api
image: ghcr.io/myorg/myapp:v1.2.3 # {"$imagepolicy": "flux-system:myapp"}ArgoCD vs Flux Karşılaştırması
| Özellik | ArgoCD | Flux |
|---|---|---|
| UI | Zengin web UI ✅ | CLI odaklı (Weave GitOps UI opsiyonel) |
| Kurulum | Tek komut | Bootstrap komutu |
| Multi-cluster | Dahili destek | Düz yapı (her cluster kendi Flux'ı) |
| Helm desteği | Dahili | HelmRelease CRD ile |
| Image automation | Argo Image Updater (ayrı) | Dahili (image.toolkit) |
| RBAC | Dahili + SSO | Kubernetes RBAC |
| Öğrenme eğrisi | Orta | Düşük-Orta |
| Topluluk | Çok büyük | Büyük (CNCF graduated) |
| Kaynak tüketimi | Orta-yüksek | Düşük |
| En iyi kullanım | Büyük ekipler, multi-cluster | Küçük-orta ekipler, basitlik |
Hangisini Seçmeli?
UI ve multi-cluster önemliyse → ArgoCD
Basitlik ve düşük kaynak istiyorsan → Flux
Helm ağırlıklı deploy → İkisi de iyi
Image automation istiyorsan → Flux (daha native)
SSO ve RBAC kritikse → ArgoCDGitOps ile Docker CI/CD — Tam Akış
Akış Diyagramı
Developer
│
▼
Application Repo (git push)
│
▼
CI Pipeline (GitHub Actions)
├── Build Docker image
├── Run tests
├── Security scan
├── Push to registry (ghcr.io/myorg/myapp:v1.2.3)
└── Update GitOps repo (image tag güncelle)
│
▼
GitOps Repo (git commit: "update myapp to v1.2.3")
│
▼
ArgoCD / Flux (detect change)
│
▼
Kubernetes Cluster (deploy new version)
│
▼
Notification (Slack: "myapp v1.2.3 deployed ✅")CI Pipeline — Image Tag'i GitOps Repo'ya Yaz
# .github/workflows/ci.yml (Application Repo)
name: CI Pipeline
on:
push:
tags: ['v*']
jobs:
build-and-push:
runs-on: ubuntu-latest
outputs:
image-tag: ${{ github.ref_name }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
update-gitops:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Checkout GitOps repo
uses: actions/checkout@v4
with:
repository: myorg/gitops-repo
token: ${{ secrets.GITOPS_TOKEN }}
path: gitops
- name: Update image tag
run: |
cd gitops
# Kustomize ile tag güncelle
cd apps/myapp/overlays/production
kustomize edit set image ghcr.io/myorg/myapp:${{ github.ref_name }}
- name: Commit and push
run: |
cd gitops
git config user.name "CI Bot"
git config user.email "ci@example.com"
git add .
git commit -m "chore: update myapp to ${{ github.ref_name }}"
git pushSecret Yönetimi — GitOps'ta Hassas Veri
Git'e secret koyamazsın. GitOps'ta secret'ları yönetmek için özel araçlar kullanılır.
Sealed Secrets (Bitnami)
# Sealed Secrets controller kur
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.25.0/controller.yaml
# kubeseal CLI kur
brew install kubeseal
# Secret'ı sealed secret'a dönüştür
kubectl create secret generic db-password \
--from-literal=password=super-secret \
--dry-run=client -o yaml | \
kubeseal --format yaml > sealed-secret.yaml
# sealed-secret.yaml Git'e güvenle commit edilebilir
# Sadece cluster'daki controller decrypt edebilir# sealed-secret.yaml (Git'e commit edilebilir)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-password
namespace: production
spec:
encryptedData:
password: AgBy8hDi...encrypted...data==External Secrets Operator
# AWS Secrets Manager entegrasyonu
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets
kind: ClusterSecretStore
target:
name: db-credentials
data:
- secretKey: password
remoteRef:
key: production/db-passwordBest Practices
✅ Yap:
Application repo ve GitOps repo'yu ayır — sorumluluk ayrımı
Automated sync ile drift'i otomatik düzelt (selfHeal: true)
Prune aç — Git'ten silinen kaynakları cluster'dan da kaldır
Sealed Secrets veya External Secrets ile secret yönet
Health check tanımla — ArgoCD/Flux deploy başarısını doğrulasın
Branch protection — main branch'e direct push engelle, PR zorunlu
Git commit mesajlarını anlamlı yaz — audit trail bu sayede oluşur
Notification kur — deploy başarılı/başarısız Slack/Teams'e bildirim
❌ Yapma:
Secret'ları plain text olarak Git'e commit etme
kubectl applyile elle deploy yapma — GitOps ihlaliCluster'a doğrudan (imperative) değişiklik yapma — Flux/ArgoCD geri alır
Sync interval'ı çok kısa tutma (1s gibi) — Git API rate limit
GitOps repo'ya CI dışında otomatik commit yapma — karmaşıklaşır
Her micro-service için ayrı GitOps repo açma — yönetimi zorlaşır
Yaygın Hatalar ve Çözümleri
1. ArgoCD "OutOfSync" ama Değişiklik Yok
# Sebep: Kubernetes bazı field'ları otomatik ekliyor (defaulting)
# Çözüm: ignoreDifferences kullan
spec:
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA replica'yı değiştiriyor
- group: ""
kind: Service
jsonPointers:
- /spec/clusterIP # K8s otomatik atıyor2. Flux Image Automation Çalışmıyor
# Marker doğru mu kontrol et
# Deployment'ta bu yorum olmalı:
image: ghcr.io/myorg/myapp:v1.2.3 # {"$imagepolicy": "flux-system:myapp"}
# ImagePolicy semver range doğru mu?
flux get image policy myapp
# ImageRepository taranıyor mu?
flux get image repository myapp3. Secret Değişiklikleri Pod'a Yansımıyor
# ConfigMap/Secret değişince pod otomatik restart olmaz
# Çözüm 1: Reloader kullan
# https://github.com/stakater/Reloader
# Çözüm 2: Annotation ile checksum
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print .Template.BasePath "/configmap.yaml") . | sha256sum }}Özet
GitOps, altyapı durumunu Git'te tanımlayan ve sürekli senkronize eden bir operasyon modelidir
Pull model: Agent (ArgoCD/Flux) cluster'dan Git'i çeker — CI'ın cluster'a erişmesi gerekmez
ArgoCD: Zengin UI, multi-cluster, SSO — büyük ekipler için ideal
Flux: Hafif, Kubernetes-native, image automation — basitlik isteyenler için
İki repo modeli: Application repo (kod) + GitOps repo (deploy tanımları)
CI pipeline image build + push yapar, GitOps repo'yu günceller — deploy otomatik olur
Secret yönetimi: Sealed Secrets veya External Secrets — plain text Git'e asla
Self-healing: Cluster'daki manuel değişiklikler otomatik geri alınır — Git tek doğru kaynak (single source of truth)
AI Asistan
Sorularını yanıtlamaya hazır