Настройка связки Nginx и PHP в контейнерах Docker

Раздел: Администрирование веб-серверов -> Docker для веб-сервера

В данном материале рассмотрены способы организации работы веб-сервера на базе Nginx и PHP в среде Docker. Основное внимание уделено эффективной связке двух контейнеров, а также альтернативные подходы. Каждый вариант сопровождается пояснениями и разбором возможных трудностей.

Основное решение: связка двух контейнеров через Docker Compose

Как настроить Nginx и PHP-FPM в отдельных контейнерах, объединённых одной сетью?

Данный подход считается наиболее гибким и масштабируемым. Nginx выступает в роли фронтенда, передавая запросы на PHP-FPM контейнер. Для управления несколькими контейнерами используется Docker Compose.


version: '3.8'
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
  php:
    image: php:8.2-fpm
    volumes:
      - ./html:/var/www/html

Docker nginx php (docker для nginx и php)

Файл nginx/default.conf содержит настройки сервера:


server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html;
    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

При запуске docker-compose up -d оба контейнера стартуют в одной сети и могут обмениваться данными через имя сервиса (php).

Типичная проблема 502 Bad Gateway возникает, если PHP-FPM не отвечает или неправильно указан адрес. Решение: проверить, что контейнер php запущен и слушает порт 9000. Команда docker logs php покажет ошибки. Другая причина – неправильный SCRIPT_FILENAME. Нужно убедиться, что корневая директория совпадает с монтированным томом.

Проблема прав доступа: файлы внутри контейнера могут принадлежать root. Для корректной работы PHP рекомендуется установить пользователя и группу в файле конфигурации PHP-FPM (www.conf).

Вариант 1: Единый контейнер с супервизором

Как объединить Nginx и PHP-FPM в одном контейнере? Для управления двумя процессами используется supervisord. Подход удобен для простых сценариев, но менее гибкий и не соответствует принципам микросервисов.


FROM ubuntu:22.04
RUN apt-get update && apt-get install -y nginx php-fpm supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

Файл supervisord.conf:


[program:nginx]
command = /usr/sbin/nginx -g "daemon off;"
[program:php-fpm]
command = /usr/sbin/php-fpm -F

Проблема: управление логами и перезапуск процессов усложняется. При сбое одного процесса контейнер может продолжать работу в нерабочем состоянии. Рекомендуется использование healthcheck для мониторинга.

Вариант 2: Использование Alpine образов для уменьшения размера

Как минимизировать размер итогового образа? Применение официальных Alpine образов nginx:alpine и php:8.2-fpm-alpine сокращает объём загрузки. Пример docker-compose с Alpine:


services:
  nginx:
    image: nginx:alpine
  php:
    image: php:8.2-fpm-alpine
    # Дополнительная установка расширений через Dockerfile

Alpine использует musl вместо glibc, что может приводить к несовместимости некоторых PHP-расширений, требующих glibc. Решение: установить необходимые пакеты через apk, либо использовать образ на основе Debian.

Вариант 3: Использование Unix-сокета вместо TCP

Как повысить производительность на одной машине? PHP-FPM может слушать Unix-сокет, а не TCP-порт. В docker-compose контейнеры обмениваются через общий том с сокетом.


# php/Dockerfile
RUN mkdir /run/php
# php-fpm.conf
listen = /run/php/php8.2-fpm.sock
# nginx default.conf
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;

Том должен быть общим:


volumes:
  - ./php-socket:/run/php

Права доступа к сокету: Nginx должен иметь право на чтение сокета. Обычно группа www-data или 777. Необходимо настроить пользователя в php-fpm.

Вариант 4: Многосайтовая конфигурация с отдельными хостами

Как обслуживать несколько сайтов на одном экземпляре Nginx? Используется конфигурация виртуальных хостов. В Docker Compose можно смонтировать несколько конфигов.


volumes:
  - ./nginx/sites-enabled:/etc/nginx/conf.d

Каждый файл конфигурации определяет свой server_name.

Ошибка 404 при обращении к другим сайтам: убедиться, что файлы сайтов доступны в монтируемых томах.

Расширенные примеры

Дополнительные сценарии использования Docker с Nginx и PHP.

Dockerfile для PHP с расширениями и кастомной конфигурацией

Пример

FROM php:8.2-fpm-alpine
RUN apk add --no-cache \
    freetype-dev \
    libjpeg-turbo-dev \
    libpng-dev \
    libzip-dev \
    zip \
    unzip \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd pdo_mysql zip \
    && docker-php-ext-enable opcache
COPY ./php.ini /usr/local/etc/php/conf.d/custom.ini

Пример php.ini:

Пример

upload_max_filesize = 20M
post_max_size = 20M
memory_limit = 256M
opcache.enable=1
opcache.memory_consumption=128

Результат: образ с готовым набором расширений и оптимизированной производительностью.

Многоконтейнерное приложение с Nginx, PHP и MySQL через Docker Compose

Пример

version: '3.8'
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./app:/var/www/html
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
  php:
    build: ./php
    volumes:
      - ./app:/var/www/html
    environment:
      - DB_HOST=mysql
      - DB_USER=app
      - DB_PASS=secret
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: app
      MYSQL_USER: app
      MYSQL_PASSWORD: secret
    volumes:
      - mysql_data:/var/lib/mysql
volumes:
  mysql_data:

Запуск: docker-compose up -d. Приложение может использовать переменные окружения для подключения к MySQL.

Настройка healthcheck для PHP-FPM контейнера

Пример

healthcheck:
  test: ["CMD", "php-fpm-healthcheck"]
  interval: 30s
  timeout: 10s
  retries: 3

Для этого требуется утилита php-fpm-healthcheck, которую можно установить через Dockerfile:

Пример

RUN git clone https://github.com/renatomefi/php-fpm-healthcheck.git /usr/local/bin/php-fpm-healthcheck

Healthcheck позволяет автоматически перезапускать контейнер при обнаружении неработоспособности.

Использование Docker secrets для хранения паролей

Пример

services:
  php:
    secrets:
      - db_password
secrets:
  db_password:
    file: ./secrets/db_password.txt

В PHP пароль можно получить через чтение файла /run/secrets/db_password.

Настройка Nginx как reverse proxy для нескольких PHP-сервисов

Пример конфигурации для балансировки нагрузки:

Пример

upstream php_backend {
    server php1:9000;
    server php2:9000;
}
server {
    location ~ \.php$ {
        fastcgi_pass php_backend;
    }
}

Такой подход используется для горизонтального масштабирования PHP-обработчиков.

Объединение логов Nginx и PHP в общий volume

Пример

volumes:
  - ./logs/nginx:/var/log/nginx
  - ./logs/php:/var/log/php

В конфиге PHP (www.conf) нужно указать путь к логу:

Пример

access.log = /var/log/php/access.log
error.log = /var/log/php/error.log
После запуска в ./logs появятся файлы access.log и error.log.

Docker для Nginx и PHP - comments

En
Docker nginx php (php)