Node.js Uygulamaları için Docker, GitHub Actions ve AWS EC2 ile Gerçek Dünya CI/CD Hattı Oluşturma
Modern yazılım geliştirme süreçlerinde hız, güvenilirlik ve tekrarlanabilirlik, ekiplerin başarısı için kritik öneme sahiptir. Sürekli Entegrasyon (CI) ve Sürekli Teslimat (CD) pratikleri, bu hedeflere ulaşmanın temel taşlarıdır. Bu makalede, Node.js tabanlı bir uygulamayı Docker ile kapsülleyerek, GitHub Actions kullanarak otomatik test, derleme ve dağıtım süreçlerini nasıl otomatikleştireceğinizi ve son olarak AWS EC2 sunucularına nasıl dağıtacağınızı adım adım inceleyeceğiz. Bu rehber, geliştirme döngünüzü hızlandırırken hata oranını düşürmenize ve daha güvenilir yazılımlar sunmanıza yardımcı olacak pratik bir yol haritası sunmaktadır.
CI/CD Neden Önemli? Temel Kavramlar
Yazılım geliştirme süreçlerinde sıkça karşılaşılan manuel hataları, yavaş dağıtım döngülerini ve entegrasyon sorunlarını ortadan kaldırmak için CI/CD (Continuous Integration/Continuous Delivery) pratikleri hayati bir rol oynar. Bu bölüm, CI/CD’nin temelini ve sağladığı faydaları açıklamaktadır.
Sürekli Entegrasyon (CI) Nedir?
Sürekli Entegrasyon (CI), geliştiricilerin kodlarını günde birkaç kez ana kod tabanına entegre etmelerini gerektiren bir yazılım geliştirme pratiğidir. Her entegrasyon, otomatik bir derleme ve test süreci tarafından doğrulanır. Bu, entegrasyon hatalarının erken tespit edilmesini sağlar, böylece sorunlar büyümeden çözülebilir ve geliştirme ekibinin verimliliği artar.
Sürekli Teslimat (CD) Nedir?
Sürekli Teslimat (CD), CI’nin bir uzantısıdır. CI aşamasından başarıyla geçen kod değişikliklerinin, her an üretime hazır bir şekilde depolanmasını ve manuel bir tetikleyici ile dağıtıma hazır hale getirilmesini sağlar. Bu, yazılımın daha sık ve daha güvenilir bir şekilde dağıtılmasına olanak tanır, böylece yeni özellikler ve hata düzeltmeleri kullanıcılara daha hızlı ulaşır.
Geleneksel Yaklaşımların Zorlukları
Geleneksel geliştirme yaklaşımlarında, kod entegrasyonu ve dağıtımı genellikle uzun aralıklarla ve manuel olarak yapılır. Bu durum, büyük entegrasyon çatışmalarına, geç keşfedilen hatalara ve dağıtım süreçlerinde yaşanan insan kaynaklı aksaklıklara yol açabilir. Ayrıca, manuel süreçler zaman alıcıdır ve tekrarlanabilirlik sorunları yaratır.
CI/CD’nin İşletmeye Faydaları
CI/CD, işletmelere bir dizi önemli fayda sunar:
- Hız: Yeni özellikler ve düzeltmeler daha hızlı bir şekilde kullanıcılara sunulur.
- Güvenilirlik: Otomatik testler sayesinde hatalar erken yakalanır, üretim ortamındaki sorunlar azalır.
- Daha Az Risk: Küçük, sık yapılan değişiklikler, büyük ve riskli dağıtımların önüne geçer.
- Geliştirici Verimliliği: Geliştiriciler, manuel dağıtım işleriyle uğraşmak yerine kod yazmaya odaklanabilirler.
- Maliyet Tasarrufu: Hata düzeltme maliyetleri azalır ve operasyonel verimlilik artar.
Proje Ortamının Hazırlanması: Node.js ve Docker
CI/CD hattımızın ilk adımı, uygulamamızı standartlaştırılmış ve taşınabilir bir formatta hazırlamaktır. Docker, bu konuda bize büyük kolaylıklar sağlar. Bu bölümde, basit bir Node.js uygulamasını Docker ile nasıl kapsülleyeceğimizi öğreneceğiz.
Node.js Uygulaması Oluşturma (Basit Bir Örnek)
Öncelikle, basit bir Node.js uygulaması oluşturalım. Proje dizininizde app.js adında bir dosya ve package.json dosyası bulunsun.
// app.js
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Merhaba Dünya! Node.js Uygulaması Çalışıyor!');
});
app.listen(port, () => {
console.log(Uygulama http://localhost:${port} adresinde çalışıyor.);
});
// package.json
{
"name": "my-nodejs-app",
"version": "1.0.0",
"description": "A simple Node.js app for CI/CD demo",
"main": "app.js",
"scripts": {
"start": "node app.js",
"test": "echo \"No tests specified\" && exit 0"
},
"dependencies": {
"express": "^4.17.1"
}
}
npm install komutunu çalıştırarak bağımlılıkları yükleyin.
Dockerfile ile Uygulamayı Kapsülleme
Uygulamamızı Docker ile kapsüllemek için bir Dockerfile oluşturalım. Bu dosya, Docker'ın uygulamamızı nasıl derleyeceğini ve çalıştıracağını tanımlar.
# Dockerfile
# Resmi Node.js imajını temel al
FROM node:18-alpine
# Çalışma dizinini ayarla
WORKDIR /app
# package.json ve package-lock.json dosyalarını kopyala
# Bağımlılıkları önbelleğe almak için bu adımı ayrı tutmak önemlidir
COPY package*.json ./
# Bağımlılıkları yükle
RUN npm install
# Uygulama kaynak kodunu kopyala
COPY . .
# Uygulamanın dinleyeceği portu aç
EXPOSE 3000
# Uygulamayı başlat
CMD ["npm", "start"]
.dockerignore Dosyası
.dockerignore dosyası, Docker imajına dahil edilmemesi gereken dosyaları ve dizinleri belirtir. Bu, imaj boyutunu küçültür ve gereksiz dosyaların kopyalanmasını engeller.
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
Dockerfile
README.md
Yerel Ortamda Docker İle Çalıştırma
Uygulamayı yerel olarak test etmek için Docker imajını derleyip çalıştırabilirsiniz:
docker build -t my-nodejs-app .
docker run -p 3000:3000 my-nodejs-app
Tarayıcınızda http://localhost:3000 adresine giderek uygulamanın çalıştığını görebilirsiniz.
GitHub Actions ile Sürekli Entegrasyon (CI)
GitHub Actions, kod depolarınızda doğrudan CI/CD iş akışları oluşturmanıza, test etmenize ve dağıtmanıza olanak tanıyan güçlü bir otomasyon platformudur. Bu bölümde, bir Node.js uygulamasının testlerini çalıştıran ve Docker imajını oluşturan bir CI iş akışı oluşturacağız.
GitHub Actions'a Giriş
GitHub Actions, deponuzdaki belirli olaylara (örneğin, push, pull request) yanıt olarak tetiklenen özelleştirilebilir iş akışları (workflows) tanımlamanıza olanak tanır. Bu iş akışları, sanal makinelerde (runner'lar) çalıştırılan bir veya daha fazla iş (job) içerir. Her iş, bir dizi adımdan (step) oluşur.
Workflow Dosyası Yapısı (.github/workflows)
GitHub Actions iş akışları, deponuzun kök dizinindeki .github/workflows/ klasöründe YAML dosyaları olarak tanımlanır. Örneğin, ci.yml adında bir dosya oluşturabiliriz.
CI Workflow Oluşturma: Test ve Docker Image Oluşturma
Aşağıdaki ci.yml dosyası, her push olayında tetiklenecek bir CI iş akışı tanımlar. Bu iş akışı, Node.js bağımlılıklarını kurar, testleri çalıştırır ve Docker imajını oluşturur.
# .github/workflows/ci.yml
name: Node.js CI with Docker
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Depoyu Çek
uses: actions/checkout@v3
- name: Node.js Kurulumu
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Bağımlılıkları Yükle
run: npm ci
- name: Testleri Çalıştır
run: npm test
- name: Docker İmajını Oluştur
run: docker build -t my-nodejs-app .
- name: Docker İmajını Etiketle ve GitHub Container Registry'ye Giriş Yap
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: İmajı Etiketle
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/my-nodejs-app
VERSION=$(echo "${{ github.sha }}" | cut -c 1-7)
docker tag my-nodejs-app:latest $IMAGE_ID:$VERSION
docker tag my-nodejs-app:latest $IMAGE_ID:latest
- name: İmajı GitHub Container Registry'ye Gönder
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/my-nodejs-app
VERSION=$(echo "${{ github.sha }}" | cut -c 1-7)
docker push $IMAGE_ID:$VERSION
docker push $IMAGE_ID:latest
Docker Image'ı GitHub Container Registry'ye (GHCR) Gönderme
Yukarıdaki iş akışı, Docker imajını oluşturduktan sonra ghcr.io adresindeki GitHub Container Registry'ye (GHCR) gönderir. Bu, dağıtım aşamasında sunucularımızın bu imaja kolayca erişmesini sağlar. GITHUB_TOKEN, GitHub Actions tarafından otomatik olarak sağlanan bir secret'tır ve depoya yazma izinlerine sahiptir.
AWS EC2 Üzerinde Dağıtım Ortamı Kurulumu
Uygulamamızı dağıtacağımız hedef ortam olarak AWS EC2 kullanacağız. Bu bölümde, bir EC2 instance'ı nasıl oluşturacağınızı ve Docker ile dağıtıma hazır hale getireceğinizi anlatacağız.
EC2 Instance Oluşturma ve Yapılandırma
- AWS Yönetim Konsolu'na Giriş: AWS hesabınıza giriş yapın.
- EC2 Servisine Git: EC2 hizmetini seçin.
- Instance Başlat: "Launch instances" butonuna tıklayın.
- AMI Seçimi: Ubuntu Server 20.04 LTS (HVM), SSD Volume Type gibi güncel bir AMI seçin.
- Instance Tipi Seçimi: T2.micro (ücretsiz katman için uygun) veya ihtiyacınıza göre başka bir tip seçin.
- Ağ Ayarları: Varsayılan VPC ve alt ağı kullanabilir veya kendi özel ağınızı yapılandırabilirsiniz.
- Depolama: Varsayılan depolama ayarlarını bırakabilirsiniz.
- Güvenlik Grubu Ayarları: Yeni bir güvenlik grubu oluşturun veya mevcut birini seçin. SSH (Port 22) erişimine izin verdiğinizden emin olun. Uygulamanızın çalışacağı portu (örneğin, 3000) de HTTP/HTTPS kuralı olarak ekleyin.
- Anahtar Çifti Oluşturma: Yeni bir anahtar çifti (key pair) oluşturun ve
.pemdosyasını güvenli bir yere indirin. Bu dosya, EC2 instance'ına SSH ile bağlanmak için kullanılacaktır. - Instance'ı Başlat: Instance'ı başlatın ve durumunun "running" olmasını bekleyin.
Güvenlik Grubu Ayarları (SSH ve Uygulama Portu)
EC2 instance'ınızın güvenlik grubunu, SSH (Port 22) ve uygulamanızın dinleyeceği port (örneğin, 3000) için gelen trafiğe izin verecek şekilde yapılandırmalısınız. Genellikle SSH için kendi IP adresinizi veya şirket IP'nizi, uygulama portu için ise tüm interneti (0.0.0.0/0) veya belirli bir IP aralığını hedefleyebilirsiniz.
Docker ve Docker Compose Kurulumu
EC2 instance'ınıza SSH ile bağlandıktan sonra (ssh -i /path/to/your-key.pem ubuntu@your-ec2-public-ip), Docker ve Docker Compose'u kurun:
# Docker Kurulumu
sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common -y
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io -y
# Mevcut kullanıcıyı docker grubuna ekle (yeniden oturum açmayı gerektirebilir)
sudo usermod -aG docker $USER
# Docker Compose Kurulumu
sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Docker ve Docker Compose versiyonlarını kontrol et
docker --version
docker-compose --version
SSH Anahtarları ve Güvenli Erişim
GitHub Actions'ın EC2 sunucunuza SSH ile bağlanabilmesi için, GitHub Secrets'a özel bir SSH anahtarı eklemeniz gerekecek. EC2 instance'ı oluştururken indirdiğiniz .pem dosyasını kullanmayın. Bunun yerine, yeni bir SSH anahtar çifti oluşturun:
ssh-keygen -t rsa -b 4096 -C "github-actions-deploy-key" -f ~/.ssh/github_actions_deploy_key -N ""
Bu, github_actions_deploy_key (özel anahtar) ve github_actions_deploy_key.pub (genel anahtar) dosyalarını oluşturacaktır.
- Genel Anahtarı EC2'ye Ekle:
github_actions_deploy_key.pubdosyasının içeriğini EC2 sunucunuzdaki~/.ssh/authorized_keysdosyasına ekleyin. - Özel Anahtarı GitHub Secrets'a Ekle:
github_actions_deploy_keydosyasının içeriğini kopyalayın. GitHub deponuzda "Settings" -> "Secrets" -> "Actions" bölümüne gidin ve "New repository secret" butonuna tıklayarak yeni bir secret oluşturun. AdınıSSH_PRIVATE_KEYolarak belirleyin ve kopyaladığınız özel anahtarı değer olarak yapıştırın.
GitHub Actions ile Sürekli Teslimat (CD)
Şimdi, CI aşamasında oluşturduğumuz Docker imajını AWS EC2 sunucumuza dağıtmak için bir CD iş akışı oluşturacağız.
Dağıtım Stratejileri (Basit Bir Yaklaşım)
Bu makalede, en basit dağıtım stratejilerinden birini kullanacağız: mevcut uygulamanın durdurulması, yeni Docker imajının çekilmesi ve uygulamanın yeniden başlatılması. Daha karmaşık senaryolar için Blue/Green, Canary dağıtımı gibi stratejiler de mevcuttur.
GitHub Actions CD Workflow'u Oluşturma
.github/workflows/cd.yml adında yeni bir dosya oluşturalım. Bu iş akışı, main branch'ine yapılan her başarılı push işleminden sonra tetiklenecektir.
# .github/workflows/cd.yml
name: Node.js CD to EC2
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
needs: build-and-test # CI iş akışının başarılı olmasını bekler
steps:
- name: Depoyu Çek
uses: actions/checkout@v3
- name: SSH Anahtarını Kur
uses: webfactory/ssh-agent@v0.7.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Dağıtım Sunucusuna Bağlan ve Uygulamayı Dağıt
env:
EC2_HOST: ${{ secrets.EC2_HOST }} # EC2 genel IP adresi veya DNS adı
EC2_USER: ubuntu # EC2 instance'ınızın kullanıcı adı (örneğin, ubuntu, ec2-user)
GHCR_USERNAME: ${{ github.actor }}
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/my-nodejs-app:latest
run: |
ssh -o StrictHostKeyChecking=no ${EC2_USER}@${EC2_HOST} << 'EOF'
# Docker'a GitHub Container Registry ile giriş yap
echo ${GHCR_TOKEN} | docker login ghcr.io -u ${GHCR_USERNAME} --password-stdin
# Mevcut uygulamayı durdur ve kaldır
docker stop my-nodejs-app || true
docker rm my-nodejs-app || true
# Yeni Docker imajını çek
docker pull ${IMAGE_NAME}
# Yeni imajı kullanarak uygulamayı çalıştır
docker run -d --name my-nodejs-app -p 3000:3000 ${IMAGE_NAME}
# Docker'dan çıkış yap
docker logout ghcr.io
EOF
Bu iş akışında kullanacağımız GitHub Secrets:
SSH_PRIVATE_KEY: EC2'ye bağlanmak için oluşturduğunuz özel SSH anahtarı.EC2_HOST: EC2 instance'ınızın genel IP adresi veya DNS adı.
Bu sırları GitHub deponuzun "Settings" -> "Secrets" -> "Actions" bölümüne eklemeyi unutmayın.
EC2 Sunucusuna SSH Üzerinden Bağlanma
Yukarıdaki CD iş akışı, webfactory/ssh-agent@v0.7.0 action'ını kullanarak SSH anahtarını kurar ve ardından ssh komutu ile EC2 sunucusuna bağlanır. StrictHostKeyChecking=no, ilk bağlantıda host anahtarı doğrulamasını atlamak için kullanılır, ancak üretim ortamlarında bu genellikle daha güvenli bir şekilde yönetilmelidir.
Yeni Docker Image'ı Çekme ve Uygulamayı Yeniden Başlatma
SSH bağlantısı kurulduktan sonra, sunucu üzerinde bir dizi komut çalıştırılır:
- GitHub Container Registry'ye (GHCR) giriş yapılır.
- Mevcut
my-nodejs-appkonteyneri durdurulur ve kaldırılır. - GHCR'den en son Docker imajı çekilir.
- Yeni imaj kullanılarak uygulama tekrar çalıştırılır ve 3000 numaralı port dışarıya açılır.
- GHCR'den çıkış yapılır.
Bu adımlar, uygulamanızın kesintisiz olmasa da hızlı bir şekilde güncellenmesini sağlar.
Güvenlik ve İyileştirmeler
Bir CI/CD hattı kurarken güvenlik ve performans iyileştirmeleri her zaman göz önünde bulundurulmalıdır.
Ortam Değişkenleri ve GitHub Secrets
Hassas bilgileri (API anahtarları, veritabanı şifreleri, SSH özel anahtarları) doğrudan kodda veya açık metin olarak saklamaktan kaçının. GitHub Secrets, bu tür bilgileri güvenli bir şekilde saklamanıza ve iş akışlarınızda kullanmanıza olanak tanır. AWS IAM rolleri ve politikaları da EC2 instance'larınızın AWS hizmetlerine güvenli bir şekilde erişmesini sağlamak için kullanılmalıdır.
Güvenlik Grubu En İyi Uygulamaları
EC2 güvenlik gruplarınızı mümkün olduğunca kısıtlı tutun. SSH (Port 22) erişimini sadece belirli IP adresleriyle sınırlayın. Uygulamanızın portunu (örneğin, 3000) sadece uygulamanızın erişilebilir olması gereken IP adreslerine veya ağlara açın. Herkese açık erişim (0.0.0.0/0) sadece gerçekten gerekliyse kullanılmalıdır.
Otomatik Testlerin Önemi
CI/CD hattının temel amacı, hataları erken yakalamaktır. Birim testleri, entegrasyon testleri ve uçtan uca (end-to-end) testler gibi kapsamlı otomatik testler yazmak, dağıtım öncesinde uygulamanızın kalitesini garanti altına almanın en iyi yoludur. GitHub Actions iş akışınızda bu testlerin çalıştırıldığından emin olun.
Rollback Stratejileri
Her dağıtımın başarılı olacağı garanti edilemez. Bir sorun durumunda hızlı bir şekilde önceki çalışan sürüme geri dönebilmek (rollback) hayati öneme sahiptir. Docker imajlarını versiyonlamak (örneğin, ghcr.io/user/app:commit_sha gibi) ve önceki bir versiyonu hızlıca dağıtabilecek bir mekanizma kurmak, bu tür durumlar için hazırlıklı olmanızı sağlar.
Sonuç
Bu makalede, Node.js uygulamaları için Docker, GitHub Actions ve AWS EC2 kullanarak sağlam bir CI/CD hattı oluşturmanın temel adımlarını inceledik. Uygulamanızı Docker ile kapsülleyerek taşınabilirliğini sağladık, GitHub Actions ile otomatik test ve imaj oluşturma süreçlerini kurduk ve son olarak AWS EC2 üzerine otomatik dağıtımı gerçekleştirdik. Bu entegre yaklaşım, geliştirme süreçlerinizi hızlandıracak, hataları azaltacak ve yazılım teslimatlarınızı daha güvenilir hale getirecektir. Gelecekte bu hattı daha da geliştirmek için izleme (monitoring), loglama (logging) ve daha gelişmiş dağıtım stratejileri (örneğin, AWS ECS/EKS, Blue/Green dağıtımlar) gibi konuları araştırabilirsiniz.
SSS (Sık Sorulan Sorular)
1. GitHub Container Registry (GHCR) yerine başka bir Docker kayıt defteri kullanabilir miyim?
Evet, kesinlikle. AWS ECR (Elastic Container Registry), Docker Hub veya kendi özel kayıt defterinizi kullanabilirsiniz. GitHub Actions'ın Docker login adımlarını ve imaj etiketleme/gönderme komutlarını ilgili kayıt defterinizin gereksinimlerine göre ayarlamanız yeterlidir.
2. EC2 yerine AWS Fargate veya Lambda gibi sunucusuz hizmetler kullanabilir miyim?
Evet, sunucusuz hizmetler CI/CD için harika alternatiflerdir. AWS Fargate, Docker konteynerlerinizi sunucu yönetimi derdi olmadan çalıştırmanıza olanak tanır. AWS Lambda ise daha çok olay tabanlı, kısa süreli iş yükleri için uygundur. Dağıtım stratejileri bu platformlara göre farklılık gösterecektir, ancak temel CI (test, derleme, imaj oluşturma) adımları benzer kalır.
3. Dağıtım sırasında kesinti yaşanmaması için ne yapmalıyım?
Bu makaledeki basit dağıtım stratejisi kısa bir kesintiye neden olabilir. Kesintisiz dağıtım için Blue/Green dağıtım, Canary dağıtımı veya Rolling Update gibi daha gelişmiş stratejiler kullanmalısınız. Bu stratejiler genellikle bir yük dengeleyici (Load Balancer) ve birden fazla sunucu (veya konteyner) gerektirir. AWS Elastic Load Balancer (ELB) ve Auto Scaling Groups, bu tür senaryolar için idealdir.
4. GitHub Actions'ta ortam değişkenlerini nasıl yönetmeliyim?
Hassas olmayan ortam değişkenlerini doğrudan workflow dosyanızda env bloğu altında tanımlayabilirsiniz. Hassas bilgiler (API anahtarları, şifreler) için GitHub Secrets kullanmalısınız. Ayrıca, farklı ortamlar (geliştirme, test, üretim) için farklı secret'lar veya ortam değişkenleri tanımlayarak yönetimi kolaylaştırabilirsiniz.