Отладка PHP в Docker контейнере

06.02.2022 / devops
Развернем среду разработки в Docker для произвольной CMS на php с использованием Apache2, PHP-FPM, MySQL (5.7/8), phpMyAdmin

После предыдущих приключений с Gitlab-CI и кастомизацией Visual Studio Code как IDE для PHP мне захотелось иметь переносимую среду разработки с использованием PHP, чтобы не настраивать среду на рабочей машине, точнее чтобы любой человек мог подключится к разработке проекта с минимальными усилиями по организации среды. Так начались новые приключения ...

В итоге получится запускать скрипты в docker контейнере, а дебажить их на хостовой машине с использованием VS Code.

Репозиторий с демкой прилагается.

Файловая структура

Файловая структура

Конфигурация VS Code

Чтобы отлаживать PHP скрипты в VS Code для него необходимо установить расширение php-debug, затем в .vscode/launch.json запишем:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "hostname": "172.17.0.1",
            "port": 9003,
            "log": true,
            "pathMappings": {
                "/var/www/html/": "${workspaceFolder}/",
            },
            "xdebugSettings": {
                "max_children": 9999,
                "show_hidden": 1
            }
        }
    ]
}

Кратко: указываем хост и порт, который должен слушать дебагер VS Code для получения отладочной информации, так же указываем сопоставление путей с контейнера на хосте (pathMappings). Дополнительно почитать про отладку в VS Code можно в доке.

Хост и порт должны быть такими же как у контейнера, в котором будем производить отладку.

Схема работы отладки будет такая:

Конфиги PHP

В минимальном варианте нам надо 2 файла:

[PHP]
# уровень ошибок
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT

# разрешаем логирование ошибок
log_errors = On

# определяем статический путь для файла ошибок
error_log = /var/www/html/logs/php_errors.log
# определяем статический лог файл
xdebug.log=/var/www/html/logs/xdebug.log

# хост и порт клиента отладчика, с которым xdebug будет вести общение
xdebug.client_host = host.docker.internal
xdebug.client_port = 9003

# режим отладки и покрытия кода тестами
xdebug.mode=coverage,debug

# старт xdebug при выполнении PHP скрипта
xdebug.start_with_request = yes

Ложим их в директорию config.

host.docker.internal - это хост контейнера с php, который мы позже определим. Кратко: он нужен чтобы xdebug открывал с ним соединение и направлял туда отладочные данные.

Dockerfile

Для примера будем собирать свой образ на основе php:7.4-cli (схема работоспособна и для fpm), скопируем внутрь конфиги PHP и XDebug, также установим xdebug:

FROM php:7.4-cli

COPY config/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
COPY config/php.ini /usr/local/etc/php/conf.d/php.ini

RUN apt update \
    && pecl install xdebug && docker-php-ext-enable xdebug \
    && mkdir /var/www/html/logs && chown www-data:www-data /var/www/html/logs

WORKDIR /var/www/html/

В качестве рабочей директории будем использовать путь /var/www/html/, там будет находится корень нашего проекта.

Здесь же мы создаем директорию /var/www/html/logs и меняем ее владельца на www-data. В нее мы будем складывать все логи, и если надо ее можно прокидывать на хост машину, а владельца меняем чтобы не конфликтовать.

Теперь можно собирать образ контейнера, назовем его php с тегом debug:

docker build -t php:debug .

Запуск и тестирование

Создадим файл app.php со следующим содержимым:

$var = 10;

// здесь поставим breakpoint
echo "Hello world from Docker container!";

Запускаем отладчик в VS Code.

Исходники будем монтировать из хоста в контейнер, чтобы иметь возможность менять код без пересборки контейнера.

Затем можно запустить контейнер:

docker run -it \
    --add-host=host.docker.internal:172.17.0.1 \
    --ip=172.17.0.1 \
    --expose 9003 \
    -v $PWD/app.php:/var/www/html/app.php \
    php:debug \
    /bin/bash -c "php -f app.php"

И тут же попадем на первый поставленный breakpoint - отладка работает!

Немного подробностей:

docker-compose

Это необязательно, но для полноты картины напишем docker-compose.yml чтобы провести эксперимент и через docker-compose:

version: '3.7'

services:

  php-debug:
    image: php:debug
    extra_hosts:
      - "host.docker.internal:172.17.0.1"
    volumes:
      - ./app.php:/var/www/html/app.php
    command: /bin/bash -c "php -f app.php"
    networks:
      - app_net

networks:
  app_net:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: "172.17.0.1/24"

Как и в предыдущем шаге поставим breakpoint в* VS Code* и запустим docker compose:

docker compose up

Итог

Подобное приходилось настраивать и в контексте работы с CMS (разработка модуля для CMS в docker контейнерах, о чем поговорим в следующих статьях) и это оказывается удобно и просто. Удаленная отладка с xdebug вполне работоспособна.