Pull to refresh
Comments 23
Последние рекомендации: ставить docker-compose через curl с оф.сайта. Устанавливается небольшой единый бинарник, которому python, уф, не нужен.

Ещё игрался с вариантом запуска docker-compose через docker-контейнер. Это прекрасно вписывается в CI-среды, но я был сильно опечален тем, что с какой-то версии у них сильно потяжелели образы и есть сложности с прокидыванием переменных среды (если используется интерполяция в docker-compose.yml)
Огромное спасибо за статью, очень актуально!
Возникло несколько вопросов:
— Почему не разделены образы для dev/prod (xdebug и composer не нужны)?
— Меня отчасти смущает то, что в gitlab-ci.yml дублируется сцераний создания образа (тот же composer install). Ведь, в принципе, у нас уже есть Dockerfile, который собирает образ и для него можно запустить тесты.
— Можете подробнее рассказать о запуске очередей, что в принципе для этого нужно, как вы решали проблемы (вижу скрипты в docker/app, это с ними связано)?
Почему не разделены образы для dev/prod (xdebug и composer не нужны)?

С целью и упрощения. Если возникнет необходимость на staging окружении сделать composer-dump — у тебя всё под руками. Да и тестировать prod образ отдельно — очень не хочется. А модуль xdebug — не загружается, просто образ чуть толще, это да


дублируется сцераний создания образа

В .gitlab-ci.yml пришлось немного дублировать по причине отсутствия на столько гибких условий запуска job-ов. Был второй вариант — проверять что в $CI_COMMIT_REF_NAME в секции script, но он оказался ещё хуже (если я правильно понял ваш вопрос, но не уверен)


Можете подробнее рассказать о запуске очередей

Слушателей очереди — это процесс artisan queue:work, которые на rancher запускаются в количестве 20..30 экземпляров. Подробнее о том, как работают очереди в Laravel хорошо написано в документации

Есть мнение, что артефакт не имеет права меняться между stage. Т.е. если мы собрали тестовый артефакт в ветке, то после прохождения тестов он должен уйти в прод. Таким образом мы можем ТОЛЬКО навешивать на один и тот же единожды собранный docker-образ разные теги. Это существенно сложнее, чем пересобирать образ каждый раз, НО! Докер не ГАРАНТИРУЕТ, что если мы делаем apk add <имя пакета> (даже с указанием конкретной версии) или npm install (конкретный пакет и его конкретная версия), что в образы попадут бинарно идентичные версии этих пакетов. Это потенциальная очень глубокая дыра. Решать в каждом случае индивидуально — стоит ли на это заморачиваться или «и так сойдет»

Здравстуйте. Спасибо за статью.


Скажите, не возникает ли в данной конфигурации проблем с деплоем новой версии? Если я правильно понимаю, то т.к. используются volume для обобщения кода между php-fpm и nginx контейнерами, после первого деплоя (т.е. когда volume был пустым) код уже не переписывается из контейнера в него, а наоборот: на новый контейнер накатывается старый код из volume. Я пока так и не смог решить эту проблему, оставив всё на docker-compose 2 и volumes_from и при деплое приходится пересоздавать как php-fpm контейнер, так и nginx. Но сейчас всё же нарастает необходимость в использовании swarm, а посему вариант заворачивать frontend и всю статику в nginx контейнер кажется всё более и более логичным.

Не возникает, так как деплой происходит с даунтаймом (минимальным, но всё же). Т.е. "вытягиваются" свежие образы полным набором, после этого текущий стек гасится, и тут же поднимается со "свежим" тегом. Есть ещё не лучшее решение с миграциями (надо запускать контейнеры в определенном порядке), но так как оно "не лучшее" — в статье его не описывал.

Я вот как раз надеялся, что увижу в статье, как управляетесь с миграциями. Код с зависимостями отправили на сервер, ок. А если разработчик добавил новую миграцию?

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

Решение уже есть — это Kubernetes. Большинство проблем уже решено. Например, проблема с даунтаймом решается так, что в процессе обновления старый деплоймент продолжает работать и отвечать на запросы и не будет остановлен, пока не заработает обновленный. Миграции же следует запускать при запуске контейнера.


Так же Docker — не просто средство запуска веб-сервера, а средство упаковки проекта со всеми зависимостями для простоты дальнейшего запуска. Монтирование папки с исходным кодом отлично подходит для разработки. Но в продакшн следует собирать полноценный Docker образ, в который на этапе сборки устанавливаются все зависимости (composer и пр.) и копируются исходные файлы проекта.

Чем на ваш взгляд не так хорош подход монтирования директории с исходниками хоть через хост, хоть через docker volume? Исходники, среда и сервисы — это три разные сущности. И не смотря на то, что первые две можно перемешать — стоит ли?

Потому что исходник является компонентом версионирования кода. Просто пример. Положим, что мы изменили базовые зависимости в основном образе, а исходник подкинули от предыдущей версии, которая хочет ДРУГУЮ версию зависимости. Получается, что они должны обновляться связанным образом.
Это более характерно для python с его requirements.txt, чем для nodejs. Но тем не менее — все сломается.
Исходники, среда и сервисы — это три разные сущности.

Докер за тем и нужен, чтобы среда и сервисы были отделены. Исходник — всего лишь сырье для сборки сервиса.

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

Давайте возьмем в качестве примера миграцию, которая изменяет тип некоторого поля в таблице.


С использованием добротной абстракции над данными реализовать проверку на актуальное состояние типа поля, конечно, можно довольно "элегантно" и в едином месте, но так как "php создан, чтобы умирать" — нужно или каждый раз перед обновлением данных выполнять дополнительный запрос на проверку "поле, какого ты сейчас типа?", или кэшировать крайнее состояние, инвалидируя кэш при релизе. Довольно таки громозко, как мне кажется, да и мертвый код… Не лучше ли придерживаться идеи, что версия кода просто должна соответствовать версии схемы?

Я имел в виду, что мы вводим мораторий на изменения типа полей. По крайней мере, которые могут привести к проблем. Столбец добавить — ок, можно. Новый код его знает, сможет с ним работать. Работоспособность же старого кода — неизменится. Удалить столбец? Нет, нельзя. Это можно будет сделать через несколько релизов, когда мы стабилизируем кодовую базу и когда это можно будет безопасно сделать и т.п.

Само собою, они и обновляются связанно — при свежем релизе мы получаем все три (в нашем примере) свежих образа с одним тегом, например — v1.2.3 (алиас которого — stable). Перед раскаткой делается pull образов (если не прошел — релиза не будет; скачаются лишь обновленные слои, так как те что не изменились с предыдущего релиза — сойдутся по хэшам).
После этого стек гасится (пользователи видят страницу-заглушку, что отдаёт хостовой nginx), и поднимается новый стек. При поднятии нового стека выполняется конкурентная операция — накатка миграций (при их присутствии), прочие "сервисные штуки", и лишь после поднимаются остальные контейнеры — nginx, fpm и так далее. Если миграция по какой-либо причине не прошла — поднимается старый стек, с отправкой нотификаций матерного содержания инициатору релиза.


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

1. что посоветуете, если проектов много и все нужно в запущенном состоянии держать на слабеньком тестовом VPS? Задача есть, но совсем еще не брался. Пока вижу проблемы с оперативкой и занятые порты.

2. php-cli (artisan) запускается под той же версии php что php-fpm?

3. если только для production нужно свои настройки nginx, то выходит нужно какой балансировщик свой впереди ставить, чтоб dev не отвалился? Допустим https c переадресация http->https.
Если на проект зайти под другим HTTP_HOST, то он запустит приложение?
  1. Смотря какие проекты, какую роль выполняют (к каким ресурсам требовательны), и что вы хотите получить в итоге. Иногда лучше ничего не менять (изменения будут не оправдано дорогими по человеко-ресурсам). Оверхед по памяти — да, есть, но совсем не большой если смотреть в целом. Порты — если в пределах одного хоста запускается не тысячи контейнеров — не думаю что есть смысл об этом беспокоиться.
  2. Да, так проще в управлении и внесении изменений.
  3. Тут не совсем понял. Если есть возможность, можете более подробно описать свой вопрос?
1. Простые сайты визитки (php, mysql). Можно сказать что нужно тестовый хостинг для них, но все контейнерах. Думал может какие-то общие сделать контейнеры с указанием хостов и папок. По сути выходит контейнеры для виртуальных хостов.

2. Выходит php-сli запускается из контейнера php-fpm? composer тоже? были проблемы что некоторые скрипты смотрят на /usr/bin/env

3. Наверно рано спросил, пока не юзаю автоматические интеграции, просто думал что на продакшн nginx поднимается с 80 портом, если я начну править конфиги контейнера для продакшн, то это скажется и для dev

Если опять-таки правильно понял, то решение напрашивается само собой:


  • Для разработки каждого проекта используется свой compose-файл, который поднимает всё своё окружение (так каждый проект в своём роде атомарен, не имеет зависимостей от хоста)
  • На продуктовом (или staging — не важно) сервере — поднимается лишь контейнер со средой (app), исходниками (sources), и фронтом (nginx), в БД все дружно ходят в одну, локальную, или стоящую рядом
  • Так же на сервере ставится nginx (с ssl и прочими), который "разводит" запросы, приходящие на домены, делая proxy_pass на внешний порт nginx контейнера нужного приложения
  • Через env каждому приложению сообщается, на каком базовом uri оно запущено, что бы оно генерировало корректные абсолютные ссылки

staging (или dev) окружение — это полная реплика продуктового, но только у него роль — быть пре-продактом, "на живую" обкатывать перед релизом.


При разворачивании нового приложения — необходимо лишь в DNS резольвить адрес сервера, где стоит "фронтовой" nginx, написать для него конфиг с proxy_pass, и поднять стек приложения. Из очевидных плюсов — изоляция, индивидуальные настройки под каждый проект, простота в эксплуатации, и безграничные возможности для автоматизации :) Как ещё один вариант — это пускать контейнеры в облаках. На то они и контейнеры

Уточните, пожалуйста — compose или docker-compose?
Мне не очень нравится путаница, связанная с существованием нескольким совершенно разных продуктов, но с очень сходными именами (да хоть вспомнить sphinx, который может быть поисковым движком и чем-то совершенно другим )))
Only those users with full accounts are able to leave comments. Log in, please.