docker php ext enable

Очередная статья про Docker для новичка [nginx + php-fpm + postgresql + mongodb]

docker php ext enable

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

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

Добро пожаловать под кат!

Итак, все что я опишу ниже, я буду делать на ноутбуке известной «фруктовой компании», но так как ранее я делал тоже самое на VDS под управлением Centos 7, то я буду делать небольшие лирические отступления с описание, как я это делал на VDS.

Начнем мы естественно в регистрации на docker hub, который будет выступать в качестве системы контроля версий, но только для наших контейнеров. Docker hub при бесплатном использовании позволяет иметь только 1 приватный репозиторий, поэтому мы будем каждый отдельный образ помечать соответствующими тегами — nginx, php7-fpm. Я не буду описывать создание репозитория, думаю, ни у кого с этим проблем не возникнет.

Теперь мы можем установить сам docker на нашу рабочую станцию — в моем случае это описание находится здесь.

При установке Docker на Mac у нас сразу устанавливается docker toolbox, в котором есть необходимый нам инструмент docker-compose. Мы будем использовать его для объединения наших контейнеров о общее окружение.

Далее логинимся в нашем Докере:

Теперь нам доступны наши приватные репозитории (правда там пока пусто), там правда сейчас пусто, но мы это скоро исправим:)

Для своего проекта я сделал следующую структуру файлов:

Для проекта я буду использовать кастомные образы для nginx и fpm, поэтому их я вынес в отдельные директории. Кастомные образы описываются при помощи Dockerfile. Вот мои:

Скрипт из официального образа PHP от Docker Hub позволяет легко установить нужные расширения:

Вся подготовительная работа проведена проведена, теперь надо описать как наши контейнеры будут взаимодействовать. Как я уже писал, делать это мы будем через docker-compose и правила взаимодействия нужно описать в файле docker-compose.yml. Вот мой:

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

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

Теперь наши контейнеры управляются через docker hub и Dockerfile’ы нам больше не нужны.
Исправим docker-compose.yml:

А вот теперь я понял, что забыл добавить расширение pcntl для php. Но это легко поправить.
Для начала подключимся к нужному контейнеру:

И добавить необходимое расширение:

Отлично, в контейнер добавили, но мы же хотели использовать docker hub в качестве VCS — значит надо закомитить изменения:

Добавим еще контейнеры баз данных (postgresql и mongodb):

И теперь выполняем:

Докер добавит нам новые контейнеры к уже запущенным. Но я открыли порты для внешнего доступа, но мы указали пароль только для PostgreSql, нужно тоже самое сделать и для mongodb — как это сделать (и не только) подробно описано здесь.

Базы данных из php теперь доступны по хостам postgres и mongo соответственно, т.е. для подключения, например, к mongodb мы должны написать следующее:

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

Источник

Docker + php-fpm + PhpStorm + Xdebug

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

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

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

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

docker php ext enable

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

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

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

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

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

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

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

Update

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

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

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

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

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

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

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

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

Также, обращаю ваше внимание на директивы

Без этих переменных образ php-fpm не запустится. Вопрос: как их передать в docker-compose.yml? Ответ: так как удобнее вам. Можно при запуске:

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

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

docker php ext enable

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

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

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

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

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

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

docker php ext enable

docker php ext enable

docker php ext enable

Настраивать будем разделы меню
— 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)

docker php ext enable

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

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

docker php ext enable

docker php ext enable

docker php ext enable

docker php ext enable

docker php ext enable

docker php ext enable

docker php ext enable

docker php ext enable

docker php ext enable

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

docker php ext enable

docker php ext enable

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

docker php ext enable

docker php ext enable

Сервер выбираем наш, созданный на предыдущем шаге.

docker php ext enable

docker php ext enable

docker php ext enable

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

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

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

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

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

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

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

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

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

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

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

Источник

Docker для локальной php разработки

В этой статье я расскажу несколько возможных вариантов настройки локального окружения для php разработки с использованием docker. Изначально я планировал написать более широкую статью, и расписать как локальную часть разработки, то есть установку всего на компьютере конкретного разработчика, так и устройство удаленного dev сервера на котором происходит тестирование проекта и сливается код от нескольких программистов. Но статья выходит слишком большая, поэтому в данной статье мы рассмотрим только локальное окружение.

В статье я больше буду акцентироваться именно на windows системе (в примере все работает на windows 10). Однако на mac насколько я понимаю все работает примерно так же, там тоже поднимается отдельная виртуалка с докером, на которой уже запускаются контейнеры. На линуксе контейнеры запускаются безо всяких виртуалок, прям на месте, так что для линуксоидов все будет даже еще проще чем описано в статье. Не стоит рассматривать описанное в статье окружение как готовый рецепт, пусть все работает при правильной реализации, но это просто пример, только один из возможных вариантов.

Что я вообще хочу от докера при разработке.
Во первых, мне нужна локальная копия сайта, именно на этой локальной копии я буду смотреть как собственно работает код. Эта локальная копия должна открываться по красивому адресу, например если основной рабочий сайт это example.com, то моя локальная копия должна открываться по адресу my.example.com. Безо всяких портов типа my.example.com:8747. Я часто открываю сразу несколько проектов и все открытые проекты не должны конфликтовать, то есть например у меня открыто два окна phpstorm в одном ведется разработка проекта example.com, во втором ведется разработка test.ru, и для каждого из этих проектов одновременно должны работать два локальных сайта my.example.com и my.test.ru.
Во вторых, мне под каждый проект нужны специальные настройки (mbstring, timezone) и расширения php (типа xdebug).
В третьих, мне нужны внешние инструменты типа phpmyadmin для работы с БД проекта или node.js для работы с gulp/webpack.
И наконец в четвертых, все это должно быстро подниматься для новых разработчиков которые подключаются к проекту.

Поехали. Попробуем это все реализовать 🙂

TL;DR
Используем docker-compose. В compose используем официальные контейнеры с docker hub, плюс небольшие правки в dockerfile. Докер в винде работает на виртуалке. Добраться до контейнеров внутри виртуалки можно тремя способами — пробросом портов, открытием публичного доступа по IP к контейнерам (плюс внутренний dns который автоматически строит пути), и reverse proxy на входе в виртуалку. Лучший способ работы с контейнерами внутри виртуалки это reverse proxy.

Дополнительное уточнение

В статье я постарался написать все довольно подробно. Но совсем уж до нуля разжевывать у меня не получится. Например про установку докера я ничего рассказывать не буду. В интернете очень много статей на эту тему, для начала прочитайте getting started на официальном сайте докера. Про то что такое volumes и как они работают я немного пояснил, но вам лучше тоже изучить этот вопрос отдельно. Аналогично про dockerfile, docker-compose и т.д. Лучше всего пробовать развернуть свое локальное окружение и все непонятные моменты искать в поисковиках или спрашивать в комментариях.

Кроме того желательно понимать зачем вам вообще локальное окружение именно на докер. Для большинства людей хватит виртуалок, обычного удаленного хостинга или любых других уже готовых сборок типа denver, xampp, open-server и т.д. Их достаточно установить обычным инсталлером винды и у вас будут работать локальные сайты. Для разработки большинства проектов ничего больше и не нужно. Пробовать организовать локальное окружение на докере стоит только если вы понимаете зачем это делаете, например вы хотите построить процесс continous integration, или вам нужны программы определенных версий, ну либо если вам нравится сам докер и принципы его работы, как по мне тоже существенная причина 🙂

Если вы будете разворачивать собственное окружение, то поймете, что там все не так просто и придется изучить много тонкостей. То что на хостингах все работает по умолчанию, это заслуга админов хостинга, здесь же вам придется настраивать все практически с нуля. Если вам интересно программирование и совсем не интересно администрирование серверов, то докер не для вас. Даже просто первый запуск сайта в докер окружении вскрывает целый пласт новых знаний, которые вам придется изучить. Без которых нет никакого смысла что-либо делать вообще. Например вам придется более детально разобраться в расширениях php, чем отличаются расширения mysql и mysqli? А без понимания у вас не факт что получится заставить работать тот же битрикс с БД на докере. И таких вопросов довольно много, это просто один пример с которым вы наверняка столкнетесь.

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

Что такое docker?

Для тех кто совсем впервые услышал слово docker сделаю небольшое вступление. Docker это программа для управления контейнерами приложений. То есть нужен вам например сервер апач с модулем php, вы берете чистый линукс, ставите на этот чистый линукс собственно апач с нужными настройками и замораживаете все это дело. Вот этот вот замороженный кубик можно считать образом контейнера. Как будто бы чистая операционка с каким-то приложением. И везде где вам нужен апач вы теперь не ставите его с нуля, а создаете контейнер на основе собранного заранее образа. Так можно завернуть вообще все что угодно в образ. Нужен nginx, берем чистую ОС ставим только nginx и вот у нас образ c nginx. Аналогично образ с базой данных или любым другим приложением.

Докер позволяет наследовать образы. Есть много базовых образов с операционными системами, с ubuntu, debian, centos и т.д. Если вы хотите сделать свой образ с каким то приложением, вы просто говорите от какого базового образа будете наследоваться и далее уже поверх базы приделываете что-то свое. Более того, можно наследоваться не только от базового образа, но и от любого другого. Например вы находите готовый образ апача с модулем php, но в нем не хватает расширения mbstring, вы наследуетесь от этого найденного образа с апачем и добавляете в него нужное расширение. Не нужно проходить всю цепочку установок от голой ОС до апача, можно сразу править образ с апачем и потом на основе правленного образа создавать свои контейнеры. Во многих случаях даже не придется править сам найденный образ, разработчики образа обычно закладывают широкие возможности по кастомизации. Например образ mysql отнаследован от debian, разработчик образа поставил на голом debian mysql и дал возможность сразу при старте контейнера создавать базу данных с произвольным названием, произвольным пользователем и паролем, для добавления базы данных не нужно создавать новый образ, достаточно сконфигурировать контейнер в момент его создания.

Старт проекта

Любой проект начинается с папки. Создадим папку localdev в любом месте. В корне папки localdev сразу создайте пустой git репозиторий. В этой папке мы собственно и будем вести всю разработку php проекта. Ранее все проекты я разрабатывал на виртуалках, то есть на отдельной виртуалке с LAMP (Linux, Apache, Mysql, PHP) я поднимал сайт, который был доступен с хоста по IP адресу виртуалки. Локально на винде я хранил только нужные для разработки файлы, а не полную копию проекта, полная копия была только на самой виртуальной машине к которой я подсоединялся по SSH в IDE и менял там файлы. Поправил что-то, отправил правку вручную (ctrl+shift+S), проверил работу в браузере и дальше что-то пишешь. Пробовал поднимать vagrant, но он мне показался не очень удобным, хватало обычных виртуалок. В связи с этим в папке проекта у меня обычно прямо в корне лежали уже файлы сайта, например localdev/index.php это главная страница сайта. Однако сейчас в связи с переходом на docker мне кажется более удобным двухуровневая структура проекта. Но при этом против корня сайта сразу в корне проекта я ничего не имею 🙂

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

Далее опять таки в корне проекта создайте папку html. Именно эта папка будет корнем сайта. Файл /localdev/html/index.php будет главной страницей сайта.

Docker compose

Приступим собственно к сборке окружения. Выше я пояснял что такое контейнеры. Однако для нормальной работы сайта нам понадобится сразу несколько контейнеров. Как минимум контейнер php и контейнер с базой данных, но по факту их будет больше. Для конкретного сайта нам нужна группа контейнеров работающая совместно. Именно для совместного запуска группы контейнеров существует утилита docker-compose. Она запускает контейнеры в соответствии с заранее описанным файлом конфигурации. В статье я буду рассматривать вот такой файл docker-compose.yml:

Еще раз повторю, это просто демонстрация, а не законченная конфигурация контейнеров которую я рекомендую использовать. Но для начала можно взять и этот вариант. Если своего варианта у вас пока что нет, то создайте пустой текстовый файл localdev/docker-compose.yml и вставьте туда вышеприведенный листинг. В дальнейшем вы в любом случае будете сами писать такие файлы с нуля или вносить нужные правки.

Если я открою проект в phpstorm и запущу всего одну команду

То у меня заработает сайт my.example.com (который сразу, в течение пары секунд будет доступен в браузере) и я уже могу писать код и тестировать. Но у вас этот файл скорее всего не запустится, так как пока что не настроены сети, нет нужных конфигов в папке localdev/docker и т.д.

Рассмотрим что вообще написано в этом файле по порядку. Первая строка

Говорит программе docker-compose какая версия конфига используется. Третья версия нужна по сути только для docker swarm, которого здесь нет. В примере будет достаточно и второй версии. Но пусть будет третья 🙂

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

Nginx

Скопирую отдельно из общего файла конфига выше:

Строка image: nginx говорит докеру какой образ использовать для старта контейнера. В данном случае используется официальный образ nginx из официального репозитория докера. Я не вижу смысла указывать версию nginx, пусть он всегда будет latest.

Конфиг nginx

Строка volumes говорит докеру, что при запуске контейнера папку /etc/nginx/conf.d/ нужно заменить на localdev/docker/nginx/ из папки проекта. Причем связь будет постоянная. Если внутри контейнера изменить файл в этой папке, то он мгновенно изменится и на хосте и наоборот. В папке localdev/docker/nginx/ у меня лежат два файла default.conf и phpmyadmin.conf. Они примерно похожи, так что приведу здесь только default.conf:

Суть конфига в том, что nginx слушает 80 порт и вообще все запросы к судбдомену my.* редиректит к другому контейнеру (в данном случае к контейнеру apache). В файле phpmyadmin.conf лежит примерно то же самое, только там nginx ожидает субдомен pma.* и если запрашивают именно этот субдомен, то редиректит к контейнеру phpmyadmin.

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

Подробнее о сетях я хочу рассказать в следующем большом блоке про доступ к контейнерам по IP, здесь же замечу, что контейнер nginx единственный контейнер который будет находиться сразу в двух сетях. Об этом нам говорит строка networks, где собственно указаны эти две сети front и backend. Вообще все контейнеры будут работать в одной сети backend, которая создается при запуске docker-compose up, и нам вообще не важно что там будет твориться. А сеть front нужна нам для того чтобы обслуживать публичные контейнеры, именно в сети front будет работать локальный DNS и в этой сети у нас будут не вся эта гора контейнеров, которая там вообще не нужна, а только один контейнер nginx. Благодаря этому сеть front будет чистая и красивая 🙂

Если же прямой доступ по IP до контейнеров вам не нужен, то в файле docker-compose.yml можно вообще убрать все упоминания о сетях. Для разработки с использованием обратного прокси отдельные сети это лишний элемент. Пусть все контейнеры будут только в дефолтной сети которую создает docker-compose по умолчанию.

Apache

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

Об этом говорит строка build. В директиве build указывается из какого dockerfile строить контейнер. В данном случае контейнер строится на основе localdev/docker/apache/Dockerfile

Первая же строка говорит нам о том, что этот dockerfile наследуется от официального образа php, где php установлен как модуль апача. По ссылке вы можете выбрать любой другой образ, например вам может быть интереснее запустить проект на php 7, и тогда вы наследуетесь уже от php:7-apache, или вам может быть апач совсем не нужен, и тогда наследуетесь от любого другого образа, где php работает в режиме php-fpm. Cписок меток приведен опять таки по ссылке. В данном случае я делаю окружение с php 5.6, более консервативное так сказать 🙂

Весь состав dockerfile сделан на основе документации официального образа php. Эти страшные строки типа libfreetype6-dev нужны для установки библиотеки gd, можете сами там чуть ниже по ссылке отмотать и посмотреть. Строки вида docker-php-ext-install, docker-php-ext-enable, docker-php-ext-configure взяты тоже из официальной документации. Создатели образа php любезно предоставили нам удобные скрипты для добавления своих расширений в контейнер. Достаточно вызвать их скрипт и параметром передать туда названия расширений. То есть мы берем их официальный образ и специальными скриптами чуть изменяем его, после чего билдим чуть измененный образ отнаследованный от официального.

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

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

Apache volumes

Еще один значимый для понимая всей статьи момент. Для апача в docker-compose.yml у нас прописано сразу два volume (два диска).

Первый диск маунтится в /var/www контейнера apache. То есть вообще вся папка проекта маунтится в /var/www, папка /var/www становится корнем проекта. Так как в конфиге апача DocumentRoot прописан на папку /var/www/html, то это значит что папка html из нашего корня проекта как раз попадает в DocumentRoot. И файл localdev/html/index.php будет лежать в /var/www/html/index.php, в контейнере. На уровень выше от DocumentRoot апача у нас будут лежать все служебные папки типа vendor композера. Если вам по каким то причинам не нравится папка html, то можете поправить это дело в конфиге апача, сделать отдельный volume для /etc/apache2/sites-enabled именно в этой папке внутри контейнера лежат конфиги апача, замените дефолтный 000-default.conf на свой собственный, где пропишите например папку public_html и тогда в корне проекта нужно будет переименовать папку html в public_html.

Второй диск из docker-compose.yml маунтится в /usr/local/etc/php/php.ini, по умолчанию в контейнере вообще нет php.ini, и мы подставляем дефолтный. Вот пример для php.ini

В данном случае я настроил только дату и mbstring. Ну плюс несколько основных параметров. Сюда можно прописать любые другие настройки и рестартовать контейнер. Официальный образ запустится и без этого файла вообще. В этом случае все параметры будут по умолчанию.

Mysql

Тут тоже ничего особо сложного

Наследуемся от официального образа mysql. Тут я думаю что стоит указывать прямо версию БД, не факт что при следующем обновлении новый образ будет иметь такую же структуру БД что и предыдущая версия, поэтому тут тэг latest лучше не использовать. На момент написания статьи официальный образ содержит версию 5.7, но на ней у меня при развертке резервной копии битрикса возникли сложности. Я посмотрел какая версия стоит на боевом сервере, там где работает сайт с которого делалась резервная копия, оказалось что там версия 5.6. Я сменил одну циферку в docker-compose.yml, рестартовал окружение одной командой и через несколько секунд повторная развертка резервной копии прошла без проблем. Так сказать мгновенная демонстрация гибкости.

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

Контейнер работает все так же во внутренней сети backend текущего окружения. При разворачивании резервной копии нужно указывать host mysql — «db», именно по адресу db:3306 будет работать cам mysql в контейнере после старта окружения. Хост db, база данных local, пользователь local, пароль пользователя local. Вы можете прописать любые другие параметры. Так как все контейнеры кроме nginx работают в сети backend, то по адресу db контейнер апача увидит именно вот этот конкретный контейнер mysql. Если вы запустите второе окружение параллельно и там будет сервис с таким же именем, никакого конфликта не случится. Тот параллельный апач будет видеть свой параллельный db.

PHPMyAdmin

Чтобы быстро смотреть БД проекта я ставлю локальный для проекта phpmyadmin. Тоже из официального образа phpmyadmin. Пусть он будет latest, ничего плохого в этом не вижу. В переменных окружения сразу конфигурируем контейнер, так чтобы он сразу при открытии подключался к нашему mysql серверу и не требовал пароль.

Строка «PMA_HOST=db» это адрес хоста mysql. Внутри сети backend все контейнеры видят друг друга по названиям. Сервис с базой данных в docker-compose.yml называется db, именно по этому имени его сможет найти любой другой контейнер. В данном случае контейнер phpmyadmin будет стучаться по адресу db:3306, то есть на 3306 порт, и сразу будет пробовать авторизоваться. Если параметры авторизации совпадают с тем, что мы в сервисе БД прописывали, то по адресу pma.example.com вы сразу увидите открытый phpmyadmin.

Создание front сети

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

Откройте консоль, я работаю в windows powershell. В консоли выполняем следующее:

Как мы видим в списке добавилась сеть front.

Если теперь выполнить

В папке с проектом, у нас поднимется окружение.

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

Доступ к контейнерам пробросом портов

Это самый простой способ добиться ответа от контейнеров. И это обычное первое чему учатся новички. Контейнер nginx если вдруг мы каким то образом сможем добраться до него слушает и отвечает по 80 порту. Но этот порт он слушает где-то в недрах виртуалки докера. Чтобы вытащить 80 порт контейнера с докером на хост можно прописать такое в docker-compose.yml

Я добавил новую секцию ports. И теперь у нас на 127.0.0.1:80 висит nginx из compose файла. Если мы пойдем и подправим файл etc/hosts. В винде он лежит в «c:\Windows\System32\drivers\etc\», файл hosts без расширения редактировать блокнотом. Допишем туда в конец следующие строки:

Теперь в браузере у нас по адресу my.example.com откроется апач, а по pma.example.com у нас откроется phpmyadmin.

В апаче папка html у нас пустая, так что там будет Forbidden. Если положим в папку localdev/html/ файл index.php

То у нас откроется страница с выводом phpinfo.

Не обязательно пробрасывать именно 80 порт на хосте. Можно в секции ports прописать «8745:80» и тогда 8745 порт хоста будет смотреть на 80 порт nginx. Для контейнера nginx вообще ничего не изменится, но на виндовом хосте нам теперь нужно будет открывать не просто my.example.com в браузере, а тот же самый адрес но уже с портом «my.example.com:8745». При открытии этого адреса отобразится тот же самый вывод phpinfo.

Свободных портов много. И теоретически можно запускать сколько угодно сайтов по разным портам хоста одновременно. Но серьезно, кому нравится смотреть на my.example.com:8745? Уродство же 🙂 Я уж не говорю что все ссылки на сайте не всегда эти порты не учитывают, и по клику браузер может открыть сайт безо всякого порта. Этот вариант я показал просто для полноты картины. Реально для php разработки его использовать не стоит.

Доступ к контейнерам docker по IP

Это другой вариант добраться до 80 порта nginx в недрах виртуалки. Попробуем достучаться до контейнера безо всякого проброса портов, прям по его IP адресу.

Создание статического маршрута до сети

Попробуем найти под каким же ипшником у нас поднялся контейнер nginx. Посмотрим список контейнеров

В списке контейнеров должен быть контейнер nginx. Я тут удалил лишние несолько колонок, чтобы посимпатичнее вывод docker ps смотрелся, чтобы по ширине влезло без переносов. У нас контейнер nginx есть, он запущен с ID 6622d28d888c, и именем localdev_nginx_1. Посмотрим конфиг контейнера

Там вылетит многостраничная текстовка, в данном случае нам интересна только вот эта часть

Как мы видим, контейнер работает сразу в двух сетях. В сети front IP контейнера 172.19.0.3, в сети localdev_backend IP контейнера 172.18.0.5. Если мы с хоста попробуем пропинговать IP контейнера в сети front, то есть выполнить в консоли ping 172.19.0.3, то мы нифига не увидим. Контейнер оказывается не доступен. Как сделать контейнер доступным по IP с хоста? Это на самом деле не тривиальная задача.

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

Вывод довольно здоровый, я оставил только значимую часть, там еще сверху и снизу от вырезанного куска по экрану текста. У вас скорее всего блок «Постоянные маршруты» будет пустой. У меня он уже создан, и поэтому команда route print его показывает.

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

Статический маршрут во внутреннюю сеть докера создан. Но это еще не все. Во всех новых версиях докера виртуалка с линуксом запрещает доступ по IP контейнера. Ранее это было возможно, но на текущий момент по дефолту запрещено правилами iptables. Официального метода не существует, разработчики докера считают что это никому не нужно / очень сложно реализовать. Для понимания проблемы почитайте вот это в официальной документации, раздел «Container communication between hosts».

Для фикса этого поведения нужно выполнить команду:

Вот. Теперь есть и статический маршрут до внутренней сети докера и правила на виртуалке разрешают такие подключения. Эта команда если вкратце изменяет правила iptables на виртуалке докера. Пробуем пропинговать контейнер по его IP из сети front, команда ping 172.19.0.3 теперь будет показывать корректный обмен пакетами.

Дополнительные пояснения по маршруту

Еще раз пройдемся по всем пунктам. Мы создали сеть front с драйвером bridge. Это все штатные команды докера, тут ничего особенного нет, все должно быть понятно. Подробнее разберем всю дальнейшую магию.

Контейнеры на винде стартуют на виртуалке. То есть докер создает виртуальную машину, и вообще все контейнеры запускаются именно на виртуалке. У виртуалки есть статический IP адрес, как правило этот адрес 10.0.75.2, вы можете безо всяких маршрутов его попробовать пропинговать. Этот адрес спокойно пингуется. Все сети которые создает докер создаются внутри виртуалки. IP контейнера nginx в сети front 172.19.0.3, это значит, что на докеровской виртуалке создана сеть в которой для контейнера nginx выделен определенный ip адрес. Во внешем мире об этом IP адресе никто не знает.

Когда мы создаем статический маршрут командой

Мы говорим винде, что маршрут до всех IP адресов начинающихся с цифры 172 надо строить через IP адрес 10.0.75.2. Если пользователю вдруг нужен ипшник 172.19.0.3, то винда будет пытаться достучаться до него через 10.0.75.2, или другими словами будет искать этот адрес внутри виртуалки.

И последняя магическая деталь касательно сети. Команда

На текущий момент я не знаю как обойти это ограничение, возможно в будущем разработчики все же реализуют настройку iptables каким нибудь внешним конфигом, или я может еще найду какое то другое постоянное решение. Пока что я других способов достучаться до контейнеров по IP не знаю.

Собственный DNS на docker

Ну вот вроде бы счастье, контейнеры пингуются, а сайт так и не работает нифига. Для начала пойдем и подправим файл etc/hosts. Допишем туда в конец следующую строку, ну либо подправьте уже существующую запись, если вы с пробросом портов экспериментировали:

То есть укажем прямой IP контейнера nginx. Как узнать этот IP я писал выше. У вас цифры могут быть другие.

Теперь открываем в браузере адрес http://my.example.com/ и если все было сделано верно, вы увидите результат работы апача. Это очень круто, однако что будет после рестарта окружения? Контейнеры поднимутся заново и IP адреса всех контейнеров будут другими. Нам поможет dnsdock

Dnsdock это DNS сервер для докер. Если запустить его внутри сети front, то он будет прям на лету смотреть какие контейнеры запущены и выдавать IP адрес контейнера по запросу. По дефолту он возвращает IP по какому то сильно замороченному адресу завязанному на образ контейнера, окружение и т.д. Нам же для целей локальной разработки пригодится другая его фишка — поиск контейнера по метке (label). Обратите внимание на блок label у контейнера nginx

Метка com.dnsdock.alias говорит нашему DNS серверу, что этот контейнер можно найти по указанным через запятую адресам. То есть если запросить у dnsdock IP для my.example.com, то он вернет IP отмеченного контейнера. Так как nginx у нас сразу в двух сетях, то изредка dnsdock будет возвращать IP nginx в сети backend и сайт работать не будет. Это лечится полным рестартом окружения через docker-compose down. После рестарта он обычно возвращает IP из сети front и все начинает работать нормально.

Давайте поднимем собственно сам контейнер dnsdock, выполним в консоли винды следующую команду

Команда достаточно длинная. Если кратко, то эта команда запускает контейнер dnsdock в сети front. Контейнер будет запускаться сразу при старте докера и будет самостоятельно рестартовать если вдруг по каким то причинам завершит свою работу. Контейнер будет отвечать на udp запросы на 53 порту (по этому порту работают все DNS) хоста, то есть он будет отвечать на все запросы по адресу 127.0.0.1:53. Кроме того, дополнительным параметром —nameserver мы говорим контейнеру, что в случае если он не нашел адреса внутри докера, то должен спросить указанный в параметре нормальный DNS сервер, в данном случае это IP адрес яндекс днс. Так же обращаю ваше внимание, что IP адрес внешнего ДНС указан вместе с портом, без порта не заработает.

Протестируем наш локальный DNS. Выполним в винде команду

Эта команда ищет указанный адрес у конкретного ДНС сервера. В данном случае она искала указанный домен на нашем локальном dnsdock. При этом если мы попробуем у этого же сервера спросить домен ya.ru, то dnsdock вернет нормальный внешний IP адрес яндекса.

Чтобы вообще все запросы шли к нашему локальному dnsdock и можно было открывать сайт в браузере, достаточно указать его в параметрах адаптера винды, в качестве DNS сервера по умолчанию. Если какой то адрес будет найден внутри докеровской виртуалки, то dnsdock переправит нас на докер, если ничего не будет найдено, то все отработает как обычно и у вас в браузере откроется полноценный интернет сайт. В качестве постоянного локального DNS я бы это решение использовать не стал, однако на время работы по программированию прописать конкретный dns не долго и потом так же быстро можно убрать. Ну еще как вариант вы можете на винде поставить acrylic dns proxy, это аналог линуксового dnsmasq, там можно настроить разные правила для работы с DNS серверами. Возможно еще какие то другие аналоги есть.

Доступ к контейнерам через nginx reverse proxy

Для этого уже есть готовый образ jwilder/nginx-proxy. По функционалу он похож на dnsdock, в том плане, что так же слушает докер и на лету генерирует какие то правки. Если мы создали новый контейнер с определенной переменной окружения «VIRTUAL_HOST», то этот прокси тут же сгенерирует nginx конфиг специально для этого контейнера (собственный конфиг, для обслуживания того стороннего контейнера, сторонний контейнер об этом конфиге ничего не знает) и мы по основному IP адресу виртуалки докера сможем увидеть результат. Давайте по порядку. Для начала повесим прокси на входе командой

Можно даже без проброса портов, в этом случае контейнер будет отвечать на все запросы по IP адресу виртуалки и на 80 порту. То есть по 10.0.75.2:80. Этот контейнер благодаря restart always будет запускаться сразу при старте докера.

Теперь добавим переменную окружения VIRTUAL_HOST в наш контейнер с nginx, в файле docker-compose.yml

И запустим окружение через

Последний шаг, в etc/hosts винды прописываем

Пробуем открыть my.example.com в браузере. Должен открыться phpinfo из файла localdev/html/index.php. По pma.example.com как обычно должен открыться phpmyadmin.

Суть метода еще раз

Еще раз другими словами повторю суть этого метода. Докер в винде работает на обычной виртуалке. У этой виртуалки есть IP адрес, по дефолту этот адрес 10.0.75.2. Мы вешаем специальный докер контейнер с nginx по этому адресу, он начинает слушать 80 порт и уже от собственного лица спрашивает все остальные контейнеры внутри виртуалки. Какой из контейнеров будет спрашивать этот прокси от собственного лица определяется переменной окружения VIRTUAL_HOST. Прокси смотрит чего от него хотят, какой домен человеку нужен, далее смотрит вообще все контейнеры которые есть внутри виртуалки, если где-то там в недрах есть контейнер с нужным значением в VIRTUAL_HOST, то прокси от собственного лица спрашивает этот найденный контейнер абсолютно то же самое что спросили его, а результат собственного запроса возвращает наружу. Таким образом создается иллюзия, что сайт работает у самого прокси, хотя реально отработать там может десяток контейнеров где-то в глубине. Такой механизм и называется reverse proxy, или по русски обратный прокси.

Отладка php в docker через xdebug

Прямая отладка

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

После этого в php.ini пропишите следующее

В самом phpstorm нужено будет просто нажать кнопку «Start listening for PHP debug connections» и открыть нужную страницу в браузере. У вас в IDE появится запрос на подключение.

При включенной в расширении браузера отладке, каждое открытие страницы сайта будет открывать отладочную сессию в phpstorm. Если вам больше не нужна отладка, то либо на стороне phpstorm отключите прослушивание, либо в браузерном расширении отключите отладку.

Отладка через proxy

Чуть сложнее дело обстоит с отладкой в том случае, когда сайты у вас доступны через reverse proxy. Напрямую по IP к контейнеру вы достучаться никак не можете. В этом случае нам поможет специальный proxy который работает по протоколу DBGP. Вот есть статья по работе с этим прокси в phpstorm. Вот готовый собранный образ для докера с этим контейнером. Есть вроде бы и какие то поменьше образы, но пусть будет этот. Можете собрать собственный образ proxy, он там ставится всего в несколько команд.

Этот отладочный proxy нужно запускать следующей командой на хосте:

Команда запускает контейнер в сети host виртуалки, то есть этот контейнер будет видеть вообще все внутренние IP адреса на виртуалке, так что он сможет общаться с контейнером апача. Этот контейнер автоматически будет запускать при каждом запуске докера благодаря параметру restart always.

В php.ini для xdebug вам тоже будут нужны другие настройки

Мы тут убрали idekey и прописали жесткий IP адрес к которому php будет стучаться для отладки. IP 10.0.75.2 это адрес хоста, именно его мы в файле etc/hosts винды прописывали. Все контейнеры внутри виртуалки тоже по этому адресу могут общаться с хостом. То есть он виден как изнутри, так и снаружи виртуалки.

Известные баги под windows

В целом для разработки на винде докер вполне можно использовать. Однако же на винде он не такой стабильный как хотелось бы и скорее всего придется смириться с некоторыми багами. В этом разделе я расскажу про те, с которыми я столкнулся. Если вы знаете как это исправить, пишете в комментариях

Запуск контейнеров после перезагрузки

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

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

2. Зайти в настройки докера, в раздел доступа к дискам и нажать «Применить», чтобы обновить состояние доступа. И после этого выполнить

То есть после обновления настроек рестартовать сборку.

Подключение новых программистов

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

Возможно понадобится еще и четвертый шаг, развернуть базу данных сайта + докачать файлы которые не хранятся под гитом. Например для битрикса достаточно скачать резервную копию сайта и развернуть ее в localdev/html.

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

Итого

Возможно статью я еще немного дополню, есть дополнительные примеры. Однако основное что я хотел рассказать, в статье уже есть. Мы посмотрели пример настройки конфигурации для compose, где я обозначил несколько вариантов контейнеров — контейнеры собирающиеся из готовых образов с настройкой через переменные окружения типа phpmyadmin, контейнеры создающиеся билдом на примере апача с модулем php, контейнеры с конфигурацией через маунт (через volume), где конфиги подтягиваются с хоста, контейнеры для хранения данных на примере mysql.

Мы посмотрели три основных метода взаимодействия с контейнерами для разработки сайтов, это проброс портов, доступ к контейнерам по IP и работа с контейнерами через nginx reverse proxy. Лично я рекомендую пользоваться именно reverse proxy. Этот вариант мне кажется самым красивым, стабильным, удобным, быстрым и производительным.

Надеюсь статья оказалась вам полезна. Если это действительно так, то скажите спасибо в комментариях и тогда я возможно буду что-нибудь писать чаще чем раз в год 😀

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *