Контейнеризация PHP с помощью Docker: от базового образа до production-сборки

Раздел: Контейнеризация PHP -> Docker образ

Основы работы с Docker образом PHP

Наиболее эффективный способ получить рабочий Docker образ PHP заключается в использовании официальных образов из Docker Hub. Они предоставляют множество вариантов: с разными версиями PHP, а также с различными базовыми ОС (Debian, Alpine, Ubuntu). Для типичного веб-приложения на PHP рекомендуется использовать образ на основе Alpine, так как он имеет минимальный размер и содержит только необходимые компоненты.

Как запустить простое PHP-приложение в Docker?

Для старта достаточно создать файл index.php с кодом:

<?php
echo 'Hello, Docker!';

Php docker image (docker образ php)

Затем написать Dockerfile, который копирует этот файл в образ и использует встроенный сервер PHP:

FROM php:8.2-cli-alpine
COPY index.php /app/
WORKDIR /app
CMD ["php", "-S", "0.0.0.0:8080"]

Сборка образа и запуск контейнера:

docker build -t my-php-app .
docker run -p 8080:8080 my-php-app

После этого приложение будет доступно по адресу http://localhost:8080. Такой подход подходит для быстрого тестирования и разработки.

Возможные проблемы:

  • Необходимость установки дополнительных расширений PHP (например, PDO, mbstring). Встроенный сервер не подходит для production из-за однопоточности.
  • Отсутствие передачи переменных окружения для конфигурации.
  • Некорректные права на файлы при монтировании томов (UID/GID контейнера отличается от хост-системы).

Решение:

Использовать официальные сборки с необходимыми расширениями через docker-php-ext-install. Для production применять готовый сервер, например, Nginx в связке с PHP-FPM, или включать расширение php-pecl для повышения производительности. Права настраивать через RUN adduser и USER в Dockerfile.

Как выбрать подходящую версию PHP и базовый образ?

Для выбора требуется учитывать требования приложения к версии PHP (7.4, 8.0, 8.1, 8.2, 8.3), необходимость в минимальном размере (Alpine) или полной среде (Debian). Пример Dockerfile для приложения, использующего PHP 8.1 с FPM:

FROM php:8.1-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql
COPY . /var/www/html

Как добавить расширения PHP в Docker образ?

Расширения устанавливаются с помощью команд docker-php-ext-install, docker-php-ext-configure и docker-php-ext-enable. Если расширение требует зависимости из ОС, их следует установить через apk add (для Alpine) или apt-get install (для Debian). Пример установки расширения gd:

FROM php:8.2-fpm-alpine
RUN apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && apk del freetype-dev libpng-dev libjpeg-turbo-dev

Типичные ошибки при установке расширений:

  • Отсутствие заголовочных файлов для компиляции (нужно устанавливать -dev пакеты).
  • Конфликт версий расширений (например, icu для intl).
  • Забытые apk del для удаления dev-пакетов, что увеличивает размер образа.

Решение:

Всегда устанавливать только необходимые dev-зависимости, компилировать расширение, затем удалять их. Использовать многоступенчатую сборку или специальные образы вроде mlocati/php-extension-installer для упрощения.

Как создать мультистейджевую сборку для продакшена?

Мультистейджевая сборка позволяет отделить этап компиляции расширений (с dev-зависимостями) от финального образа, что уменьшает его размер. Пример для приложения на Laravel:

# Первый этап: сборка зависимостей
FROM composer:latest AS composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

# Второй этап: PHP-FPM с Nginx
FROM php:8.2-fpm-alpine AS base
RUN docker-php-ext-install pdo pdo_mysql
COPY --from=composer /app/vendor /app/vendor
COPY . /app
RUN chown -R www-data:www-data /app/storage /app/bootstrap/cache

# Третий этап: Nginx
FROM nginx:alpine
COPY --from=base /app /app
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

Проблемы мультистейджевой сборки:

  • Ошибки копирования артефактов между стадиями (неверные пути).
  • Различия в библиотеках между стадиями (например, php:8.2-fpm-alpine и composer:latest могут иметь разные версии glibc).

Решение:

Использовать одинаковую базовую ОС для всех стадий, либо копировать только итоговый артефакт в образ, где установлены все runtime-зависимости.

Как работать с Composer в Docker?

Composer можно установить напрямую в образ или использовать официальный образ composer:latest как отдельный этап. Для разработки удобно монтировать директорию с хост-системы и выполнять composer install локально, для продакшена зависимости устанавливаются во время сборки. Пример Dockerfile для продакшена с предустановкой vendor:

FROM php:8.2-cli-alpine AS build
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader

FROM php:8.2-cli-alpine
COPY --from=build /app/vendor /app/vendor
COPY . /app

Частые проблемы с Composer:

  • Несоответствие версии PHP в образе Composer и целевом образе.
  • Потребность в авторизации для приватных пакетов (нужно передавать SSH-ключи или токены через build args).
  • Кэширование зависимостей на хост-машине может привести к устаревшим версиям.

Решение:

Использовать --no-cache и явно указывать версию PHP через конфигурацию Composer. Для приватных репозиториев применять --build-arg и удалять ключи после установки.

Как использовать образ PHP для CLI-скриптов?

Для выполнения консольных скриптов (миграции, воркеры очередей, cron) подходит образ на основе CLI. Пример Dockerfile для запуска artisan-команд в Laravel:

FROM php:8.2-cli-alpine
RUN docker-php-ext-install pcntl posix pdo_mysql
COPY . /app
WORKDIR /app
CMD ["php", "artisan", "queue:work"]

Запуск:

docker run -d --name worker my-php-worker

Потенциальные сложности:

  • Необходимость в расширении pcntl для работы с процессами (недоступно на Windows).
  • Проблемы с сигналами при остановке контейнера (нужно правильно обрабатывать SIGTERM).

Решение:

Устанавливать pcntl через docker-php-ext-install. В сценариях использовать exec для замены процесса, чтобы сигналы доходили до PHP.

Расширенные примеры сборки Docker образа PHP

В этом разделе представлены нестандартные сценарии использования Docker образа PHP с подробными примерами и результатами.

Пример 1. Образ PHP-FPM с Nginx и поддержкой PostgreSQL

Цель: запустить традиционный стек LEMP (Linux, Nginx, MySQL, PHP) на Docker. Вместо MySQL используем PostgreSQL.

Dockerfile для PHP:

Пример
FROM php:8.2-fpm-alpine
RUN apk add --no-cache postgresql-dev libpq-dev \
    && docker-php-ext-install pdo pdo_pgsql \
    && apk del postgresql-dev libpq-dev
COPY . /var/www/html

docker-compose.yml:

Пример
version: '3.8'
services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./src:/var/www/html
    depends_on:
      - php
  php:
    build: .
    volumes:
      - ./src:/var/www/html
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_NAME=app
      - DB_USER=user
      - DB_PASSWORD=pass
    depends_on:
      - postgres
  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=app
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:

Результат: после docker-compose up -d сайт доступен на localhost. PHP успешно подключается к PostgreSQL через PDO.

# Проверка подключения из PHP-контейнера:
docker exec -it php_container php -r "echo extension_loaded('pdo_pgsql') ? 'OK' : 'FAIL';"
# Вывод: OK

Пример 2. Установка Xdebug для отладки

Xdebug полезен при разработке. Его включение в Docker образ требует настройки как расширения, так и параметров IDE.

Dockerfile для разработки:

Пример
FROM php:8.2-fpm-alpine
RUN apk add --no-cache $PHPIZE_DEPS \
    && pecl install xdebug \
    && docker-php-ext-enable xdebug
COPY . /var/www/html

docker-compose.override.yml (для dev):

Пример
version: '3.8'
services:
  php:
    environment:
      - XDEBUG_MODE=debug
      - XDEBUG_START_WITH_REQUEST=yes
      - XDEBUG_SESSION=PHPSTORM
    extra_hosts:
      - "host.docker.internal:host-gateway"

Результат: при открытии страницы с браузером, в котором включен Xdebug (расширение), IDE (PhpStorm, VS Code) ловит точки останова.

# Проверка, что Xdebug активен:
docker exec -it php_container php -v
# PHP 8.2.x (cli) (built: ... )
# with Xdebug v3.2.x, Copyright (c) 2002-2023, by Derick Rethans

# Информация о настройках:
docker exec -it php_container php -i | grep xdebug
# (список директив)

Пример 3. Запуск cron-задач внутри PHP контейнера

Для выполнения периодических задач (например, очистка кэша) можно использовать crond в том же контейнере или отдельный образ.

Dockerfile с cron:

Пример
FROM php:8.2-cli-alpine
RUN apk add --no-cache dcron
COPY crontab /etc/crontabs/www-data
RUN chmod 0644 /etc/crontabs/www-data
COPY . /app
WORKDIR /app
CMD crond -l 2 -f

crontab:

Пример
* * * * * php /app/artisan schedule:run >> /dev/null 2>&1

Результат: каждую минуту выполняется artisan-команда Laravel. Логи выводятся в stdout (но в примере они перенаправлены в /dev/null, для отладки можно перенаправить в лог).

# Проверка работы cron:
docker logs -f cron_container
# Появится вывод при запуске crond (если не перенаправлен)

Пример 4. Использование образа с специфическими расширениями: imagick и gd

Иногда требуется одновременно установить несколько расширений для работы с изображениями.

Dockerfile:

Пример
FROM php:8.2-fpm-alpine
RUN apk add --no-cache imagemagick-dev imagemagick libpng-dev libjpeg-turbo-dev \
    && pecl install imagick \
    && docker-php-ext-enable imagick \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install gd \
    && apk del imagemagick-dev libpng-dev libjpeg-turbo-dev

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

# Список установленных расширений:
docker exec -it php_container php -m | grep -E 'imagick|gd'
imagick
gd

Пример 5. Минимальный образ для CLI с оптимизацией размера

Использование php:8.2-cli-alpine и удаление ненужных файлов может уменьшить образ до 50 МБ.

Пример
FROM php:8.2-cli-alpine
RUN apk add --no-cache --virtual .builddeps $PHPIZE_DEPS \
    && docker-php-ext-install pcntl \
    && apk del .builddeps \
    && rm -rf /tmp/* /var/cache/apk/*
COPY my_script.php /app/
WORKDIR /app
CMD ["php", "my_script.php"]

Результат: итоговый размер образа около 30-40 МБ.

# Проверка размера образа:
docker images my-php-cli
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
my-php-cli    latest    abcdef123456   10 seconds ago  38.2MB

Примечание:

Все примеры предполагают, что приложение написано с учетом окружения Docker. Рекомендуется использовать переменные окружения для конфигурации, читать их через getenv() или библиотеки (например, phpdotenv). Также стоит обратить внимание на логирование: stdout/stderr контейнера собираются Docker демоном и доступны через docker logs.

Docker образ PHP - comments

En
Php docker image (php)