Если ваш PHP-проект до сих пор разворачивается вручную на сервере с помощью FTP и магии, а слово «Docker» вызывает смешанные чувства, эта статья для вас. Мы разберём, как перевести типичное PHP-приложение в контейнеры, не сломав продакшен и не поседев за ночь.

Digital-студия WNDER

Зачем вообще нужен Docker в PHP?

PHP-разработка исторически славилась «зоопарком» версий: на одном сервере PHP 5.6, на другом — 7.4, а у коллеги на локалке — 8.2. Docker решает эту проблему раз и навсегда. Вы описываете окружение в файле, и оно одинаково запускается везде: на ноутбуке, в CI/CD и на продакшене.

Кроме того, контейнеры изолируют зависимости. Больше не нужно переживать, что обновление библиотеки для одного проекта сломает другой. Каждый сервис живёт в своём «пузыре».

Создаём Dockerfile для PHP-приложения

Начнём с базового образа. Для типичного Laravel или Symfony-проекта понадобится PHP с расширениями и Composer. Вот пример Dockerfile:

FROM php:8.2-fpm

RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

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

WORKDIR /var/www/html

COPY . .

RUN composer install --no-interaction --optimize-autoloader --no-dev

RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache

EXPOSE 9000

CMD ["php-fpm"]

Этот Dockerfile делает несколько вещей: устанавливает системные зависимости, расширения PHP, Composer, копирует код и ставит зависимости. Обратите внимание на --no-dev — для продакшена dev-зависимости не нужны.

Правило: Никогда не запускайте Composer install от root в контейнере. Используйте отдельный шаг сборки или флаг --no-scripts, чтобы избежать проблем с правами.

Настраиваем docker-compose для локальной разработки

Один контейнер — это скучно. Настоящий PHP-проект обычно требует базу данных, Redis, возможно, Nginx. Docker Compose позволяет описать всё в одном YAML-файле. Пример для Laravel:

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/var/www/html
    depends_on:
      - db
      - redis
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis

  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - dbdata:/var/lib/mysql

  redis:
    image: redis:alpine

volumes:
  dbdata:

Здесь мы определили четыре сервиса: PHP-FPM, Nginx, MySQL и Redis. Том dbdata сохраняет данные базы между перезапусками. Для локальной разработки удобно монтировать код внутрь контейнера — изменения сразу видны без пересборки.

Оптимизация и подготовка к продакшену

Для продакшена подход немного меняется. Во-первых, не монтируйте код — копируйте его в образ при сборке. Во-вторых, используйте многоступенчатую сборку, чтобы уменьшить размер образа. Вот пример оптимизированного Dockerfile:

# Стадия сборки
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist

# Финальный образ
FROM php:8.2-fpm-alpine

RUN apk add --no-cache \
    libpng-dev \
    libzip-dev \
    && docker-php-ext-install pdo_mysql mbstring zip gd

WORKDIR /var/www/html

COPY --from=vendor /app/vendor ./vendor
COPY . .

RUN composer dump-autoload --optimize --no-dev

RUN chown -R www-data:www-data storage bootstrap/cache

USER www-data

EXPOSE 9000

CMD ["php-fpm"]

Обратите внимание на USER www-data — контейнер не должен работать от root. Это базовое правило безопасности.

Лайфхак: Добавьте в .dockerignore файлы вроде .git, node_modules, vendor, storage/logs. Это ускорит сборку и уменьшит контекст.

Сравнение подходов: что выбрать?

При миграции часто встаёт вопрос: использовать готовые образы (например, bitnami/php-fpm) или писать свой Dockerfile? Вот краткая таблица для сравнения:

Критерий Свой Dockerfile Готовый образ
Гибкость Высокая Ограниченная
Время настройки Больше Меньше
Размер образа Контролируемый Может быть большим
Безопасность Зависит от вас Обновляется сообществом

Для большинства проектов оптимален свой Dockerfile на основе официального образа PHP. Это даёт баланс между контролем и удобством.

Что в итоге?

Миграция PHP-проекта на Docker — это не страшно, если действовать поэтапно. Начните с локальной разработки, напишите простой Dockerfile, добавьте docker-compose с базой данных. Затем оптимизируйте образ для продакшена: многоступенчатая сборка, непривилегированный пользователь, .dockerignore.

В результате вы получите воспроизводимое окружение, лёгкое масштабирование и меньше головной боли при деплое. Docker не решит все проблемы, но точно сделает вашу жизнь проще.

Студия WNDER