Biri Github Actions mı dedi?
GitHub Actions ile CI/CD'ye giriş: iş akışları (workflow) nasıl kurulur ve süreçler nasıl otomatikleştirilir?
GitHub Actions, bir push veya pull request gibi repository olaylarına bağlı olarak otomatik iş akışları çalıştırmanızı sağlayan bir otomasyon platformudur. Test, build ve deployment adımlarını aynı yerde, YAML dosyaları olarak tanımlarsınız; GitHub gerisini yönetir.
Bu yazıda bir PHP projesini SSH üzerinden sunucuya deploy eden, birden fazla ortamı ve IP kısıtlamasını ele alan gerçek senaryolara bakacağım. Kuralları kuru kuru geçmek yerine çalışan yapıları önce gösterip sonra üzerinden konuşmayı tercih ederim.

Temel bileşenler
Bir Actions yapılandırmasının anatomisi şöyle:
- Workflow:
.github/workflows/altında yaşayan YAML dosyası. Her dosya bir workflow. - Job: Workflow içindeki bağımsız çalışma birimi. Varsayılan olarak paralel çalışır;
needsile sıralı hale getirilir. - Step: Job içindeki tek bir komut veya action. Sıralı çalışır; bir step başarısız olursa sonrakiler varsayılan olarak çalışmaz.
- Action: Marketplace’ten çekilen veya repository içinde tanımlanan yeniden kullanılabilir step.
uses:ile çağrılır. - Runner: Job’un üzerinde çalıştığı sanal makine.
ubuntu-latestyaygın tercih; kendi self-hosted runner’ınızı da bağlayabilirsiniz.
Tek sunucuya basit deployment
main branch’e her push geldiğinde sunucuya bağlanıp kodu çeken minimal bir workflow:
name: Deploy PHP Application
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: PHP Kurulumu
uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
- name: Composer paketlerinin kurulumu
run: composer install --no-progress --no-suggest --prefer-dist
- name: SSH Bağlantısı ve Son Kodun Alınması
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
HOST: ${{ secrets.SSH_HOST }}
USER: ${{ secrets.SSH_USER }}
REMOTE_PATH: ${{ secrets.REMOTE_PATH }}
run: |
echo "$SSH_PRIVATE_KEY" > private_key
chmod 600 private_key
ssh -i private_key -o StrictHostKeyChecking=no $USER@$HOST "cd $REMOTE_PATH && git pull origin main"
Workflow içinde kullanılan credential değerleri repository’de düz metin olarak yaşamaz; Actions secrets olarak saklanır. SSH anahtarı, host adresi, kullanıcı adı ve uzak dizin bilgisinin her biri için bir secret tanımlanır. Peki bu secret’lar nasıl üretilir?
SSH anahtarı ve yetkilendirme
Sunucuda bir SSH anahtar çifti oluşturun:
ssh-keygen -t rsa -b 4096 -C "yourmail@example.com"
Bu komut id_rsa (özel anahtar) ve id_rsa.pub (açık anahtar) üretir. Açık anahtarı sunucunun authorized_keys dosyasına ekleyin:
cat ~/.ssh/id_rsa.pub
nano ~/.ssh/authorized_keys
Özel anahtarın içeriği (cat ~/.ssh/id_rsa çıktısı) GitHub’da SSH_PRIVATE_KEY secret’ı olarak kaydedilir. Repository’de Settings → Secrets and variables → Actions altından şu dört secret’ı ekleyin:
SSH_PRIVATE_KEY— sunucuda oluşturduğunuz özel anahtarSSH_HOST— sunucunun IP adresiSSH_USER— SSH bağlantısında kullanılacak kullanıcı adıREMOTE_PATH— sunucuda projenin bulunduğu dizin
Bunlar yerindeyken main’e yapılan her push sunucuya ulaşır.
Farklı branch, farklı ortam
Canlı ve test ortamları genellikle ayrı sunuculara karşılık gelir. Branch koşulunu workflow içinde ele almak mümkün:
name: Deploy PHP Application
on:
push:
branches:
- main
- develop
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH Bağlantısı ve Son Kodun Alınması
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
REMOTE_USER="canli_kullanici_adi"
REMOTE_HOST="canlı_sunucu_adresi"
REMOTE_PATH="/home/remote/project/path"
elif [ "${{ github.ref }}" == "refs/heads/develop" ]; then
REMOTE_USER="test_kullanici_adi"
REMOTE_HOST="test_sunucu_adresi"
REMOTE_PATH="/home/remote/project/path"
fi
ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no $REMOTE_USER@$REMOTE_HOST "cd $REMOTE_PATH && git pull origin ${GITHUB_REF##*/}"
SSH_KNOWN_HOSTS secret’ı için sunucunun host anahtarını şöyle alabilirsiniz:
ssh-keyscan -H your_server_domain
Birden fazla sunucuya paralel deployment
Canlı ortamda birden fazla sunucu varsa döngü tercih edilebilir bir yol:
name: Deploy PHP Application
on:
push:
branches:
- main
- develop
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH Bağlantısı ve Son Kodun Alınması
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
echo "${{ secrets.SSH_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
SERVERS=("canli_sunucu1" "canli_sunucu2" "canli_sunucu3")
REMOTE_USER="canli_kullanici_adi"
REMOTE_PATH="/home/remote/project/path"
elif [ "${{ github.ref }}" == "refs/heads/develop" ]; then
SERVERS=("test_sunucu1" "test_sunucu2")
REMOTE_USER="test_kullanici_adi"
REMOTE_PATH="/home/remote/project/path"
fi
for SERVER in "${SERVERS[@]}"
do
echo "Deploying to $SERVER"
ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no $REMOTE_USER@$SERVER "cd $REMOTE_PATH && git pull origin ${GITHUB_REF##*/}"
done
Birden fazla sunucuya SSH_KNOWN_HOSTS için aynı anda host anahtarı toplabilirsiniz:
ssh-keyscan -H your_server1_domain your_server2_domain your_server3_domain ...
Bastion host üzerinden IP kısıtlamalı sunuculara erişim
Sunucularınıza belirli IP adreslerinden erişimi kısıtladıysanız — ki kısıtlamak doğrudur — Actions runner’ının IP aralığı dinamik değişir; doğrudan bağlantı kurulamaz. Bu durumda bir bastion host üzerinden geçmek çalışan çözümdür.
Bastion host’ta SSH anahtarı oluşturup diğer sunuculara erişim yapılandırmasını ~/.ssh/config dosyasına yazın:
Host sunucu1
HostName sunucu1_ip
User kullanici_adi
IdentityFile ~/.ssh/id_rsa
Host sunucu2
HostName sunucu2_ip
User kullanici_adi
IdentityFile ~/.ssh/id_rsa
# Diğer sunucular için benzer yapılandırma...
Bu yapılandırmayla deployment workflow’u bastion üzerinden SSH ProxyJump kullanır:
name: Deploy PHP Application
on:
push:
branches:
- main
- develop
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH Bağlantısı ve Son Kodun Alınması
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
BASTION_HOST: ${{ secrets.BASTION_HOST }}
BASTION_USER: ${{ secrets.BASTION_USER }}
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.BASTION_HOST }} >> ~/.ssh/known_hosts
if [ "${{ github.ref }}" == "refs/heads/main" ]; then
SERVERS=("canli_sunucu1" "canli_sunucu2" "canli_sunucu3")
REMOTE_USER="canli_kullanici_adi"
REMOTE_PATH="/home/remote/project/path"
elif [ "${{ github.ref }}" == "refs/heads/develop" ]; then
SERVERS=("test_sunucu1" "test_sunucu2")
REMOTE_USER="test_kullanici_adi"
REMOTE_PATH="/home/remote/project/path"
fi
for SERVER in "${SERVERS[@]}"
do
echo "Deploying to $SERVER"
ssh -i ~/.ssh/id_rsa -o StrictHostKeyChecking=no -J ${{ secrets.BASTION_USER }}@${{ secrets.BASTION_HOST }} $REMOTE_USER@$SERVER "cd $REMOTE_PATH && git pull origin ${GITHUB_REF##*/}"
done
-J bayrağı SSH ProxyJump’ı etkinleştirir; runner önce bastion’a bağlanır, oradan hedef sunucuya tünel açar.
Gözden kaçan tuzaklar
Bu yapıyı birkaç projede kurarken tekrar tekrar karşılaştığım birkaç nokta var:
StrictHostKeyChecking=no riskini anlayın. -o StrictHostKeyChecking=no host key doğrulamasını devre dışı bırakır; bu bir man-in-the-middle vektörü açar. Üretim ortamları için SSH_KNOWN_HOSTS secret’ı ile gerçek host key doğrulaması yapmak daha güvenlidir.
Özel anahtar dosyasını temizleyin. İlk örnekte private_key dosyasını geçici olarak oluşturuyorsunuz. Step bitince runner temizlense de aynı job içinde sonraki bir step bu dosyaya erişebilir; rm -f private_key eklemek iyi alışkanlıktır.
Composer çıktısını önbelleğe alın. Her build’de composer install çalıştırmak runner zamanı yer. actions/cache ile vendor/ dizinini composer.lock hash’ine göre önbellekleyebilirsiniz.
Deployment sonrası sağlık kontrolü. SSH komutu başarıyla döndü diye uygulama ayakta demek değildir. git pull ardından bir curl ile endpoint kontrolü eklemek erken uyarı sağlar.
GitHub Actions bu senaryo seti için yeterli ve yerinde bir araç. Workflow dosyaları repository içinde yaşadığı için deployment mantığı da kod geçmişine dahil olur — neyin ne zaman değiştiğini görmek kolaylaşır.