Построение эффективного Dockerfile для PHP проектов

Раздел: DevOps -> Docker для PHP

Введение в Dockerfile для PHP приложений

Dockerfile описывает сборку образа контейнера с PHP окружением. Важно выбрать правильную стратегию для разных целей: разработка, тестирование, production. Рассмотрим основные подходы.

Основной рекомендованный подход: мультистадийная сборка

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


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

# Второй этап: финальный образ с PHP
FROM php:8.2-fpm-alpine
WORKDIR /var/www/html
COPY --from=composer /app/vendor ./vendor
COPY . .
RUN docker-php-ext-install pdo_mysql opcache

Php docker file (dockerfile для php приложения)

Пояснение: Первый этап использует официальный образ Composer, устанавливает зависимости без dev-пакетов. Второй этап берет только vendor и исходный код, устанавливает расширения PHP. Такой образ получается компактным и безопасным.

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

Вместо php:8.2-fpm-alpine можно использовать образы от другого семейства: debian-based (php:8.2-fpm) или cli для консольных приложений. Выбор зависит от требований к совместимости библиотек.


FROM php:8.1-cli
...

Цель: получить среду для выполнения скриптов командной строки без веб-сервера.

Как добавить специфическое расширение PHP, которое требует системные библиотеки?

Многие расширения (gd, zip, bcmath) требуют предустановки системных пакетов. Используйте apt-get для Debian или apk для Alpine.


RUN apk add --no-cache libpng-dev libjpeg-turbo-dev \
    && docker-php-ext-configure gd --with-jpeg \
    && docker-php-ext-install gd

Проблема: если забыть очистить кеш пакетного менеджера, образ увеличивается. Решение: флаг --no-cache у apk.

Как настроить Dockerfile для разработки с Xdebug?

Для отладки часто требуется Xdebug. Его установка в образе разработки может отличаться от production.


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

Проблема: установка через pecl требует PHPIZE_DEPS, что добавляет компилятор и заголовки. Для production эти пакеты не нужны, поэтому такой образ неоптимален. Решение: использовать мультистадийную сборку или отдельные Dockerfile для dev и prod.

Типичные ошибки и их решение

Ошибка: неверный порядок инструкций COPY приводит к повторной сборке всех слоев при изменении исходного кода. Решение: копировать файлы зависимостей (composer.json) отдельно перед запуском composer install, а затем копировать остальной код.


COPY composer.json composer.lock ./
RUN composer install --no-dev
COPY . .

Ошибка: права доступа к файлам внутри контейнера. Решение: задать пользователя www-data или создавать своего пользователя.


RUN useradd -m -u 1000 myuser && chown -R myuser:myuser /var/www/html
USER myuser

Как объединить PHP-FPM и Nginx в одном образе?

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


FROM php:8.2-fpm-alpine
RUN apk add --no-cache nginx \
    && mkdir /run/nginx
COPY nginx.conf /etc/nginx/nginx.conf
COPY . /var/www/html
CMD sh -c "php-fpm & nginx -g 'daemon off;'"

Цель: минимизировать количество контейнеров для простых приложений. Недостаток: сложнее масштабировать и обновлять отдельные компоненты.

Расширенные примеры Dockerfile для PHP

Пример 1: Оптимизированный Dockerfile для Laravel (production)

Пример

# Этап 1: зависимости
FROM composer:2 AS deps
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist

# Этап 2: сборка (оптимизация автозагрузки)
FROM deps AS build
COPY --from=deps /app/vendor ./vendor
COPY . .
RUN composer dump-autoload --no-dev --optimize

# Этап 3: финальный образ
FROM php:8.2-fpm-alpine
RUN apk add --no-cache unzip libpq-dev \
    && docker-php-ext-install pdo_mysql pdo_pgsql opcache
COPY --from=build /app /var/www/html
COPY --from=build /var/www/html/storage /var/www/html/storage
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/public
WORKDIR /var/www/html
# Сборка образа:
docker build -t my-laravel-app:latest .
# Размер образа: ~120MB (php:8.2-fpm-alpine ~80MB + расширения + код)

Пояснение: Используется два этапа: сначала устанавливаются зависимости, затем строится финальный образ с минимальным набором расширений. Кеширование слоев ускоряет повторные сборки.

Пример 2: Dockerfile для Symfony с дополнительными расширениями и настройкой часового пояса

Пример

FROM php:8.2-fpm-bullseye

RUN apt-get update && apt-get install -y --no-install-recommends \
        libicu-dev \
        libxml2-dev \
        libzip-dev \
    && docker-php-ext-install intl xml zip pdo_mysql opcache \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /srv/app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction

COPY . .
RUN echo "date.timezone = UTC" > /usr/local/etc/php/conf.d/timezone.ini

RUN chown -R www-data:www-data /srv/app/var

USER www-data
# Сборка:
docker build -t symfony-app .
# Команда для проверки:
docker run --rm symfony-app php bin/console about

Проблема: образ на основе Debian больше по размеру (около 350MB), но совместимость с библиотеками выше. Решение: выбирать Debian, когда Alpine не поддерживает нужное расширение.

Пример 3: Dockerfile для WordPress с включенным mod_rewrite и поддержкой кэширования

Пример

FROM php:8.2-apache

RUN a2enmod rewrite headers \
    && docker-php-ext-install mysqli pdo_mysql \
    && pecl install redis \
    && docker-php-ext-enable redis

COPY --from=wordpress:6.2 /usr/src/wordpress /var/www/html
RUN chown -R www-data:www-data /var/www/html
# Сборка:
docker build -t wp-custom .
# Запуск с базой данных MySQL:
docker run -d -p 8080:80 wp-custom

Цель: упростить развертывание WordPress, добавив необходимые модули Apache и расширения PHP для кэширования Redis.

Пример 4: Dockerfile для API с использованием PHP JIT (Just-In-Time compilation)

Пример

FROM php:8.2-cli-alpine

RUN apk add --no-cache oniguruma-dev \
    && docker-php-ext-install mbstring pcntl

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction

COPY . .

# Включение JIT
RUN echo "opcache.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini \
    && echo "opcache.jit=tracing" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini \
    && echo "opcache.jit_buffer_size=100M" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini

EXPOSE 8000
CMD ["php", "-S", "0.0.0.0:8000", "-t", "public"]
# Сборка и запуск:
docker build -t api-jit .
docker run -p 8000:8000 api-jit
# Результат: производительность API увеличена за счет JIT (до 2-3x на CPU-intensive операциях)

Примечание: JIT эффективен для длительных скриптов (например, в консольных командах), но для коротких запросов веб-сервера улучшение может быть незначительным.

Dockerfile для PHP приложения - comments

En
Php docker file (php)