Контейнеризация PHP при помощи Docker: от разработки до продакшена
Зачем Docker для PHP проектов
Docker позволяет изолировать окружение PHP приложения, гарантируя идентичность настроек на всех этапах - от локальной разработки до production. Это решает проблему «на моей машине работает» и упрощает развертывание.
Основное решение: PHP-FPM + Nginx + Docker Compose
Как создать минимальное окружение PHP с Nginx и PHP-FPM в Docker?
Используется два сервиса: web (Nginx) и app (PHP-FPM). Они общаются через общий volume и внутреннюю сеть.
# docker-compose.yml
version: '3.8'
services:
web:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./public:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
networks:
- appnet
app:
image: php:8.2-fpm-alpine
volumes:
- ./public:/var/www/html
environment:
PHP_IDE_CONFIG: "serverName=Docker"
networks:
- appnet
networks:
appnet:обработка php (обработка данных в php)
# nginx.conf
server {
listen 80;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}Php learning (обучение php)
Пояснения: контейнер app использует официальный образ PHP с FPM. Директория ./public монтируется в оба контейнера. Nginx обрабатывает статику, а PHP-файлы переадресует на fastcgi через порт 9000.
Типичная ошибка: файл nginx.conf не читается из-за неправильного пути монтирования. Убедитесь, что целевая папка в контейнере - /etc/nginx/conf.d/.
Ошибка File not found - часто из-за того, что PHP-FPM не видит файлы. Проверьте SCRIPT_FILENAME и пути volume.
Варианты настройки
Как заменить Nginx на Apache?
Используем образ php:8.2-apache. Встроенный mod_php не требует отдельного FPM, но для производительности лучше использовать FPM.
services:
web-apache:
image: php:8.2-apache
ports:
- "8080:80"
volumes:
- ./public:/var/www/html
# можно также включить mod_rewrite:
# docker-php-ext-enable rewrite
Php учебник (учебник по php)
Этот вариант проще, но хуже масштабируется. Подходит для простых проектов.
Как собрать образ с дополнительными расширениями PHP?
Напишите собственный Dockerfile, который наследует официальный образ и устанавливает нужные расширения.
FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo_mysql mysqli bcmath
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY . /var/www/html
WORKDIR /var/www/html
CMD ["php-fpm"]Apis php (api на php)
Расширения (pdo_mysql, bcmath) устанавливаются через встроенные скрипты. Композер копируется из официального образа - это экономит размер.
Проблема: при копировании COPY --from=composer может возникнуть конфликт версий. Используйте точную версию, например composer:2.7.
Как оптимизировать образ для продакшена с помощью multi-stage сборки?
Первый этап - сборка зависимостей (composer install), второй - чистый образ с минимальным набором.
# Dockerfile.prod
FROM composer:2 AS build
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader
FROM php:8.2-fpm-alpine
COPY --from=build /app/vendor ./vendor
COPY . /var/www/html
# ...Php categoryid (категории в php)
Итоговый образ не содержит composer, dev-зависимостей и лишних файлов.
Как использовать Alpine образы для уменьшения размера?
В примерах выше уже используется php:8.2-fpm-alpine. Альтернатива - php:8.2-cli-alpine для консольных задач. Размер ~100 МБ против 300+ МБ у стандартного Debian.
Внимание: Alpine использует musl, что может вызвать несовместимость с некоторыми C-расширениями (например, imagick). Для таких случаев используйте образ на основе Debian.
Как настроить Xdebug в Docker для отладки?
Добавьте в Dockerfile установку xdebug и настройте host.docker.internal в качестве remote_host.
FROM php:8.2-fpm-alpine
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug \
&& echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
&& echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.iniHtml javascript php (интеграция html, javascript и php)
Для PhpStorm дополнительно настройте Path mappings в Docker сервере.
Как выполнять composer команды без установки в образ?
Используйте временный контейнер composer с монтированием исходного кода.
docker run --rm -v $(pwd):/app composer install --no-devЭто не загромождает основной образ и подходит для CI/CD.
Общие рекомендации и ошибки
При работе с Docker для PHP часто встречаются:
- Права доступа: www-data (uid 33) в контейнере не совпадает с uid хоста. Решается установкой user в docker-compose или настройкой маппинга через переменные.
- Использование docker-php-ext-install или pecl зависит от расширения. Для некоторых требуется предварительная установка системных пакетов (например, libzip-dev).
- Кэширование слоёв Dockerfile: располагайте редко изменяемые инструкции (установка расширений) вверху, а копирование кода - внизу.
Расширенные примеры конфигураций
Ниже приведены нестандартные сценарии и детальные конфигурации с пояснениями.
Пример 1: Docker Compose с Redis и healthcheck
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
environment:
- REDIS_HOST=redis
depends_on:
redis:
condition: service_healthy
healthcheck:
test: ["CMD", "php", "-r", "echo 'OK';"]
interval: 30s
redis:
image: redis:alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5sОбъяснение: healthcheck позволяет убедиться, что Redis готов к работе, прежде чем PHP попытается подключиться. Команда php -r 'echo "OK";' проверяет, что PHP-FPM корректно обрабатывает запросы.
# При запуске docker-compose up вы увидите: redis | 1:M 01 Jan 2025 12:00:00.000 * Ready to accept connections app | healthcheck: HTTP/1.1 200 OK
Пример 2: Запуск artisan команд (Laravel) в отдельном сервисе
services:
scheduler:
build: .
command: ['php', 'artisan', 'schedule:work']
volumes:
- .:/var/www/html
restart: unless-stopped
# Для миграций используйте docker-compose run:
# docker-compose run --rm artisan migrateТакой сервис запускает планировщик Laravel в фоне, не блокируя веб-контейнер.
Пример 3: Сборка с несколькими расширениями и системными зависимостями
FROM php:8.2-fpm-bookworm
# Установка системных пакетов для расширений
RUN apt-get update && apt-get install -y \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
zip \
unzip
# Настройка и установка расширений
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd pdo_mysql zip bcmath opcache
# Копирование конфигурации PHP (opcache.ini и т.д.)
COPY php.ini /usr/local/etc/php/conf.d/custom.iniВ этом примере показано, как устанавливать зависимости для GD, Zip, PDO. Использование -j$(nproc) ускоряет компиляцию на многоядерных хостах.
Пример 4: Production образ с минимальным набором инструментов
# Dockerfile.prod (multi-stage)
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-interaction
FROM node:18 AS assets
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --production
COPY resources/js ./resources/js
RUN npm run build
FROM php:8.2-fpm-alpine
RUN docker-php-ext-install pdo_mysql opcache && \
docker-php-ext-enable opcache
COPY --from=vendor /app/vendor ./vendor
COPY --from=assets /app/public/build ./public/build
COPY . /var/www/html
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
USER www-data
EXPOSE 9000
CMD ["php-fpm"]Итоговый образ содержит только минимально необходимые файлы для работы приложения. Размер такого образа обычно не превышает 200 МБ.