Построение эффективного Dockerfile для 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 эффективен для длительных скриптов (например, в консольных командах), но для коротких запросов веб-сервера улучшение может быть незначительным.