Server produksi menjalankan aplikasi Laravel melalui beberapa proses yang bekerja berdampingan, yaitu web server, kumpulan proses PHP, worker antrian, dan daemon cron. Topik ini membahas arsitektur Nginx dan PHP-FPM beserta pembagian tugas keduanya, konfigurasi worker antrian di bawah proses supervisor, serta entri cron yang menggerakkan scheduler pada server. Setelah topik ini dibaca, komponen yang harus berjalan pada satu server aplikasi dapat diinventarisasi lengkap dengan berkas konfigurasinya.
Arsitektur Nginx dan PHP-FPM#
Nginx dan PHP-FPM membagi penanganan satu permintaan web menjadi dua tanggung jawab yang berbeda. Nginx adalah web server berbasis event yang menerima koneksi HTTP, melayani berkas statis seperti CSS dan gambar secara langsung, lalu meneruskan permintaan PHP melalui protokol FastCGI. PHP-FPM, singkatan dari FastCGI Process Manager, mengelola kumpulan proses PHP yang siap mengeksekusi skrip. Pemisahan ini membuat proses PHP tidak pernah melayani jaringan secara langsung, sehingga permukaan serangan lebih kecil daripada model PHP yang tertanam pada web server.
Di dalam PHP-FPM, satu proses utama (master) mengatur kumpulan proses anak yang benar-benar mengeksekusi kode. Proses utama membaca konfigurasi pool pada berkas www.conf, kemudian membuka soket Unix tempat Nginx mengirim permintaan. Setiap proses anak menangani satu permintaan pada satu waktu, dan jumlahnya dibatasi oleh parameter pm beserta turunannya.
pm = dynamic
pm.max_children = 10
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 4Nilai pm.max_children menjadi batas atas seberapa banyak permintaan PHP dapat diproses bersamaan. Permintaan yang melebihi batas tersebut menunggu di antrian Nginx, sehingga angkanya perlu disesuaikan dengan memori server dan kebutuhan aplikasi. Diagram berikut merangkum seluruh proses yang terlibat pada satu server produksi aplikasi catatan, termasuk dua proses yang dibahas pada bagian akhir topik.
/etc/nginx/sites-available/, kemudian dihubungkan ke sites-enabled melalui symlink agar konfigurasinya aktif. Berkas berikut adalah konfigurasi utuh untuk aplikasi catatan pada server produksi. Isinya mencakup empat hal, yaitu penentuan dokumen root, penerusan permintaan biasa, penerusan PHP, dan penolakan berkas tersembunyi.
# tidak-dijalankan
# /etc/nginx/sites-available/aplikasi-catatan
server {
listen 80;
server_name catatan.example.test;
# dokumen root menunjuk ke subfolder public, bukan ke root proyek
root /var/www/aplikasi-catatan/public;
index index.php;
# berkas statis dilayani Nginx, sisanya diteruskan ke front controller
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# permintaan PHP diteruskan ke PHP-FPM melalui soket unix
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
# berkas tersembunyi seperti .env tidak boleh dilayani
location ~ /\.(?!well-known).* {
deny all;
}
}Direktif root menunjuk ke subdirektori public karena direktori itulah satu-satunya bagian proyek yang dapat diakses dari web. Blok location / menerapkan pola front controller, yaitu permintaan yang tidak cocok dengan berkas statis diteruskan ke index.php. Blok location ~ \.php$ menyerahkan eksekusi PHP ke soket php8.3-fpm.sock, sedangkan blok terakhir menolak berkas tersembunyi. Ketiga blok tersebut diperiksa Nginx sesuai bentuk permintaan yang datang.
Worker antrian sebagai proses permanen#
Worker antrian tidak terikat pada siklus permintaan web, sehingga prosesnya harus dijalankan terpisah dari Nginx dan PHP-FPM. Perintah queue:work berperilaku sebagai daemon, yaitu proses yang berjalan terus-menerus dan memantau media antrian, sebagaimana telah dipraktikkan pada topik Worker. Pada server produksi, worker tidak dijalankan langsung dari terminal, melainkan diserahkan kepada supervisor yang mengawasi proses tersebut dan memulainya ulang secara otomatis. Berkas konfigurasi supervisor berikut mendefinisikan program bernama laravel-worker untuk aplikasi catatan.
# tidak-dijalankan
# /etc/supervisor/conf.d/laravel-worker.conf
[program:laravel-worker]
command=php /var/www/aplikasi-catatan/artisan queue:work --sleep=3 --tries=3
numprocs=2
autostart=true
autorestart=true
stopwaitsecs=3600
user=www-data
stdout_logfile=/var/log/supervisor/worker.log
redirect_stderr=trueDirektif numprocs=2 memulai dua proses worker sehingga dua job dapat diproses bersamaan, sedangkan autorestart=true memulai ulang worker yang berhenti karena error maupun karena menerima sinyal. Nilai stopwaitsecs=3600 memberi waktu satu jam bagi worker untuk menyelesaikan job yang sedang berjalan sebelum prosesnya dihentikan secara paksa, sehingga ekspor yang berdurasi panjang tidak terputus di tengah eksekusi. Setelah berkas konfigurasi ditempatkan, konfigurasi dimuat dengan supervisorctl reread dan supervisorctl update, lalu status semua proses dipantau melalui supervisorctl status.
Sebelum worker dijalankan pada server, lapisan antrian pada aplikasi harus sudah benar. Koneksi antrian, keberadaan tabel jobs, dan batas waktu retry_after dapat diperiksa langsung pada proyek aplikasi catatan. Pemeriksaan ini meniru langkah yang dilakukan supervisor ketika memulai worker, yaitu memastikan media antrian tersedia lebih dahulu.
# memeriksa lapisan antrian yang dipakai worker
cd ~/aplikasi-catatan
php artisan tinker --execute='
use Illuminate\Support\Facades\Schema;
echo "Koneksi antrian: " . config("queue.default") . PHP_EOL;
echo "Tabel jobs ada: " . (Schema::hasTable("jobs") ? "ya" : "tidak") . PHP_EOL;
echo "retry_after: " . config("queue.connections.database.retry_after") . PHP_EOL;
'Koneksi antrian: database
Tabel jobs ada: ya
retry_after: 90Keluaran tersebut menunjukkan koneksi database, yang berarti media antriannya adalah tabel jobs pada database yang sama dengan aplikasi. Nilai retry_after sebesar 90 detik menentukan berapa lama sebuah job boleh dipegang worker sebelum dianggap hilang dan dikembalikan ke antrian. Nilai ini harus selalu lebih besar daripada durasi job terlama, karena jika tidak, satu job dapat diproses dua kali oleh dua worker sekaligus.
Entri cron pada server#
Daemon cron adalah proses bawaan sistem Linux yang mengeksekusi perintah pada waktu terjadwal. Scheduler Laravel memanfaatkan daemon ini melalui satu entri cron yang memanggil php artisan schedule:run setiap menit, sedangkan definisi frekuensinya ditulis pada routes/console.php sebagaimana dibahas pada topik Penjadwalan tugas. Server produksi hanya perlu entri berikut, yang diedit melalui perintah crontab -e.
* * * * * cd /var/www/aplikasi-catatan && php artisan schedule:run >> /dev/null 2>&1Jalur /var/www/aplikasi-catatan pada contoh mengikuti konvensi direktori pemasangan produksi, sedangkan pada lingkungan uji jalurnya adalah ~/aplikasi-catatan. Rangkaian >> /dev/null 2>&1 membuang seluruh keluaran perintah agar cron tidak mengirim surel berisi hasil setiap menit. Berbeda dengan worker yang diawasi supervisor, entri cron ini tidak perlu digandakan, karena schedule:run sendiri menentukan tugas mana yang jatuh tempo pada menit tersebut.
Keandalan entri cron bergantung pada daemon crond yang berjalan di server. Statusnya diperiksa melalui systemctl status cron pada sistem berbasis systemd, dan apabila daemon berhenti, seluruh tugas terjadwal ikut berhenti tanpa pesan error apa pun pada aplikasi. Karena itu, daftar proses yang harus berjalan di server bertambah satu, yaitu cron di samping Nginx, PHP-FPM, dan worker antrian.
Kesalahan umum#
Kesalahan pertama adalah menjalankan worker langsung dari terminal dengan php artisan queue:work tanpa supervisor. Proses yang dimulai dari sesi terminal akan berhenti ketika sesi berakhir atau ketika server dimulai ulang, dan tidak ada mekanisme apa pun yang memulainya kembali. Antrian pun menumpuk tanpa terdeteksi. Perbaikannya adalah menempatkan worker di bawah supervisor dengan autostart=true dan autorestart=true sebagaimana pada berkas laravel-worker.conf di atas.
Kesalahan kedua adalah menganggap konfigurasi PHP CLI sama dengan konfigurasi PHP-FPM. Perintah php artisan berjalan pada PHP CLI yang memuat berkas php.ini tersendiri, sedangkan permintaan web dieksekusi proses FPM dengan berkas php.ini yang berbeda. Ekstensi yang sudah dimuat oleh CLI belum tentu dimuat oleh FPM, sehingga aplikasi dapat terlihat sehat di terminal tetapi error ketika diakses melalui browser. Bandingkan ekstensi yang terpasang pada kedua sisi dengan php --ini dan php -m sebelum rilis.
Kesalahan ketiga adalah memuat ulang layanan yang salah setelah menyunting konfigurasi. Perubahan pada default.conf dimuat Nginx melalui systemctl reload nginx, sedangkan perubahan pada www.conf hanya berlaku setelah systemctl restart php8.3-fpm. Memuat ulang Nginx setelah menyunting konfigurasi pool tidak memberikan efek apa pun, sehingga pengaturan lama tetap berlaku.
Ringkasan#
- Nginx melayani permintaan HTTP dan berkas statis, lalu meneruskan PHP melalui FastCGI ke pool PHP-FPM dengan dokumen root yang menunjuk ke direktori
public. - Worker antrian berjalan sebagai daemon di bawah supervisor, dengan jumlah prosesnya diatur melalui
numprocsdan pemulihan otomatis melaluiautorestart. - Satu entri cron memanggil
schedule:runsetiap menit, sehingga seluruh jadwal aplikasi cukup didefinisikan sekali padaroutes/console.php. - Server produksi yang sehat menjalankan Nginx, pool PHP-FPM, worker antrian, dan daemon cron secara bersamaan.
Referensi#
- Deployment — Laravel — Panduan resmi penyiapan server produksi, termasuk Nginx, PHP-FPM, supervisor, dan cron.
- FastCGI Process Manager (FPM) — PHP — Dokumentasi mekanisme FPM beserta konsep proses utama dan pool worker.
- Queues — Laravel
— Dokumentasi perintah
queue:workbeserta opsi yang dipakai pada konfigurasi supervisor. - nginx documentation — Nginx
— Referensi direktif
location,try_files, danfastcgi_passpada berkas konfigurasi.