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

Digital-студия WNDER

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

Раньше для запуска PHP-проекта нужно было установить Apache или Nginx, PHP с кучей расширений, MySQL, настроить права, прописать конфиги. И всё это на каждой машине разработчика и на сервере. Ошибка в версии расширения — и проект падает. Docker решает эту проблему: вы один раз описываете окружение в файлах и запускаете его где угодно одинаково.

Главные плюсы:

  • Изоляция — каждый проект живёт в своём контейнере со своими зависимостями.
  • Повторяемость — на локалке, у тестировщика и на проде всё одинаково.
  • Быстрый старт — новый разработчик запускает проект одной командой.
  • Легкое масштабирование — можно поднять несколько копий приложения.

Но есть и подводные камни: нужно разобраться в Docker, правильно настроить volumes и сеть, а также позаботиться о производительности.

С чего начать: готовим Dockerfile

Первый шаг — создать Dockerfile для вашего PHP-приложения. В нём вы описываете, как собрать образ. Начните с базового образа, например php:8.2-apache. Если используете Nginx + PHP-FPM, то лучше взять php:8.2-fpm и отдельный контейнер для Nginx.

Пример простого Dockerfile для PHP с Apache:

FROM php:8.2-apache

# Устанавливаем расширения
RUN docker-php-ext-install pdo_mysql mysqli

# Копируем код
COPY . /var/www/html/

# Включаем mod_rewrite
RUN a2enmod rewrite

# Права (если нужно)
RUN chown -R www-data:www-data /var/www/html

Этот файл создаёт образ, в котором уже есть PHP 8.2 с Apache и нужными расширениями. Код копируется внутрь. Всё просто.

Но для реального проекта часто нужно больше: Composer, Node.js для сборки фронтенда, xdebug для отладки. Всё это можно добавить в Dockerfile.

Правило: Не устанавливайте в контейнер лишние пакеты. Чем меньше образ, тем быстрее деплой и меньше уязвимостей. Для продакшена используйте php:8.2-fpm-alpine — он легче.

Описываем сервисы в docker-compose

Один контейнер — это скучно. Обычно PHP-проекту нужны база данных, кеш (Redis), возможно, очередь. Для управления несколькими контейнерами используйте docker-compose.yml. Это файл, где вы описываете все сервисы, их связи и настройки.

Пример для типичного проекта на Laravel или Symfony:

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/var/www/html
    ports:
      - "8080:80"
    depends_on:
      - db
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: app
      MYSQL_USER: user
      MYSQL_PASSWORD: secret
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

Здесь два сервиса: app (наш PHP) и db (MySQL). Том db_data сохраняет данные базы между перезапусками. Приложение доступно на порту 8080.

Обратите внимание на volumes — это способ «прокинуть» папку с кодом с хоста в контейнер, чтобы изменения сразу были видны без пересборки. Удобно для разработки.

Настройка окружения и конфигов

В PHP-проектах часто есть файлы конфигурации, которые зависят от окружения: .env, параметры подключения к БД и т.д. В Docker нельзя просто скопировать .env в образ, потому что он может содержать секреты. Лучше передавать переменные окружения через docker-compose.yml или файл .env, который подхватывает Compose.

Например, в docker-compose.yml можно указать:

environment:
  APP_ENV: dev
  DB_HOST: db
  DB_DATABASE: app
  DB_USERNAME: user
  DB_PASSWORD: secret

А в коде читать эти переменные через getenv() или использовать библиотеку для работы с окружением (например, vlucas/phpdotenv).

Также не забудьте про настройку PHP: может понадобиться увеличить memory_limit или upload_max_filesize. Это делается через кастомный php.ini, который копируется в образ.

Лайфхак: Для локальной разработки создайте отдельный файл docker-compose.override.yml, в котором переопределите порты и смонтируйте папки с кодом. В продакшене используйте только основной docker-compose.yml без монтирования хостовых папок.

Типичные ошибки и как их избежать

При миграции новички часто наступают на одни и те же грабли. Вот самые распространённые:

Ошибка Последствия Решение
Копирование всего кода в образ без .dockerignore Образ раздувается, в него попадают логи, кеш, .git Создайте .dockerignore и исключите лишнее
Запуск от root Потенциальные уязвимости Создайте пользователя и переключитесь на него в Dockerfile
Хранение данных БД внутри контейнера При перезапуске данные теряются Используйте volume для /var/lib/mysql
Игнорирование кеша Composer Долгая сборка образа Используйте многоступенчатую сборку и кеширование слоёв

Ещё одна частая проблема — права доступа к файлам. Если в контейнере PHP работает от пользователя www-data, а файлы на хосте принадлежат вашему пользователю, могут возникнуть ошибки записи в кеш или логи. Решение: либо выставить одинаковые UID/GID, либо в режиме разработки монтировать папку с нужными правами.

Деплой в продакшен

Когда проект успешно работает локально в Docker, пора переносить его на сервер. Здесь есть несколько подходов:

  • Простой: установить Docker на сервер и запустить docker-compose up -d с продакшен-конфигом.
  • CI/CD: собирать образ в пайплайне (GitLab CI, GitHub Actions) и пушить в реестр (Docker Hub, GitLab Registry), а на сервере только обновлять контейнеры.
  • Оркестрация: использовать Kubernetes или Docker Swarm для управления кластером.

Для небольших проектов достаточно первого варианта. Главное — не забудьте отключить монтирование исходников и использовать собранный образ с кодом внутри. Также настройте логирование: контейнеры должны писать логи в stdout/stderr, а Docker уже позаботится об их сборке.

Пример команды для обновления на сервере:

docker-compose pull
docker-compose up -d --remove-orphans

Если используете собранный образ, то вместо pull будет build или push в зависимости от схемы.

Совет: На продакшене обязательно настройте healthcheck для контейнеров, чтобы Docker мог перезапускать упавшие сервисы. Например, для PHP-FPM можно проверять доступность порта 9000.

Что в итоге

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

Попробуйте перевести небольшой проект, чтобы набить руку. А когда освоитесь, сможете использовать более продвинутые инструменты вроде Kubernetes. Главное — начать.

Студия WNDER