Node.js Uygulamasını PM2 ile Üretimde Çalıştırma

Üretim ortamında Node.js uygulaması çalıştırmak kulağa basit gelir ama node app.js yazıp terminali kapatınca her şeyin durduğunu ilk kez gördüğünde işin ciddiyetini anlarsın. PM2, bu sorunu çözmek için geliştirilmiş bir process manager ve Node.js ekosisteminin olmazsa olmazlarından biri. Sunucu yeniden başladığında uygulamanı otomatik ayağa kaldırmak, log yönetimi, cluster modu, canlı izleme gibi üretim ortamının gerektirdiği her şeyi tek bir araçla halledebilirsin.

PM2 Nedir ve Neden Kullanmalısın

PM2, Node.js uygulamaları için yazılmış bir process manager olmakla birlikte Python, Ruby gibi diğer dillerdeki uygulamaları da yönetebilir. Keymetrics firması tarafından geliştirilen bu açık kaynak araç, üretim ortamlarında şu ihtiyaçları karşılar:

  • Otomatik yeniden başlatma: Uygulaman çökerse PM2 onu hemen yeniden ayağa kaldırır
  • Sistem başlangıcında otomatik başlatma: Sunucu reboot olduğunda uygulamalar otomatik başlar
  • Cluster modu: Node.js’in single-threaded yapısını aşmak için CPU çekirdeklerinin tamamını kullanabilirsin
  • Log yönetimi: Stdout ve stderr loglarını dosyaya yazar, log rotation destekler
  • Zero-downtime deployment: Uygulamayı restart ederken kısa bir süre bile erişilemez hale gelmemesini sağlar
  • Monitoring: CPU ve RAM kullanımını gerçek zamanlı izleyebilirsin

Sadece forever gibi alternatiflere kıyasla PM2’nin ekosistemi çok daha olgunlaşmış durumda ve aktif olarak geliştiriliyor.

Kurulum

PM2’yi global olarak npm ile kuruyorsun. Bunun için sisteminizde Node.js ve npm’in kurulu olması gerekiyor.

npm install -g pm2

Kurulumun başarılı olduğunu doğrulamak için:

pm2 --version

Eğer Node.js versiyonları arasında geçiş yapıyorsan ve nvm kullanıyorsan, PM2’yi her Node versiyonu için ayrı kurman gerekebilir. Bunu aşmak için şu komutu kullanabilirsin:

pm2 install pm2-runtime

İlk Uygulama Başlatma

Basit bir Express uygulamasının olduğunu düşünelim. app.js dosyana şunlar yazılmış olsun:

# Önce basit bir test için uygulamayı başlat
pm2 start app.js --name "my-api"

Bu komut uygulamanı my-api adıyla arka planda başlatır. Çıktıda şuna benzer bir tablo görürsün:

# Çalışan tüm processleri listele
pm2 list

Buradan her processin durumunu, PID’ini, CPU ve RAM kullanımını, restart sayısını ve uptime bilgisini görebilirsin. online, stopped veya errored durumlarına dikkat et. Eğer uygulaman errored moduna düşüyorsa logları hemen kontrol et:

pm2 logs my-api
pm2 logs my-api --lines 50

Ecosystem Dosyası ile Profesyonel Konfigürasyon

Gerçek üretim ortamlarında komut satırı parametreleri yerine bir ecosystem dosyası kullanman çok daha iyi bir pratik. Bu dosya hem versiyon kontrolüne girebilir hem de farklı ortamlar için ayrı konfigürasyonlar tanımlayabilirsin.

Proje kök dizininde ecosystem.config.js oluştur:

# Örnek bir ecosystem dosyası oluştur
pm2 ecosystem

Bu komut temel bir şablon oluşturur. Onu aşağıdaki gibi özelleştirebilirsin:

# ecosystem.config.js içeriği - bunu bir editörle oluştur
cat > ecosystem.config.js << 'EOF'
module.exports = {
  apps: [
    {
      name: "my-api",
      script: "./src/app.js",
      instances: "max",
      exec_mode: "cluster",
      watch: false,
      max_memory_restart: "500M",
      env: {
        NODE_ENV: "development",
        PORT: 3000
      },
      env_production: {
        NODE_ENV: "production",
        PORT: 8080
      },
      error_file: "/var/log/pm2/my-api-error.log",
      out_file: "/var/log/pm2/my-api-out.log",
      log_date_format: "YYYY-MM-DD HH:mm:ss Z",
      merge_logs: true,
      autorestart: true,
      restart_delay: 4000,
      max_restarts: 10,
      min_uptime: "10s"
    }
  ]
}
EOF

Buradaki parametreler hakkında birkaç önemli not:

  • instances: “max”: CPU çekirdek sayısı kadar worker başlatır, daha fazlası için sayı da verebilirsin
  • exec_mode: “cluster”: Node.js cluster API’sini kullanarak yük dengeleme yapar
  • max_memory_restart: Process bu bellek sınırını aşarsa otomatik restart eder
  • min_uptime: Bu sürede ayakta kalamazsa restart döngüsüne girmiş sayılır
  • max_restarts: Belirli süre içinde bu kadar restart yaparsa stopped’a düşer
  • restart_delay: Her restart arasında beklenecek milisaniye

Ecosystem dosyasıyla uygulamayı production modunda başlatmak için:

pm2 start ecosystem.config.js --env production

Sistem Başlangıcında Otomatik Başlatma

Sunucu reboot olduğunda PM2’nin ve uygulamalarının otomatik başlaması için startup scriptini oluşturman gerekiyor. Bu, üretim ortamında asla atlanmaması gereken bir adım.

pm2 startup

Bu komut sana çalıştırman gereken bir komut gösterir. Genellikle şuna benzer bir çıktı gelir:

# PM2 size şuna benzer bir komut verecek, bunu root ile çalıştır:
sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu

O komutu kopyalayıp çalıştırdıktan sonra, mevcut process listenin bu startup konfigürasyonuna kaydedilmesi için şunu yap:

pm2 save

Bu adım kritik. pm2 save çalıştırmazsanız, sunucu reboot olduğunda PM2 başlayacak ama hiçbir uygulama başlamayacak. Hem startup hem save komutlarını çalıştırdıktan sonra bir test reboot’u yapıp kontrol etmeni öneririm.

Cluster Modu ve Performans

Node.js tek bir thread üzerinde çalışır ve varsayılan olarak tek bir CPU çekirdeğini kullanır. 16 çekirdekli bir sunucuda sadece 1 çekirdeği kullanmak büyük bir israf. PM2’nin cluster modu bu sorunu çözer.

# 4 instance ile başlat
pm2 start app.js -i 4 --name "my-api"

# Tüm CPU'ları kullan
pm2 start app.js -i max --name "my-api"

# Çalışırken instance sayısını değiştir
pm2 scale my-api 8

# Instance sayısını azalt
pm2 scale my-api -2

Cluster modunun çalışması için uygulamanın stateless olması gerekir. Yani session bilgilerini process hafızasında tutuyorsan cluster moduna geçtiğinde sorun yaşarsın. Session’ları Redis veya başka bir merkezi depolama alanına taşıman gerekir.

Cluster modundaki performans artışını test etmek için basit bir benchmark yapabilirsin:

# Apache Bench ile test et (ab aracını kur: apt install apache2-utils)
ab -n 10000 -c 100 http://localhost:8080/api/health

Zero-Downtime Deployment

Bu özellik PM2’yi bir adım öne çıkaran en önemli şeylerden biri. Uygulamanı güncellerken kullanıcılar hiçbir kesinti yaşamaz.

# Graceful reload - sırayla her worker'ı yeniden başlatır
pm2 reload my-api

# Ecosystem dosyasıyla başlatılmışsa
pm2 reload ecosystem.config.js --env production

reload ile restart arasındaki fark çok önemli. restart tüm processleri anında öldürür ve yeniden başlatır, kısa bir downtime oluşur. reload ise cluster modunda sırayla her worker’ı yeniden başlatır, birisi tamamen ayağa kalkmadan diğerini öldürmez.

Deployment script’ine bunu entegre edebilirsin:

#!/bin/bash
# deploy.sh
set -e

echo "Kodları güncelliyorum..."
git pull origin main

echo "Bağımlılıkları kuruyorum..."
npm ci --production

echo "Veritabanı migration'larını çalıştırıyorum..."
npm run migrate

echo "Uygulamayı sıfır kesinti ile yeniden yüklüyorum..."
pm2 reload ecosystem.config.js --env production

echo "Deployment tamamlandı!"
pm2 status

Log Yönetimi ve İzleme

PM2 logları varsayılan olarak ~/.pm2/logs/ dizinine yazar. Üretimde bu logları düzgün yönetmek önemli.

# Gerçek zamanlı log takibi
pm2 logs

# Sadece belirli bir uygulama
pm2 logs my-api

# Son 200 satır
pm2 logs my-api --lines 200

# Log dosyalarını temizle
pm2 flush my-api

# Tüm logları temizle
pm2 flush

Log rotation için pm2-logrotate modülünü kur:

pm2 install pm2-logrotate

# Maksimum log dosya boyutu (varsayılan 10MB)
pm2 set pm2-logrotate:max_size 50M

# Kaç tane eski log tutulsun
pm2 set pm2-logrotate:retain 7

# Günlük rotation
pm2 set pm2-logrotate:rotateInterval "0 0 * * *"

Gerçek zamanlı monitoring için terminal tabanlı dashboard:

pm2 monit

Bu komut CPU, RAM, log akışı ve process durumlarını tek ekranda gösterir. Bir sorun çıktığında ilk bakacağın yer burasıdır.

Environment Variables ve Güvenlik

Üretim ortamında API anahtarları ve veritabanı şifrelerini ecosystem dosyasına yazmak yerine sistem environment variable’larından ya da .env dosyasından yüklemen daha güvenli.

# .env dosyasını PM2 ile kullanan yaklaşım
# ecosystem.config.js içinde:
# env_file: ".env.production"

# Veya doğrudan sisteme tanımla ve ecosystem'de sadece referans ver
export DB_PASSWORD="supersecretpassword"
pm2 start ecosystem.config.js --env production

# Çalışan bir processe environment variable ekle
pm2 restart my-api --update-env

dotenv paketini kullanıyorsan app.js‘nin en başında require('dotenv').config() çağrısını yapman yeterli, PM2 bunu ekstra bir konfigürasyon olmadan destekler.

Watchdog ve Health Check Entegrasyonu

PM2’nin yerleşik health check özelliğini veya özel bir script ile sağlık kontrolü ekleyebilirsin. Uygulaman yanıt vermez hale geldiğinde otomatik restart için:

# HTTP endpoint kontrolü ile birlikte başlatma
pm2 start app.js 
  --name "my-api" 
  --max-memory-restart 500M 
  --restart-delay 3000

Ecosystem dosyasına health check eklemek için:

# Uygulamanın /health endpoint'ini PM2'nin izlemesi
# Bu bilgiyi ecosystem.config.js içinde tanımlayabilirsin
# health_check_url: "http://localhost:8080/health"
# health_check_grace_period: 3000

# Manuel health kontrolü için basit script
cat > health-check.sh << 'EOF'
#!/bin/bash
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health)
if [ "$STATUS" != "200" ]; then
    echo "Health check failed! Restarting..."
    pm2 restart my-api
fi
EOF
chmod +x health-check.sh

# Crontab'a ekle - her 5 dakikada bir kontrol
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/scripts/health-check.sh") | crontab -

Yaygın Sorunlar ve Çözümleri

Uygulama sürekli restart döngüsüne giriyorsa:

# Önce logları kontrol et
pm2 logs my-api --lines 100 --err

# Process detaylarını gör
pm2 describe my-api

# Uygulamayı stopped moduna al, sorunu çöz, sonra başlat
pm2 stop my-api
# Sorunu çöz...
pm2 start my-api

Port zaten kullanımda hatası:

# Portu kim kullanıyor?
lsof -i :8080
# veya
ss -tlnp | grep 8080

# PM2 processlerini tamamen temizle
pm2 kill
pm2 start ecosystem.config.js --env production

Memory leak şüphesi:

# Her 500MB'da bir restart ayarla
pm2 start app.js --max-memory-restart 500M

# Bellek kullanımını izle
pm2 monit

# Process bilgilerini JSON formatında al
pm2 prettylist

Nginx ile Entegrasyon

Üretimde PM2’yi doğrudan internete açmak yerine Nginx’i reverse proxy olarak önüne koymalısın. Bu hem güvenlik hem de performans açısından doğru yaklaşım.

# Nginx konfigürasyonu örneği
cat > /etc/nginx/sites-available/my-api << 'EOF'
upstream my_api {
    server 127.0.0.1:8080;
    keepalive 64;
}

server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://my_api;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_cache_bypass $http_upgrade;
    }
}
EOF

ln -s /etc/nginx/sites-available/my-api /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Cluster modunda 4 instance çalışıyorsa PM2 bunlar arasında round-robin load balancing yapar, Nginx ise tek bir upstream endpoint görür. Bu mimari oldukça temiz ve ölçeklenebilir.

PM2 Plus ve Uzaktan İzleme

Ücretsiz olan keymetrics.io platformuna bağlanarak birden fazla sunucudaki PM2 processlerini tek panelden izleyebilirsin:

# PM2 Plus bağlantısı (keymetrics.io'dan token alman gerekir)
pm2 link <secret-key> <public-key>

# Bağlantıyı kes
pm2 unlink

Küçük ekipler için bu özellik çok işe yarar. Sunucuna SSH atmadan tarayıcıdan tüm uygulamaların durumunu görebilirsin.

Sonuç

PM2, Node.js uygulamalarını üretimde çalıştırmanın en pratik yolu olmayı sürdürüyor. node app.js‘den ecosystem dosyasına, cluster moduna ve zero-downtime deployment’a uzanan bu yolculuk aslında her Node.js geliştiricisinin geçmesi gereken bir olgunlaşma süreci.

Özetle dikkat etmen gereken kritik noktalar:

  • Ecosystem dosyasını her zaman versiyon kontrolüne ekle
  • pm2 startup ve pm2 save komutlarını asla atlama
  • Cluster modunda uygulamanın stateless olduğundan emin ol
  • restart yerine reload kullanarak zero-downtime sağla
  • pm2-logrotate modülünü kur, disk dolmadan önce önlem al
  • Nginx’i her zaman PM2’nin önüne koy, portu doğrudan açma

Bu yapıyı kurduğunda sunucu reboot’larından, kod güncellemelerinden veya beklenmedik crashlerden endişe etmeden uyuyabilirsin. Ve bu, bir sysadmin için paha biçilmez bir rahatlık.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir