L
Docker • Literally Web ID

Dockerizing PHP-FPM dan Nginx untuk Skala Produksi Skala Tinggi

⏱️ 18 menit baca • 📅 19 Oct 2026 • 👁️ 5 views
Dockerizing PHP-FPM dan Nginx untuk Skala Produksi Skala Tinggi

1. Konteks Industri & Analisis Akar Masalah

Dalam lanskap pengembangan web modern, transisi menuju arsitektur berbasis kontainer telah menjadi standar industri. Namun, menjalankan aplikasi PHP-FPM dan Nginx di lingkungan produksi berskala tinggi sering kali menemui berbagai tantangan fundamental yang jika diabaikan dapat mengakibatkan degradasi performa, kebocoran memori (memory leak), hingga kerentanan keamanan yang fatal.

Secara historis, banyak pengembang menjalankan Nginx dan PHP-FPM dalam satu kontainer tunggal (monolithic container) dengan menggunakan supervisor atau init system kustom. Pendekatan ini melanggar filosofi dasar Docker di mana satu kontainer harus menjalankan satu proses utama (single-process per container). Akibatnya, manajemen sinyal (SIGTERM, SIGQUIT), pemantauan kesehatan (health check), dan proses logging menjadi sangat rumit.

Bottleneck utama dalam performa PHP-FPM di lingkungan kontainer umumnya terletak pada:

  • Konfigurasi alokasi memori yang buruk yang memicu proses OOM (Out Of Memory) Killer pada sistem operasi host.
  • Penggunaan TCP loopback (127.0.0.1:9000) untuk komunikasi antara Nginx dan PHP-FPM yang membawa overhead jaringan yang tidak perlu, alih-alih menggunakan Unix Domain Socket.
  • Ukuran image Docker yang membengkak karena menyertakan seluruh tool build (seperti gcc, make, header development) di dalam production image.

Perbandingan Kondisi: Sebelum vs Sesudah Penerapan

Parameter Sebelum (Monolithic / Default Dockerfile) Sesudah (Multi-stage, Optimized Production Architecture)
Ukuran Image Docker 850MB - 1.5GB 120MB - 250MB
Komunikasi Nginx-PHP TCP Port (Latensi jaringan, overhead port allocation) Unix Domain Socket (Zero overhead, latensi minimal)
Manajemen Proses Supervisor (Sering gagal meneruskan sinyal SIGTERM) Native PID 1 handling, graceful shutdown sempurna
Keamanan Menjalankan proses sebagai root Non-root user terisolasi (www-data)
Penggunaan RAM Boros, sering terjadi memory leak tanpa daur ulang worker Terkontrol ketat melalui pm.max_requests dan pm.max_children

2. Konsep Inti & Deep Dive Arsitektur

Arsitektur produksi berkinerja tinggi untuk PHP-FPM dan Nginx memisahkan kedua komponen ini ke dalam dua kontainer terpisah yang berjalan dalam satu pod atau dihubungkan melalui Docker Compose/Kubernetes Network, namun berbagi volume khusus untuk komunikasi Unix Domain Socket.

Alur Eksekusi Permintaan (Request Lifecycle)

[ Client / Browser ]
       │
       ▼ (HTTPS / HTTP/2)
[ Nginx Reverse Proxy Container ]
       │
       ├─► Static Files (.css, .js, .png) ──► Served Directly from Disk
       │
       └─► Dynamic Request (.php) ──► [ Unix Domain Socket: /var/run/php/php-fpm.sock ]
                                              │
                                              ▼
                                   [ PHP-FPM Pool Container ]
                                              │
                                              ▼
                                   [ OPcache & Zend Engine ]

Pada arsitektur di atas, Nginx bertindak sebagai front-end web server yang menangani SSL termination, caching aset statis, dan reverse proxy untuk permintaan dinamis. Ketika Nginx menerima permintaan .php, ia meneruskannya bukan melalui jaringan TCP, melainkan melalui file socket Unix (/var/run/php/php-fpm.sock) yang di-mount menggunakan Docker volume bersama (shared volume). Hal ini memangkas overhead TCP handshake dan meningkatkan throughput hingga 15-20% pada beban tinggi.


3. Perbandingan Komparatif / Analisis Pendekatan

Untuk menghubungkan Nginx dan PHP-FPM di dalam Docker, terdapat dua pendekatan utama yang sering diperdebatkan:

  1. Pendekatan A: TCP Loopback (127.0.0.1:9000 atau php-fpm:9000)

    • Kelebihan: Sangat mudah diskalakan ke berbagai host berbeda (distributed architecture), mudah di-debug menggunakan telnet atau nc.
    • Kekurangan: Memiliki overhead jaringan TCP/IP stack meskipun berjalan di host yang sama; rentan terhadap kehabisan port ephemeral pada beban ultra-tinggi.
  2. Pendekatan B: Unix Domain Socket (/var/run/php/php-fpm.sock)

    • Kelebihan: Performa tertinggi, latensi terendah, tidak memakan port jaringan, sangat aman karena terisolasi pada filesystem lokal.
    • Kekurangan: Nginx dan PHP-FPM wajib berada di dalam host yang sama (atau berbagi volume mount jika menggunakan kontainer terpisah, yang membutuhkan konfigurasi hak akses user/group yang presisi).

[!TIP] Untuk arsitektur kontainer tunggal berbasis sidecar atau multi-container pod (seperti Kubernetes), Unix Domain Socket adalah pilihan mutlak terbaik untuk performa maksimal.


4. Prasyarat Sistem & Persiapan Lingkungan

Sebelum menyusun konfigurasi, pastikan lingkungan host Anda memenuhi spesifikasi berikut:

  • Docker Engine: Versi 24.0+ dengan dukungan BuildKit aktif.
  • Docker Compose: Versi 2.20+.
  • Sistem Operasi Host: Linux Ubuntu 22.04 LTS / Debian 12 (Disarankan kernel Linux 5.15+).
  • Resource Minimum: 2 vCPU, 4GB RAM.

Struktur direktori proyek yang akan kita bangun adalah sebagai berikut:

├── docker/
│   ├── nginx/
│   │   ├── Dockerfile
│   │   └── conf.d/
│   │       └── default.conf
│   └── php/
│       ├── Dockerfile
│       ├── conf.d/
│       │   ├── opcache.ini
│       │   └── custom.ini
│       └── www.conf
├── html/
│   └── index.php
└── docker-compose.yml

5. Panduan Implementasi Langkah demi Langkah

1. PHP-FPM Dockerfile (Multi-stage Build)

Buat file docker/php/Dockerfile:

# Stage 1: Build dependencies and extensions
FROM php:8.3-fpm-alpine AS builder

# Install system dependencies and build tools
RUN apk add --no-cache \
    git \
    curl \
    libpng-dev \
    libjpeg-turbo-dev \
    freetype-dev \
    libzip-dev \
    icu-dev \
    oniguruma-dev \
    autoconf \
    g++ \
    make

# Install PHP extensions
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) \
        gd \
        pdo_mysql \
        zip \
        intl \
        opcache \
        bcmath \
        mbstring

# Stage 2: Production image
FROM php:8.3-fpm-alpine AS production

# Install runtime dependencies only
RUN apk add --no-cache \
    libpng \
    libjpeg-turbo \
    freetype \
    libzip \
    icu \
    oniguruma \
    bash \
    fcgi

# Copy compiled extensions from builder stage
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/

# Create system user and group to match host permissions
ARG PUID=1000
ARG PGID=1000
RUN apk --no-cache add shadow && \
    usermod -u ${PUID} www-data && \
    groupmod -g ${PGID} www-data

# Set working directory
WORKDIR /var/www/html

# Expose port (not strictly needed for socket, but good practice)
EXPOSE 9000

CMD ["php-fpm"]

2. Nginx Dockerfile

Buat file docker/nginx/Dockerfile:

FROM nginx:1.25-alpine

# Remove default configuration
RUN rm /etc/nginx/conf.d/default.conf

# Create non-root directories if required, though nginx master runs as root and workers as nginx
WORKDIR /var/www/html

EXPOSE 80 443

CMD ["nginx", "-g", "daemon off;"]

3. Konfigurasi PHP-FPM Pool (www.conf)

Buat file docker/php/www.conf:

[www]
user = www-data
group = www-data

listen = /var/run/php/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500

clear_env = no

; Ensure worker stdout and stderr are sent to the main error log
catch_workers_output = yes
decorate_workers_output = no

4. Konfigurasi OPcache (opcache.ini)

Buat file docker/php/conf.d/opcache.ini:

[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.revalidate_freq=0
opcache.validate_timestamps=0
opcache.save_comments=1
opcache.fast_shutdown=1

[!WARNING] opcache.validate_timestamps=0 sangat ideal untuk lingkungan produksi demi performa maksimal, namun mengharuskan Anda melakukan rebuild atau flush cache setiap kali kode PHP diperbarui.

5. Konfigurasi Virtual Host Nginx (default.conf)

Buat file docker/nginx/conf.d/default.conf:

server {
    listen 80;
    server_name localhost;
    root /var/www/html;

    index index.php index.html index.htm;

    charset utf-8;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Content-Type-Options "nosniff" always;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # Handle PHP execution via Unix Socket
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/var/run/php/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        
        # Timeouts for high concurrency
        fastcgi_read_timeout 300;
        fastcgi_connect_timeout 60;
        fastcgi_send_timeout 300;
    }

    # Deny access to hidden files (.ht*)
    location ~ /\.ht {
        deny all;
    }

    # Cache static assets
    location ~* \.(jpg|jpeg|gif|png|css|js|ico|webp|svg|woff|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, no-transform";
    }
}

6. File Orkestrasi docker-compose.yml

Buat file docker-compose.yml di root direktori:

version: '3.8'

services:
  php:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    container_name: production_php
    restart: always
    volumes:
      - .:/var/www/html
      - php_socket:/var/run/php
      - ./docker/php/www.conf:/usr/local/etc/php-fpm.d/www.conf
      - ./docker/php/conf.d/opcache.ini:/usr/local/etc/php/conf.d/opcache.ini
    networks:
      - app_net

  nginx:
    build:
      context: .
      dockerfile: docker/nginx/Dockerfile
    container_name: production_nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - .:/var/www/html
      - php_socket:/var/run/php
      - ./docker/nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - php
    networks:
      - app_net

volumes:
  php_socket:

networks:
  app_net:
    driver: bridge

6. Bedah Kode & Penjelasan Detail Baris Kunci

  • php_socket:/var/run/php (Docker Volume): Baris ini krusial. Karena Nginx dan PHP-FPM berjalan di kontainer terpisah, keduanya memerlukan direktori bersama di filesystem host untuk menulis dan membaca Unix Domain Socket (php-fpm.sock). Tanpa volume ini, Nginx tidak akan bisa menemukan socket PHP-FPM.
  • listen.mode = 0660 dan listen.owner = www-data: Pengaturan hak akses pada file socket memastikan bahwa proses Nginx yang berjalan dapat berinteraksi dengan proses PHP-FPM tanpa mengalami error Permission Denied (biasanya memunculkan kode error 502 Bad Gateway di Nginx).
  • pm = dynamic dengan kalkulasi pm.max_children: Formula umum menentukan pm.max_children adalah (Total RAM yang dialokasikan untuk PHP) / (Rata-rata konsumsi RAM per proses PHP-FPM). Jika setiap proses PHP memakan 30MB dan total RAM server untuk PHP adalah 1.5GB, maka 1500 / 30 = 50 anak proses.

7. Skenario Produksi & Studi Kasus Dunia Nyata

Pada sistem e-commerce berskala besar dengan ribuan transaksi per detik (TPS), lonjakan trafik (flash sale) dapat melumpuhkan server jika antrean PHP-FPM tidak dikelola dengan benar. Dengan menggunakan konfigurasi socket di atas yang dipadukan dengan Nginx worker process yang dioptimalkan (worker_processes auto; worker_connections 10240;), latensi rata-rata dapat ditekan di bawah 15 milidetik.

Selain itu, pemisahan kontainer memungkinkan tim infrastruktur melakukan rolling update pada kontainer Nginx atau PHP secara independen tanpa mematikan layanan secara keseluruhan, memastikan zero-downtime deployment tercapai.


8. Common Pitfalls, Anti-Patterns & Solusi

  1. Kesalahan 1: Menjalankan Kontainer sebagai Root
    • Dampak: Jika aplikasi web mengalami kerentanan RCE (Remote Code Execution), penyerang mendapatkan akses penuh ke root kontainer.
    • Solusi: Pastikan direktori aplikasi di-chown ke www-data dan jalankan proses dengan user non-root.
  2. Kesalahan 2: Mengabaikan pm.max_requests
    • Dampak: Terjadinya memory leak secara perlahan yang menyebabkan server kehabisan RAM setelah beberapa hari berjalan.
    • Solusi: Selalu set pm.max_requests = 500 (atau angka wajar lainnya) agar proses PHP-FPM didaur ulang secara berkala.
  3. Kesalahan 3: Menggunakan localhost:9000 di dalam Docker Compose
    • Dampak: Error koneksi karena localhost merujuk ke dalam kontainer Nginx itu sendiri, bukan ke kontainer PHP.
    • Solusi: Gunakan nama servis Docker (php:9000) jika menggunakan TCP, atau gunakan Unix Domain Socket melalui volume bersama.

9. Benchmark, Metrik Kinerja & Tuning Performa

Untuk menguji performa setup ini, gunakan utilitas beban seperti wrk:

wrk -t12 -c400 -d30s http://localhost/

Metrik utama yang harus dipantau meliputi:

  • Requests per Second (RPS): Target minimal 3000+ req/sec untuk aplikasi PHP dasar pada hardware standard.
  • Latency Distribution: Pastikan persentil ke-99 (p99) berada di bawah 50ms.
  • CPU & Memory Utilization: Pastikan penggunaan RAM stabil dan tidak merangkak naik secara kontinu (menandakan tidak adanya memory leak).

10. Checklist Penerapan Produksi & Kesimpulan

Sebelum merilis arsitektur ini ke lingkungan Live Production, pastikan checklist berikut terpenuhi:

  • Multi-stage build diterapkan untuk memperkecil ukuran image.
  • Komunikasi menggunakan Unix Domain Socket dengan permission 0660 yang aman.
  • OPcache diaktifkan dengan validate_timestamps=0 untuk performa maksimal.
  • Alokasi memori pm.max_children disesuaikan dengan kapasitas RAM server.
  • Security headers terpasang sempurna di konfigurasi Nginx.
  • Volume log diatur dengan rotasi log (logrotate) agar tidak menghabiskan disk space.

Dengan menerapkan seluruh panduan deep-dive ini, aplikasi PHP Anda akan berjalan di atas infrastruktur kontainer yang kokoh, aman, scalable, dan siap menghadapi beban trafik enterprise kelas tinggi.

🏷️ Tags: #Nginx #Devops #Infrastructure #Php-Fpm #Docker