Почему "rebuild" так тесно связан со статусом запуска контейнера?

По правде говоря, я должен начать с того, что я новичок в этой платформе и в коде, поэтому не имею представления о том, как сейчас работает rebuild «под капотом». Однако моё текущее понимание процесса реконструкции таково:

  1. Остановка текущего контейнера.
  2. Создание нового контейнера с данными из исходного дерева.
  3. Ожидание запуска вами нового контейнера.

С точки зрения DevOps, почему нельзя собрать новый контейнер (возможно, в другой ветке или временной директории), пока старый ещё работает? Это, казалось бы, сделало бы замену старого контейнера новым гораздо более быстрым процессом (по крайней мере, с точки зрения простоя), возможно, на порядок секунд, а не минут.

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

Это просто вопрос, на который ещё никто не обратил внимания, или существует какое-то архитектурное решение, требующее остановки одного контейнера перед сборкой другого?

Rebuild — это универсальное обновление, которое может:

  • Обновить исходный код Discourse
  • Обновить зависимости на уровне ОС, например, основную версию Ruby
  • Обновить PostgreSQL до более новых и несовместимых версий, при этом автоматически обновится формат диска с данными для новой версии
  • Обновить Docker-образ. Например, в начале этого года мы перешли с Ubuntu 16.04 на последнюю версию Debian, и для пользователя это осталось прозрачным: достаточно ввести ./launcher rebuild app.

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

Для более продвинутых тем по DevOps вы можете ознакомиться с:

и многим другим в канале #howto:sysadmin

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

Если я правильно понимаю Docker, следующие аспекты Discourse можно пересобрать в фоновом режиме в новом контейнере, пока существующий контейнер продолжает работать:

  • Обновление исходного кода Discourse
  • Обновление зависимостей на уровне ОС, например, основной версии Ruby
  • Обновление образа Docker. В качестве примера: в начале этого года мы перешли с Ubuntu 16.04 на последнюю Debian, и для пользователя это было абсолютно прозрачно — достаточно было ввести ./launcher rebuild app.

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

Возможно, в начале пересборки сайт можно было бы перевести в режим только для чтения, оставив старый контейнер с его существующим томом, а новую базу данных, которая создаётся в процессе пересборки, можно было бы разместить в новом Docker-томе?

Что касается обновления исходного кода Discourse, зависимостей на уровне ОС, базового образа Docker, Ruby-гемов и подобного, это можно сделать, разбив сборку на два этапа и выполнив вышеупомянутые задачи на первом этапе.

Этот первый этап не зависит от среды и может выполняться даже в CI-окружении (таким образом, можно использовать практически идентичный образ в staging и production, избегая возможных ошибок из-за пересборки в разные даты, не говоря уже о сокращении времени простоя).

Задачи миграции базы данных и assets:precompile всё ещё нужно будет выполнять на целевой машине. Миграция базы данных в большинстве случаев будет быстрой. С другой стороны, задача assets:precompile является проблемой, так как это самый продолжительный этап. Я думаю, что это связано с тем, что некоторые ассеты нуждаются в информации об окружении, определённой в базе данных (например, некоторые правила CSS), чтобы выполнить сборку.

Было бы чрезвычайно полезно, если бы эту задачу можно было разделить на две части: сначала собирались бы все ассеты, не зависящие от окружения, и их можно было бы выполнять в CI-окружении, а на втором этапе компилировались бы только те ассеты, которые зависят от данных в базе данных и т.д. Тем не менее, я не знаю, насколько сложно технически реализовать это.

Я обсуждаю запуск контейнера приложения в два этапа в следующей теме:

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