Bu makalede7 bölüm
- Teknoloji
- GitHub Actions, YAML, OIDC, SSH, rsync, Node.js, GitLab CI/CD
- Konu
- Web projesi için test, derleme ve dağıtım otomasyonu
- Seviye
- Orta
- Ön koşullar
- GitHub’da barındırılan bir web projesi
- Hedef sunucuya SSH erişimi veya bulut hesabı
- Projenin test ve derleme komutlarının bilinmesi
Kodun sunucuya FTP ya da elle scp ile kopyalandığı bir web projesinde basit bir sorunun cevabı çoğu zaman yoktur: yayında hangi commit çalışıyor? Testler aceleyle atlanır, sunucuda yerel değişiklik birikir, geri dönüş tahminle yapılır. CI/CD hattı aynı adımları her değişiklikte aynı sırayla çalıştırır ve bu belirsizliği kaldırır. Aşağıda GitHub Actions’ta test, derleme, artefakt ve SSH ile dağıtım yapan bir iş akışı kuruyor; gizli bilgi, environment korumaları ve OIDC’yi ele alıyor, sonda GitLab CI karşılıklarını veriyoruz.
CI/CD hattının aşamaları
İyi kurgulanmış bir hat, her aşamada bir soruyu yanıtlar ve yanıt olumsuzsa durur:
- Doğrulama: Kod derleniyor mu, lint ve tip kontrolü geçiyor mu?
- Test: Birim ve entegrasyon testleri başarılı mı?
- Derleme ve paketleme: Yayına çıkacak çıktı (statik dosyalar, Docker container imajı) bir kez üretilir ve sürümlenir.
- Dağıtım: Aynı çıktı önce staging’e, onaydan sonra üretime gönderilir.
- Doğrulama sonrası kontrol: Sağlık uç noktası ve temel sayfalar yanıt veriyor mu?
Temel ilke: bir kez derle, aynısını her yere dağıt. Üretim sunucusunda yeniden derleme yapmak, test edilen çıktıyla yayına çıkan çıktının farklı olma riskini getirir; bağımlılık sürümü arada değişmişse hata ilk kez üretimde görünür. Staging ortamının üretimle tutarlı kurulması ayrı bir konudur ve staging ortamı kurulumu rehberinde anlatılıyor.
Örnek iş akışı yapısı
Aşağıdaki iş akışı Node.js tabanlı bir web projesi içindir; .github/workflows/ci-cd.yml dosyasına yerleştirilir. Pull request’lerde yalnızca test çalışır; main dalına yapılan push’ta derleme ve üretim dağıtımı da devreye girer. Örnekteki @v4 etiketleri yaygın kullanılan sürümlerdir; eylemlerin çalışma zamanı güncellemeleriyle yeni ana sürümler yayımlanabildiği için her eylemin sürüm sayfasından güncel ana sürümü kontrol edin. Derleme işindeki Node.js sürümü, sunucuda çalışan sürümle aynı olmalıdır.
name: ci-cd
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [22, 24]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
build:
needs: test
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: web-dist
path: dist/
retention-days: 7
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: production
url: https://www.example.com
concurrency:
group: deploy-production
cancel-in-progress: false
steps:
- uses: actions/download-artifact@v4
with:
name: web-dist
path: dist
- name: SSH yapılandırması
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
run: |
install -m 700 -d ~/.ssh
printf '%s\n' "$SSH_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
printf '%s\n' "$KNOWN_HOSTS" > ~/.ssh/known_hosts
- name: Yükle ve etkinleştir
env:
HOST: ${{ vars.DEPLOY_HOST }}
run: |
RELEASE="$(date +%Y%m%d%H%M%S)-${GITHUB_SHA::7}"
rsync -az --delete dist/ "deploy@$HOST:/var/www/app/releases/$RELEASE/"
ssh "deploy@$HOST" "/var/www/app/bin/activate $RELEASE"
- name: Duman testi
run: curl -fsS --retry 5 --retry-delay 3 https://www.example.com/healthz
Bu dosyada sırayı ve güvenliği belirleyen üç unsur var: needs işleri sıraya bağlar ve önceki iş başarısızsa sonraki çalışmaz; if koşulu derleme ve dağıtımı pull request’lerde devre dışı bırakır; en üstteki permissions bloğu, iş akışına verilen GITHUB_TOKEN yetkisini salt okunura indirir. Varsayılan token yetkileri depo veya organizasyon ayarına bağlıdır; her iş akışında açıkça daraltın. Geniş yetkili token, ele geçirilen bir eylemin elinde depoya yazma anahtarıdır.
Test ve derleme adımları
npm ci, package-lock.json dosyasına birebir uyan bir kurulum yapar ve dosya ile package.json uyuşmazsa hata verir; CI’da npm install yerine tercih edilmesinin nedeni budur. actions/setup-node içindeki cache: npm seçeneği npm önbellek dizinini kilit dosyasının özetine göre saklar ve sonraki çalışmalarda geri yükler. Başka paket yöneticileri veya derleme çıktıları için actions/cache eylemi aynı mantıkla kullanılır; önbellek anahtarını mutlaka kilit dosyasının özetinden üretin, aksi halde bayat bağımlılıklarla test yaparsınız. Yeşil test, yanlış bağımlılıkla.
Matris (strategy.matrix) aynı testleri birden fazla çalışma zamanı sürümünde paralel çalıştırır; sunucudaki sürümü yükseltmeden önce yeni sürümle uyumu görmek için kullanışlıdır. Veritabanı gerektiren entegrasyon testlerinde iş tanımına services: bloğuyla geçici bir PostgreSQL veya MariaDB container’ı eklenebilir; her çalışma temiz bir veritabanıyla başlar.
Derleme işi çıktıyı actions/upload-artifact ile saklar, dağıtım işi aynı çıktıyı actions/download-artifact ile alır. Böylece test edilen işten sonra tek bir derleme yapılır ve dağıtılan dosyalar tam olarak bu derlemenin ürünüdür. Container tabanlı projelerde artefakt yerine sürüm etiketli bir imaj registry’ye gönderilir; imajın sunucuda nasıl çalıştırılacağı Docker Compose üretim yayını yazısında ele alınıyor.
Gizli bilgiler ve ortam korumaları
GitHub’da üç düzeyde yapılandırma değeri tutulur: organizasyon, depo ve environment. Hassas olmayan değerler (sunucu adı, URL) için vars, parola ve anahtarlar için secrets kullanılır. Secret’lar loglarda maskelenir, ancak maskeleme bir güvenlik sınırı değildir: değeri dönüştüren (base64, parçalama) bir komut maskeyi atlatabilir.
Üretim dağıtımının asıl güvenlik kapısı environment’tır. production adlı bir environment için şunlar tanımlanabilir:
- Zorunlu onaylayıcılar: İş, belirlenen kişi veya ekip onaylamadan başlamaz. Bu özelliğin özel depolarda kullanılabilirliği GitHub planınıza bağlıdır.
- Dal kısıtı: Yalnız
maindalından veya belirli etiketlerden gelen çalışmalar bu environment’a dağıtım yapabilir. - Environment secret’ları: Üretim anahtarı yalnızca bu environment’ı kullanan ve onayı geçen işe verilir; test işleri bu değeri hiçbir zaman görmez.
İş akışı incelemelerinde en sık rastlanan güvenlik hataları:
${{ github.event.pull_request.title }}gibi kullanıcı kontrolündeki değerleri doğrudanrun:içine yazmak; bu, komut enjeksiyonuna açıktır. Değeri önceenv:ile ortam değişkenine atayıp kabukta tırnaklı kullanın; örnekteki SSH adımı bu deseni izler.- Üçüncü taraf eylemleri değişebilen bir etiketle (
@v1) kullanmak. Resmi olmayan eylemleri tam commit SHA’sına sabitleyin ve güncellemeleri Dependabot ile kontrollü yapın. pull_request_targettetikleyicisinde fork’taki kodu checkout edip çalıştırmak; bu tetikleyici secret’lara erişimle çalışır.
OIDC ile bulut kimliği
Azure, AWS veya Google Cloud’a dağıtımda uzun ömürlü erişim anahtarını secret olarak saklamak yerine OpenID Connect kullanılabilir. İş akışı, GitHub’ın imzaladığı kısa ömürlü bir token alır; bulut tarafında bu token’daki depo, dal veya environment bilgisine güvenen bir kimlik tanımlanır. Environment kullanan bir işte token’ın konu (subject) alanı repo:org/depo:environment:production biçimindedir; bulut tarafındaki güven ilişkisini tam bu değerle sınırlayın.
permissions:
id-token: write
contents: read
steps:
- uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
AWS için aynı rolü aws-actions/configure-aws-credentials eylemi role-to-assume parametresiyle üstlenir. Kimlik tanımlayıcıları gizli bilgi değildir; secret olarak saklanmaları gerekmez, ancak yetkiler bulut tarafında en az yetki ilkesine göre verilmelidir. Ayrıntılar GitHub Actions belgelerinde “security hardening” başlığı altında yer alır.
Sunucuya dağıtım yöntemleri
| Yöntem | Uygun olduğu durum | Dikkat |
|---|---|---|
| SSH + rsync ile sürüm dizini | Kendi Linux sunucusu, PHP veya statik site | Kısıtlı kullanıcı, sabit known_hosts |
Registry’ye imaj + SSH ile compose pull/up |
Container tabanlı yayın | İmaj etiketi commit ile eşleşmeli |
| Sunucunun çektiği model (pull) | Sunucuya dışarıdan SSH açılamıyor | Sunucuda ajan veya zamanlanmış görev |
| Self-hosted runner | Hedef iç ağda | Herkese açık depolarda kullanmayın |
| OIDC ile bulut servisi | App Service, S3, Cloud Run vb. | Güven ilişkisini environment ile sınırlayın |
SSH ile dağıtımda iki kural var. Birincisi: known_hosts değerini iş akışında ssh-keyscan ile anlık üretmek yerine önceden doğrulanmış parmak iziyle secret olarak saklayın; aksi halde ilk bağlantıda araya giren bir sunucuya güvenmiş olursunuz. İkincisi: dağıtım kullanıcısı yalnızca uygulama dizinine yazabilmeli, sudo yetkisi yalnızca gereken tek komutla (örneğin PHP-FPM reload) sınırlı olmalıdır.
Örnekteki releases/ dizini ve activate betiği, her sürümün ayrı dizine yüklenip ardından tek adımda etkinleştirilmesini sağlar. Bu geçişin nasıl kesintisiz yapılacağı ve blue-green, rolling gibi alternatifler sıfır kesintili dağıtım rehberinin konusudur; burada hattın kendisine odaklanıyoruz.
GitLab CI karşılıkları
Aynı hattı GitLab’a taşırken kavramlar büyük ölçüde eşleşir; sözdizimi ve bazı özelliklerin lisans katmanı farklıdır:
| GitHub Actions | GitLab CI/CD |
|---|---|
.github/workflows/*.yml |
.gitlab-ci.yml |
jobs ve needs |
stages ve needs |
runs-on |
image ve runner tags |
on: ve if: |
rules: ve workflow:rules |
actions/upload-artifact |
artifacts: paths |
actions/cache |
cache: key / paths |
secrets / vars |
CI/CD değişkenleri (protected, masked) |
| Environment korumaları | Protected environments, deployment approvals |
concurrency |
resource_group |
id-token: write (OIDC) |
id_tokens |
GitLab’da belirleyici fark şu: protected değişkenlerin yalnızca korumalı dal ve etiketlerde çalışan işlere verilmesidir; üretim anahtarlarını bu şekilde işaretlemek GitHub’daki environment secret’larına benzer bir sınır sağlar. Ayrıntılar için GitLab belgelerine bakılabilir.
Hat kurulduktan sonra da bakım ister. Yavaşlayan testleri izleyin, önbelleği gözden geçirin, eylem sürümlerini güncel tutun; yirmi dakika süren bir hat, ekibi kısa yollara iter. Mevcut bir projeye CI/CD eklemek veya elle yapılan yayını otomasyona taşımak için CI/CD ve web DevOps otomasyonu hizmet sayfamıza bakabilirsiniz.
Sık sorulan sorular
Fork’tan gelen pull request’lerde neden gizli bilgilere erişilemiyor?
GitHub, güvenlik nedeniyle fork’tan açılan pull request’lerin tetiklediği pull_request iş akışlarına depo secret’larını vermez ve GITHUB_TOKEN’ı salt okunur sağlar. Bu bilinçli bir korumadır. pull_request_target ile bu sınırı aşmak mümkündür, ancak bu tetikleyicide fork’taki kodu checkout edip çalıştırmak secret’ların ele geçirilmesine yol açabilir.
Dağıtım için SSH anahtarı mı yoksa OIDC mi kullanmalıyım?
Hedef bir bulut sağlayıcısıysa (Azure, AWS, Google Cloud) OIDC tercih edilmelidir; uzun ömürlü anahtar saklanmaz, kısa süreli kimlik her çalışmada yeniden üretilir. Kendi Linux sunucunuza dağıtımda SSH anahtarı pratik seçimdir; anahtarı yalnızca dağıtıma ayrılmış, yetkisi kısıtlı bir kullanıcıya bağlayın ve düzenli döndürün.
Aynı anda iki dağıtımın çalışmasını nasıl engellerim?
Dağıtım işine concurrency grubu tanımlayın ve cancel-in-progress değerini false bırakın. Böylece aynı gruptaki ikinci çalışma, ilki bitene kadar bekler; yarım kalmış bir dağıtımın üzerine yenisi başlamaz. Grupta aynı anda yalnızca bir bekleyen çalışma tutulur; daha yenisi gelirse bekleyen eski çalışma iptal edilir, bu da en son commit’in dağıtılmasını sağlar.
Test ve dağıtımı aynı iş akışında mı yoksa ayrı dosyalarda mı tutmalıyım?
Küçük ve orta projelerde needs ile bağlanmış işleri tek iş akışında tutmak hem okunabilir hem de test edilen artefaktın dağıtılan artefaktla aynı olmasını sağlar. Proje büyüdükçe ortak adımlar yeniden kullanılabilir iş akışlarıyla (workflow_call) ayrı dosyaya alınabilir.
- Teknoloji
- GitHub Actions, YAML, OIDC, SSH, rsync, Node.js, GitLab CI/CD
- Konu
- Web projesi için test, derleme ve dağıtım otomasyonu
- Seviye
- Orta
- Ön koşullar
- GitHub’da barındırılan bir web projesi
- Hedef sunucuya SSH erişimi veya bulut hesabı
- Projenin test ve derleme komutlarının bilinmesi
Web Sunucuları ve DevOps