İçeriğe geç
Muhammet Şafak
Araçlar & Teknolojiler 5 dk okuma

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.

GitHub Actions iş akışı (workflow) ekranı

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; needs ile 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-latest yaygı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 anahtar
  • SSH_HOST — sunucunun IP adresi
  • SSH_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.

Etiketler: #GitHub Actions
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi