Approval, Gate ve Deploy Kontrolleri: Pipeline'a İnsan ve Kural Eklemek
Otomatik gate'ler (smoke test, health check, deploy freeze) 'bu değişiklik çalışıyor mu?' sorusunu, manuel approval ise 'şimdi mi?' sorusunu cevaplamalı; DORA, ağır dış onayların change fail rate'i düşürdüğüne dair kanıt bulamadı. Jenkins input step'inde herkes yöneticiyken submitter'ın işe yaramadığını gösteren gerçek çıktı, GitHub environment protection rule'ları ve Azure DevOps check'leri.
Sürüm notu: Bu yazıdaki örnekler .NET 10, Jenkins 2.580.2 LTS (Long-Term Support) ve Matrix Authorization Strategy plugin'i, act 0.2.97, actionlint 1.7.12 ve Docker 29 ile denenmiştir.
Önce Tahmin Et
Bookstore'un Jenkins pipeline'ında production aşaması bir onay bekliyor ve onayı sadece Grace verebilsin diye submitter 'grace' yazılmış. Jenkins kurulumu kolay olsun diye "giriş yapan herkes her şeyi yapabilir" yetki ayarıyla kurulmuş. Ada onay ekranına gidip "Deploy"a basıyor. Jenkins ne yapar: reddeder mi, yoksa Ada'nın onayıyla production'a çıkar mı? Gerçek konsol çıktısı yazının ortasında.
Manuel Approval ve Otomatik Gate
Pipeline'a iki tür kontrol noktası eklenebiliyor. Manuel approval'da bir insan "devam" der. Otomatik gate'te bir kural ya da ölçüm karar verir: deploy'dan sonra smoke test geçti mi, health check yeşil mi, son 10 dakikada hata oranı eşiğin altında mı, şu an bir deploy freeze dönemi mi?
İkisi farklı soruları cevaplıyor. Otomatik gate "bu değişiklik çalışıyor mu?" sorusunda bir insandan çok daha iyi: yorulmaz, atlamaz, her seferinde aynı şeyi kontrol eder. İnsan onayı ise "bunu şimdi yapmalı mıyız?" sorusunda değerli: kampanya pazartesi mi başlıyor, müşteriye duyuru yapıldı mı, bu riski kabul ediyor muyuz? Sık yapılan hata, insanı birinci soruyu cevaplamaya zorlamak. Onaylayan kişi diff'in 2.000 satırını okumaz; testlerin yeşil olduğunu görür ve basar. Bu durumda onay bir güvence değil, bir bekleme süresi.
Otomatik Gate'ler: Smoke Test ve Deploy Freeze
Smoke test: deploy'dan hemen sonra
Bir önceki yazıda gördük: container "running" görünse bile uygulama ayakta olmayabilir. Deploy'un gerçekten başarılı olduğunu, uygulamanın kendisine sorarak öğreniyoruz:
#!/usr/bin/env bash
# Kullanım: smoke-test.sh <base-url>
set -euo pipefail
base_url="$1"
for attempt in 1 2 3 4 5; do
if curl -fsS --max-time 5 "$base_url/health" > /dev/null; then
echo "health ok (attempt $attempt)"
curl -fsS --max-time 5 "$base_url/api/books/1" > /dev/null
echo "smoke test passed"
exit 0
fi
echo "health check failed (attempt $attempt), retrying in 3s"
sleep 3
done
echo "smoke test failed after 5 attempts" >&2
exit 1
Script önce health endpoint'ini bekliyor (uygulamanın açılması birkaç saniye sürebilir), sonra gerçek bir iş endpoint'ine istek atıyor. Health check yeşil olup iş endpoint'i 500 dönen bir uygulama da başarısız sayılmalı. Çalışan staging'e ve var olmayan bir adrese karşı:
$ ./deploy/smoke-test.sh http://localhost:18101
health ok (attempt 1)
smoke test passed
$ ./deploy/smoke-test.sh http://localhost:18199
curl: (7) Failed to connect to localhost port 18199 after 0 ms: Couldn't connect to server
health check failed (attempt 1), retrying in 3s
…
health check failed (attempt 5), retrying in 3s
smoke test failed after 5 attempts
$ echo $?
1
Sıfırdan farklı çıkış kodu, pipeline'ın o aşamada durması demek. Daha olgun bir gate, deploy'dan sonraki birkaç dakika boyunca hata oranını ve gecikmeyi izleyip eşik aşılırsa otomatik rollback tetikler; bunu canary ile birlikte ilerideki Deployment Stratejileri serisinde ele alacağım.
Deploy freeze ve change window
Bazı dönemlerde deploy yapmamak bir iş kararı: Bookstore için Black Friday haftası ve yıl sonu. Bu kararı bir wiki sayfasında değil, pipeline'ın okuduğu bir dosyada tutmak daha güvenilir:
# start end reason
2026-11-27 2026-11-30 black-friday
2026-12-24 2027-01-02 year-end
#!/usr/bin/env bash
# Kullanım: freeze-check.sh (belirli bir günü denemek için DEPLOY_DATE=YYYY-MM-DD)
set -euo pipefail
today="${DEPLOY_DATE:-$(date -u +%F)}"
while read -r start end reason; do
[[ -z "$start" || "$start" == \#* ]] && continue
if [[ ! "$today" < "$start" && ! "$today" > "$end" ]]; then
echo "deploy freeze: $reason ($start..$end), today is $today" >&2
exit 1
fi
done < "$(dirname "$0")/freeze-windows.txt"
echo "no freeze on $today"
$ DEPLOY_DATE=2026-11-08 ./deploy/freeze-check.sh
no freeze on 2026-11-08
$ DEPLOY_DATE=2026-11-28 ./deploy/freeze-check.sh
deploy freeze: black-friday (2026-11-27..2026-11-30), today is 2026-11-28
$ DEPLOY_DATE=2026-12-31 ./deploy/freeze-check.sh
deploy freeze: year-end (2026-12-24..2027-01-02), today is 2026-12-31
Freeze dosyası repository'de durduğu için değişikliği de PR'dan (pull request) geçiyor; "freeze'i kim, neden kaldırdı" sorusunun cevabı git log'da. Freeze'in bir de istisna yolu olmalı: freeze sırasında kritik bir güvenlik açığı çıkarsa ne yapılacağı önceden yazılmış olmalı.
Jenkins'te Approval
Bookstore'un deploy pipeline'ı bu iki gate'i ve bir insan onayını sırayla bağlıyor:
pipeline {
agent any
options {
disableConcurrentBuilds()
}
environment {
IMAGE = "bookstore/api:${env.GIT_COMMIT.take(7)}"
}
stages {
stage('Deploy to staging') {
steps {
echo "deploying ${IMAGE} to staging"
}
}
stage('Smoke test') {
steps {
sh './deploy/smoke-test.sh http://cicdwork-api-staging:8080'
}
}
stage('Freeze check') {
steps {
sh './deploy/freeze-check.sh'
}
}
stage('Deploy to production') {
input {
message 'Deploy to production?'
ok 'Deploy'
submitter 'grace'
submitterParameter 'APPROVER'
}
steps {
echo "${APPROVER} approved ${IMAGE} for production"
}
}
}
}
disableConcurrentBuilds() aynı anda iki deploy'un yarışmasını engelliyor. Staging deploy'u burada sadece bir echo; asıl deploy komutu platforma göre değişir. Smoke test ise gerçek: Jenkins container'ı ile staging API (Application Programming Interface) container'ını aynı Docker network'üne koydum. Giriş sorusundaki kurulumda, yani herkesin her şeyi yapabildiği yetki ayarıyla, Ada onay verdiğinde:
+ ./deploy/smoke-test.sh http://cicdwork-api-staging:8080
health ok (attempt 1)
smoke test passed
+ ./deploy/freeze-check.sh
no freeze on 2026-11-08
[Pipeline] { (Deploy to production)
[Pipeline] input
Deploy to production?
Deploy or Abort
Approved by ada
ada approved bookstore/api:5a8dda7 for production
Finished: SUCCESS
Ada onayladı ve production'a çıkıldı. submitter sadece sıradan kullanıcıları kısıtlıyor; Jenkins yöneticileri her input'u onaylayabiliyor. "Giriş yapan herkes her şeyi yapabilir" ayarında herkes yönetici olduğu için submitter satırı hiçbir şey yapmıyordu. Matrix Authorization Strategy plugin'iyle yetkileri ayırdım: yönetici sadece admin, diğerleri okuma ve build yetkisine sahip. Aynı pipeline'ı tekrar koşturup Ada ile onaylamayı denediğimde Jenkins'in yanıtı:
Error You need to be grace to submit this.
Grace onayladığında:
Deploy to production?
Deploy or Abort
Approved by grace
grace approved bookstore/api:5a8dda7 for production
Finished: SUCCESS
submitterParameter sayesinde onaylayanın adı hem log'da hem de bir değişkende; deploy kaydına ya da bir bildirime yazılabilir. Bu, segregation of duties (görevler ayrılığı) ve audit trail için ihtiyaç duyduğun şey: kim, neyi, ne zaman onayladı.
Bir detay daha: bu Jenkinsfile'da agent pipeline seviyesinde tanımlı ve konsolda input, node bloğunun içinde bekledi. Yani onay beklerken bir executor da meşgul. Jenkins dokümantasyonu stage seviyesindeki input'un o stage'in agent'ına girmeden önce beklediğini söylüyor; executor'ı boşa tutmamak için pipeline seviyesinde agent none yazıp agent'ı stage'lere vermek gerekiyor.
GitHub Actions: Environment Protection Rules
GitHub Actions'ta onay workflow dosyasında değil, repository ayarlarındaki environment'ta tanımlanıyor. Workflow sadece hangi environment'a deploy ettiğini söylüyor:
name: deploy
on:
push:
branches: [main]
jobs:
staging:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v7
- run: echo "deploying ${GITHUB_SHA::7} to staging"
- run: ./deploy/smoke-test.sh "${{ vars.STAGING_URL }}"
production:
needs: staging
runs-on: ubuntu-latest
environment: production
concurrency:
group: production
cancel-in-progress: false
steps:
- uses: actions/checkout@v7
- run: ./deploy/freeze-check.sh
- run: echo "deploying ${GITHUB_SHA::7} to production"
production environment'ına GitHub'da şu kurallar eklenebiliyor: en fazla altı kişi ya da takımdan oluşan required reviewers (birinin onayı yeterli, deploy'u başlatanın kendi onayını vermesi engellenebilir), 1 ile 43.200 dakika (30 gün) arası wait timer, hangi branch ve tag'lerin bu environment'a deploy edebileceği ve GitHub App tabanlı custom deployment protection rules. Environment secret'ları, koruma kuralları geçilmeden job'a verilmiyor. Bir sınır: GitHub Free, Pro ve Team planlarında required reviewers ve wait timer sadece public repository'lerde var.
Bu workflow'u act ile koşturdum:
$ act push -W .github/workflows/deploy.yml --var STAGING_URL=http://cicdwork-api-staging:8080 --network cicdwork-net
[deploy/staging] | deploying 4e50775 to staging
[deploy/staging] | health ok (attempt 1)
[deploy/staging] | smoke test passed
[deploy/staging] 🏁 Job succeeded
[deploy/production] | no freeze on 2026-11-08
[deploy/production] | deploying 4e50775 to production
[deploy/production] 🏁 Job succeeded
Production job'ı hiç beklemedi, çünkü act environment koruma kurallarını uygulamıyor; onlar GitHub'ın sunucu tarafında yaşıyor. Smoke test ve freeze check act ile yerelde koşuldu; required reviewers davranışı GitHub'da koşulmadı. Bu, onay mekanizmalarını test ederken unutulmaması gereken bir şey: yerel araçlar pipeline'ın mantığını doğrular, platformun yetki kurallarını değil.
Tool Eşlemesi ve Azure DevOps Checks
Azure DevOps'ta onaylar ve kontroller YAML (YAML Ain't Markup Language) dosyasında değil, environment, service connection, agent pool gibi resource'ların üzerinde tanımlanıyor. Bunun güzel bir sonucu var: pipeline YAML'ını değiştirebilen biri o kontrolleri kaldıramıyor. Kontroller sırayla değerlendiriliyor: önce branch control ve required template gibi statik kontroller, sonra onaylar ve business hours, Azure Function ya da REST (Representational State Transfer) API çağrısı gibi dinamik kontroller, en son exclusive lock.
| İhtiyaç | GitHub Actions | Jenkins | Azure DevOps |
|---|---|---|---|
| İnsan onayı | Environment required reviewers | input + submitter | Approvals check |
| Bekleme süresi ya da zaman penceresi | Wait timer | Script ya da when koşulu | Business hours check |
| Sadece belirli branch'ten deploy | Deployment branches and tags | Multibranch'te when { branch } | Branch control check |
| Aynı anda tek deploy | concurrency | disableConcurrentBuilds() | Exclusive lock |
| Dış sisteme sorarak karar | Custom deployment protection rule | Script ya da plugin | Invoke REST API check |
Tool'a özgü ayrıntılar ilerideki GitHub Actions, Jenkins ve Azure DevOps Pipelines serilerinde.
Kim, Hangi Branch'ten Deploy Edebilir
Production'a giden yol tek olmalı ve o yol korumalı bir branch'ten geçmeli. Bunun üç katmanı var. Birincisi branch protection ya da rulesets: main'e doğrudan push yok, merge için PR, en az bir review ve yeşil required check'ler gerekiyor. Bunu Git & GitHub serisinin "Git ile Ekip Çalışması: Branch Stratejileri, Rulesets ve Conventional Commits" yazısında anlattım. İkincisi environment'ın deployment branch kuralı: production'a sadece main'den ve v* tag'lerinden deploy edilebilsin. Üçüncüsü yetki: onay verebilecek kişiler listesi ve o kişilerin yönetici olmaması, giriş sorusunda gördüğün sebeple.
Segregation of duties çoğu zaman "kodu yazan production'a çıkaramaz" diye okunuyor. Bunu sağlamanın tek yolu ayrı bir onay adımı değil. PR review'u zaten ikinci bir kişinin değişikliği incelemesi; review kaydı ve pipeline log'ları birlikte, kimin neyi ne zaman değiştirip onayladığını gösteren bir audit trail oluşturuyor.
DORA'nın Ağır Onaylar Hakkında Söyledikleri
DORA (DevOps Research and Assessment) araştırması, değişiklik başına dışarıdan onay isteyen ağır süreçleri, örneğin her deploy için bir CAB (Change Advisory Board) toplantısını, teslimat performansıyla birlikte inceledi. Bulgu net: bu tür resmi dış onayların change fail rate'i düşürdüğüne dair kanıt bulunamadı. Tersine, onay süreci teslimatı yavaşlattığı için ekipler değişiklikleri biriktiriyor, daha büyük ve daha seyrek release'ler çıkıyor ve bunlar daha riskli. DORA'nın önerdiği: değişiklik sırasında peer review, sürekli test ve izleme gibi otomasyon ve CAB'ın tek tek değişikliği onaylamak yerine ekipler arası koordinasyon ve süreç iyileştirmesine odaklanması.
Bookstore için bunun anlamı şu: her environment'a onay koymak yerine, staging'e kadar her şey otomatik; production öncesinde tek bir onay, o da "doğru mu?" değil "şimdi mi?" sorusu için. Ekip olgunlaştıkça ve otomatik gate'ler güven kazandıkça o onay da kaldırılabilir; bu, continuous deployment'a geçiş demek.
Üretimde Dikkat
Onaya bir zaman aşımı koy. Cevapsız kalan bir onay pipeline'ı süresiz bekletir; Jenkins'te stage'e timeout, Azure DevOps'ta check'in timeout'u, GitHub'da ise onay bekleyen job'lar 30 gün sonra otomatik olarak başarısız oluyor. Süresi dolan onay, deploy'un iptali anlamına gelmeli; bir hafta sonra onaylanan eski bir build'in production'a gitmesi sürpriz olur.
Acil durum yolunu ayrı bir yol yapma. Hotfix için "onayları atla" düğmesi, en stresli anda en az kontrol edilen yolu kullanmak demek. DORA'nın önerisi de bu: standart süreç acil değişiklikleri de taşıyacak kadar hızlı olmalı.
Onay bildirimini doğru yere gönder. Onaylayacak kişi bir e-posta yığınında bildirimi görmüyorsa onay saatlerce bekler ve lead time'ı şişirir.
Sık Düşülen Tuzaklar
Herkesi yönetici yapmak: giriş sorusunun cevabı. submitter, required reviewers ya da approvals, onaylayanların yönetici yetkisiyle atlanabildiği bir kurulumda süs olarak kalır. GitHub'da da yöneticiler environment kurallarını varsayılan olarak atlayabiliyor; bu ayrıca kapatılmalı.
Lastik damga onaylar: onaylayan kişi neyi onayladığını bilmiyorsa, onay sadece bekleme süresi ekler. Onay ekranında hangi commit'lerin, hangi image'ın ve hangi test sonuçlarının gittiği görünmeli.
Onayı testlerden önce koymak: insan, otomatik kontrollerin yakalayacağı bir hatayı onaylamak için vakit harcamamalı. Önce gate'ler, sonra insan.
Onayı pipeline dosyasının içinde tutmak: pipeline'ı değiştirebilen herkes onayı da kaldırabilir. Onay kuralını platformun environment ya da resource ayarına koy.
Küçük Bir Deneme
★☆☆ Kendi CI (Continuous Integration) tool'unda production deploy'unu kimlerin onaylayabildiğini ve bu kişilerden kaçının yönetici olduğunu listele.
★★☆ smoke-test.sh'ı kendi API'ne uyarla ve staging deploy'unun hemen arkasına ekle. Bir sonraki deploy'da uygulamayı bilerek kır (ör. zorunlu bir configuration'ı kaldır) ve pipeline'ın hangi adımda durduğunu gör.
★★★ Ekibinin son on production deploy'u için onay talebinden onaya kadar geçen süreyi çıkar. Bu süre, toplam lead time'ın yüzde kaçı?
Bir sonraki yazıda pipeline'ın kendisini koruyacağız: production anahtarlarına sahip en güçlü kullanıcı olarak pipeline, secret'ların log'a sızması, uzun ömürlü cloud anahtarları yerine OIDC (OpenID Connect) ve üçüncü parti action'ları pin'lemek.