Pull to refresh

Comments 26

А знаете в чем проблема такого метода? Запущенный контейнер (с точки зрения докер-демона) — совершенно не эквивалентно работающему контейнеру. Соответственно, запросто может быть кейс, что сервисы поднялись, порты открыты и принимают соединения, но сервиы не готовы к подключениям. И, соответственно, зависимые сервисы при старте будут сыпать эксепшенами (нежелательный кейс).
Еще хуже, когда сервис стартанул, а потом упал. Упс. Мы это уже отследить не можем никак.


Вывод простой — не пользуйтесь docker-compose. Он годится только для одной единственной задачи — смоделировать запуск нескольких сервисов на локальной машине разработчика. Точка. Никакой сложной логики в него не внедряли. А то, что есть — даже извращает стандартные команды docker (run, build, network create etc.). Для более сложных задач — можно взять, например, ansible, благо в нем есть встроенный модуль работы с контейнерами: https://docs.ansible.com/ansible/latest/modules/docker_container_module.html

ansible это какой-то оверкилл для локалсервера. А вот compose как раз то что надо.

Вопрос терминологии. И точки зрения. Что такое локалсервер — локальная машина разраба? Сервер в вагранте? Или выделенный стенд для команды разработки? И с Ваших слов, как будто, конфигурацию этих серверов не нужно описывать, не нужно ею управлять.
К тому же, я предложил ещё вариант — wrapper в виде bash/Make, если до ansible не доросли, но docker-compose уже мало (а, поверьте, очень быстро вылезаешь за его возможности)

wait-for-it.sh — это костыли.
Можно придумать более хитрый вариант с хелсчеками (хелсчеки + depends_on: service_healthy), но он работает только в спецификации docker-compose v.2.4. 3-й — это для docker swarm. Вы его не используете почти наверняка, поэтому использование третьей версии формата docker-compose не оправдано.
Есть еще вариант — делать внешний запускальщик для контейнеров на базе баш скрипта или Makefile — в принципе ОК,

gecube, спасибо за отзыв. То, что wait-for-it это костыль — полностью согласен. Тему дожимал уже из интереса. На практике пользуемся entrypoint.
Про внешний запскальщик… вы имеете в виду совсем без docker-compose?

На Ваше усмотрение. Смотрите. docker-compose — это по сути интерфейс для команд докер-клиента. Вы с тем же успехом можете вообще отказаться от docker-compose в пользу баш-скрипта с каким-то определенным количеством аргументов (типа start, stop) или несколькими скриптами. Действительно — какая разница как запускать контейнеры? Удобство compose в том, что он позволяет немного избежать повторения (через те же yaml anchor; можно ссылаться на сами контейнеры через docker-compose up имя_контейнера, например, что уменьшает кол-во простыней кода).
Про ansible и его модуль для работы с контейнерами я уже ссылку приводил вроде.

да, я прочитал все ваши сообщения. Спасибо, очень полезные замечания.
UFO just landed and posted this here

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

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

Мне кажется проверка готовности должна быть сделана в docker-compose, а всякие wait-for-it.sh это костыли, которые приходится пихать в образ только для тестирования на локалхосте.
Очень хорошо сделано в kubernetes, там есть readynessprobe и livenessProbe по сути можно проверить порт, опросить по http, или даже запустить скрипт.
С учётом механизма dependencies можно красиво настроить сервисы.

Вот мне тоже показалось что автор пытается из жигулей 6й модели сделать комфортабельный представительский седан.
Мне кажется проверка готовности должна быть сделана в docker-compose

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

справедливое замечание, но тут такой сценарий, что запуская пачку микросервисов docker-compose, все сервисы стартуют одновременно, например postgres может быть ещё не готов, а другие сервисы уже успели сделать retry_connection несколько раз и отвалиться или для интеграционных тестов я не хочу тратить время на обвязку в виде retry-wrapper-ов.
То есть всё это можно порешать, но хочется чтоб интрумент позволял хотя бы запускать в нужной последовательности.
Ещё раз, это про локалхост.

nonname, ну почему сразу седан — так жизнь облегчить немного.
На самом деле, согласен со всеми, кто говорит, что wait-for-it это костыль. Зацепился за него только по тому, что сразу не заработало, стало интересно раскопать.
Пробуем!
И к сожалению, ничего не получаем.

Очень жаль, что не разобрались, почему не работает. Вообще странная история — новый entrypoint по идее не должен переопределять старый CMD, который в Dockerfile. Вот интересно даже

gecube, да, на самом деле это основной целью было возни с wait-for-it. Но, тем не менее, это так.
mapcuk, да. У нас на самом деле так и сделано. На проде стоит kubernetes, а для приёма от программеров мы сначала релиз раскатываем локально, через docker-compose.
container_name: conteiner_a
ну как тут можно было допустить ошибку в слове «container». я читаю статью, и кровь из глаз.
извините за несодержательный и эмоциональный комментарий.
yatneo, да, каюсь, видел, думал потом поправлю…

azirumga поделюсь концептом как еще можно решить задачу.
Итак. Входные данные. Есть контейнеризованное приложение. Пускай есть еще БД. И есть еще контейнер с миграциями. Как бы все готово к миграции в кубер. Хелсчеки на базе и на приложении тоже есть. Пишем такой компоуз-файл:


version: 2.4
services:
  db:
    container_name: postgres
    image: postgres:10.1-alpine
    healthcheck:
        test: ["CMD-SHELL", "pg_isready -U postgres"]
        interval: 10s
        timeout: 5s
        retries: 5
  migrate:
    image: my_app
    cmd: migrate && touch /tmp/flag && sleep 100
    healthcheck:
        test: ["CMD-SHELL", "test -f /tmp/flag"]
        interval: 20s
        timeout: 5s
        retries: 5    
    depends_on:
      db:
        condition: service_healthy
  app:
    image: my_app
    depends_on:
      migrate:
        condition: service_healthy
    cmd: app
...

Смысл в чем. Поднимается база. Дальше мы не стартуем все остальное ПОКА она не даст готовность. Иначе дальнейшие действия бессмысленны. Потом стартуют миграции. Т.к. процесс завершается, то я создают файл флага и потом сплю какое-то время. Во время сна должен отработать хелсчек — тут нужно точно определить первоначальный интервал (~время миграции) + таймауты. Иначе не сработает. Но с другой стороны, если миграции идут дольше, чем нужно, то это явно проблема. К сожалению, в ванильном докере нет флага ready у контейнера, поэтому пришлось так извращаться. Ну, и сам приклад стартует только после того, как миграции успешны. На самом деле в кубере у вас скорее всего, во-первых, миграции будут в том же контейнере, что и приложение. Но, во-вторых, это создает проблему связанную с тем, что возможно, что если запустите несколько инстансов приложения, то они параллельно начнуть модифицировать базу и будет ой-ой-ой. Это можно решить внедрением какого-нибудь алгоритма выбора кто же из копий приложения является ведущей копией и имеет право накатывать миграции — туда про алгоритмы raft, paxos etc., либо можно поверх распределенного k-v хранилища это реализовать. Либо вызывать миграции руками (тогда оператор сам решает где и когда их применять).
Из плюсов реализованной схемы — у вас в памяти нет "лишних" контейнеров. Из минусов — очередные костыли.

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

Вопрос на самом деле очень глубокий. Потому что если рассматривать теоретически, то там много чего вылезает — и RTO/RPO, и переделка процессов, и организация бекапов, и необходимость 0 time downtime, и вопрос стоимости.
Если же рассмотреть чисто практически — для начала нужно научиться делать миграции не деструктивными. Т.е. если вы хотите удалить колонку, то очевидно, что роллбек на такую миграцию сделать будет нельзя. Типичный вариант как сделать красиво: переименовать колонку (вывести из эксплуатации приложением), подождать, пока все приложение перекатится на новую версию и потом уже когда-нибудь удалить колонку безопасно. Аналогично с переименованиями и пр.
Как черновой вариант — можно вообще на все забить и тупо снимать снапшот перед осуществлением миграции. Завалились? Ну, ок — восстанавливаемся. Но это работает только на маленьких базах.

думаю, что мой вариант это снапшоты… к сожалению
Мы используем поверх compose еще и portainer. Он позволяет при локальной инсталяции бесконечно перезапускать контейнер пока ему не станет хорошо, а хорошо контейнеру определяется healthcheck.
Sign up to leave a comment.

Articles