VPS'te JekCMS: Üretim Yapılandırma Referansı

jekcms'i VPS'te işletecek herkese verdiğim PHP-FPM, OPcache, Nginx, MariaDB, cron ve yedek ayarları — deploy'da beni yakan OPcache tuzağı dahil.

VPS'te JekCMS: Üretim Yapılandırma Referansı

jekcms'i VPS'te işletecek herkese verdiğim PHP-FPM, OPcache, Nginx, MariaDB, cron ve yedek ayarları — deploy'da beni yakan OPcache tuzağı dahil.

Kendi filom — on dört içerik sitesi, toplamda 5.500'den fazla yazı — jekcms'i Cloudflare arkasında paylaşımlı hostingde koşturuyor; o hikâyeyi ayrıca yazdım. Ama biri bana "kendi sunucumda jekcms'i nasıl kurayım" diye sorduğunda gönderdiğim kağıt bu yazı. Aşağıdaki her değer savunabileceğim bir başlangıç noktası; sihirli sayı değil. Kendi yükünüzü ölçün, sonra ayarlayın.

Varsayımlar: Ubuntu ya da Debian, Nginx, PHP 8.3 (FPM), MariaDB, 2 vCPU ve 4 GB RAM. jekcms'in kendisi PHP 8.0+ ile yetinir; ama bugün sıfırdan sunucu kuruyorsanız daha eskisiyle başlamanın anlamı yok.

Önce PHP-FPM havuzunu boyutlandırın

Debian ailesindeki varsayılan havuz ayarı işe yaramayacak kadar çekingendir. /etc/php/8.3/fpm/pool.d/www.conf dosyasını düzenleyin:

pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500

Ardından sudo systemctl restart php8.3-fpm.

Bu sayılar 4 GB RAM varsayıyor. Hesapta gizem yok: kurulumunuzun worker başına gerçek bellek kullanımını ps -o rss= -C php8.3-fpm ile ölçün, MariaDB'ye ve işletim sistemine pay bırakın, kalanı bölün. jekcms istekleri hafiftir — çoğu sayfa önbelleğinden yanıtlanır, ağır kod yoluna hiç girmez — dolayısıyla 4 GB'ta 20 çocuk süreç sıkışık değil, rahat bir değerdir.

pm.max_requests = 500 worker'ları periyodik olarak tazeler. Yavaş bellek sızıntılarına karşı kaba bir araç, kabul. Ama gece üçte sürünerek büyüyen bir RSS kovalamaktansa gereksiz yere geri dönüşüm yaparım.

OPcache — ve deploy'da beni yakan tuzak

php.ini'de ya da conf.d altına bırakılmış bir dosyada:

opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0

validate_timestamps=0 üretim ayarıdır: PHP her istekte her dosyayı stat'lamayı bırakır, derlenmiş bytecode'u doğrudan bellekten servis eder. NVMe'de kazanç mütevazı; ağa bağlı depolamada veya döner diskte gayet gerçek.

Şimdi tuzak — ve bunu bir blog yazısından öğrenmedim. Zaman damgası doğrulaması kapalıyken deploy, OPcache sıfırlanana kadar yürürlüğe girmez. Benim deploy'larım GitHub Actions + rsync üzerinden çıkıyor ve diskteki dosyalar kanıtlanabilir biçimde yeniyken sitenin eski bytecode'u çalıştırmaya devam ettiğini gözlerimle gördüm. Hata yok, uyarı yok — dosyaların üstünde bugünün tarihi, bellekte dünün kodu. O günden beri boru hattımda OPcache reset, dosya senkronu kadar zorunlu bir deploy adımı; arkasından da sayfa önbelleği temizliği gelir.

Her deploy'dan sonra OPcache sıfırlama

En basit güvenilir yol FPM'i reload etmek:

sudo systemctl reload php8.3-fpm

Worker'lara hiç dokunmak istemiyorsanız cachetool, FPM ile soketi üzerinden konuşur:

php cachetool.phar opcache:reset --fcgi=/run/php/php8.3-fpm.sock

Hangisini seçerseniz seçin, deploy script'inin içine koyun — okumayı unutacağınız bir runbook'a değil.

Nginx: worker ayarları ve fastcgi_cache

2 vCPU'lu bir kutu için temeller:

worker_processes auto;

events {
    worker_connections 1024;
}

http {
    keepalive_timeout 65;
    client_max_body_size 50m;  # medya yüklemeleri
}

jekcms zaten kendi sayfa, veri ve sorgu önbelleklerini taşıyor; dolayısıyla Nginx'te tam sayfa önbelleği zorunlu değil — ama VPS'te ucuz bir sigorta, çünkü fastcgi_cache isabetinde PHP hiç çalışmaz:

fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=jekcms:10m
                   max_size=1g inactive=60m;

# location ~ \.php$ bloğunun içinde:
fastcgi_cache jekcms;
fastcgi_cache_valid 200 10m;
fastcgi_cache_bypass $cookie_PHPSESSID;
fastcgi_no_cache $cookie_PHPSESSID;

Oturum çerezindeki bypass, insanların atlayıp sonra pişman olduğu kısımdır: anonim trafiği önbellekle, giriş yapmış admin'i asla. Kurulumunuz özel bir oturum adı kullanıyorsa çerez adını ona göre değiştirin. Bu katmanın HTML tuttuğunu da unutmayın — deploy'da temizlenecek bir önbellek daha, TTL'lerimin kısa kalmasının bir nedeni daha.

Let's Encrypt ile HTTPS

sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d ornek.com -d www.ornek.com

Certbot yenilemeler için bir systemd timer kurar — systemctl list-timers | grep certbot ile doğrulayın. Yazdığı server bloğu idare eder; ben sıkılaştırırım:

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

İki bilinçli eksik var. Önce HSTS: uzun max-age'li Strict-Transport-Security doğru varış noktasıdır ama tarayıcıların sizi tutacağı bir sözdür — sitedeki son kaynak da HTTPS'ten yüklenene kadar eklemeyin. Göçten bir hafta sonra; ilk gün değil. İkincisi, Content-Security-Policy tek satırı hak etmiyor; jekcms için üretime hazır CSP şablonunu ayrı bir yazıda tutuyorum.

php.ini sertleştirme

  • expose_php = Off — X-Powered-By başlığını kaldırır
  • display_errors = Off artı log_errors = On — hatalar günlüğe gider, ziyaretçiye asla
  • session.cookie_httponly = 1 ve session.cookie_secure = 1 — oturum çerezleri JavaScript'ten okunamaz, yalnızca HTTPS
  • upload_max_filesize = 50M ve post_max_size = 52M — ikisini de Nginx'teki client_max_body_size ile uyumlu tutun; yoksa yüklemeler hangi katman küçükse orada kafa karıştıran hatalarla düşer
  • max_execution_time = 30 — kontrolden çıkan istek ölmeli, bir worker'ı işgal etmemeli

MariaDB: tampon havuzu ve yavaş sorgu günlüğü

/etc/mysql/mariadb.conf.d/50-server.cnf içinde:

innodb_buffer_pool_size = 2G
slow_query_log = 1
long_query_time = 1
log_queries_not_using_indexes = 1

Klasik tavsiye, tampon havuzuna RAM'in çoğunu vermektir. O tavsiye, makinenin sadece veritabanına ait olduğunu varsayar. PHP-FPM ve Nginx'in de koştuğu 4 GB'lık bir kutuda dürüst tavan 2G'dir — daha yükseğe çıkarsanız, kurbanı sizin yerinize OOM killer'ın seçtiği bir trafik zirvesine bir adım kalır.

Yavaş sorgu günlüğünü işler yavaşlayınca değil, ilk gün açın. Yavaş yavaş bozulan bir sorguyu iki haftalık geçmişle teşhis etmek kolay, olayın ortasında teşhis etmek sefalettir. log_queries_not_using_indexes küçük tablolarda gürültücüdür; yine de açık tutuyorum, çünkü eksik indeksler o gürültünün içinde saklanır.

Dosya izinleri ve deploy'un asla dokunmayacakları

Sahiplik ve taban izinler:

sudo chown -R www-data:www-data /var/www/jekcms
sudo find /var/www/jekcms -type d -exec chmod 755 {} \;
sudo find /var/www/jekcms -type f -exec chmod 644 {} \;
sudo chmod 600 /var/www/jekcms/.env

uploads/, cache/ ve logs/ FPM kullanıcısı tarafından yazılabilir olmalı; geri kalan her şey salt okunur kalabilir. .env dosyası veritabanı kimlik bilgilerinizi taşır — 600, istisnasız; ve asla sürüm kontrolüne girmez.

Bölümün ikinci yarısı, kendi boru hattımda zorla uyguladığım bir kural. Deploy'larım GitHub Actions'tan rsync ile çıkar ve o rsync satırının beni birden fazla kez kurtaran iki özelliği var: --delete bayrağı yok, asla; ve sunucunun ürettiği her şeyi kapsayan bir exclude listesi:

rsync -az \
  --exclude '.env' \
  --exclude 'uploads/' \
  --exclude 'cache/' \
  --exclude 'logs/' \
  ./ deploy@vps:/var/www/jekcms/

uploads/ veya .env dosyasını ezebilen bir deploy dolu silahtır — yazarlarınızın yüklediği bütün görselleri silmeye bir kötü checkout kadar uzaktasınız. Ve sonra, yukarıda anlattığım gibi: OPcache reset, önbellek temizliği. Deploy dediğiniz şey bu dizinin tamamıdır. rsync sadece ilk adım.

Gerçek cron kurun

jekcms zamanlanmış yazıları hiç cron olmadan da yayınlar: "sıradaki iş" damgası tutar ve yayıncıyı, yanıt ziyaretçiye teslim edildikten hemen sonra çalıştırır — deterministik olarak; ziyaretçi bunu asla beklemez. Paylaşımlı hostingi yaşanabilir kılan geri düşüş budur. Ama VPS'te bir crontab'ınız var, kullanın; gerçek cron kurulduğunda ziyaretçi-tetiklemeli zamanlayıcı bunu algılar ve kendini tamamen kapatır.

Web kullanıcısının crontab'ını düzenleyin — root'unkini değil:

sudo -u www-data crontab -e
* * * * * cd /var/www/jekcms && /usr/bin/php cron.php >> storage/logs/cron.log 2>&1

Baştaki cd önemli — bazı include'lar çalışma dizininin kurulum kökü olduğunu varsayar — ve log yönlendirmesi, bir gün bir şey takıldığında iz bırakır. Çalıştığını doğrulayın: tail -f storage/logs/cron.log her dakika taze çıktı göstermeli, admin'deki Zamanlanmış Görevler ekranındaki son çalışma zamanları birkaç dakikadan eski olmamalı. Ortamınız farklıysa (cPanel, Plesk, systemd timer) dokümantasyondaki cron kurulum sayfası hepsini tek tek, doğrulama sorgularıyla birlikte anlatıyor.

Yedekler: 3-2-1 ve gerçekten test edilmiş

Üç kopya, iki depolama türü, biri site dışında. Somut hali:

# root crontab — her gece 03:00'te DB dump'ı
0 3 * * * mysqldump --single-transaction --quick jekcms | gzip > /var/backups/jekcms/db-$(date +\%F).sql.gz

# 03:30 — uploads site dışı depolamaya
30 3 * * * rsync -az /var/www/jekcms/uploads/ backup@offsite:/backups/jekcms-uploads/

Kaçışlı \% işaretine dikkat: cron çıplak % karakterini satır sonu sayar ve tam da bu satır, birden fazla kişiye sessizce db-.sql.gz adında dosyalar üretmiştir. --single-transaction, tabloları kilitlemeden tutarlı bir dump alır. 30 günlük dump tutun, sonrasını aylığa seyreltin; sadece veri değil sunucu yapılandırması da kurtarılabilsin diye sağlayıcınızın panelinden haftalık VPS snapshot'ı ekleyin.

Sonra düzenli aralıkla bir tanesini geri yükleyin. Hiç geri yüklenmemiş yedek bir hipotezdir. Ben bir dump'ın temiz bir veritabanında ayağa kalktığını en az bir kez görmeden ona güvenilir demem.

Kağıdın tamamı

RAM'e göre boyutlanmış FPM. Sabitlenmiş OPcache — reset'i deploy yoluna bağlanmış halde. PHP'ye hiç uğramayan bir ön önbellek. HSTS'i aceleye getirilmemiş TLS. Kendi yavaş noktalarını siz ihtiyaç duymadan önce günlükleyen bir veritabanı. Sunucunun ürettiğini koruyan izinler, ona dokunamayan bir deploy, gerçek bir cron ve gerçekten geri yüklediğiniz yedekler. Hiçbiri egzotik değil — ama toplamı, sizin yönettiğiniz bir VPS ile sizi yöneten bir VPS arasındaki farktır.

Yazar

Celil Uyanıkoğlu

25 yılı aşkın süredir bilgi işlem sektöründe çalışan bir bilgisayar mühendisi. jekcms'i geliştiriyor ve kendi yayın ağındaki sitelerin tamamını jekcms üzerinde çalıştırıyor — burada yayımlanan her rehber önce o canlı kurulumlarda denenir.

Tüm yazılarını gör →

Hemen Sipariş Verin

Tek seferlik ödeme, ömür boyu erişim. Kurulum 30 dakika.

Fiyatlara Bak
  • 30 dakikada kurulum ve yayın
  • 13 profesyonel tema
  • AVIF/WebP görsel optimizasyonu
  • Otomatik SEO — Sitemap, Schema.org
  • ZeroTrack çerezsiz analitik

Yeniliklerden ilk sen haberdar ol

Yeni özellikler, sürüm notları ve CMS rehberleri — ayda birkaç e-posta, spam yok.