Birden Fazla PHP Sürümünü Aynı Sunucuda Çalıştırma
Eski bir müşteri projesini güncellemeye çalışırken “bu uygulama sadece PHP 7.2’de çalışıyor” cümlesini duyduğunuzda, bir yandan da yeni projelerin PHP 8.2 gerektirdiğini bildiğinizde kafanız karışabilir. Ama bu durum aslında çok yaygın ve çözümü de oldukça temiz. Aynı sunucuda birden fazla PHP sürümünü paralel olarak çalıştırmak, modern sysadmin’lerin rutin iş süreçlerinin bir parçası haline geldi. Bu yazıda Ubuntu/Debian tabanlı sistemler üzerinde bu yapıyı nasıl kuracağınızı, Nginx ve Apache ile nasıl yapılandıracağınızı ve günlük yönetim işlerini nasıl kolaylaştıracağınızı detaylıca anlatacağım.
Neden Birden Fazla PHP Sürümüne İhtiyaç Duyarız?
Gerçek hayattan bir senaryo çizelim: Diyelim ki sunucunuzda üç ayrı web uygulaması var. Birincisi 2015 yılında yazılmış bir legacy e-ticaret sistemi ve PHP 5.6 ya da 7.x gerektiriyor. İkincisi aktif geliştirme aşamasında olan bir Laravel projesi ve PHP 8.1 kullanıyor. Üçüncüsü ise yeni başladığınız bir proje ve PHP 8.3’ün yeni özelliklerinden yararlanmak istiyorsunuz.
Bu üç projeyi ayrı sunuculara taşımak hem maliyetli hem de gereksiz. PHP-FPM’nin her sürüm için bağımsız çalışabilme özelliği sayesinde bu uygulamaların tamamını tek bir sunucuda ayrı process pool’ları altında çalıştırabilirsiniz.
Ön Hazırlık: Ondrej PPA’sını Eklemek
Ubuntu’nun resmi depolarında genellikle tek bir PHP sürümü bulunur ve bu sürüm dağıtım versiyonuna göre sabitlenmiştir. Birden fazla sürüme ulaşmak için Ondrej Sury’nin PPA deposunu kullanmamız gerekiyor. Bu PPA, PHP topluluğunda son derece güvenilir bir kaynak olarak kabul görüyor.
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
Bu işlemden sonra PHP 5.6’dan PHP 8.3’e kadar neredeyse tüm sürümleri kurabilir hale geliyorsunuz. Şimdi ihtiyacımız olan sürümleri yükleyelim.
PHP Sürümlerini Yüklemek
Birden fazla sürümü aynı anda yükleyebilirsiniz. Her sürüm için temel paketleri ve sık kullanılan eklentileri şu şekilde kurabilirsiniz:
# PHP 7.4 kurulumu
sudo apt install -y php7.4 php7.4-fpm php7.4-cli php7.4-common
php7.4-mysql php7.4-zip php7.4-gd php7.4-mbstring
php7.4-curl php7.4-xml php7.4-bcmath
# PHP 8.1 kurulumu
sudo apt install -y php8.1 php8.1-fpm php8.1-cli php8.1-common
php8.1-mysql php8.1-zip php8.1-gd php8.1-mbstring
php8.1-curl php8.1-xml php8.1-bcmath
# PHP 8.3 kurulumu
sudo apt install -y php8.3 php8.3-fpm php8.3-cli php8.3-common
php8.3-mysql php8.3-zip php8.3-gd php8.3-mbstring
php8.3-curl php8.3-xml php8.3-bcmath
Kurulum tamamlandıktan sonra her PHP sürümü için ayrı bir FPM servisi oluşturulur. Bunları kontrol edelim:
sudo systemctl status php7.4-fpm
sudo systemctl status php8.1-fpm
sudo systemctl status php8.3-fpm
Her servisin aktif ve çalışır durumda olduğunu görmelisiniz. Servisler varsayılan olarak Unix socket üzerinden dinliyor. Socket dosyaları /run/php/ dizininde bulunur.
PHP-FPM Pool Yapılandırması
Her PHP sürümünün kendi pool yapılandırma dizini vardır. Örneğin PHP 8.1 için bu dizin /etc/php/8.1/fpm/pool.d/ altında yer alır. Varsayılan www.conf dosyası iyi bir başlangıç noktasıdır ama production ortamında her uygulama için ayrı pool tanımlamak çok daha iyi bir pratik.
Önce PHP 8.1 için bir örnek pool oluşturalım. Diyelim ki “myapp” adında bir Laravel projemiz var:
# /etc/php/8.1/fpm/pool.d/myapp.conf
[myapp]
user = www-data
group = www-data
listen = /run/php/php8.1-myapp.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10
pm.max_requests = 500
; Ortam değişkenleri
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[APP_ENV] = production
; PHP ayarları bu pool için override edilebilir
php_admin_value[error_log] = /var/log/php/myapp-error.log
php_admin_flag[log_errors] = on
php_value[memory_limit] = 256M
php_value[upload_max_filesize] = 64M
php_value[post_max_size] = 64M
php_value[max_execution_time] = 120
Bu yapılandırmada dikkat edilmesi gereken birkaç nokta var. listen direktifi her pool için benzersiz olmalı. pm.max_children değerini sunucunuzun RAM kapasitesine göre ayarlamalısınız. Genel kural olarak her PHP process’i ortalama 50-100 MB RAM tüketir.
Nginx ile Sanal Host Yapılandırması
Nginx ile her sanal host’u farklı bir PHP-FPM socket’ine yönlendirmek son derece temiz ve performanslı bir yaklaşım. İşte üç farklı uygulama için örnek yapılandırmalar:
# /etc/nginx/sites-available/legacy-app.conf
# PHP 7.4 kullanan eski uygulama
server {
listen 80;
server_name legacy.example.com;
root /var/www/legacy-app/public;
index index.php index.html;
access_log /var/log/nginx/legacy-app-access.log;
error_log /var/log/nginx/legacy-app-error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
fastcgi_pass unix:/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /.ht {
deny all;
}
}
# /etc/nginx/sites-available/laravel-app.conf
# PHP 8.1 kullanan Laravel uygulaması
server {
listen 80;
server_name app.example.com;
root /var/www/laravel-app/public;
index index.php;
access_log /var/log/nginx/laravel-app-access.log;
error_log /var/log/nginx/laravel-app-error.log;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
# Özel pool socket'ini kullanıyoruz
fastcgi_pass unix:/run/php/php8.1-myapp.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 120;
}
location ~ /.(?!well-known).* {
deny all;
}
}
Yapılandırmaları aktifleştirip Nginx’i yeniden yükleyelim:
sudo ln -s /etc/nginx/sites-available/legacy-app.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/laravel-app.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
nginx -t komutu yapılandırma dosyalarında sözdizimi hatası olup olmadığını kontrol eder. Production’da reload yapmadan önce bu adımı atlamayın.
Apache ile Yapılandırma
Apache kullananlar için durum biraz farklı. Apache’nin mod_php modülü aynı anda yalnızca bir PHP sürümüyle çalışabilir. Bu yüzden Apache ile çoklu PHP sürümü kullanmak istiyorsanız PHP-FPM + mod_proxy_fcgi kombinasyonunu kullanmanız gerekiyor.
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.1-fpm
sudo systemctl restart apache2
Apache sanal host yapılandırması şu şekilde görünür:
# /etc/apache2/sites-available/legacy-app.conf
<VirtualHost *:80>
ServerName legacy.example.com
DocumentRoot /var/www/legacy-app/public
<Directory /var/www/legacy-app/public>
AllowOverride All
Require all granted
</Directory>
# PHP 7.4 FPM socket'ine yönlendirme
<FilesMatch .php$>
SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"
</FilesMatch>
ErrorLog ${APACHE_LOG_DIR}/legacy-app-error.log
CustomLog ${APACHE_LOG_DIR}/legacy-app-access.log combined
</VirtualHost>
CLI’de PHP Sürümü Yönetimi
Sunucu üzerinde Composer çalıştırırken veya Artisan komutları verirken hangi PHP sürümünün kullanıldığı önemli. Sistem genelindeki varsayılan PHP sürümünü update-alternatives ile yönetebilirsiniz:
# Mevcut alternatifleri listele
sudo update-alternatives --list php
# İnteraktif seçim
sudo update-alternatives --config php
# Doğrudan belirli bir sürümü ayarla
sudo update-alternatives --set php /usr/bin/php8.1
Belirli bir proje için geçici olarak farklı bir sürümü kullanmak istiyorsanız:
# Belirli sürümü doğrudan çağır
php7.4 artisan migrate
php8.1 /usr/local/bin/composer install
# Ya da proje dizininde geçici alias kullan
alias php=/usr/bin/php8.3
Cron job’larında da doğru PHP sürümünü belirtmek önemli. /etc/crontab ya da kullanıcının crontab dosyasında şu şekilde yazabilirsiniz:
# Her gün gece 2'de PHP 8.1 ile Laravel scheduler
0 2 * * * www-data /usr/bin/php8.1 /var/www/laravel-app/artisan schedule:run >> /dev/null 2>&1
# PHP 7.4 gerektiren eski script
*/15 * * * * www-data /usr/bin/php7.4 /var/www/legacy-app/cron/process.php >> /var/log/legacy-cron.log 2>&1
PHP Sürümlerini Doğrulamak ve Test Etmek
Her şeyi kurduğunuzda gerçekten doğru sürümlerin çalışıp çalışmadığını test etmelisiniz. Her web uygulamasının kök dizinine geçici bir phpinfo dosyası koyarak kontrol edebilirsiniz:
# Test dosyası oluştur
echo "<?php phpinfo();" | sudo tee /var/www/legacy-app/public/phpinfo.php
echo "<?php phpinfo();" | sudo tee /var/www/laravel-app/public/phpinfo.php
# Curl ile kontrol et
curl -s http://legacy.example.com/phpinfo.php | grep "PHP Version"
curl -s http://laravel-app.example.com/phpinfo.php | grep "PHP Version"
# Test bittikten sonra MUTLAKA sil!
sudo rm /var/www/legacy-app/public/phpinfo.php
sudo rm /var/www/laravel-app/public/phpinfo.php
FPM process’lerinin çalışıp çalışmadığını ve hangi socket’leri dinlediğini görmek için:
# Tüm PHP-FPM process'lerini listele
ps aux | grep php-fpm
# Socket dosyalarını kontrol et
ls -la /run/php/
# Belirli bir pool'un loglarını izle
sudo tail -f /var/log/php/myapp-error.log
Güncelleme ve Bakım Süreçleri
Birden fazla PHP sürümü yönetirken güncelleme süreçleri biraz daha dikkat istiyor. Her sürümü bağımsız olarak güncelleyebilirsiniz:
# Tüm PHP paketlerini güncelle
sudo apt update
sudo apt upgrade php7.4* php8.1* php8.3*
# Güncellemeden sonra FPM servislerini yeniden başlat
sudo systemctl restart php7.4-fpm php8.1-fpm php8.3-fpm
# Servis durumlarını toplu kontrol et
for version in 7.4 8.1 8.3; do
echo "=== PHP $version FPM ==="
sudo systemctl is-active php${version}-fpm
done
Hangi uygulamanın hangi PHP sürümünü kullandığını takip etmek için basit bir script hazırlayabilirsiniz:
#!/bin/bash
# /usr/local/bin/php-status.sh
echo "===== PHP Sürüm Durumu ====="
for version in 5.6 7.4 8.0 8.1 8.2 8.3; do
if systemctl is-active --quiet php${version}-fpm 2>/dev/null; then
echo "PHP ${version}-FPM: AKTIF"
echo " Yüklü eklentiler: $(php${version} -m 2>/dev/null | wc -l) adet"
fi
done
echo ""
echo "===== Socket Dosyaları ====="
ls -la /run/php/*.sock 2>/dev/null
echo ""
echo "===== Nginx Site Yapılandırmaları ====="
grep -h "fastcgi_pass" /etc/nginx/sites-enabled/*.conf 2>/dev/null | sort | uniq
Sık Karşılaşılan Sorunlar ve Çözümleri
502 Bad Gateway hatası: Bu genellikle PHP-FPM servisinin çalışmadığı veya socket dosyasının olmadığı anlamına gelir. Önce systemctl status php8.1-fpm ile servis durumunu kontrol edin. Ardından /run/php/ dizininde ilgili socket dosyasının var olduğunu doğrulayın.
Permission denied hatası: Nginx veya Apache’nin socket dosyasına erişemediği durumlarda bu hata alınır. FPM pool yapılandırmasındaki listen.owner ve listen.group değerlerinin web sunucusunun çalıştığı kullanıcıyla eşleşmesi gerekir.
Yanlış PHP sürümü çalışıyor: phpinfo() çıktısı beklediğinizden farklı bir sürüm gösteriyorsa Nginx veya Apache yapılandırmanızda socket yolunu yanlış yazmış olabilirsiniz. Yapılandırma dosyasındaki fastcgi_pass ya da SetHandler direktifini kontrol edin.
OPcache çakışmaları: Her PHP sürümünün kendi OPcache’i vardır, bu yüzden normalde sorun olmaz. Ama OPcache yapılandırmasını her sürümün php.ini dosyasında ayrı ayrı yapmanız gerekir. Dosya yolları: /etc/php/8.1/fpm/php.ini ve /etc/php/8.1/cli/php.ini
Sonuç
Birden fazla PHP sürümünü aynı sunucuda çalıştırmak başlangıçta karmaşık görünse de doğru yapılandırıldığında son derece kararlı ve yönetilebilir bir ortam oluyor. Temel prensip şu: her PHP sürümü için bağımsız FPM servisi çalıştırıyorsunuz, her uygulama kendi pool’una yönlendiriliyor ve web sunucusu doğru socket’i kullanıyor.
Bu yapının en büyük avantajlarından biri de kademeli migration imkanı. Eski bir uygulamayı yeni PHP sürümüne geçirmek istediğinizde önce uygulamayı yeni sürümde test edebilir, her şey yolunda giderse sadece Nginx yapılandırmasındaki socket yolunu değiştirerek geçişi tamamlayabilirsiniz. Geri dönmek gerekirse aynı işlemi tersine yapıyorsunuz, bu kadar basit.
Uzun vadede kullanılmayan PHP sürümlerini sunucudan kaldırmayı ihmal etmeyin. Aktif olarak kullanılmayan ama çalışmaya devam eden FPM process’leri gereksiz kaynak tüketir. Hangi uygulamanın hangi sürümü kullandığını bir belgede ya da basit bir script ile takip etmek, ilerleyen zamanlarda size çok vakit kazandıracak.
