Comments 82

Шёл 2019 год. PHP разработчикам рассказали про Docker… Продолжение следует )

согласен, что технологию сложно назвать новой, да и все что я могу рассказать для знатоков ни в коей мере Америки не откроет, но по своему опыту вижу довольно много разработчиков-староверов, которые даже из подобных статей что-то могут почерпнуть для себя нового)
Нуу, они продолжат так же запихивать все в один контейнер. Ничего не изменится.
Да хрен бы с ним с одним контейнером, это уже прорыв. Знаю человека который до сих пор работает исключительно на Open Server, и уверен что vagrant и docker это какая-то ненужная фигня.
UFO landed and left these words here
Видимо у вас какие-то очень мелкие проекты, ведь помимо раздачи самого php (с чем, в принципе, вполне справится и php -S) часто надо держать mysql, mongo, memcached, иногда ещё websocket-сервер на ноде. Да при этом проектов несколько, и им надо разное окружение, разную версию PHP. И вот тут уже хочешь не хочешь, а надо нормальное решение.

По поводу
на докер я трачу больше времени чем на разработку
— у меня на докер в день уходит ну секунд 6, чтобы не соврать. Утром его запускаю с одной кнопки (точнее не сам докер, а все нужные контейнеры), вечером выключаю. Если не гасить контейнеры — то вообще будет уходить 0 секунд. Что с ним нужно делать чтобы времени уходило больше чем на разработку?)
UFO landed and left these words here
Два слова
docker multi-stage

Первый этап это image с композер в котором ставится все необходимое
Второй это сам php
Контейнеры это не ВМ как обычно все ее представляют, а готовое окружение которое не нужно потом через docker exec менять.
А что делать мне?
У меня тоже один мелкий проект. Для своей полной работы требует что-то под 120Гб RAM и поднимает порядка 80+ контейнеров

Доктор, мне поможет докер?
Поначалу когда начинаешь с ним разбираться, да, бывают сложности. Но это именно от опускания каких-то моментов и недостатка опыта. Как и в любом деле со временем рука набивается. Лично мне кажется, что докер может стать в перспективе неотъемлемой частью программирования. Развертывание окружение это тоже самое что и минимальные познания в веб-серверах и прочей инфраструктуре.

Есть сложности, которые решаются только разного уровня костылями. Банально, сделать composer install в билд-тайме с закрытыми репозиториями, чтобы ключи в образ не попали. Multi stage билды не сразу были. Запуск процесса типа того же php-fpm от пользователя, соответствующего локальному, чтобы права и в контейнере, и на хосте были минимальные, но работали. Несколько способов обеспечить правильное и удобное заполнение статики и динамики на образах nginx и php, желательно без дублирования, каждый со своими плюсами и минусами. Да совсем банально: запустить две ветки проекта под разными доменными именами, не лазая в /etc/hosts

Кстати, какими именно костылями вы делали, чтобы ключи в образ не попали? Ведь сокет ssh-agent напрямую не прокидывается с MacOS и Linux-контейнер.

Прокидывал ssh-agent (что такое MacOS? :), передавал через ARG как сами ключи, так и ссылки на внешние самописные сервисы, недоступные снаружи, предоставляющие ключ с очень ограниченным сроком действия, много чего пробовал и всегда это был компромис за счёт безопасности — закрывали одни векторы атаки, создавая новые, которые волевым решением были признаны как маловероятные.

Вот тут волевым усилием нашелся еще вариант:

  1. Не шарить volume с хостом, а просто давать ему висеть «в воздухе» (docker-compose исправно данные на нем сохраняет, не стирает). Когда volume не берется с хост-машины, то и тормозов файловых операций между MacOS и Linux нет. Это «volumes: blah: external: false» в docker-compose.yml.
  2. Для редактирования файлов внутри контейнера есть вот такое расширение: code.visualstudio.com/docs/remote/containers (оно реально создает иллюзию локального редактирования; кстати, вся разработка в фейсбуке ведется похожим образом, через IDE и «толстый» серверный модуль-агент для него).
  3. Вот с ключами git проблема (никак ее не решить, если ключ защищен паролем), но зато данное расширение для vscode магически прокидывает токен от credential.helper, так что если клонировать git-репозиторий по https (а не по ssh), то логин-пароль приходится вводить всего один раз (а не для каждой git-операции). И в терминале vscode работают git-команды без пароля. Я сам всю жизнь хожу в git по ssh-ключам, но, похоже, https-способ и правда более современный.

Костыльно все это, конечно. А что делать.
Так не поможет же, если ключ с паролем. Между MacOS host и Linux guest domain socket не пробросить, а сокет нужен для ssh-agent'а.

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

Но ведь можно было бы в такую статью хотя бы добавить как установить docker-compose и указать откуда в названии сети и контейнера взялся lesson1, и как работает service: в котором ничего не прописано — что именно происходит по умолчанию.
Пока что скорее про docker-compose, чем про Docker, но ничего плохого в этом не вижу ибо инструмент крайне полезный!

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


volumes:
  code:
    driver: local
    driver_opts:
      type: 'none'
      o: 'bind'
      device: $PWD

services:
  myservice:
    container_name: mycontainer
    build:
      context: .
      dockerfile: docker/go/Dockerfile
    volumes:
      - ./docker/go/path:/data/path  # обычный
      - code:/go/src/myproject  # именованный

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


$ docker volume ls
DRIVER              VOLUME NAME
local               b4a3ceda689f820e5ef1add990e1853b032fe9048e1ed7668a9cf160de9cff3d
local               myservice_code

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

Представленный вариант это по сути и есть монтирование текущей папки. А при простом монтирование по моему именной volume не создается (может конечно путаю). И я особо никогда не задавался большой целью удаления лишних вольюмов. В конце концов есть команда docker volume prune, которая подчищает неиспользуемые вольюмы.

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

А подскажите, как использовать Docker, когда у тебя несколько проектов параллельно? Экосистема Docker предполагает изоляцию отдельных сервисов в отдельные контейнеры.
Теперь представим, у нас есть один проект на PHP, для его работы крутятся 3 конейтенера: nginx + php-fpm + mysql server. Из контейнера nginx проброшен 80 порт на хост машину (в конфигах nginx прописан доступ по your-domain.loc).
И вот появляется необходимость развернуть еще один проект параллельно. Как быть в этом случае?
1) Добавлять в существующие контейнеры настройки для второго проекта — протеворечить принципам изоляции сервисов, ну и, соотвественно, в случае падения одного из проектов (ошибка nginx или типа того) все проекты, которые были завязаны на этих контейнерах тоже упадут.
2) Создать +3 контйенра для второго пректа, но так, как 80 порт уже занят первым проектом, то нам необходимо будет из браузера стучаться через кастомный порт, что весьма неудобно. Та же самая проблема касается и всех остальных сервисов. С хост машины в MySQL уже придется ломиться по кастомному порту.
3) Находил еще вариант с добавлением доп. контейнера nginx, который будет выступать в качестве прокси сервера, принимащего все запросы, выявлять имя домена и направлять запросы в соотвествующие контейнеры. Но этот вариант тоже не самый удобный, и не решает проблем с остальными сервисами (php-fpm и mysql тоже придется вешать на другие порты).

Вот и сижу и думаю, как этот вопрос решается с Docker, наверняка многие должны были сталкиваться с ним.
С 80м портом это довольно известная и типовая проблема. Для ее решения можно использовать реверс-прокси. К примеру, у меня довольно популярен вот этот hub.docker.com/r/jwilder/nginx-proxy. Отдельно запускается данный прокси только у него прокидывается 80 порт и к нему коннектятся отдельные фронты уже без проброса 80го порта.
Касаемо Mysql, исключительно для нужд приложения проброс портов на хостовую машину вообще не нужен. Я порой пробрасываю кастомные порты, чтобы соединятся с контейнером через PHPSTORM (может быть кто-то из местных подскажет функцию у шторма, чтобы без проброса коннектиться к базе в докере.) Или если использовать например phpmyadmin настроенную через прокси, то можно обойтись и без проброса.

Ну да, nginx-proxy — это и есть мой вариант №3. Он мне не понравился наличием отдельного контейнера, но, возможно, я тогда погорячился и на самом деле это не так страшно.


может быть кто-то из местных подскажет функцию у шторма, чтобы без проброса коннектиться к базе в докере

Тоже активно использую Phpstrom, на сколько мне известно, на текущий момент функции прямого подключения БД в Docker нет. Вот только недавно подвезли возможность запускать composer через Docker из шторма, не думаю, что есть вариант коннекта для БД.

Можно же подключится со шторма к бд через ssh тунель? Или я не правильно понял проблему?

Можно через SSH тунель, но экосистема Docker не рекомендует использование SSH внутри контейнеров (хоть и не запрещено).

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

Я честно не разработчик и шторм использовать не приходилось, но если указывать localhost — клиенты mysql обычно цепляются к сокету. Если у IDE нет каких-либо особенностей — должно сработать кмк.

Ну, можно же юникс-сокет пробросить, не? Как volume? И к нему подключаться уже.

может быть кто-то из местных подскажет функцию у шторма, чтобы без проброса коннектиться к базе в докере

Можно, как немного костыльный вариант, после создания контейнера выполнить
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' idOrName
и подключаться уже по этому адресу.
Минус в том, что при перезапуске этот адрес можем меняться, а так да, вариант

На хостингах могут добавлять выделенные IP для отдельных доменов, на локальной машине возможно такое изобразить?

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

Просто-же, начали работать со вторым проектом — закрыли первый. Или умеете программировать асинхронно? :)

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


Для сравнения, при использовании Vagrant достаточно всего пару строк добавить в конфиг, дабы поднять новый хост. Да-да, я знаю разницу с Docker-ом, и понимаю, что у Vagrant есть свои недостатки, поэтому и пытаюсь найти решения как использовать Docker, но при этом решить возникающие проблемы.

Vagrant достаточно всего пару строк добавить в конфиг, дабы поднять новый хост.
Ну, вагрант решает эту проблему пока у нас оба проекта на одной версии php. В ином случае нам надо две вагрант-машины, и вот они не поделят порт 80 точно так же само как докер.

Используя, например, homestead (бокс от Laravel), можно в конфиге и версию php тоже прописывать. Внутри бокса крутится nginx + несколько версий php-fpm, в зависимости от конфига homestead создает нужный локейшн для nginx с подключением нужной версии php-fpm сокета.

Фигасе, не знал что homestead такое умеет. Тогда согласен, vagrant в этом плане удобнее.

Мне кажется, лучше просто развертывать проекты на отдельных тестовых серверах и оттуда переносить.

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

Подскажите, а как в 2019 году на практике обстоят дела с производительностью файлового доступа к подмонтированной таким образом папке? Хост-система MacOS (вероятно, это важно!), в контейнере Linux.


Я слышал, что раньше люди мучались с дев-окружением в Докере, потому что доступ к примонтированным файлам был ну ооочень медленным (например, запускать какой-нибудь build, лопатящий много файлов, внутри контейнера было весьма неприятно). А как сейчас?

с mac к сожалению возможности не имел работать, хотя коллеги работают с докером в маке и вроде как не жалуются. В виндовсе действительно проблема со скоростью есть. Симфони могла свой кеш грузить секунды по три, в то время как в линуксе скорость не вызывает нареканий. По сравнению с виндовс симфони там работает в десятки раз быстрее. (миллисекунды против секунд)
UFO landed and left these words here
Если небольшое количество файлов, то не заметно, а к примеру magenta 2 с пару десятком тысяч файлов будет работать ооооочень медленно, отработка запроса пару минут. Но docker sync эту проблему решает.
UFO landed and left these words here

Нет, под Docker Mac OS + docker sync работает так же быстро как и под Linux. Без sync работать невозможно под Mac OS.

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

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


Но если без мазохизма никак, то docker sync и dinghy

Понятно.

1. Docker sync создает новый свой искусственный контейнер, в котором крутится линукс, и монтирует в него нашу папку с MacOS под именем dir1.
2. Дальше он уже эту папку dir1 там, в этом контейнере, unison-синкает с другой папкой в том же (!) контейнере, dir2. Фактически, копирует пофайлово в обе стороны.
3. И, наконец, мы монтируем папку dir2 из искусственного контейнера в наш настоящий контейнер (с webpack-ами, php и т.д.). Т.к. это докер-монтирование «из линукса в линукс», то файловый доступ происходит мгновенно.

И все это с 2016-го где-то года. Какой ужас. Неудивительно, что Соломон ушел. :)

(Unison написан на Окамле в стиле «плюс-минус разница в третьей цифре версии Окамла между клиентом и сервером — смерть», а также почти что не поддерживается уже. Впрочем, для п. 1 это не важно, т.к. искусственный контейнер уже заранее подготовлен.)
проверил только что — разница очень небольшая, <10%.

docker -v
Docker version 18.09.1, build 4c52b90
MacOs 10.13.6
1 :-) (то есть сотня, но копировал я один большой файл)
уже увидел, что жаловались на медленную работу при большом кол-ве файлов.

Пропустил момент, откуда появился идентификатор lesson1 и куда класть docker-compose.yml, чтобы docker-compose up его нашел

Ничего сложного. docker-compose.yml класть в то место, где ты его будешь запускать. Он ищет в той папке, где ты вызвал команду docker-compose up -d. Идентификатор lesson1 появился из названия папки в которой запускается проект. То есть автор запускал проект в папке lesson1.

Еще есть параметр -f. С его помощью можно подцепить несколько yml конфигов. Это позволяет раскидать настройки между несколькими файлами. В одном фале базовые сервисы (MySQL, Redis, их базовая настройка и др), во втором файле запуск контейнеров вашего приложения, а в третьем например override портов. Выглядеть это будет примерно так

docker-compose -f docker-compose.yml -f docker-compose.app.yml -f docker-compose.override.yml up -d
Не хочу негативить но:
1. Почему так коротко? Это больше на новость похоже
2. Подобных статей навалом, чем Ваша статья отличается от остальных? Например, этой habr.com/ru/post/346086

У меня в закладках эта habr.com/ru/company/flant/blog/336654
А когда просят рассказать как работает, даю эту habr.com/ru/company/ruvds/blog/438796
Господа, а что в июле 2019 принято делать в таком случае:

  • Есть production-образ с каким-нибудь php и веб-приложением внутри. Исходники приложения принадлежат www-data:www-data.
  • Есть локальная машина для разработки с docker-compose. В docker-compose стягивается этот production-образ и в docker-compose.override.yml исходники монтируется снаружи, но принадлежат они vasya:vasya.

Вот такие советы иначе как костылями язык не поворачивается назвать toster.ru/q/542064
По идее если вы монтируете исходники снаружи, то вы должны полностью перекрывать то что внутри и принадлежало www-data, потом выполнять сборку своих исходников в виде. composer install или чего-то подобного. И тогда никаких проблем с правами быть не должно.
А по хорошему, вообще кажется не совсем верный подход монтировать образ с прода.
Должно быть локальное окружение для нужд разработчиков и отдельно сборка из исходников образа для прода.

А мне кажется неверным подход использовать разные образы. Опять увеличиваем вероятность "локально у меня всё работает". В крайнем случае дев-образ наследуем от прода, но очень аккуратно.

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

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

Всё так, вместо prod-образа может быть какой-нибудь dev-образ, но сорцы точно так же будут в контейнере лежать в папке с gid/uid отличным от хоста. Я встречал разные варианты вплоть до использования шаблонов в www.conf, nginx.conf и где там ещё может использоваться юзер. При этом отдельно стоит отметить это чудо:

/bin/sh -c "envsubst < /tmp/www.conf.tpl > /tmp/www.conf"

Проблема достаточно распространённая и каждый решает на свой лад:
stackoverflow.com/questions/23544282/what-is-the-best-way-to-manage-permissions-for-docker-shared-volumes
www.inanzzz.com/index.php/post/q1rj/running-docker-container-with-a-non-root-user-and-fixing-shared-volume-permissions-with-dockerfile
denibertovic.com/posts/handling-permissions-with-docker-volumes
github.com/moby/moby/issues/2259
medium.com/@nielssj/docker-volumes-and-file-system-permissions-772c1aee23ca

Что касается composer install, то это отдельная головная боль. Для установки зависимостей приходится запускать отдельный контейнер из-под root, в который пробрасывается приватный ключ.

#!/usr/bin/env bash
source .env
IMAGE=composer
docker run --rm --interactive --tty \
    --volume $PWD/app:/usr/src/app \
    --workdir /usr/src/app \
    --env COMPOSER_ALLOW_SUPERUSER=1 \
    --entrypoint '/bin/bash' \
    ${IMAGE} \
     -c "mkdir -p /root/.ssh && chmod 0700 /root/.ssh && \
    cp /usr/src/app/deploy_key /root/.ssh/id_rsa && chmod 600 /root/.ssh/id_rsa && \
    composer $@ &&
    chown -R www-data:www-data vendor composer.json composer.lock"

Можно было бы пробросить SSH_AUTH_SOCK, но есть ещё Windows/Mac.
В последнее время использую следующий подход. В контейнер с композером копируется два файла composer.json composer.lock при необходимости можно добавлять приватный ключ в контейнер и запускается инстал, без проверки зависимостей. Затем в основной образ бекенда копируется все то что собрал композер. Копирование выполняется с использованием параметра chown: COPY [--chown=:]. И далее в основном образе выполняется композер дамп и пост-скрипты. Если изменений в composer.json composer.lock не было, то при сборке используется кеш, что значительно ускоряет процесс.

Приватный ключ в уже поднятый контейнер копируется или в его образ?

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

Можно, но здесь тоже есть подводные камни: если chown засунуть, к примеру, в entrypoint, то контейнер с крупным приложением на Symfony может стартовать этак минут 5

github.com/docker/for-linux/issues/388
Не, такими темпами, в час по чайной ложке, народ как раз к завершению поддержки всея докера и научится им пользоваться… Вы бы лучше во вводной статье показали зачем это надо (можно не просто поставить сервисы на комп, но поставить сервисы единообразно — уж если у вас есть докер, то одни и те же образы заработают и под виндой и под макосью и под линуксом; что удалить их потом можно одной командой; что можно запустить 3 разных версии сайта в разных папках и просто на разных портах они будут висеть; что удобно переносить между компами разработчиков один и тот же метод запуска, который 1в1 будет и на проде так же работать; что удобно экономить диск, если надо запустить 15 «виртуалок», отличающихся чуть чуть в паре конфигов. Ну и ссылки на хорошие статьи по теме сразу выдайте… куда полезнее, чем переписывать сейчас тут учебник черепашьими темпами.
Я постараюсь разогнаться) А за мысли спасибо, но как-то они воспринимались само-собой разумеющимися.

В разных папках это мелочи. Вот из одной папки запустить разные ветки на одном порту с разными доменами очень удобно

Мои комплименты автору. Все хотел разобраться с Docker'ом, но не находил статьи под свой уровень
Only those users with full accounts are able to leave comments. Log in, please.