PHP va Laravel — PHP-FPM, Nginx, Composer
Muammodan boshlaylik
PHP loyihasi Node.js dan farq qiladi: PHP o'zi HTTP serveri emas (php -S — faqat ishlab chiqish uchun, PHP kursi 2-dars). Production da odatda ikki qism:
- Nginx — HTTP so'rovlarni qabul qiladi, statik fayllarni (CSS, JS, rasmlar) beradi
- PHP-FPM — PHP kodini bajaradi
Plus: Composer, PHP kengaytmalari (pdo_mysql, gd, intl), Laravel ning storage papkasi va navbat ishlovchilari. Ularni konteynerlarga qanday taqsimlash kerak?
Arxitektura
Brauzer → [ nginx:80 ] ── .php so'rovlar ──→ [ php-fpm:9000 ] → [ mysql ]
│ │
└── statik fayllar (public/) ←───────┘ umumiy kod
Ikki konteyner, bitta kod. Nginx public/ dan statik fayllarni o'zi beradi, .php so'rovlarni FastCGI orqali PHP-FPM ga yuboradi.
Dockerfile — PHP-FPM
# syntax=docker/dockerfile:1
FROM php:8.4-fpm-alpine AS base
# PHP kengaytmalari
RUN apk add --no-cache icu-libs libzip libpng \
&& apk add --no-cache --virtual .build-deps $PHPIZE_DEPS icu-dev libzip-dev libpng-dev \
&& docker-php-ext-install pdo_mysql intl zip gd opcache bcmath \
&& apk del .build-deps
# Composer — rasmiy image dan bitta fayl (16-dars)
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
# ===== dev =====
FROM base AS dev
RUN cp "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
CMD ["php-fpm"]
# ===== production =====
FROM base AS production
RUN cp "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY docker/php/opcache.ini "$PHP_INI_DIR/conf.d/opcache.ini"
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist --no-interaction
COPY . .
RUN composer dump-autoload --optimize --classmap-authoritative \
&& chown -R www-data:www-data storage bootstrap/cache
USER www-data
CMD ["php-fpm"]
docker-php-ext-install— rasmiy PHP image ning kengaytma o'rnatish vositasi.build-deps— kompilyatsiya uchun vaqtinchalik paketlar, o'shaRUNda o'chiriladi (14-dars)composer install→ keyinCOPY . .— kesh tartibi (14-dars)php.ini-production—display_errors=Off(PHP kursi 27-dars)- OPcache — kompilyatsiya qilingan PHP kodini xotirada saqlash, production da 2-3 barobar tezroq
docker/php/opcache.ini:
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0
validate_timestamps=0 — fayllar o'zgarishini tekshirmaslik (production da kod o'zgarmaydi — image ichida).
Nginx konfiguratsiyasi
docker/nginx/default.conf:
server {
listen 80;
root /var/www/html/public;
index index.php;
client_max_body_size 20M;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known) {
deny all;
}
}
root .../public— faqatpublic/ochiq;.env,vendor/,app/— veb orqali yetib bo'lmaydi (PHP kursi 33-dars)try_files ... /index.php— Laravel routing (front controller)fastcgi_pass php:9000—php— Compose xizmat nomi (24-dars)
compose.yaml
name: laravel-dokon
services:
php:
build:
context: .
target: production
restart: unless-stopped
env_file: .env
volumes:
- storage:/var/www/html/storage/app
depends_on:
mysql:
condition: service_healthy
nginx:
image: nginx:1.27-alpine
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./public:/var/www/html/public:ro
depends_on:
- php
queue:
build:
context: .
target: production
command: ["php", "artisan", "queue:work", "--tries=3", "--max-time=3600"]
restart: unless-stopped
env_file: .env
depends_on: [mysql, redis]
scheduler:
build:
context: .
target: production
command: ["php", "artisan", "schedule:work"]
restart: unless-stopped
env_file: .env
mysql:
image: mysql:8.4
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?}
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "--silent"]
interval: 5s
retries: 20
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
mysql-data:
storage:
- Bitta image — to'rt xizmat:
php(veb),queue(navbat),scheduler(cron o'rniga) — faqatcommandfarq qiladi (27-dars) - Nginx ga
public/papkasi kerak (statik fayllar). Production da yaxshiroq — Nginx uchun hampublic/ni o'z ichiga olgan alohida image yasash storage— foydalanuvchi yuklagan fayllar uchun volume
Laravel .env da: DB_HOST=mysql, REDIS_HOST=redis, CACHE_STORE=redis, QUEUE_CONNECTION=redis, LOG_CHANNEL=stderr (loglar konsolga!).
Artisan buyruqlari
docker compose exec php php artisan migrate --force
docker compose exec php php artisan config:cache
docker compose exec php php artisan route:cache
docker compose exec php php artisan tinker
Tayyor yechimlar
- Laravel Sail — Laravel ning rasmiy Docker muhiti (ishlab chiqish uchun):
php artisan sail:install - FrankenPHP — PHP ni o'z ichiga olgan zamonaviy veb-server: bitta konteyner, Nginx + FPM siz, Laravel Octane bilan
- serversideup/php — production uchun tayyor sozlangan PHP image lar
Ishlab chiqish uchun Sail — eng tez boshlash. Lekin ichida nima borligini tushunish uchun bu darsdagi tuzilmani bilish foydali.
Nima uchun shunday?
Nima uchun PHP uchun ikki konteyner (Nginx + PHP-FPM), Node.js uchun esa bitta?
Node.js — o'zi doimiy ishlaydigan HTTP server: so'rovlarni qabul qiladi, javob beradi. PHP esa tarixan har bir so'rov uchun skriptni ishga tushiradigan modelda ishlaydi (PHP kursi 1-dars): kimdir HTTP ni qabul qilib, PHP ga "shu faylni bajar" deyishi kerak. PHP-FPM — PHP jarayonlari hovuzi, Nginx — HTTP bilan ishlashda va statik fayllarni berishda juda samarali. Har biri o'z ishini eng yaxshi qiladi. FrankenPHP kabi yangi yechimlar bu ikkalasini bitta jarayonga birlashtiradi.
Xulosa
Eslab qoling:
- PHP: Nginx (HTTP, statik) + PHP-FPM (PHP kodi) — FastCGI orqali,
php:9000 docker-php-ext-install— kengaytmalar;.build-deps— vaqtinchalikCOPY --from=composer:2— Composercomposer install→ keyinCOPY . .;--no-dev,--optimizephp.ini-production, OPcache (validate_timestamps=0)- Nginx
root— faqatpublic/ - Bitta image —
php,queue,schedulerxizmatlari - Laravel:
LOG_CHANNEL=stderr,DB_HOST=mysql - Tez boshlash — Sail; zamonaviy — FrankenPHP
Keyingi dars
35-dars: Python — FastAPI / Django konteynerda