Docker + php-fpm + PhpStorm + Xdebug

  • Tutorial
Не так давно тимлид нашей команды сказал: ребята я хочу, чтобы у всех была одинаковая среда разработки для наших боевых проектов + мы должны уметь дебажить всё — и web приложения, и api запросы, и консольные скрипты, чтобы экономить свои нервы и время. И поможет нам в этом docker.

Сказано — сделано. Подробности под катом.

В сети есть много мануалов по контейнеризации, но как их применить к реальной боевой разработке? Для каждого проекта написать свой docker-compose.yml? Но все наши проекты общаются между собой через апи, они все используют стандартный стек технологий: nginx + php-fpm + mysql.

Поэтому, давайте уточним условия задачи:

  1. Мы работаем в компании, в команде, сопровождаем несколько боевых проектов. Все работаем под Ubuntu + PhpStorm
  2. Для локальной разработки мы хотим использовать докер, для того, чтобы иметь одинаковую среду разработки у каждого члена команды, а также для того, чтобы когда придет новый разработчик, он смог быстро развернуть рабочее окружение
  3. Мы хотим разрабатывать с комфортом, мы хотим дебажить всё: и web приложения, и консольные скрипты, и api запросы.

Еще раз: мы хотим завести в докер несколько рабочих проектов.

На боевых серверах используется стандартная связка nginx + php-fpm + mysql. И, в чем проблема?

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



Всё это действительно так — всё работает из коробки. Но, давайте попробуем заглянуть под капот нашего рабочего окружения.

Nginx + php-fpm общаются через сокет, xdebug слушает порт 9000, PhpStorm тоже, по умолчанию, слушает порт 9000 для дебага и всё вроде бы замечательно. А если у нас открыто несколько приложений в PhpStorm, и включена прослушка («трубка)» для нескольких приложений? Что сделает PhpStorm? Он начнет ругаться, что обнаружено новое подключение для Xdebug, вы хотите его игнорировать, или нет?

Т.е., при настройках по умолчанию в PhpStorm, в конкретный момент времени, я могу дебажить только одно приложение. У всех других открытых приложений дебаг должен быть выключен. Блин, но это же неудобно. Я хочу слушать для дебага все приложения, и если в каком-то из них стоит точка останова, то я хочу, чтобы PhpStorm остановился в этом приложении, на той строке, где мне нужно.

А что для этого нужно? А нужно, чтобы каждое приложение запускалось со своими настройками для Xdebug. Чтобы каждое приложение слушало свой порт, искало свой сервер, а не так, как у нас всё общее, всё в одной куче.

А для этого есть замечательный докер! Мы можем запустить каждое наше боевое приложение в отдельном контейнере, на основе одного общего образа, например, php:7.1-fpm. Благодаря технологии докера мы можем изолировать наши приложения, при минимальных накладных расходах.

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

И, упс, первая проблема: контейнеры в докере запускаются от имени суперпользователя root, а локально мы работаем, обычно, от пользователя с uid 1000, gid 1000. Приложения боевые, и давать каждому приложению права 777 на всё — это не выход. Наши же приложения под гитом, и если мы дадим права 777 локально, то гит всё это запишет, и передаст на боевой сервер.

Костылим, вот пример образа php:7.1-fpm, который будет собираться.

Update


Как справедливо указало сообщество — совсем уж жестко костылить всё-таки не надо.
Например хаброюзер 1ntrovert в своем комментарии

Первоначальный пример образа php:7.1-fpm (uid и gid жестко прописаны)
FROM php:7.1-fpm

RUN apt-get update && apt-get install -y \
        git \
        curl \
        wget \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng-dev zlib1g-dev libicu-dev g++ libmagickwand-dev libxml2-dev \
    && docker-php-ext-configure intl \
    && docker-php-ext-install intl \
    && docker-php-ext-install mbstring zip xml gd mcrypt pdo_mysql \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd \
    && pecl install imagick \
    && docker-php-ext-enable imagick \
    && pecl install xdebug \
    && docker-php-ext-enable xdebug

ADD ./php.ini /usr/local/etc/php/php.ini

RUN wget https://getcomposer.org/installer -O - -q \
    | php -- --install-dir=/bin --filename=composer --quiet

RUN usermod -u 1000 www-data && groupmod -g 1000 www-data

WORKDIR /var/www

USER 1000:1000

CMD ["php-fpm"]



Поправленный пример Dockerfile

FROM php:7.1-fpm

ARG USER_ID
ARG GROUP_ID

RUN apt-get update && apt-get install -y \
        git \
        curl \
        wget \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng-dev zlib1g-dev libicu-dev g++ libmagickwand-dev --no-install-recommends libxml2-dev \
    && docker-php-ext-configure intl \
    && docker-php-ext-install intl \
    && docker-php-ext-install mbstring zip xml gd mcrypt pdo_mysql \
    && pecl install imagick \
    && docker-php-ext-enable imagick \
    && pecl install xdebug-2.5.0 \
    && docker-php-ext-enable xdebug

ADD ./php.ini /usr/local/etc/php/php.ini

RUN wget https://getcomposer.org/installer -O - -q \
    | php -- --install-dir=/bin --filename=composer --quiet


RUN usermod -u ${USER_ID} www-data && groupmod -g ${GROUP_ID} www-data

WORKDIR /var/www

USER "${USER_ID}:${GROUP_ID}"

CMD ["php-fpm"]


При запуске контейнера из данного образа пользователь www-data получает uid=1000, gid=1000. Обычно такие права у первого созданного пользователя в операционной системе Линукс. И, именно с такими правами будут работать наши php-fpm контейнеры. Буду очень благодарен, если кто-то подскажет как можно работать без костылей с правами доступа в докер.

При запуске контейнера из данного образа пользователь www-data получает uid и gid, которые будут переданы извне.

Также в комментариях поднималась тема: зачем вообще менять права пользователю www-data, чем не устраивают стандартные права 33. Только одним: когда мы зайдем в контейнер, и создадим, например, файл миграции, то на машине хоста владельцем этого файла будем не мы. И каждый раз надо будет запускать что-то вроде
 sudo chown -R user:user ./


И вторая небольшая проблема: для корректной работы Xdebug необходимо прописать верный ip адрес для машины хоста. У каждого члена команды он разный. 127.0.0.1 не катит. И тут нам на помощь приходит сам докер. Например, мы можем явно сконфигурировать сеть — 192.168.220.0/28. И тогда наша машина всегда будет иметь адрес 192.168.220.1. Этот адрес мы будем использовать как для настройки PhpStorm, так и для настройки других приложений. Например, при работе с MySql.

Сам docker-compose.yml, после учета замечаний, выглядит так:

version: '3'
services:
    php71-first:
      build:
        context: ./images/php71
        args:
          - USER_ID
          - GROUP_ID
      volumes:
        - ./www:/var/www
        - ./aliases/php71/bash.bashrc:/etc/bash.bashrc
      environment:
        XDEBUG_CONFIG: "remote_host=192.168.220.1 remote_enable=1 remote_autostart=off  remote_port=9008"
        PHP_IDE_CONFIG: "serverName=first"
      networks:
        - test-network
    php71-two:
      build:
        context: ./images/php71
        args:
          - USER_ID
          - GROUP_ID
      volumes:
        - ./www:/var/www
        - ./aliases/php71/bash.bashrc:/etc/bash.bashrc
      environment:
        XDEBUG_CONFIG: "remote_host=192.168.220.1 remote_enable=1 remote_autostart=off  remote_port=9009"
        PHP_IDE_CONFIG: "serverName=two"
      networks:
        - test-network
    nginx-test:
      image: nginx
      volumes:
        - ./hosts:/etc/nginx/conf.d
        - ./www:/var/www
        - ./logs:/var/log/nginx
      ports:
        - "8080:80"
      depends_on:
        - php71-first
        - php71-two
      networks:
        test-network:
          aliases: # алиасы нужны если нужно общаться внутри сети между хостами. Например, если вы используете api
            - first.loc
            - two.loc
#    mysql:
#      image: mysql:5.7
#      ports:
#        - "3306:3306"
#      volumes:
#        - ./mysql/data:/var/lib/mysql
#      environment:
#        MYSQL_ROOT_PASSWORD: secret
#      networks:
#        - test-network
networks:
  test-network:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 192.168.220.0/28


Мы видим, что в данном конфиге создаются два контейнера php71-first и php71-two, на основе одного образа php:7.1-fpm. У каждого контейнера свои настройки для Xdebug. Каждый отдельно взятый контейнер будет слушать, для дебага, свой порт и свой сервер.

Также, обращаю ваше внимание на директивы
        args:
          - USER_ID
          - GROUP_ID


Без этих переменных образ php-fpm не запустится. Вопрос: как их передать в docker-compose.yml? Ответ: так как удобнее вам. Можно при запуске:
USER_ID=$(id -u) GROUP_ID=$(id -g) docker-compose up -d

Можно прописать эти переменные в файле .env, который лежит на одном уровне с файлом docker-compose.yml
USER_ID=1000
GROUP_ID=1000

Мне больше нравится вариант с .env файлом. Конечно-же можно использовать Makefile. Кому как больше нравится.

Полный код для демо версии выложен на гитхабе.

Листинг демо проекта:



Пробежимся, кратко, по листингу проекта.

Каталог aliases -> php71 -> bash.bashrc. Спорный момент. Я предпочитаю общаться с php-fpm контейнерами через алиасы.

Данный файл пробрасывается в docker-compose.yml: — ./aliases/php71/bash.bashrc:/etc/bash.bashrc
Стандартный инструмент Линукса.

Каталог hosts — конфигурационные файлы для Nginx. В каждом конфиге прописан свой контейнер php-fpm. Пример:

server {
    listen 80;
    index index.php;
    server_name first.loc;
    error_log  /var/log/nginx/first_error.log;
    root /var/www/first.loc;

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

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        # контейнер php-fpm
        fastcgi_pass php71-first:9000;
        fastcgi_index index.php;
        fastcgi_read_timeout 1000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

Каталог images — инструкции для сборки образов php-fpm, каталог mysql — храним базы, каталог www — все наши web проекты, в нашем примере first.loc и two.loc.

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

Осталось корректно настроить PhpStorm для каждого из проектов. При настройке мы должны прописать порт для дебага и имя сервера в нескольких местах.

Создаем проект в PhpStorm







Настраивать будем разделы меню
— PHP (необходимо верно прописать CLI Interpreter),
— Debug (меняем порт на 9008, как в файле docker-compose.yml),
— DBGp proxy (IDE key, Host, Port),
update Спасибо хаброюзеру CrazyLazy за важное замечание. Пункт меню DBGp proxy настраивать не надо.
— Servers (необходимо верно указать имя сервера, как в файле docker-compose.yml, и use path mappings)



Все дальнейшие скрины буду прятать под спойлер.

Настраиваем CLI Interpreter из docker-compose.yml файла
Хитрого ничего нет — важно, при настройке выбрать нужный образ, и верно прописать имя сервера. По умолчанию имя сервера Docker, у нас оно своё.



















Настраиваем раздел меню Debug
Опять же всё прописываем из настроек docker-compose.yml для конкретного контейнера. На этом же шаге валидируем как работает наш дебаг.






Настраиваем раздел меню Servers
Важно правильно прописать use path mappings, имя сервера опять же берем из настроек





Уходим из раздела меню File -> Settings, идем в раздел меню Run -> Edit Configuration, создаем Php Web Page
Сервер выбираем наш, созданный на предыдущем шаге.







Ну, собственно и всё. Написано много букв, вроде бы всё непросто


На самом деле — главное понять очень простую вещь. Благодаря технологии докера мы можем запустить все наши рабочие приложения в едином пространстве, но с разными настройками для Xdebug. Каждое приложение работает в своем контейнере, и нам остаётся аккуратно прописать настройки для каждого приложения в PhpStorm.

И на выходе мы получаем чудесную картину.

1. Клонируем репозиторий на гитхабе. Создаем .env файл с переменными
USER_ID=ваш uid
GROUP_ID=ваш gid


2. Прописываем узлы first.loc и two.loc в файле /etc/hosts

127.0.0.1 first.loc
127.0.0.1 two.loc

3. В папке с гитом запускаем команду docker-compose up -d

4. Настраиваем оба проекта first.loc и two.loc в PhpStorm, так как описано выше, и запускаем оба проекта в PhpStorm. Т.е. у нас открыто два окна PhpStorm, с двумя проектами, каждый из них слушает входящие соединения (трубка включена).

5. В проекте two.loc ставим точку останова на второй, например, строке. В первом проекте first.loc запускаем http запрос из файла http.http

И о чудо! Нас перекидывает во второй проект, на нашу точку останова.

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

Что-то вроде:

alex@alex-Aspire-ES1-572 ~ $ php71first 
www-data@a0e771cfac72:~$ cdf
www-data@a0e771cfac72:~/first.loc$ php index.php 
I'am first host
www-data@a0e771cfac72:~/first.loc$

Где php71first — алиас на машине хоста:

alias php71first="cd ~/docker_git && docker-compose exec php71-first bash"

cdf — алиас, который работает в контейнере. Выше я писал о том, что для общения с контейнерами предпочитаю использовать алиасы.

На этом всё, конструктивная критика, замечания приветствуются.

P.S. Хочется выразить огромную благодарность Денису Бондарю за его статью PhpStorm + Docker + Xdebug, которая была отправной точкой для написания данного туториала.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 79

    0

    Зачем настраивать DBGp Proxy если 1) вы его нигде не используете и 2) он вам попросту не нужен здесь?

      0
      Спасибо. Это называется инерция мышления. DBGp Proxy настраивать абсолютно ни к чему. Чуть позже внесу правки в статью.
        0
        Поправил. Спасибо еще раз за важное замечание
        +2

        Мне кажется таких статей тысяча и одна и все с одним посылом — как запустить hello-world.php.
        Было бы круто описать работу с k8s, какие-нибудь траблы по пути познания докера и способы решения.

          0
          Облачные дебагеры в k8s для ловли багов по куберподам?
          Месье знает толк в извращениях! Но вообще классно что даже такую задачу можно решить докером и настроить рабочее окружение на свой вкус. Другое дело что в принципе такой дебагинг — занятие крайне сомнительно и лучше покрывать тестами узкие места. На то приложения и находятся отдельно для разделения логики. А так выходит логику разнесли, а дебажить все равно будем в едином флоу привязывая их друг к другу?! Почему тогда не слить вместе в одну репу и усложнять?
            +2

            Мой посыл в том, что статей вроде "Docker + php" достаточно много.
            Но мало статей, как развернуть все это в проде, как масштабировать, как обеспечить "целостность данных", элементарно взаимодействие между сервисами и т.д.

          0
          Вместо написания алисов рекомендую использовать Makefile.
            0
            Спасибо за рекомендацию. Пробовал, лично у меня не пошла работа с Makefile. Не зря же я написал в статье, что это очень спорный момент.
            0
            Наши же приложения под гитом, и если мы дадим права 777 локально, то гит всё это запишет, и передаст на боевой сервер.

            git config core.fileMode false решает проблему с правами и гитом
              0
              Так оно так. Спасибо за комментарий. Но само действие давать на что-то права 777 меня глубоко нервирует.
                0

                До тех пор пока не понадобиться залить какой-нибудь исполняемый bash скрипт. Можно включить, залить и выключить, но как правило все проблемы с правами доступа решаются без chmod.

                0

                Недавно решал похожую задачу — органзиовать идентичную среду разработки нескольких проектов для всей команды, используюя Docker.


                С правами на файлы закостылил точно так же, как и Вы. Лучше решения тоже не смог найти, буду очень рад, если кто-то подскажет.


                Правда под каждый отдельный проект я решил поднимать свои контейнеры (nginx + php-fpm + mysql). Да, в этом случае появляется необходимость использования разных портов, но за счет этого появляется больше гибкости и изолированности. Долго размышлял над этим вопросов, и решил, что при использовании разных конфигов уменьшается связанность проектов между собой.
                Например, в вашем варианте, если допустить ошибку в конфиге nginx (например, в файле first.conf), то ляжет весь контейнер с nginx, а вместе с ним и все проекты, которые зависят от него.
                Тоже самое касается и контейнера с mysql. Например, разные проекты могут использовать разные версии MySQL (5.7 или 8.0). Имея отдельный контейнеры можно более гибко управлять отдельными проектами, не нарушая работу остальных.


                В docker-compose.yml вы передаете "XDEBUG_CONFIG", но в Dockerfile я не вижу использования этого. А как тогда это попадает внутрь контейнера? Специальный проверил базовый образ php:7.1-fpm, он тоже не ожидает увидеть такую перменную окружения.

                  0
                  Огромное спасибо за развернутый комментарий. Да, действительно, когда мы запускаем каждое приложение отдельно, мы можем быть более гибкими. Но, тут уже дело вкуса. В нашей команде все конфиги вылизаны, у нас очень непросто с конфигами для nginx, например. Но, мы спокойны — если к нам придет джун, то мы дадим ему 100% рабочую конфигурацию.

                  А как попадает XDEBUG_CONFIG внутрь контейнера? Точно так же как все другие переменные окружения (environment). Как это происходит на уровне приложения я не знаю. Я знаю только то, что докер нам позволяет настраивать контейнеры на лету, за что ему огромное спасибо. Изначально Xdebug в наших контейнерах не настроен вообще никак. Если бы мы развернули рабочее окружение без докера, нам мы пришлось прописывать настройки для Xdebug явно в файле php.ini, например. А тут докер нам позволяет всё это сделать на лету.
                    0
                    А как попадает XDEBUG_CONFIG внутрь контейнера? Точно так же как все другие переменные окружения (environment). Как это происходит на уровне приложения я не знаю. Я знаю только то, что докер нам позволяет настраивать контейнеры на лету, за что ему огромное спасибо. Изначально Xdebug в наших контейнерах не настроен вообще никак. Если бы мы развернули рабочее окружение без докера, нам мы пришлось прописывать настройки для Xdebug явно в файле php.ini, например. А тут докер нам позволяет всё это сделать на лету.

                    Все, я понял почему это работает. Это сам xdebug поддерживает передачу параметров через переменную окружения XDEBUG_CONFIG.
                    Из оф. документации xdebug:


                    All settings that you can set through the XDEBUG_CONFIG setting can also be set with normal php.ini settings.
                      0
                      Может быть параметры xdebug`а установлены в его локальном php.ini
                      ADD ./php.ini /usr/local/etc/php/php.ini
                        0

                        Нет, тогда какой смысл конфиг xdebug передавать через переменные окружения, и хардкодить их в php.ini?
                        В предыдущем комментарии я уже расписал почему это работает :)

                    0
                    с правами на файлы:
                    — в Dockerfile: ARG USERID=1000
                    — в docker-compose.yml USERID: $USERID
                    — В папке с докером надо сделать export USERID=$UID (баш подставляет в UID юзерайди. direnv у меня подхватывает конфиг с env)
                    — Дальше в Dockerfile useradd с нужным UID, USER название_юзера

                    И контейнер начинает делать файлы с тем же uid что и хостовой пользователь. Если не хочется заморачиваться то в Dockerfile ручками ARG менять. В такой схеме еще и контейнер не от рута работает, что поидее безопаснее (при подтягивании зависимостей, например).
                      0

                      Дело в том, что мне не понравился подход, в котором каждый член команды должен локально билдить образы. А в случае обновления Dockerfile (который можно положить под git) еще и потом перебилдивать заново. Для упрощения, я поднял свой приватный реестр для образов, один раз сбилдил свои образы и запушил туда, а членам команды предоставил docker-compose.yml конфиги, в которых уже просто указывается ссылка на наш реестр.
                      При таком подходе, тиммейтам не нужно делать никаких лишних телодвижений, достаточно просто выполнить одну команду "docker-compose up" и готовые образы просто выкачаются с нашего реестра. Это на много быстрее и проще для всех. А в случае обновления, я просто сам ребилдю образы, снова пушу в реестр и обновляю версии образов в docker-compose.yml (который находится под git). Таким образом, у всех членов команды автоматически обновятся их контейнеры (после git pull и перезапуска контейнеров, естественно).


                      Так что, на этапе сборки образов мне просто неизвестны UID будущих пользователей. Сейчас просто надеюсь, что там будут 1000, в инном случае решаю проблему по ходу их появления.

                        0
                        Очень интересный подход, спасибо. Именно это и будет следующим этапом на пути развития создания единой среды разработки для членов команды. Отлично!
                          0

                          Я потратил не мало времени, пока пришел к такой схеме.
                          Если вдруг понадобится совет — обращайтесь в ЛС, может чем смогу помочь ;)

                          0
                          Следующим этапом будет подключение этого реестра в цепочку ci\cd, чтобы сборки сами сохраняли в него артефакты и оттуда же их брали для тестирования. А потом можно будет оттуда же и деплоиться :) Тогда мало того что у вас локально у всей команды будут одни и те же образы (которые регулярно обновляются в реестре), так у вас еще и на проде будут те же самые образы, что даст вам еще больше уверенности при отладке — если что-то идет не так, вы просто выкачиваете из реестра образ и дебажите конкретный образ, это супер-удобно
                          0
                          Тоже так делал, потом понял, что можно то же самое, но без ARG и ребилда. Используя ENTRYPOINT, т.е. добавлять юзера и приводить в соответствие UID и GID не на стадии сборки, а на стадии запуска.
                            0

                            Кстати, хорошая идея. Я и так использую ENTRYPOINT и скрипты-хелперы для своих нужд (автоматизирую установку composer зависимостей и т.п. вещи). На этапе старта контейнеров можно чекать UID пользователя и если что апдейтить пользователя в контейнере. Спасибо за наводку.

                          0
                          Да, в этом случае появляется необходимость использования разных портов

                          В нашей фирме мы делаем огромное количество проектов и запоминать для каждого порт было бы ужасно, еще одиним решением было держать запущенным только один проект. И оба эти решения имеют свои минусы.
                          На помощь нам пришел Traefik, мы назначили каждому контейнеру домен вида project.domain.localhost и теперь когда нам надо работать над отдельным проектом достаточно написать его домен в браузере + .localhost что бы обратиться к бегущему локально контейнеру этого проекта.
                          0

                          Для того что бы не было заморочек с настройками сети, в контейнер с php прописываем XDEBUG_CONFIG: "remote_host= host.docker.internal remote_enable=1", а в конфиг Xdebug, в секции IP адреса так же указываем host.docker.internal. Это полностью решает проблему с разными IP

                            0

                            А оно работает в Linux? А то тут (https://stackoverflow.com/q/58539029/783119 — в комментариях) вот пишут что только в Mac & Windows

                              0
                              Спасибо за комментарий. К великому сожалению я не знаю как будет работать данный мануал не под Линукс. Вся наша команда сидит на Ubuntu-подобных OS, данный мануал писался именно для Линукса.
                                0

                                Это понятно. Я спрашивал о host.docker.internal, работает ли оно в Linux (так как на Mac & Windows работает).

                                  0
                                  В данной статье дана ссылка на blog.denisbondar.com/post/phpstorm_docker_xdebug. Денис пишет что инструкция host.docker.internal работает только для Mac и Windows. Как это на самом деле я не знаю.
                                0

                                В Линуксе не работает.
                                Я сделал костыль в виде промежуточного контейнера, который пытается отрезолвить host.docker.internal, если не выходит — берет ip default gw, и nat-ит на него весь диапазон портов.

                                  0

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

                                    0
                                    Мы делаем примерно так в докер-композе:
                                        environment:
                                          PHP_IDE_CONFIG: serverName=${XDEBUG_SERVER_NAME:-some_project.docker}
                                          XDEBUG_CONFIG: remote_host=${XDEBUG_HOST:-192.168.99.1}
                                    

                                    192.168.99.1 — это стандартный айпишник у коллег под маком для докера в virtualbox
                                    Если айпишник отличается, то в .env файле можно задать свой локальный айпишник, на котором ваш шторм (или другая IDE) будет слушать дебаг

                                    XDEBUG_HOST=10.22.33.44
                                    
                                      0

                                      Докер в виртуалбоксе? OMG! а зачем?

                                        0
                                        ну, тут много причин. первая — раньше работало только так, под мак не было нормально гипервизора, как и под винду и станартный docker4win и docker2mac поднимали все в виртуалбоксе. Сейчас с гипервизорами все хорошо, но остался второй момент — в виртуалбоксе можно иметь несколько докер-хостов, что удобно при параллельной разработке нескольких проектов. если у них общий пул портов (например 80+9000 в каждом проекте), то гораздо проще гасить и ресторить целиком докер хост, чем каждый раз гасить и ресторить.
                                    +1
                                    В Linux xdebug.remote_connect_back чудесно работает! Не нужно ничего отдельно резолвить.
                                      0
                                      В Linux xdebug.remote_connect_back чудесно работает! Не нужно ничего отдельно резолвить.

                                      Все так, но не в консоли. Если не нужно дебажить консольные скрипты, то при включенной директиве xdebug.remote_connect_back, директива xdebug.remote_host будет проигнорирована
                                    0

                                    Сижу на маке, поэтому не знал что работает не везде. Вот здесь вроде предложили решение, что бы на Linux заработало — https://github.com/docker/for-linux/issues/264

                                  0
                                  Докер очень медленный, не подходит для проектов с 1к+ файлов. Если на Симфони или Ларавеле еще можно работать с ним, то Magento 2 и Ко неюзабельны вообще.
                                  Пока не поправят скорость работы файловой системы рассматривать докер для разработки я бы не стал.
                                    0

                                    Это в какой ОС? На линуксе все прекрасно. На маке — ну, да, есть такое, но вполне терпимо с opcache. Насчёт винды я не в курсе, может, действительно все совсем плохо.

                                      0
                                      На маке все очень плохо. Ждать 5 минут до загрузки страницы — норма. Более-менее решил проблему через локальный nfs, который монтируется в контейнере.
                                      Тут даже не проблема php, nginx отдает статические файлы по 30-40 секунд.
                                        0

                                        Статические?.. Надо разбираться, во что упирается. У меня медленнее, чем с хостноды, раза в 2-3, но чтобы по 30 секунд… Наблюдал такое только в проекте, где scss на лету компилировался php-библиотекой, но это из разряда «не делайте так».

                                          0

                                          Сейчас еще подумал — может, в конфигурации вебсервера включен direct io/aio? Надо выключить.

                                          0
                                          Попробуйте «consistency: delegated» на Volumes поставить. Я почти не вижу разницы между локально и в контейнере. Laravel / phalcon.
                                          docs.docker.com/docker-for-mac/osxfs-caching
                                          0
                                          На Винде. Именно для Винды и Мака Докер является вынужденной мерой, для них в первую очередь решается проблема окружения.
                                          Уже сказали на счет Мака, я скажу на счет Винды. Реально первую загрузку страницы когда еще нет кэшей можно ждать минутами на системе 9900k samsung nvme 970 pro. На условном i3 и hdd наверное можно успеть пообедать пока загрузится страница.
                                          С кэшами и прочими оптимизациями получится так: в Линуксе 0,5 сек, в Винде 10 сек. И это не преувеличение. Работать с таким невозможно.

                                          Проблема глобальная, про нее всем известно, но пока решений нет. Возможно, вопрос решится с wsl 2.0.
                                            0

                                            Проблема известная и довольно старая. Тоже от неё страдаем. https://github.com/docker/for-win/issues/188


                                            WSL 2 уже зарелизили, но мне ещё не довелось опробовать. https://devblogs.microsoft.com/commandline/wsl-2-is-now-available-in-windows-insiders/

                                              0
                                              Да, да, помню этот Issue. Оттуда копировал команды и проверял у себя.
                                              Падение производительности получается настолько большое что проще уже поставить мини-сервер рядом и работать через сеть. Что, в прочем, я и сделал.
                                          0
                                          К меня не самый мощный ноутбук, но все проекты, включая БД на ssd диске.Все летает. Так что я бы не стал так категорично утверждать, что докер медленный сам по себе.
                                          0
                                          Используем в проекте примерно такое же решение, но так как uid у всех разный (часть сидит на никсах, часть на маке, где uid первого пользователя 501, часть на компах, оставшихся после ушедших людей), то сделали следующий костыль:
                                          * Есть файл из репозитория, например, composer.json.
                                          * Получаем uid/gid файла
                                          * Создаем пользователя user с нужными uid/gid
                                          * Стартовый скрипт запускается от рута, но тут же делает su user -c <скрипт для старта проекта>

                                          Тоже неудобно местами, иногда определяет неправильно права, но лучше решения не нашли
                                            0

                                            Иметь удобное локальное окружение — это удобство разработчика. Зачем унифицировать рабочий компьютер, снижая производительность труда? Унифицируйте лучше тестовые и продакшен среды.
                                            С использованием докера как раз можно позволить работать локально так, как нравится.


                                            Просто это не получится конкретно у вас, например потому что вы uid/gid прибиваете гвоздями в значение 1000.
                                            К тому же чем стандартный 33 тоже будет работать, если в коде позаботитесь. Популярные фреймворки в дев-режиме к докеру привыкшие и кеш с umask пишут.


                                            composer лучше устанваливать через COPY --from.


                                            Вы делаете слишком много лишнего, от чего можно избавиться.

                                              0
                                              Спасибо за комментарий. Сколько людей, столько и мнений. Нашу команду, пока всё устраивает в плане удобства. А по правам доступа — я же честно написал в статье, что это костыль. Можно вообще с ними не заморачиваться, дать 777 на файлы с кешем, папку assets и т.д.

                                              И все будет читаться и писаться.

                                              Но композер, например, ругается на работу под рутом. Все миграции, или иные файлы, которые вы создадите, когда зайдете в контейнер, будут принадлежать не вам, что очень неудобно. Так, что я пока работаю с костылем для решения проблемы прав доступа.
                                                0
                                                А по правам доступа — я же честно написал в статье, что это костыль.

                                                Так почему не написать хотя бы для статьи (если самим не надо) нормальный способ. который не будет костылем?

                                              0
                                              proctoleha, для того что бы не было проблем локально на хостовой машине с правами, как уже писали выше — достаточно удобно использовать Makefile. Вот маленький пример того, что сможет помочь с отсутствием файлов и папок с неправильными правами:
                                              CURRENT_ID=$([[ $(id -u) -gt 9999 ]] && echo "root" || id -u)
                                              CURRENT_GROUP=$([[ $(id -g) -gt 9999 ]] && echo "root" || id -g)
                                              DC := CURRENT_USER=${CURRENT_ID}:${CURRENT_GROUP} docker-compose
                                              build:
                                                          @$(DC) build
                                              start:
                                                          @$(DC) up -d
                                              stop:
                                                          @$(DC) down
                                              

                                              Данная конструкция работает как на Linux так и на Windows — правильно подставляя id пользователя и группы от имени которого идет работа.
                                              Если Windwos то uid и gid будут выше 9999, и там подставляем рута — с Windows это вполне себе ок, если нет — используем uid и gid текущего пользователя.
                                                0
                                                Спасибо за комментарий. Есть только одно маленькое но: наши контейнеры ничего не знают о пользователе от имени которого мы их пытаемся запустить. И также мне, лично, не надо запускать все контейнеры от имени текущего пользователя. Мне нужно запустить только контейнеры с php-fpm
                                                0

                                                концепция контейнеризации нам говорит: один контейнер — один процесс.
                                                один контейнер на nginx, один на php-fpm. Вроде все логично и хорошо.


                                                Но что первому что второму надо доступ к одним и тем же ресурсам (файлам) и мы подключаем том с ними


                                                volumes:
                                                        - ./www:/var/www

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


                                                И тут приходишь к мысли что ну никак не выйдет "12 factor app". Его не прикрутишь к условному куберу легко, без костылей.


                                                Может лучше плюнуть и запустить nginx и fpm в одном контейнере, а в замен получить артефакт какому хоть и требуются другие сервисы, но минимально он самодостаточен?


                                                если что сорри, я ненастоящий сварщик, просто не так давно пытался разобраться в теме php-fpm и так остались открытие вопросы по контейнеризации этого стека

                                                  0

                                                  Если статика и php-код разнесены по разным репозиториям, то вполне себе выйдет.
                                                  Если нет, то, наверное, логичнее использовать вместо php-fpm nginx unit (да, он недавно научился раздавать статику), а nginx-у оставить маршрутизацию, балансировку, терминирование SSL и вот это все (для разработки, кстати, не факт что это все вообще нужно).

                                                  0
                                                  Для проброса в докер uid & gid можно использовать:
                                                  # docker-compose.yml
                                                  web:
                                                    image: ruby:2.4
                                                    user: "${UID}:${GID}"

                                                  Где переменные берутся из окружения
                                                    0
                                                    Вместо
                                                    RUN wget https://getcomposer.org/installer -O - -q \ | php -- --install-dir=/bin --filename=composer --quiet

                                                    вы можете использовать зависимость из другого образа:
                                                    FROM composer:latest AS composer
                                                    FROM php:7.3.9-alpine3.9

                                                    COPY --from=composer /usr/bin/composer /usr/bin/composer
                                                    ......
                                                      0
                                                      Спасибо, буду иметь в виду. Попробую обязательно
                                                      +2
                                                      Для решения проблемы с правами я использую Docker entrypoint, переменные окружения и команду runuser:

                                                      docker-php-entrypoint:
                                                      #!/usr/bin/env bash
                                                      set -e
                                                      
                                                      if [ -z "${UID}" ]; then
                                                          echo "Необходимо задать значение переменной окружения UID"
                                                          exit 1
                                                      fi
                                                      
                                                      if [ -z "${GID}" ]; then
                                                          echo "Необходимо задать значение переменной окружения GID"
                                                          exit 1
                                                      fi
                                                      
                                                      
                                                      groupmod -g ${GID} hostuser
                                                      usermod -u ${UID} hostuser  > /dev/null 2>&1
                                                      
                                                      exec runuser -u hostuser -- "$@"
                                                      

                                                      Dockerfile:

                                                      COPY docker-php-entrypoint /usr/local/bin/
                                                      RUN chmod +x /usr/local/bin/docker-php-entrypoint
                                                      RUN useradd -m -U -s /bin/bash hostuser
                                                      ENTRYPOINT ["docker-php-entrypoint"]
                                                      CMD ["php", "-a"]


                                                      Команды будут выполняться внутри контейнера под hostuser, uid и gid которого настраиваются при запуске контейнера. Если не указать их — не запустится. С консольными командами помогает, с веб-сервером, возможно, тоже поможет. Можно попробовать в Entrypoint модифицировать uid и gid www-data (я пробовал, но не помню чем кончилось)))).

                                                      Юзаю один контейнер — один процесс (группа схожих процессов). Даже под консоль и веб-сервер разные контейнеры). docker-compose хорош.

                                                      Если кто-такое пробовал, или есть какие-то замечения, пишите, указывайте на недостатки. Свежий взгляд со стороны всегда полезен.

                                                      А чем 777 на ассеты и кеш не устраивают? По моему это хуже чем когда владельцем файлов с кодом является пользователь для запуска веб-сервера. Это гораздо более опасно. Из каталогов с ассетами и кешем просто выполнение php запретить и всё.
                                                        0
                                                        Огромное спасибо за комментарий, обязательно покручу ваше решение.
                                                        А чем 777 на ассеты и кеш не устраивают? По моему это хуже чем когда владельцем файлов с кодом является пользователь для запуска веб-сервера. Это гораздо более опасно. Из каталогов с ассетами и кешем просто выполнение php запретить и всё.


                                                        Почему не устраивает. Все устраивает кроме одного: вот мы зашли в контейнер, и создали миграцию, или иной файл, находясь в контейнере. И от того, что на нужные папки у нас стоят права 777, нам легче не станет. Если не шаманить с правами, то владельцем этого файла будет суперпользователь root, и когда мы вернемся на машину хоста, то нам придется совершать определенные действия, чтобы работать с такими файлами.
                                                          0
                                                          Поигрался я с вашим рецептом, в принципе всё написано верно. Но, давайте конкретизируем задачу, о которой речь идет в статье.
                                                          Для контейнеров на образе php-fpm необходимо изменить права доступа таким образом, чтобы мы могли заходить в эти контейнеры, и создавать файлы с правами, которые должны совпадать с правами текущего пользователя.
                                                          Важно: в таком контейнере уже есть пользователь www-data, от имени которого и работает демон php-fpm.

                                                          Что произойдет, если использовать ваш рецепт в данном конкретном случае? Контейнер запустится от имени суперпользователя root, после запуска отработает docker-php-entrypoint, будет создан пользователь hostuser c нужными правами, и php-fpm попытается запуститься от имени этого пользователя, и вылетит с ошибкой. Данный процесс, php-fpm, может запустить только пользователь www-data. Можно конечно как-то подшаманить, но зачем?

                                                          Проще просто передать uid и gid текущего пользователя в докерфайл, и использовать
                                                          RUN usermod -u ${USER_ID} www-data && groupmod -g ${GROUP_ID} www-data при сборке. Т.е. не жестко приколачивать гвоздями uid и gid в докерфайле, как у меня, сейчас в статье, а передать их извне.

                                                            0
                                                            А, понял. Нужно чтобы веб-сервер в контейнере писал файлы с uid/gid что и текущий пользователь хозяйской системы. В этом случае можно usermod www-data (а hostuser вообще не добавлять) в ENTRYPOINT скрипте делать, тогда пересборка не потребуется. Я посмотрю, как это сделать, чтоб Apache или php-fpm в этом случае норм запускались, отпишусь попозже.
                                                              +1
                                                              Сделал маленький шаблончик. php-fpm пишет под uid и gid юзера, в консоли полноценный юзер, чтоб композер работал без проблем.
                                                                0
                                                                Отличный и очень познавательный пример. Очень. Спасибо огромное. Может тогда подскажешь (убедительно прошу: давай перейдем на ты) — как оптимально масштабировать твой пример на несколько проектов? С твоей точки зрения? У меня пока просто тупая мысль — прописывать отдельно php-fpm, и php-cli для каждого проекта.
                                                                  +1
                                                                  У меня такие же мысли) Я копирую эти файлы на старте, ведь часто в проектах бывают свои оригинальные docker-зависимости, напрмер в одном проекте может быть mysql, в другом postgres, в третьем еще нужен redis или очереди. Все это добавляется в docker-compose.yml, так что делать библиотеку какую-то смысла особого не вижу, если только под что-то наподобие composer create-project, чтобы консольная команда клонировала репозиторий и производила начальную настройку.
                                                                    0
                                                                    Только такой маленький вопрос ): а зачем использовать такую экзотическую команду в docker-php-entrypoint:
                                                                    exec runuser -u hostuser -- "$@"

                                                                    Чем не устраивает простое
                                                                    su hostuser

                                                                    У меня баш ругался на нее. Хотя и работал.
                                                                      0
                                                                      Вот не подскажу, не помню, давно пользуюсь runuser. Может просто не догадался использовать su)
                                                            0

                                                            Реализация с адресацией выглядит слегка костыльной, рассматривали ли вы traefik если да то почему он вам не подошёл?

                                                              0
                                                              Спасибо за комментарий. Мы не рассматривали traefik. Буду очень благодарен, если вы объясните в чем костыльность адресации?
                                                                0
                                                                Прошу прощения за сумбурный коментарий, писал его еще до первой кружки кофе.
                                                                У меня стояла подобная задача и мне показалось ваше решение слегка усложненным и недостаточно автоматическим. Поэтому хочу написать отличия от вашего решения и спросить что вы о них думаете.

                                                                1.
                                                                Например, мы можем явно сконфигурировать сеть — 192.168.220.0/28.

                                                                Можно избежать конфигурации подсети, если вы собираетесь использовать makefile. Тогда достаточно на команду поднятия контейнеров передать что-то подобное: XDEBUG_REMOTE_HOST := $(shell hostname -I | grep -o "^\S*") и заменить в компоузе на remote_host=${XDEBUG_REMOTE_HOST}
                                                                Минусы: Не будет работать на windows.

                                                                2.
                                                                2. Прописываем узлы first.loc и two.loc в файле /etc/hosts

                                                                Если использовать трафик и домены типа your.domain.localhost, то можно избежать пункта с изменением файла хостс.
                                                                Плюсы: При добавлении/удалении сервисов не надо будет заставлять всех менять файл хостс т.к. вся конфигурация трафика будет прямо в репозитории.
                                                                  0
                                                                  Спасибо за развернутый ответ. У нас немного разные задачи, и разные, соответственно, подходы. В нашей компании, на сейчас, 6 боевых проектов, и их никогда не будет много. И нам проще делать так, как написано в статье. Но, у вас очень интересный подход. Спасибо еще раз. Для полного ответа мне не хватает квалификации. Буду думать, учиться.
                                                              0
                                                              с правами я решил проблему через bindfs
                                                                0

                                                                а Вы пытались настроить xdebug используя xdebug.remote_connect_back, а не вот это вот всё с ip?

                                                                  0
                                                                  Нет, не пытался. Буду благодарен за рабочий пример.
                                                                    0

                                                                    как-то так:


                                                                    xdebug.remote_connect_back = 1 # включаем обратное подключение
                                                                    xdebug.remote_enable = 1
                                                                    xdebug.remote_port = 9000 # изменяем на нужный порт

                                                                    не пробовал в такой конфигурации как у Вас, но для одного сервиса работает хорошо и не надо выдумывать ничего с сетью внутри контейнера и биндить ip адрес

                                                                  0
                                                                  ошибочный комментарий, не там разместил
                                                                    0

                                                                    Может глупый вопрос. А чем плох запуск nginx и php-fpm от рута? Просто у меня 3 сервиса nginx не помню с каким юзером, php-fpm c www-data и один консольный воркер php-cli который от рута запускается.

                                                                      0
                                                                      Конкретно в вашем случае: php-cli работает от рута. Вы заходите и делаете Composer update, например. Он начнет ругаться, и правильно, с моей точки зрения, что композер под рутом запускать крайне не рекомендуется. Это первое.
                                                                      Второе: вы заходите в php-cli контейнер, и создаете из консоли, какой-либо файл, например, файл миграции. Кто будет его владельцем? Суперпользователь root.
                                                                      Сможете вы с этим файлом просто так работать, когда вернетесь на машину хоста? Нет. Вам придется делать лишние движения
                                                                        0

                                                                        В php-cli даже нет composer, он просто использует vendor из php-fpm.
                                                                        Все миграции, composer install я выполняю через такую команду docker-compose exec -u (id -u) php-fpm <комманда> все файлы имеют владельца текущего пользователя.

                                                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                    Самое читаемое