PHP va Laravel — PHP-FPM, Nginx, Composer

~20 daq · Docker · Real loyihalar

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'sha RUN da o'chiriladi (14-dars)
  • composer install → keyin COPY . . — 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 — faqat public/ 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) — faqat command farq qiladi (27-dars)
  • Nginx ga public/ papkasi kerak (statik fayllar). Production da yaxshiroq — Nginx uchun ham public/ 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 — vaqtinchalik
  • COPY --from=composer:2 — Composer
  • composer install → keyin COPY . .; --no-dev, --optimize
  • php.ini-production, OPcache (validate_timestamps=0)
  • Nginx root — faqat public/
  • Bitta image — php, queue, scheduler xizmatlari
  • Laravel: LOG_CHANNEL=stderr, DB_HOST=mysql
  • Tez boshlash — Sail; zamonaviy — FrankenPHP

Keyingi dars

35-dars: Python — FastAPI / Django konteynerda