“Geliştirme bitti, prod’a nasıl çıkıyoruz?” sorusunun cevabı 2026’da hâlâ “dev’e SCP, prod’da git pull” olmamalı. Modern CI/CD pipeline, kod değişikliğinin commit’ten production’a güvenli, izlenebilir ve geri alınabilir biçimde geçişini sağlar. Bu yazıda bir SaaS müşterimizde kurduğumuz pipeline’ı tüm bileşenleriyle paylaşıyorum: GitHub Actions, Argo CD, CloudSpark Kubernetes (CSPK), HashiCorp Vault, Prometheus.
Pipeline’ın 7 Aşaması
| Aşama | Araç | Süre (tipik) |
|---|---|---|
| 1. Code commit + PR | GitHub | — |
| 2. CI: build + unit test + lint | GitHub Actions | 3-5 dk |
| 3. Container image build + scan | GitHub Actions + Trivy | 2-4 dk |
| 4. Image push to registry | CloudSpark Container Registry | 30-60 sn |
| 5. Manifest update (image tag) | GitHub Actions (commit to gitops repo) | 10 sn |
| 6. CD: GitOps reconciliation | Argo CD | 30-60 sn |
| 7. Smoke test + alert | Synthetic monitoring | 1-3 dk |
Toplam commit-to-production: ~~10-15 dakika (canary aşaması dahil ~~30 dk).
GitHub Actions Workflow
name: CI/CD
on:
push:
branches: [main]
tags: ['v*']
pull_request:
branches: [main]
env:
REGISTRY: registry.cloudspark.com.tr
IMAGE: api-service
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm test -- --coverage
- uses: codecov/codecov-action@v4
build-and-push:
needs: test
if: github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_PASS }}
- name: Compute tags
id: tags
run: |
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
echo "tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "env=production" >> $GITHUB_OUTPUT
else
SHORT_SHA=$(git rev-parse --short HEAD)
echo "tag=main-${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "env=staging" >> $GITHUB_OUTPUT
fi
- uses: docker/build-push-action@v5
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ steps.tags.outputs.tag }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ steps.tags.outputs.tag }}
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Update gitops manifest
env:
GITOPS_TOKEN: ${{ secrets.GITOPS_TOKEN }}
run: |
git clone https://x:${GITOPS_TOKEN}@github.com/myorg/gitops.git
cd gitops
yq eval -i '.spec.template.spec.containers[0].image = "${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ steps.tags.outputs.tag }}"' apps/api/${{ steps.tags.outputs.env }}/deployment.yaml
git config user.email "ci@example.com"
git config user.name "CI Bot"
git add .
git commit -m "chore: update api-service image to ${{ steps.tags.outputs.tag }}"
git push
GitOps: Argo CD ile Reconciliation
Pipeline image tag’ini gitops repo’ya commit eder. Argo CD bu repo’yu izliyor, değişiklik tespit edince cluster’a apply ediyor.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: api-service-prod
namespace: argocd
spec:
project: production
source:
repoURL: https://github.com/myorg/gitops.git
targetRevision: main
path: apps/api/production
destination:
server: https://kubernetes.default.svc
namespace: api-prod
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 30s
maxDuration: 5m
factor: 2
selfHeal=true: Cluster state ile manifest farklıysa Argo CD otomatik manifest’e dönderiyor. Drift önleme.
prune=true: Manifest’ten kaldırılan resource cluster’dan da silinir.
Multi-Environment Promotion
Klasik akış: dev → staging → production. Her ortam ayrı namespace, ayrı manifest klasörü.
gitops/
├── apps/
│ └── api/
│ ├── base/
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ ├── dev/
│ │ ├── kustomization.yaml # base + dev-specific patches
│ │ └── deployment.yaml # image tag: main-abc123
│ ├── staging/
│ │ └── deployment.yaml # image tag: main-xyz789
│ └── production/
│ └── deployment.yaml # image tag: v1.4.7
Promotion: Staging’de doğrulanan image tag’i production manifest’ine kopyalanıyor (manuel commit veya automation script ile).
Canary Deployment
Production’a yeni versiyon çıkarken hep aynı anda %100’üne değil, önce %10’una vermek. Trafik gözlemlenir, problem yoksa kademeli olarak %100’e çıkılır.
Argo Rollouts ile canary tanımı:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: api-service
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 5m}
- setWeight: 25
- pause: {duration: 10m}
- setWeight: 50
- pause: {duration: 10m}
- setWeight: 100
analysis:
templates:
- templateName: success-rate
startingStep: 1
args:
- name: service-name
value: api-service
template:
spec:
containers:
- name: api
image: registry.cloudspark.com.tr/api-service:v1.4.7
analysis: Prometheus query çalıştırır (“HTTP 5xx oranı %1’i geçti mi?”). Geçti ise rollback otomatik.
Secret Yönetimi: HashiCorp Vault
Database password, API key gibi secret’lar manifest içine yazılmaz. Vault’tan çekilir.
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: api-db-creds
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: api-db-creds
data:
- secretKey: DB_PASSWORD
remoteRef:
key: secret/data/api/prod
property: db_password
External Secrets Operator Vault’tan secret’ı çeker, Kubernetes Secret olarak cluster’a yazar. Pod env var olarak alır. Vault’taki secret rotate edildiğinde 1 saat içinde otomatik güncellenir.
Rollback Otomasyonu
Argo Rollouts canary analysis fail olduğunda otomatik rollback. Manuel rollback gerekirse:
# Önceki revision'a dön
kubectl argo rollouts undo api-service -n api-prod
# Veya specific revision
kubectl argo rollouts undo api-service -n api-prod --to-revision=5
# Rollback durumunu izle
kubectl argo rollouts status api-service -n api-prod -w
SaaS müşterimizde 2 ayda 4 production rollback yapıldı (hepsi otomatik canary fail sonucu, kullanıcı etkilenmedi).
Sahada Düşülen Üç Tuzak
- Pipeline yavaş: 30+ dakika commit-to-prod kabul edilemez. Test parallelization, layer caching, image scan’i async.
- Manuel onay her aşamada: “Güvenlik için” diye 5 ayrı manuel onay koymak akışı durdurur. Otomatik gate (test pass, security scan pass) ile akış sürmeli, sadece production’a deploy son onay gerekirse.
- Rollback test edilmiyor: Otomatik rollback var sanıyorsun, ihtiyaç anında “yanlış yere döndü” yaşıyorsun. Quarter’da 1 rollback drill.
Sonuç: CI/CD Bir Lüks Değil, Modern Yazılım Geliştirmenin Şartı
Manuel deployment 2026’da kabul edilemez. Pipeline kurulduktan sonra geliştirici “kod yazıyorum, prod’a 15 dakika sonra çıkıyor” deneyimini yaşıyor. Bu deneyim ekip moralini, geliştirme hızını, kalite metriklerini doğrudan etkiliyor.
SaaS müşterimizde CI/CD öncesi haftalık 3 release, sonrası günlük 8-12 release. Production incident sayısı azaldı (küçük değişiklikler daha az risk).
CloudSpark olarak GitHub Actions / Azure DevOps / GitLab pipeline kurulumu, GitOps (Argo CD, Flux), CSPK üzerinde Kubernetes deployment ve secret management projelerinde end-to-end danışmanlık veriyoruz.



