OpenLiteSpeed ile Ruby on Rails Uygulaması Sunumu

Ruby on Rails uygulamalarını production ortamında ayağa kaldırmak, özellikle doğru web sunucusu seçimi yapıldığında hem performans hem de yönetim kolaylığı açısından büyük fark yaratıyor. Apache veya Nginx’e alışkın olan çoğu sysadmin, OpenLiteSpeed’i atlıyor. Oysa OLS’nin event-driven mimarisi, built-in cache mekanizması ve düşük kaynak tüketimi, Rails uygulamaları için son derece uygun bir zemin sunuyor. Bu yazıda sıfırdan başlayarak, gerçek bir Rails uygulamasını OpenLiteSpeed arkasında nasıl production-ready hale getireceğimizi adım adım inceleyeceğiz.

Neden OpenLiteSpeed ve Rails Birlikte?

Rails uygulamaları genellikle Nginx + Puma veya Nginx + Unicorn kombinasyonuyla çalıştırılır. Bu kurulum sağlamdır, ancak OpenLiteSpeed bu tabloya birkaç önemli avantaj ekler.

LSCache modülü sayesinde dinamik içerikleri bile önbelleğe alabilirsiniz. Statik dosyalar için ayrı bir CDN veya Nginx katmanı kurmak zorunda kalmadan, OLS’nin kendi içinde gzip sıkıştırma ve HTTP/2 desteğiyle yüksek throughput elde edebilirsiniz. Bunun yanı sıra web tabanlı admin paneli, konfigürasyon hatalarını minimuma indirir ve takım üyelerinin sunucuya doğrudan SSH bağlantısı olmadan bazı ayarları değiştirmesine olanak tanır.

Rails tarafında ise Puma, multi-threaded mimarisiyle OLS’nin reverse proxy yapısıyla mükemmel uyum sağlar. OLS, gelen istekleri Puma’ya iletir, statik dosyaları kendisi karşılar ve SSL termination görevini üstlenir.

Ortam Hazırlığı

Ubuntu 22.04 LTS üzerinde çalışacağız. Sunucunuzda en az 2GB RAM ve 2 CPU core olmasını öneririm.

Sistem Güncellemesi ve Temel Paketler

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget git build-essential libssl-dev libreadline-dev 
  zlib1g-dev libsqlite3-dev libpq-dev nodejs yarn

OpenLiteSpeed Kurulumu

OLS’yi resmi LiteSpeed deposundan kuruyoruz:

wget -O - https://repo.litespeed.sh | sudo bash
sudo apt install -y openlitespeed
sudo systemctl enable lsws
sudo systemctl start lsws

Kurulum tamamlandıktan sonra admin paneline erişmek için şifreyi ayarlayalım:

sudo /usr/local/lsws/admin/misc/admpass.sh

Bu komut size kullanıcı adı ve şifre belirleme imkanı sunar. Panel varsayılan olarak https://sunucu-ip:7080 adresinde çalışır. Tarayıcıdan bu adrese gittiğinizde self-signed sertifika uyarısı alacaksınız, bunu geçebilirsiniz.

Ruby Kurulumu (rbenv ile)

Sistem Ruby’si yerine rbenv kullanacağız. Bu sayede farklı projelerde farklı Ruby sürümleri yönetmek çok daha kolay hale gelir:

curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer | bash
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc

rbenv install 3.2.2
rbenv global 3.2.2
ruby --version

Gem kurulumlarında dokümantasyonu atlayarak hız kazanalım:

echo "gem: --no-document" >> ~/.gemrc
gem install bundler rails

Rails Uygulaması Oluşturma

Gerçek dünya senaryosu olarak bir blog uygulaması hazırlayacağız. PostgreSQL kullanacağız, çünkü production ortamlarında SQLite önerilmez.

sudo apt install -y postgresql postgresql-contrib
sudo systemctl start postgresql
sudo systemctl enable postgresql

sudo -u postgres psql -c "CREATE USER railsapp WITH PASSWORD 'guclu_sifre_123';"
sudo -u postgres psql -c "CREATE DATABASE railsblog_production OWNER railsapp;"

Rails uygulamasını oluşturalım:

cd /var/www
sudo mkdir rails_apps
sudo chown $USER:$USER rails_apps
cd rails_apps

rails new myblog --database=postgresql
cd myblog

config/database.yml dosyasını production için düzenleyin:

production:
  adapter: postgresql
  encoding: unicode
  database: railsblog_production
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: railsapp
  password: <%= ENV["DATABASE_PASSWORD"] %>
  host: localhost

Puma Konfigürasyonu

config/puma.rb dosyasını production ortamına göre optimize edelim:

# config/puma.rb
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"

port ENV.fetch("PORT") { 3000 }

environment ENV.fetch("RAILS_ENV") { "development" }

pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

workers ENV.fetch("WEB_CONCURRENCY") { 2 }

preload_app!

on_worker_boot do
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
end

plugin :tmp_restart

Bu konfigürasyonda workers 2 ile 2 Puma worker process başlatıyoruz. Her worker RAILS_MAX_THREADS kadar thread kullanacak. 2 CPU core’lu bir sunucu için bu değerler makul başlangıç noktalarıdır.

Systemd Servisi Oluşturma

Uygulamanın sistem başlangıcında otomatik çalışması ve çökmesi durumunda yeniden başlaması için bir systemd unit dosyası oluşturuyoruz:

sudo nano /etc/systemd/system/myblog.service
[Unit]
Description=MyBlog Rails Application
Requires=network.target
After=network.target postgresql.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/rails_apps/myblog
ExecStart=/home/ubuntu/.rbenv/shims/bundle exec puma -C config/puma.rb
ExecStop=/bin/kill -SIGTERM $MAINPID
Restart=always
RestartSec=1

Environment=RAILS_ENV=production
Environment=DATABASE_PASSWORD=guclu_sifre_123
Environment=RAILS_MASTER_KEY=master_key_buraya
Environment=PORT=3000

StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=myblog

[Install]
WantedBy=multi-user.target

Önemli not: Ortam değişkenlerini doğrudan service dosyasına yazmak küçük projeler için kabul edilebilir olsa da, hassas bilgileri /etc/myblog.env gibi ayrı bir dosyada saklayıp EnvironmentFile=/etc/myblog.env direktifiyle okumak daha güvenli bir yaklaşımdır.

sudo chown -R www-data:www-data /var/www/rails_apps/myblog
sudo systemctl daemon-reload
sudo systemctl enable myblog
sudo systemctl start myblog
sudo systemctl status myblog

OpenLiteSpeed’de Virtual Host Konfigürasyonu

Şimdi OLS’yi Rails uygulamamıza proxy yapacak şekilde ayarlayacağız. Bu işlemi hem admin panelinden hem de konfigürasyon dosyaları üzerinden yapabilirsiniz. Ben dosya tabanlı yaklaşımı tercih ediyorum çünkü bu yöntem version control’e daha uygun.

Virtual host konfigürasyon dizinini oluşturalım:

sudo mkdir -p /usr/local/lsws/conf/vhosts/myblog
sudo nano /usr/local/lsws/conf/vhosts/myblog/vhost.conf
virtualHost myblog {
  vhRoot                  /var/www/rails_apps/myblog/public
  configFile              $SERVER_ROOT/conf/vhosts/myblog/vhost.conf
  allowSymbolLink         1
  enableScript            0
  restrained              0
  setUIDMode              0
}

Ana konfigürasyon dosyası olan /usr/local/lsws/conf/httpd_config.conf dosyasına virtual host tanımını ve listener’ı eklememiz gerekiyor. Admin paneli üzerinden gitmek burada daha pratik:

Admin Paneli adımları:

  • Virtual Hosts > Add ile yeni virtual host ekleyin
  • Document Root olarak /var/www/rails_apps/myblog/public girin
  • Virtual Host Name: myblog

Asıl kritik kısım, external application tanımıdır. Admin panelinde Virtual Hosts > myblog > External Apps bölümüne gidin:

  • Type: Web Server
  • Name: RailsPuma
  • Address: localhost:3000
  • Max Connections: 35
  • Connection Keepalive Timeout: 60
  • Response Buffering: Yes

Ardından Context bölümünde yeni bir context ekleyin:

  • Type: Proxy
  • URI: /
  • Web Server: RailsPuma

Statik dosyalar için ayrı bir context daha ekleyin:

  • Type: Static
  • URI: /assets
  • Location: /var/www/rails_apps/myblog/public/assets

Bu sayede CSS, JavaScript ve resim dosyaları doğrudan OLS tarafından servis edilir, Puma’ya hiç uğramaz.

SSL Sertifikası ile HTTPS Kurulumu

Let’s Encrypt ile ücretsiz SSL sertifikası alacağız. Certbot’u kuralım:

sudo apt install -y certbot
sudo certbot certonly --standalone -d myblog.example.com

OLS admin panelinde Listeners > Add ile yeni bir HTTPS listener ekleyin:

  • Listener Name: HTTPS
  • IP Address: ANY
  • Port: 443
  • Secure: Yes
  • Certificate File: /etc/letsencrypt/live/myblog.example.com/fullchain.pem
  • Key File: /etc/letsencrypt/live/myblog.example.com/privkey.pem

HTTP’den HTTPS’e otomatik yönlendirme için 80 portunu dinleyen listener’a rewrite rule ekleyin:

RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]

Sertifika yenileme için cron job ekleyelim:

sudo crontab -e
0 12 * * * /usr/bin/certbot renew --quiet && /usr/local/lsws/bin/lswsctrl restart

Production Ortamına Hazırlık

Assets derleme ve veritabanı migration işlemleri:

cd /var/www/rails_apps/myblog

RAILS_ENV=production bundle exec rails db:create
RAILS_ENV=production bundle exec rails db:migrate
RAILS_ENV=production bundle exec rails assets:precompile

sudo systemctl restart myblog

config/environments/production.rb dosyasında bazı ayarları gözden geçirin:

# Statik dosyaları Rails serve etmesin, OLS halleder
config.public_file_server.enabled = true
config.public_file_server.headers = {
  'Cache-Control' => 'public, max-age=31536000'
}

# Log seviyesi
config.log_level = :warn

# Force SSL
config.force_ssl = true

Performans Tuning ve Cache Ayarları

OLS’nin en güçlü özelliklerinden biri LSCache’dir. Rails uygulamanız için temel cache headers ayarlayalım. app/controllers/application_controller.rb dosyasına:

class ApplicationController < ActionController::Base
  before_action :set_cache_headers

  private

  def set_cache_headers
    if request.get? && !current_user
      response.headers['X-LiteSpeed-Cache-Control'] = 'public, max-age=3600'
    else
      response.headers['X-LiteSpeed-Cache-Control'] = 'no-cache'
    end
  end
end

Oturum açmış kullanıcılar için cache’i devre dışı bırakmak kritik önem taşır. Aksi halde bir kullanıcının görüntülediği sayfa başka bir kullanıcıya gösterilebilir.

OLS’nin Gzip konfigürasyonunu admin panelinden optimize edin:

  • Server Configuration > Tuning > GZIP Compression: Enabled
  • Compression Level: 6
  • Auto Update Static File: Yes
  • MIME Types: text/html, text/css, application/javascript, application/json

Loglar ve Monitoring

Sorunları hızlıca tespit etmek için log yapısını anlayalım:

# OLS access logları
tail -f /usr/local/lsws/logs/access.log

# OLS error logları
tail -f /usr/local/lsws/logs/error.log

# Rails uygulama logları (systemd journal üzerinden)
sudo journalctl -u myblog -f

# Puma process kontrolü
sudo systemctl status myblog

Bir sorun anında ilk bakacağınız yerler sırasıyla şunlar: önce journalctl -u myblog --since "1 hour ago" ile Rails tarafındaki hataları kontrol edin, sonra OLS error loguna bakın. Genellikle 502 Bad Gateway hataları Puma’nın cevap vermediğini, 503 hataları ise bağlantı havuzunun dolduğunu gösterir.

Basit bir health check endpoint eklemek de izleme açısından faydalıdır:

# config/routes.rb
get '/health', to: proc { [200, {}, ['OK']] }

Bu endpoint’i bir monitoring aracı ile her dakika kontrol edebilir, Puma’nın ayakta olup olmadığını doğrulayabilirsiniz.

Deployment Workflow

Capistrano veya manuel deployment ile her güncelleme sırasında izlenecek adımlar:

cd /var/www/rails_apps/myblog

# Kodu çek
git pull origin main

# Gem bağımlılıklarını güncelle
bundle install --deployment --without development test

# Veritabanı güncellemelerini uygula
RAILS_ENV=production bundle exec rails db:migrate

# Assets yeniden derle
RAILS_ENV=production bundle exec rails assets:precompile

# Puma'ya sıfır downtime restart sinyali gönder
sudo kill -SIGUSR1 $(cat tmp/pids/server.pid)

# Ya da systemd ile yeniden başlat
sudo systemctl reload myblog

SIGUSR1 sinyali Puma’ya “mevcut istekleri bitir, sonra yeniden başla” der. Bu sayede kullanıcılar deployment sırasında 502 hatası görmez. Ancak bu yalnızca kod değişiklikleri için geçerlidir; yeni worker veya thread ayarı değişikliklerinde tam restart gerekir.

Güvenlik Sıkılaştırma

OLS ve Rails tarafında temel güvenlik önlemleri:

# Güvenlik duvarı ayarları
sudo ufw enable
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 7080/tcp  # Sadece yönetim IP'niz için açın

OLS admin paneline erişimi kısıtlamak için Server Configuration > Security > Allowed List bölümünden yalnızca kendi IP adresinizi ekleyin. 7080 portunu herkese açık bırakmak ciddi bir güvenlik açığıdır.

config/initializers/security_headers.rb dosyası oluşturun:

Rails.application.config.action_dispatch.default_headers = {
  'X-Frame-Options' => 'SAMEORIGIN',
  'X-XSS-Protection' => '1; mode=block',
  'X-Content-Type-Options' => 'nosniff',
  'Referrer-Policy' => 'strict-origin-when-cross-origin',
  'Permissions-Policy' => 'geolocation=(), microphone=()'
}

Yaygın Sorunlar ve Çözümleri

502 Bad Gateway: Puma’nın çalışıp çalışmadığını kontrol edin. sudo systemctl status myblog ve curl localhost:3000 komutlarıyla doğrulayın.

Assets yüklenmiyor: public/assets dizininin mevcut olduğundan ve OLS’nin bu dizini okuyabildiğinden emin olun. sudo chown -R www-data:www-data public/ komutu genellikle sorunu çözer.

Database connection errors: PostgreSQL servisinin çalıştığını ve database.yml dosyasındaki kimlik bilgilerinin doğru olduğunu kontrol edin.

Slow response times: İlk olarak Puma worker sayısını artırmayı deneyin. Sonra N+1 query sorunlarını bullet gem’iyle tespit edin. OLS tarafında connection keepalive ayarlarını gözden geçirin.

Permission denied hataları: tmp/, log/ ve public/ dizinlerinin www-data kullanıcısına ait olması gerekir.

Sonuç

OpenLiteSpeed ile Ruby on Rails birlikteliği, doğru yapılandırıldığında oldukça güçlü bir production ortamı ortaya çıkarıyor. Nginx’in statik dosya servis gücünü, LiteSpeed’in cache yetenekleriyle birleştiren bu kurulum, orta ölçekli Rails uygulamaları için ek bir reverse proxy veya CDN kurma ihtiyacını önemli ölçüde azaltıyor.

Bu yazıda anlattığımız kurulumun özeti şu şekilde:

  • rbenv ile izole Ruby ortamı
  • Puma’yı systemd servisi olarak çalıştırma
  • OLS’yi reverse proxy ve statik dosya sunucusu olarak yapılandırma
  • Let’s Encrypt ile SSL entegrasyonu
  • LSCache ile temel önbellekleme
  • Sıfır downtime deployment akışı

Uygulamanız büyüdükçe bu kurulumu horizontal scaling için genişletmek mümkündür. OLS’nin load balancer özelliğiyle birden fazla Puma instance’ını, hatta farklı sunuculardaki worker’ları yönetebilirsiniz. Ama başlangıç için tek sunucuda bu mimari, çoğu küçük ve orta ölçekli uygulama için yeterli performans ve güvenilirliği sağlayacaktır.

Bir yanıt yazın

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