Can Discourse ship frequent Docker images that do not need to be bootstrapped?

Полагаю, лаунчер был создан до того, как появился Docker Compose:

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

В моей настройке Discourse используется один контейнер с Discourse и nginx, работающими через UNIX-сокет. PostgreSQL и Redis запущены как сервисы на хосте. Это довольно отклонение от стандартной конфигурации, но оно возможно из коробки.

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

Вы просто переносите проблему.

Чистая настройка Compose для Discourse включала бы следующие сервисы в отдельных контейнерах:

  • Discourse
  • nginx
  • PostgreSQL
  • Redis

Discourse и nginx должны разделять том, это не проблема.

PostgreSQL и Redis… это те сервисы, которые вы, возможно, захотите разместить где-то ещё, не создавая для них специфичные для Discourse контейнеры. И тогда Docker Compose становится проблемой: команда docker compose up -d запустит нежелательный PostgreSQL. Хорошо, тогда мы используем docker compose --profile postgresql up -d для запуска базовой конфигурации Discourse с контейнером PostgreSQL, и docker compose --profile postgresql --profile redis up -d для «полной» полностью автономной конфигурации Discourse. Лучше не забыть аргумент --profile ..., иначе возникнут дополнительные проблемы.

Так что для улучшения пользовательского опыта вы создаете лаунчер, который берет на себя формирование нужной команды Docker Compose. Теперь мы вроде бы вернулись туда, откуда начали. За исключением того, что модификации контейнера nginx пока невозможны. Значит, мне нужны контейнеры nginx-http и nginx-unix, которые должны быть взаимно исключающими? …

Конечно, управление плагинами можно улучшить, но делать это с помощью Docker Compose — это будет ад.

Итак, подытожим. Официального Docker-образа не будет.

Что касается невероятного количества ложной информации о том, что такое Docker и как он работает: What is Docker? | Docker Docs

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

Кроме того, с вашим любимым «лаунчером» — как я могу запустить Discourse в кластере Kubernetes, если нет Docker-образа, который я мог бы использовать и развернуть там?

Да-а-а…

Этот скрипт должен запускаться от имени root. Сначала используйте sudo или войдите как root.

Нет, спасибо. Я не люблю запускать случайные скрипты из интернета. Я просто хочу иметь красивый Docker-образ, который я мог бы безопасно запускать в изоляции :slight_smile:

Я успешно развернул Discourse в продакшн с помощью Docker Compose, используя опубликованный образ, как и многие другие клиенты.

Однако да, это довольно запутанно и требует значительного объема кастомной работы.

Гораздо проще придерживаться стандартной установки с загрузчиком CDCK, если у вас нет настоятельной необходимости использовать другой метод из-за личных предпочтений, специфической архитектуры или иных особых требований.

Использование подхода, отличного от «мастера, который проведёт вас за руку», не является «предвзятым, специфичным или особым» :roll_eyes:

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

Я попробовал следовать официальному руководству и использовать «launcher» просто чтобы поиграть с настройкой — безрезультатно (за исключением довольно глупой проверки на то, является ли пользователь root, что просто приглашает к проблемам).

Затем я обратился к образу Bitnami (и их предложенному файлу compose) с bitnami/discourse - Docker Image, и после одной команды docker compose up (плюс куча ошибок SQL [что не сулит ничего хорошего], а также предупреждений от Ruby [тот же недостаток]) у меня запустился экземпляр Discourse:

Чистая запутанная и «нестандартная магия»…

Так что ещё раз — почему нет обычного образа?

Изучая исходный код и репозитории, я обнаружил, что используется базовый образ (discourse/base - Docker Image), хотя его версионирование/тегирование меня удивило (discourse/base - Docker Image)… Нет ли нормальных версий, я wondered? И действительно, глядя на основной репозиторий, там вроде бы не было релизов, но эх — по крайней мере есть правильно размеченные версии (Tags · discourse/discourse · GitHub).

Теперь вопрос: почему нет соответствующих образов в основном Docker Hub?

Так что да — посмотрев на то, как всё устроено, я согласен:

:smiley:


С другой стороны (или скорее: «как должно быть»), пример проекта (меньше по масштабу, но логика та же): GitHub - miniflux/v2: Minimalist and opinionated feed reader · GitHub.

У него есть нормальные релизы: Releases · miniflux/v2 · GitHub

Которым соответствуют образы с правильными тегами: miniflux/miniflux - Docker Image

И тогда обновление сводится к смене версии с x на y (или использованию тега latest, так что простое остановка/pull/запуск даст обновлённую версию, которая автоматически выполнит все необходимые миграции).

(кстати, размер образа примерно в 70 раз меньше, но это уже другая история)

Отличная работа!

Всего два вопроса… Какие именно ошибки SQL и предупреждения Ruby? Я с ними не знаком. Но, возможно, это потому, что я использую Docker и подобные инструменты совершенно неправильно :thinking:

Вы делаете что-то вроде этого:

./launcher bootstrap myimage
# команда, которая загружает этот образ в ваше предпочтительное хранилище для запуска образов
./launcher start-cmd

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

На самом деле это не так уж сложно. Вы даже можете интегрировать это в действие GitHub, чтобы оно собирало ваш образ и загружало его в ваш репозиторий Docker (или что-то подобное). Судя по коммитам в docker_compose, ведутся работы по созданию образа, который не требует этапа bootstrap, или, по крайней мере, упрощению выполнения необходимых действий (миграция базы данных, компиляция ассетов и загрузка в S3 и т. д.). Я делал это для клиентов, которые хотели использовать Kubernetes или какой-то загрузчик образов AWS (название уже не помню).

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

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

Если образы Bitnami подходят вам — используйте их, но если нет, вам понадобится помощь от кого-то, кто ими пользуется, а таких здесь, по сути, нет.

Эх… Я уже удалил файл и всю настройку, так как нашёл NodeBB, который кажется менее враждебным к пользователю при развёртывании (руководство: https://docs.nodebb.org/installing/cloud/docker/#docker-compose-usage; простой файл docker-compose с тремя сервисами: nodebb, postgres и redis: NodeBB/docker-compose-pgsql.yml at master · NodeBB/NodeBB · GitHub; и аккуратно помеченные образы: Package nodebb · GitHub)

Однако, я получил образ, и вот соответствующие ошибки:

  1. Похоже на проблему со схемой SQL:
postgresql-1  | 2025-07-06 17:15:40.518 GMT [155] ERROR:  relation "translation_overrides" does not exist at character 523
postgresql-1  | 2025-07-06 17:15:40.518 GMT [155] STATEMENT:  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
postgresql-1  | 	       pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
postgresql-1  | 	       c.collname, col_description(a.attrelid, a.attnum) AS comment,
postgresql-1  | 	       attidentity AS identity,
postgresql-1  | 	       attgenerated as attgenerated
postgresql-1  | 	  FROM pg_attribute a
postgresql-1  | 	  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
postgresql-1  | 	  LEFT JOIN pg_type t ON a.atttypid = t.oid
postgresql-1  | 	  LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
postgresql-1  | 	 WHERE a.attrelid = '"translation_overrides"'::regclass
postgresql-1  | 	   AND a.attnum > 0 AND NOT a.attisdropped
postgresql-1  | 	 ORDER BY a.attnum
postgresql-1  |
postgresql-1  | 2025-07-06 17:15:40.616 GMT [156] ERROR:  relation "translation_overrides" does not exist at character 523
postgresql-1  | 2025-07-06 17:15:40.616 GMT [156] STATEMENT:  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
postgresql-1  | 	       pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod,
postgresql-1  | 	       c.collname, col_description(a.attrelid, a.attnum) AS comment,
postgresql-1  | 	       attidentity AS identity,
postgresql-1  | 	       attgenerated as attgenerated
postgresql-1  | 	  FROM pg_attribute a
postgresql-1  | 	  LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
postgresql-1  | 	  LEFT JOIN pg_type t ON a.atttypid = t.oid
postgresql-1  | 	  LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation
postgresql-1  | 	 WHERE a.attrelid = '"translation_overrides"'::regclass
postgresql-1  | 	   AND a.attnum > 0 AND NOT a.attisdropped
postgresql-1  | 	 ORDER BY a.attnum
  1. Предупреждения Ruby (как в контейнерах discourse, так и sidekiq, что в целом логично, так как они используют один и тот же образ :D):
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:5: warning: already initialized constant DiscourseAutomation::Scripts::ADD_USER_TO_GROUP_THROUGH_CUSTOM_FIELD
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:5: warning: previous definition of ADD_USER_TO_GROUP_THROUGH_CUSTOM_FIELD was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:6: warning: already initialized constant DiscourseAutomation::Scripts::APPEND_LAST_CHECKED_BY
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:6: warning: previous definition of APPEND_LAST_CHECKED_BY was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:7: warning: already initialized constant DiscourseAutomation::Scripts::APPEND_LAST_EDITED_BY
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:7: warning: previous definition of APPEND_LAST_EDITED_BY was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:8: warning: already initialized constant DiscourseAutomation::Scripts::AUTO_RESPONDER
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:8: warning: previous definition of AUTO_RESPONDER was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:9: warning: already initialized constant DiscourseAutomation::Scripts::AUTO_TAG_TOPIC
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:9: warning: previous definition of AUTO_TAG_TOPIC was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:10: warning: already initialized constant DiscourseAutomation::Scripts::BANNER_TOPIC
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:10: warning: previous definition of BANNER_TOPIC was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:11: warning: already initialized constant DiscourseAutomation::Scripts::CLOSE_TOPIC
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:11: warning: previous definition of CLOSE_TOPIC was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:12: warning: already initialized constant DiscourseAutomation::Scripts::FLAG_POST_ON_WORDS
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:12: warning: previous definition of FLAG_POST_ON_WORDS was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:13: warning: already initialized constant DiscourseAutomation::Scripts::GIFT_EXCHANGE
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:13: warning: previous definition of GIFT_EXCHANGE was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:14: warning: already initialized constant DiscourseAutomation::Scripts::GROUP_CATEGORY_NOTIFICATION_DEFAULT
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:14: warning: previous definition of GROUP_CATEGORY_NOTIFICATION_DEFAULT was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:15: warning: already initialized constant DiscourseAutomation::Scripts::PIN_TOPIC
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:15: warning: previous definition of PIN_TOPIC was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:16: warning: already initialized constant DiscourseAutomation::Scripts::POST
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:16: warning: previous definition of POST was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:17: warning: already initialized constant DiscourseAutomation::Scripts::SEND_PMS
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:17: warning: previous definition of SEND_PMS was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:18: warning: already initialized constant DiscourseAutomation::Scripts::SUSPEND_USER_BY_EMAIL
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:18: warning: previous definition of SUSPEND_USER_BY_EMAIL was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:19: warning: already initialized constant DiscourseAutomation::Scripts::TOPIC
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:19: warning: previous definition of TOPIC was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:20: warning: already initialized constant DiscourseAutomation::Scripts::TOPIC_REQUIRED_WORDS
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:20: warning: previous definition of TOPIC_REQUIRED_WORDS was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:21: warning: already initialized constant DiscourseAutomation::Scripts::USER_GLOBAL_NOTICE
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:21: warning: previous definition of USER_GLOBAL_NOTICE was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:22: warning: already initialized constant DiscourseAutomation::Scripts::USER_GROUP_MEMBERSHIP_THROUGH_BADGE
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:22: warning: previous definition of USER_GROUP_MEMBERSHIP_THROUGH_BADGE was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:23: warning: already initialized constant DiscourseAutomation::Scripts::ZAPIER_WEBHOOK
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/scripts.rb:23: warning: previous definition of ZAPIER_WEBHOOK was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:5: warning: already initialized constant DiscourseAutomation::Triggers::AFTER_POST_COOK
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:5: warning: previous definition of AFTER_POST_COOK was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:6: warning: already initialized constant DiscourseAutomation::Triggers::API_CALL
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:6: warning: previous definition of API_CALL was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:7: warning: already initialized constant DiscourseAutomation::Triggers::CATEGORY_CREATED_EDITED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:7: warning: previous definition of CATEGORY_CREATED_EDITED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:8: warning: already initialized constant DiscourseAutomation::Triggers::PM_CREATED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:8: warning: previous definition of PM_CREATED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:9: warning: already initialized constant DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:9: warning: previous definition of TOPIC_TAGS_CHANGED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:10: warning: already initialized constant DiscourseAutomation::Triggers::POINT_IN_TIME
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:10: warning: previous definition of POINT_IN_TIME was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:11: warning: already initialized constant DiscourseAutomation::Triggers::POST_CREATED_EDITED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:11: warning: previous definition of POST_CREATED_EDITED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:12: warning: already initialized constant DiscourseAutomation::Triggers::RECURRING
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:12: warning: previous definition of RECURRING was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:13: warning: already initialized constant DiscourseAutomation::Triggers::STALLED_TOPIC
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:13: warning: previous definition of STALLED_TOPIC was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:14: warning: already initialized constant DiscourseAutomation::Triggers::STALLED_WIKI
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:14: warning: previous definition of STALLED_WIKI was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:15: warning: already initialized constant DiscourseAutomation::Triggers::TOPIC
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:15: warning: previous definition of TOPIC was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:16: warning: already initialized constant DiscourseAutomation::Triggers::USER_ADDED_TO_GROUP
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:16: warning: previous definition of USER_ADDED_TO_GROUP was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:17: warning: already initialized constant DiscourseAutomation::Triggers::USER_BADGE_GRANTED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:17: warning: previous definition of USER_BADGE_GRANTED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:18: warning: already initialized constant DiscourseAutomation::Triggers::USER_FIRST_LOGGED_IN
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:18: warning: previous definition of USER_FIRST_LOGGED_IN was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:19: warning: already initialized constant DiscourseAutomation::Triggers::USER_PROMOTED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:19: warning: previous definition of USER_PROMOTED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:20: warning: already initialized constant DiscourseAutomation::Triggers::USER_REMOVED_FROM_GROUP
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:20: warning: previous definition of USER_REMOVED_FROM_GROUP was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:21: warning: already initialized constant DiscourseAutomation::Triggers::USER_UPDATED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers.rb:21: warning: previous definition of USER_UPDATED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/recurring.rb:6: warning: already initialized constant DiscourseAutomation::Triggers::Recurring::RECURRENCE_CHOICES
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/recurring.rb:6: warning: previous definition of RECURRENCE_CHOICES was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/stalled_wiki.rb:6: warning: already initialized constant DiscourseAutomation::Triggers::StalledWiki::DURATION_CHOICES
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/stalled_wiki.rb:6: warning: previous definition of DURATION_CHOICES was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:7: warning: already initialized constant DiscourseAutomation::Triggers::TopicTagsChanged::TriggerOn::TAGS_ADDED_OR_REMOVED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:7: warning: previous definition of TAGS_ADDED_OR_REMOVED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:8: warning: already initialized constant DiscourseAutomation::Triggers::TopicTagsChanged::TriggerOn::TAGS_ADDED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:8: warning: previous definition of TAGS_ADDED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:9: warning: already initialized constant DiscourseAutomation::Triggers::TopicTagsChanged::TriggerOn::TAGS_REMOVED
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:9: warning: previous definition of TAGS_REMOVED was here
sidekiq-1     | /opt/bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:11: warning: already initialized constant DiscourseAutomation::Triggers::TopicTagsChanged::TriggerOn::MODES
sidekiq-1     | /bitnami/discourse/plugins/automation/lib/discourse_automation/triggers/topic_tags_changed.rb:11: warning: previous definition of MODES was here
sidekiq-1     | 2025-07-06T17:15:52.699Z pid=1 tid=4ox INFO: Booted Rails 7.2.2.1 application in production environment

Это отличный момент. На самом деле нет веской причины, по которой этот скрипт не может работать без прав root.

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

Скрипт запуска не требует прав root, ему нужен только доступ на запись к /var/discourse и разрешение на управление контейнерами Docker.

Эм… :slight_smile:

Скрипту даже не нужен доступ к /var/discourse (зачем он там вообще?!).

Вся проблема коренится в нескольких вещах:

  1. огромное непонимание того, что такое Docker, как он работает и какие возможности предоставляет;
  2. смешение понятий Docker и Docker Compose (это не одно и то же!).

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

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

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

Также скрипт пытается убедиться, что DNS настроен правильно, настраивает сертификаты, обратный прокси, SMTP и прочее — опять же, это вполне нормально для упрощённой установки.

НО!

Проблема не в том, чтобы отказаться от этого, а в том, чтобы ДОПОЛНИТЕЛЬНО предоставить чистый Docker-образ (он уже существует и используется скриптами и шаблонами! discourse_docker/templates/postgres.template.yml at main · discourse/discourse_docker · GitHub & discourse_docker/launcher at main · discourse/discourse_docker · GitHub) с:

  1. правильной версионированием: образ помечается тегом версии выпущенного Discourse (3.4.5 или другой);
  2. разумной и простой документацией ожидаемых переменных окружения (для настройки подключения к базе данных, Redis и т. д.) и возможных путей/томов, которые можно подключить к хост-системе.

Только этого достаточно…

Посмотрите на упомянутое руководство по Miniflux: Miniflux Installation with Docker — там подробно описывается образ (и репозитории, где он размещён), а также возможные переменные окружения для его настройки.
Или образ Docker для MySQL: mysql - Official Image | Docker Hub — то же самое: руководство, объясняющее, что можно настроить (особенно обратите внимание на раздел «Переменные окружения»).

Никто не говорит: «вы должны использовать mysql launcher для сборки образа MySQL, чтобы его можно было использовать», или то же самое для Redis — в этих случаях вы просто используете существующие образы, и именно в этом суть использования Docker. Однако в случае с Discourse это вдруг становится «плохим» решением, и все кричат: «вы должны собрать свой собственный образ!» — зачем!?}

Нет. Из-за плагинов.

Эм… пришлось немного покопаться в вопросе плагинов, и снова я вынужден сказать/привести цитату:

:smiley:

Итак, что я сделал — зашёл в раздел плагинов, нажал на первый плагин в поиске инструкций и руководства по установке (Plugin directory | Discourse - Civilized DiscussionSign in with Apple | Discourse - Civilized DiscussionDiscourse Apple AuthenticationInstall plugins on a self-hosted site) и… господи, какой запутанный «бардак».

Зачем вообще нужен новый образ только для плагинов? С Docker можно просто указать каталог монтирования для плагинов и поместить их туда…

Я не Ruby-разработчик, так что здесь всё может быть ещё страннее, но, например, в мире Java вы просто кладёте jar-файл с плагином, и он загружается — вуаля, он работает. При этом вы используете тот же образ, что и все остальные…

(Не утверждаю, что это не беспорядок, просто объясняю, почему всё устроено именно так)

Из-за конвейера активов Ember.

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

Имеете в виду это: GitHub - ember-engines/ember-asset-loader: Asset loading support for Ember applications · GitHub?

Почему бы и нет?

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

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

Это я не до конца понимаю (возможно, не хватает контекста в отношении Ruby…).

В целом, при использовании Docker/контейнеров/оркестрации можно применять стратегии развёртывания blue-green или red-black: вы подготавливаете новый контейнер с новой версией и переключаете трафик, как только он готов. Однако для небольших развёртываний это обычно не является проблемой, так как время запуска сервиса (контейнера) чаще всего составляет всего несколько секунд, поэтому простой при переключении версий будет минимальным. Основная сложность заключается в необходимости выполнения обновлений схемы базы данных, но это актуально независимо от того, пересобираете ли вы образ или нет.

Да, но в текущей реализации это работает иначе, поэтому именно здесь требуется доработка. Как вы планируете выполнять это через интерфейс администратора Discourse? Как вы будете уменьшать масштаб синего контейнера, одновременно запуская зелёный (у систем нет бесконечных ресурсов, поэтому перед запуском синего нужно сократить количество рабочих процессов unicorn в зелёном)? Или можно просто сначала полностью остановить зелёный, а затем запустить синий.

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

Плагины и пользовательские образы всё ещё требуют сборки, но ванильная сборка для Compose в наши дни становится вполне реальной — я создал небольшой эксперимент в качестве доказательства концепции здесь для всех, кому это интересно.

Очевидно, что поскольку это все мои репозитории (а не репозитории Discourse™), это не является чем-то официальным, поэтому результат может быть непредсказуем, и точно не стоит рассчитывать на официальную поддержку :dragon: Я публикую это здесь, так как уже упоминалось, что люди уже используют Bitnami и другие неофициальные способы развёртывания.

Готовые образы PostgreSQL (или pgvector) не подходят для Discourse — Discourse требует изменения конфигурации по умолчанию, не подключается через суперпользователя, поддерживает локали и нуждается в поддержке обновлений PostgreSQL без необходимости выгрузки и восстановления базы данных администратором Discourse.

Эх… думаю, это ключевое различие. Эта «обновление через интерфейс администратора» немного напоминает старые времена, когда у вас был только «веб-хостинг» с интерпретатором PHP, и всё строилось вокруг этой концепции. Мне кажется, это перенеслось и на то, как работает Discourse и как ожидается его управление.

Что ж, это ещё один момент, который я заметил — Discourse довольно ресурсоёмкий (контейнер начал потреблять ресурсы во время запуска на пугающем уровне), так что да, это может стать проблемой.

Как именно они «собираются» в контексте Ruby (который в основном интерпретируемый, а не компилируемый язык)?

Разве не лучше попытаться использовать готовые решения? Даже для postgres конфигурацию можно легко изменить, просто предоставив/смонтировав собственный файл postgresql.conf, нет необходимости в полностью кастомном образе с уже встроенной конфигурацией… и даже если это потребуется, Discourse мог бы предоставить такой предсобранный образ, который просто скачивался бы вместо повторной сборки каждый раз…

Этот «интерфейс административного обновления» — это плагин (называемый «docker manager»). Вам не нужно его включать, если у вас есть другой способ выполнять обновления онлайн.