Docker для PHP-разработчика: полный гайд по контейнеризации

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему ваш PHP-проект заслуживает Docker

Вы когда-нибудь тратили полдня, чтобы объяснить новому разработчику, как настроить окружение? Или сталкивались с ситуацией, когда код работает на вашем localhost, но падает на сервере? Знакомо? Тогда эта статья — для вас.

Docker — это не просто модный buzzword. Это инструмент, который превращает фразу «у меня работает» в «работает у всех». Для PHP-разработчика контейнеризация — это суперсила: вы упаковываете приложение, веб-сервер и базу данных в легкие контейнеры, которые гарантированно ведут себя одинаково на любой машине.

В этом гайде мы не будем читать скучную теорию. Мы сразу перейдем к делу: напишем Dockerfile для PHP, настроим связку Nginx + PHP-FPM + MySQL через Docker Compose и развернем реальное приложение. Вжух — и ваш проект готов к деплою!

Что такое контейнеризация и зачем она PHP-разработчику?

Представьте, что вы переезжаете в новую квартиру. Вместо того чтобы каждый раз собирать мебель заново (устанавливать PHP, настраивать Apache, ставить модули), вы просто берете готовую коробку с вашим окружением. Docker — это такие коробки. Они называются контейнерами.

Для PHP это особенно актуально. Версии PHP меняются, требуются разные расширения (gd, mysqli, redis), а в продакшене всё должно быть стабильно. Контейнеризация решает три главные проблемы:

  • Воспроизводимость — окружение одинаково у всех: разработчик, тестировщик, сервер.
  • Изоляция — проекты не конфликтуют (у вас больше никогда не сломается старый сайт из-за обновления PHP).
  • Простота деплоя — запуск одной командой docker-compose up.

Готовим Dockerfile для PHP-приложения

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

# Используем официальный образ PHP 8.2 с FPM
FROM php:8.2-fpm-alpine

# Устанавливаем необходимые системные пакеты
RUN apk add --no-cache \
    nginx \
    supervisor \
    libpq-dev \
    && docker-php-ext-install pdo pdo_mysql

# Копируем конфиги (создадим их позже)
COPY ./docker/nginx.conf /etc/nginx/nginx.conf
COPY ./docker/supervisord.conf /etc/supervisord.conf

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

# Права на запись для логов и кеша
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache

# Открываем порты
EXPOSE 80

# Запускаем supervisor (он будет управлять nginx и php-fpm)
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

Что здесь важно? Мы используем alpine версию — она весит в разы меньше. Устанавливаем только нужные расширения через docker-php-ext-install. И да, мы используем супервизор, чтобы внутри одного контейнера крутились и Nginx, и PHP-FPM. Это удобно для небольших проектов.

Создаем конфиг Nginx

Теперь нам нужно настроить веб-сервер. Создайте файл docker/nginx.conf:

worker_processes auto;
events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    server {
        listen 80;
        server_name _;
        root /var/www/html/public;
        index index.php index.html;

        location / {
            try_files $uri $uri/ /index.php?$query_string;
        }

        location ~ \.php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }
}

Обратите внимание: PHP-FPM слушает на порту 9000 внутри контейнера. Это стандартный порт для php-fpm.

Docker Compose: собираем всё вместе

Один контейнер — это круто. Но в реальном проекте нужна база данных. И Redis для кеша. И почему бы не добавить phpMyAdmin для удобства? Тут в игру вступает Docker Compose — оркестратор для мультиконтейнерных приложений.

Создаем docker-compose.yml в корне проекта:

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: php_app
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html
    depends_on:
      - db
      - redis
    networks:
      - app-network

  db:
    image: mysql:8.0
    container_name: mysql_db
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: root_password
      MYSQL_DATABASE: my_app
      MYSQL_USER: user
      MYSQL_PASSWORD: user_password
    ports:
      - "3307:3306"
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - app-network

  redis:
    image: redis:7-alpine
    container_name: redis_cache
    ports:
      - "6379:6379"
    networks:
      - app-network

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: root_password
    ports:
      - "8081:80"
    depends_on:
      - db
    networks:
      - app-network

volumes:
  db_data:

networks:
  app-network:
    driver: bridge

Что мы видим? Четыре сервиса:

  • app — наше PHP-приложение. Собирается из Dockerfile.
  • db — MySQL 8.0 с настройками пользователя.
  • redis — для кеширования сессий.
  • phpmyadmin — удобная админка для базы.

Все сервисы подключены к одной сети app-network, поэтому могут общаться по именам контейнеров. Например, в PHP-коде к БД мы обращаемся как к db:

<?php
// config/database.php
return [
    'host' => getenv('DB_HOST') ?: 'db',
    'database' => getenv('DB_DATABASE') ?: 'my_app',
    'username' => getenv('DB_USERNAME') ?: 'user',
    'password' => getenv('DB_PASSWORD') ?: 'user_password',
];

Пример PHP-приложения, работающего с базой

Давайте напишем простой скрипт, который выведет список пользователей из БД. Создайте public/index.php:

<?php
require_once __                

Блоги

Рекомендаций по книгам