CI/CD Pipeline Patterns#
"Pipeline'ın 10 dakika sürüyorsa ekip 'bekleyim de bir kahve içeyim' diyor. 30 dakika sürüyorsa context-switch'le başka iş yapıyor. 1 saat sürüyorsa pipeline ölü demektir."
🎯 Pipeline'ın görevleri (sırasıyla)#
1. Lint & Format (10-30 sn)
2. Test (1-3 dk)
3. Security scan (SAST/SCA) (1-2 dk)
4. Build artifact (image) (1-3 dk, cached)
5. Image vulnerability scan (30 sn-1 dk)
6. Image sign + SBOM (10 sn)
7. E2E / smoke (preview env) (3-5 dk)
8. Promote / GitOps update (10 sn)
─────────────────────────────────────────────
Toplam PR feedback hedefi: < 10 dk
10 dakika geçilirse: paralel çalış, cache iyileştir, test'leri parçala.
📋 Temel pattern'ler#
Pattern 1: Layered (sırayla, fail-fast)#
Lint → Test → SAST → Build → Sign → Scan → Smoke → Deploy
↓ ↓ ↓ ↓ ↓ ↓ ↓
her aşama bir öncekini doğrular; biri fail = sonrakiler skip
Ne zaman: Küçük-orta servisler. Net, debug kolay.
# GitHub Actions örneği
jobs:
lint: { ... }
test: { needs: [lint] }
sast: { needs: [test] }
build: { needs: [sast] }
scan: { needs: [build] }
smoke: { needs: [scan] }
deploy: { needs: [smoke], if: github.ref == 'refs/heads/main' }
Pattern 2: Parallel Fan-out#
┌── Lint ───┐
│ │
PR ─────┼── Test ───┼── Build ── Scan ── Sign ── Deploy
│ │
└── SAST ───┘
│
└── SCA ─┘
İlk üç bağımsız → paralel; build önce hepsinin geçmesini bekler.
Ne zaman: Hız öncelikli; her aşama bağımsızsa.
jobs:
lint: { runs-on: ubuntu-latest, steps: [...] }
test: { runs-on: ubuntu-latest, steps: [...] }
sast: { runs-on: ubuntu-latest, steps: [...] }
sca: { runs-on: ubuntu-latest, steps: [...] }
build:
needs: [lint, test, sast, sca]
runs-on: ubuntu-latest
# ...
Pattern 3: Matrix Build#
Aynı kodu birden fazla ortamda (dil versiyonu, OS, arch).
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
node: [20, 22, 23]
include:
- os: ubuntu-latest
node: 22
coverage: true # sadece bir matrix'te coverage
steps:
- uses: actions/setup-node@v4
with: { node-version: ${{ matrix.node }} }
- run: npm ci && npm test
Pattern 4: DAG (GitLab CI / Argo Workflows)#
Karmaşık bağımlılıklar; "bunu bekle, şuna bağlı değil" diyebilirsin.
# GitLab CI
deploy-prod:
stage: deploy
needs:
- build-image
- integration-test
# `unit-test` ve `lint` bu job için gerekmiyor; bekleme yok
Pattern 5: Reusable / Callable Workflow#
Aynı iş akışını N repo'da tekrar yazma.
# .github/workflows/_build-app.yml (template repo'da)
on:
workflow_call:
inputs:
app-name: { required: true, type: string }
outputs:
image-digest:
value: ${{ jobs.build.outputs.digest }}
jobs:
build:
# ...
# Caller workflow (tüketici repo'da)
jobs:
build:
uses: <ORG>/.github/.github/workflows/_build-app.yml@main
with: { app-name: payments }
secrets: inherit
💡
<ORG>/.githubrepo'su GitHub'da özel — org-wide template'ler oraya konur, otomatik tüm repo'lara açılır.
⚡ Hız optimizasyonu#
A. Caching#
| Stack | Ne cache'lenmeli |
|---|---|
| Node.js | ~/.npm, node_modules, .next/cache |
| Python | ~/.cache/pip, .venv (uv kullanıyorsan ~/.cache/uv) |
| Go | ~/.cache/go-build, ~/go/pkg/mod |
| Rust | ~/.cargo, target/ |
| Docker | BuildKit cache-from/cache-to (registry veya GHA) |
| Maven/Gradle | ~/.m2, ~/.gradle |
# GitHub Actions example
- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: npm-${{ runner.os }}-
B. Test parçalama (sharding)#
test:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1/4, 2/4, 3/4, 4/4]
steps:
- run: npm test -- --shard=${{ matrix.shard }}
C. Sadece değişen dosyaları test (monorepo için)#
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
api: 'apps/api/**'
web: 'apps/web/**'
- if: steps.changes.outputs.api == 'true'
run: npm test --workspace=api
Daha gelişmiş: Nx affected, Bazel target tracking, Turborepo.
D. BuildKit registry cache#
docker buildx build \
--cache-from type=registry,ref=<REGISTRY>/<IMAGE>:cache \
--cache-to type=registry,ref=<REGISTRY>/<IMAGE>:cache,mode=max \
--push .
E. Self-hosted runner (büyük org)#
GitHub-hosted runner = 2 CPU, 7 GB RAM. Kendi runner'ın 16 CPU + 64 GB olabilir → 5x hız mümkün. Maliyet: ekstra altyapı + güvenlik.
🔐 Güvenlik#
OIDC ile cloud auth (uzun-ömürlü key yok)#
permissions:
id-token: write
contents: read
jobs:
deploy:
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::<ACCOUNT_ID>:role/gh-actions-role
aws-region: <REGION>
- run: aws s3 sync ./dist s3://<BUCKET>/
# Terraform: AWS IAM role + GitHub OIDC trust
resource "aws_iam_role" "gh_actions" {
name = "gh-actions-role"
assume_role_policy = jsonencode({
Statement = [{
Action = "sts:AssumeRoleWithWebIdentity"
Effect = "Allow"
Principal = { Federated = "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com" }
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
"token.actions.githubusercontent.com:sub" = "repo:<ORG>/<REPO>:ref:refs/heads/main"
}
}
}]
})
}
Secret yönetimi#
- ✅ GitHub Secrets / GitLab Variables — masked
- ✅ Vault / AWS Secrets Manager — runtime'da fetch
- ❌
.github/workflows/*.ymliçinde plain - ❌ Logged variable (
echo "${{ secrets.X }}") — masked olmasına rağmen pipe'lardan sızabilir
Permission scoping#
GitHub Actions default = repo'da read+write. Kısıtla:
permissions:
contents: read
id-token: write # OIDC için
packages: write # ghcr.io push için
# diğer her şey explicit eklenmeli
🎯 Branch Strategy + Pipeline#
Trunk-based + feature flag (önerilen)#
main (sürekli deploy edilebilir)
↓ kısa-ömürlü branch (1-2 gün)
feature/auth → PR → CI → review → squash merge → main
↓
auto-deploy to dev
↓
promote staging
↓
promote prod (manual approval)
Pipeline farkları#
| Branch | Hangi adımlar |
|---|---|
| Feature branch | lint, test, sast, build, scan (no deploy) |
| Main | + sign, push image, GitOps tag bump (auto-deploy dev) |
| Tag (v1.2.3) | + production deploy (manual approval) |
deploy-prod:
if: startsWith(github.ref, 'refs/tags/v')
environment:
name: production
url: https://<DOMAIN>
# `environment` GitHub'da manual approval ister
🚀 Progressive Delivery#
Canary (Argo Rollouts)#
apiVersion: argoproj.io/v1alpha1
kind: Rollout
spec:
strategy:
canary:
steps:
- setWeight: 5
- pause: { duration: 5m }
- analysis:
templates:
- templateName: success-rate
- setWeight: 25
- pause: { duration: 10m }
- analysis:
templates:
- templateName: success-rate
- setWeight: 50
- pause: { duration: 30m }
- setWeight: 100
Otomatik analysis fail → rollback.
Blue/Green#
strategy:
blueGreen:
activeService: my-app-active
previewService: my-app-preview
autoPromotionEnabled: false # manuel onay
Feature Flag#
Kod prod'da ama kapalı; per-cohort açma. Tools: LaunchDarkly, Flagsmith, OpenFeature (vendor-neutral SDK).
if feature_flags.is_enabled("new-checkout", user_id=user.id):
return new_checkout_flow(...)
else:
return old_checkout_flow(...)
📊 Pipeline Metrikleri#
Her pipeline ekibe DORA metric çıkarır:
# Deployment frequency (per day)
sum(rate(pipeline_runs_total{branch="main",status="success"}[7d])) * 86400
# Lead time (commit → deploy, p95)
histogram_quantile(0.95, sum by (le) (rate(commit_to_deploy_seconds_bucket[7d])))
# Change failure rate
sum(rate(deploy_failures_total[7d])) / sum(rate(deploys_total[7d]))
# Time to restore (incident → resolved, p95)
histogram_quantile(0.95, sum by (le) (rate(incident_duration_seconds_bucket[7d])))
⚠️ Anti-pattern'ler#
| ❌ Anti-pattern | ✅ Çözüm |
|---|---|
| Tüm CI sequential | Paralel fan-out + matrix |
npm install her run'da | npm ci + cache |
| 10 farklı YAML CI workflow | Reusable workflow + composite action |
| Production deploy CI'dan otomatik | GitOps repo PR + ArgoCD reconcile |
| Secret env var'da plain | OIDC veya Vault fetch |
| "Bypass approval" power user | Bypass'sız policy-as-code |
| Test 30 dk sürüyor | Sharding + selective test |
| Image build her PR'da no-cache | BuildKit cache + COPY sırası |
latest tag deploy | Semantic / SHA-pinned tag |
| CI'da bir adım manuel ("re-run") | Otomatik retry + flaky test ayrımı |
🎓 Pipeline Maturity Levels#
Level 1: "It works" — manuel adımlar, tek-tıkla deploy
Level 2: "Automated" — push → CI → auto-deploy dev
Level 3: "Tested" — CI'da unit + integration test, fail = no deploy
Level 4: "Secured" — SAST/SCA/IaC scan + image sign + Kyverno verify
Level 5: "Observed" — DORA metric'ler track ediliyor, pipeline'a feedback
Level 6: "Optimized" — pipeline P95 latency < 10 dk, parallel + cache
Level 7: "Self-healing" — failed deploy auto-rollback, error budget gate
Her ekip nerede olduğunu bilmeli ve bir sonraki seviyeyi hedeflemeli.
📚 Devamı#
17-Templates/github-actions/— hazır workflow'lar08-Security/DevSecOps-Pipeline.md06-GitOps/— pipeline → ArgoCD → cluster akışı- [Continuous Delivery — Jez Humble & David Farley]