← Kursa Dön
📄 Text · 30 min

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

  1. Declarative: Tüm altyapı YAML/JSON ile tanımlı (imperative komutlar değil)

  2. Versioned: Her değişiklik Git'te — tam geçmiş, audit trail

  3. Automated: Git'teki değişiklikler otomatik olarak uygulanır

  4. 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)
ÖzellikGeleneksel CI/CDGitOps
Deploy nasıl?CI pipeline cluster'a push ederAgent cluster'dan Git'i çeker
Cluster erişimiCI'ın cluster credential'ları varSadece agent'ın erişimi var
Drift detectionYok — elle kontrolOtomatik — sürekli reconciliation
RollbackPipeline'ı tekrar çalıştırGit revert
Audit trailCI log'larıGit history
GüvenlikCI'a cluster yetkisi vermek riskliAgent 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 overlays

GitOps 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.md

ArgoCD — 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> --insecure

ArgoCD 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=true

ArgoCD 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-staging

ArgoCD 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 eder

Flux 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: 3m

Flux 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ı

ÖzellikArgoCDFlux
UIZengin web UI ✅CLI odaklı (Weave GitOps UI opsiyonel)
KurulumTek komutBootstrap komutu
Multi-clusterDahili destekDüz yapı (her cluster kendi Flux'ı)
Helm desteğiDahiliHelmRelease CRD ile
Image automationArgo Image Updater (ayrı)Dahili (image.toolkit)
RBACDahili + SSOKubernetes RBAC
Öğrenme eğrisiOrtaDüşük-Orta
ToplulukÇok büyükBüyük (CNCF graduated)
Kaynak tüketimiOrta-yüksekDüşük
En iyi kullanımBüyük ekipler, multi-clusterKüçü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 → ArgoCD

GitOps 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 push

Secret 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-password

Best 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 apply ile elle deploy yapma — GitOps ihlali

  • Cluster'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ıyor

2. 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 myapp

3. 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)