Docker и PHP: контейнеризация приложений на продвинутом уровне
Контейнеризация PHP-приложений с помощью Docker давно перестала быть просто удобным инструментом для разработки. На продвинутом уровне это — ключ к созданию масштабируемых, безопасных и воспроизводимых окружений. В этой статье мы разберём техники, которые помогут вам перейти от базового запуска PHP-контейнера к профессиональной оркестрации: мультистейдж-сборка, оптимизация образов, работа с Docker Compose для микросервисной архитектуры и продвинутое кеширование.
1. Мультистейдж-сборка: минимальный финальный образ
Одна из главных проблем стандартных Docker-образов — их размер. Базовый образ php:8.2-apache весит более 400 МБ, а с установкой расширений — все 600+. Для продакшена это недопустимо: большой образ медленнее загружается, занимает место в реестре и увеличивает поверхность атаки.
Мультистейдж-сборка (multi-stage build) позволяет разделить процесс на этапы: один для компиляции и установки зависимостей (с полным набором инструментов), второй — для минимального рантайма. Финальный образ содержит только скомпилированные артефакты и необходимые библиотеки.
Пример Dockerfile для Laravel-приложения:
# Этап 1: Компиляция и установка зависимостей
FROM composer:latest AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction
# Этап 2: Сборка Node-ресурсов (если есть фронтенд)
FROM node:18-alpine AS frontend
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production
COPY resources/ ./resources/
RUN npm run production
# Этап 3: Финальный PHP-образ
FROM php:8.2-fpm-alpine AS production
RUN docker-php-ext-install pdo_mysql opcache
COPY --from=vendor /app/vendor /var/www/html/vendor
COPY --from=frontend /app/public/build /var/www/html/public/build
COPY . /var/www/html
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
EXPOSE 9000
CMD ["php-fpm"]
Такой подход сокращает размер образа с 500+ МБ до ~120 МБ. composer:latest и node:18-alpine используются только на этапах сборки и не попадают в финальный слой.
2. Оптимизация кеширования слоёв Docker
Каждая инструкция в Dockerfile создаёт слой. Чтобы ускорить повторные сборки, нужно располагать редко меняющиеся слои как можно раньше. Для PHP-проекта это означает:
- Копировать
composer.jsonиcomposer.lockдо копирования кода. - Запускать
composer installдо копирования исходников. - Копировать код последним.
Пример оптимизированного порядка инструкций:
FROM php:8.2-fpm-alpine AS base
WORKDIR /var/www/html
# Устанавливаем системные зависимости один раз
RUN apk add --no-cache $PHPIZE_DEPS \
&& docker-php-ext-install pdo_mysql opcache bcmath
# Копируем только файлы зависимостей
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-interaction --no-scripts --prefer-dist
# Копируем остальной код
COPY . .
# Генерируем автозагрузку и оптимизируем
RUN composer dump-autoload --optimize
Совет: используйте .dockerignore, чтобы исключить vendor, .git, node_modules и другие временные файлы из контекста сборки. Это ускорит передачу контекста демону Docker.
3. Docker Compose для микросервисной архитектуры
Современные PHP-приложения редко состоят из одного сервиса. Типичная архитектура включает PHP-FPM, Nginx, Redis, очередь задач (RabbitMQ/Beanstalkd) и базу данных. Docker Compose позволяет описать всю инфраструктуру в одном docker-compose.yml.
Пример конфигурации для микросервиса на PHP:
version: '3.8'
services:
app:
build:
context: .
target: production
volumes:
- ./storage:/var/www/html/storage
env_file:
- .env
depends_on:
- db
- redis
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./public:/var/www/html/public
depends_on:
- app
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE}
volumes:
- db_data:/var/lib/mysql
ports:
- "3306:3306"
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
db_data:
redis_data:
Для масштабирования используйте docker-compose up --scale app=3. Однако для продакшена лучше перейти на оркестратор (Kubernetes или Docker Swarm), где управление репликами и балансировкой автоматизировано.
Продвинутые настройки для PHP-FPM
В конфигурации PHP-FPM внутри контейнера важно настроить пулы для разных приложений. Используйте переменные окружения для динамической настройки:
; /usr/local/etc/php-fpm.d/zz-custom.conf
[www]
pm = dynamic
pm.max_children = ${PHP_FPM_MAX_CHILDREN:-50}
pm.start_servers = ${PHP_FPM_START_SERVERS:-5}
pm.min_spare_servers = ${PHP_FPM_MIN_SPARE_SERVERS:-3}
pm.max_spare_servers = ${PHP_FPM_MAX_SPARE_SERVERS:-10}
pm.max_requests = 500
Передавайте значения через environment в docker-compose.yml:
app:
environment:
PHP_FPM_MAX_CHILDREN: 100
PHP_FPM_START_SERVERS: 10
4. Безопасность и деплой
На продвинутом уровне нельзя забывать о безопасности контейнеров:
- Не запускайте контейнеры от root. Используйте
USER www-dataв Dockerfile. - Минимизируйте количество установленных пакетов. Используйте
php:8.2-fpm-alpine