Сколько нужно времени, чтобы обновить RocketChat с 7 на 8 версию? Пять минут? А нет — пять дней с перерывами.
Как же так получилось? Неужели всё сломалось, гипс снимают, клиент уезжает… Ой, не та опера. Начнём издалека для тех, кто не в теме.
Преамбула
Есть такой мессенджер Ма^ RocketChat, сервер которого можно развернуть локально. Развёртывание мне проще всего проводить через docker compose. Там всего два сервиса: сам сервер и база данных. Сервер отвечает за интерфейс и хранение вложений, база данных на mongodb отвечает за хранение сообщений и настроек. Есть официальный репозиторий — https://github.com/RocketChat/Rocket.Chat в котором ведётся разработка и выкладываются релизы, почитав которые, можно быть в курсе, что вышла новая версия и какие там произошли изменения. Есть репозиторий специально для докера — https://github.com/RocketChat/Docker.Official.Image.
Обновление сервера производить проще простого: поменял тег в образе в композ файле для сервиса rocketchat, выполнил «docker compose up -d» и всё. Сервер обновился, пользователи радуются новым рюшечкам.
С базой тоже всё просто. Раньше использовалась пятая версия от bitnami (компания специализирующаяся на создании защищённых сборок программ и образов). Потом они перешли на шестую, но неудобств от перехода не было — сменил версию и автоматизация внутри сделает своё дело. Знай меняй версии на новые и горя не знай. Для удобства можно даже клонировать и обновлять репозиторий, всё автоматически обновится.
Фабула
Однако первый звоночек прозвенел в сентябре 2025 года, когда в композ файле официального образа для докера появилась строка
This compose file is deprecated, please migrate to https://github.com/RocketChat/rocketchat-compose
«Ну и отлично же», — воскликнет любимый читатель, пройдёт по ссылке, прочитает простую инструкцию, склонирует репозиторий и будет радоваться, как за него любимого и красивого всё сделали. Так-то оно так, но «есть нюанс» — у вас микросервисы в проде, и у меня микросервисы в проде…
Сделали, конечно, там немало, но и добавили много чего ещё, что лично мне нафиг не нужно, например Grafana и Prometheus. Так что я со спокойной душой взял, что нужно из композ файлов и просто обновлял версии, забив на новый репозиторий.
Второй звонок прозвучал в октябре 2025 года, когда ссылка на образ базы данных из репозитория сменилась с docker.io/bitnami/mongodb:6.0 на docker.io/bitnamilegacy/mongodb:6.0. (Здесь и далее не буду приводить ссылку с переменными, как в репозитории, для лучшей удобочитаемости). Предупреждающую надпись я увидел не сразу и смена адреса образа базы данных прошла одновременно со сменой репозитория. В общем, стало понятно, что bitnami перестали поддерживать защищённые образы и надо бы думать куда переходить в скором времени. Скорое время как обычно не наступило, все забили\забыли, пока не прозвучал колокол обновления rocketchat’а на восьмую версию.
Обновление
Итак.
Дано:
– RocketChat 7.13.1 (последняя стабильная на момент обновления версия)
– Mongodb 6.0 от bitnami
– Система контейнеризации docker с использованием compose плагина. Для удобства запуска.
Лирическая часть
На странице релиза RocketChat белым по чёрному (тёмная тема всё же) написано:
«Breaking changes
This release introduces a wide set of breaking changes focused on removing deprecated, end-of-life, and legacy functionality. Support for MongoDB 5.0 and 6.0 has been discontinued, and workspaces must upgrade to MongoDB 8.2 before moving to Rocket.Chat v8.0.0…»
«Фигня вопрос», – скажет пытливый читатель и обновит тег в образе для mongodb с 6.0 на 8.2
«Error response from daemon: manifest for bitnamilegacy/mongodb:8.2 not found», – ответит докер и не станет ничего качать.
А всё потому что bitnami, будучи купленными ещё в 2019 году VMWare, которую в 2023 году купила Broadcom, опубликовали новость, где рассказали, что ограничат бесплатный доступ к своим образам. Таким образом, разработчикам придётся или платить за использование образов ПО, либо искать альтернативы.
Вот и получается, чтобы обновить rocketchat до 8 версии, нужно сначала обновить mongodb, а у нас денег нет репозитория нет.
«Так давайте зайдём на официальный репозиторий, да увидим ссылку на новый образ», — выдвинет предположение читатель, и я с ним полностью соглашусь. В самом деле, для чего ещё эти официальные репозитории нужны?
Заходим (в начале января) туда и видим множество yml файлов с сервисами. Удобно, никаких простыней кода, всё разложено по полочкам. Но чу, что мы видим в файле compose.database.yml? Файл изменён два месяца назад и в нём тот же старый адрес старого образа от bitnami. По ссылке можно посмотреть.
«Но как же так?», — огорчится читатель, — «Неужели разработчики забыли исправить нужные теги или сами не знают где брать новый образ». Получается, что так и есть.
Но давайте любопытства ради, а не злорадства для, зайдём на старый репозиторий, в котором всё так же написано, что этот репозиторий устарел, используйте вон тот новый (и будет вам счастье). Там наверное и коммитов не будет? Ага, щас. Репозиторий цветёт и пахнет, а в compose.yml указан адрес mongodb/mongodb-community-server:8.2-ubi8
И вот как вот в таком случае верить написанному? Как доверять разработчикам, которые «лучше знают». «А вот никак, бардак там, причём полный», — подумал я, и внёс в свой композ файл необходимый адрес.
И тут новая база подкинула сюрприз. И не один, а целых… много.
- Первый. В образе от bitnami владелец файлов, он же пользователь внутри контейнера имел id 1001, а в образе от community версии владелец уже с id 998.
- Второй. Поменялся внутренний путь к файлам. Раньше он был /bitnami/mongodb, а стал /data/db.
- Третий. Раньше база инициализировалась с опцией «MONGODB_REPLICA_SET_MODE: primary», что означало создание кластера из нескольких нод, теперь этой опции нет, mongo стартует как одиночный сервер. В этом коммите ясно видно удаление этих опций.
- Четвёртый. Так как новый образ не от bitnami, то при старте необходимо проинициализировать сервер для репликации вручную, а не передаваемыми переменными, которые раньше были встроены в старый образ Делается добавлением в композ файл entrypoint с командами инициализации.
Так что каждый раз при тестировании и попытках миграции, необходимо было остановить контейнер с базой, сменить на хосте владельца файлов, отредактировать композ файл и запустить контейнер с новой базой
Дальше были попытки нахрапом взять эту крепость запустить новую базу на основе старой. Было замечено следующее:
— запуск 7-й версии mongodb от Bitnami успешен c 6-й.
— запуск 8-й версии от bitnami (устаревшей, но тем не менее существующей) закончился неудачей при миграции с 7-й
— то же самое наблюдалось при использовании 8-й версии rocketchat’а. Версия rocketchat’а в общем-то не играет роли, главное — мигрировать базу, а с 7-й на 8-ую версию она просто так не мигрирует.
Значит надо делать бэкап текущей базы (можно даже 6-й версии) и восстанавливать на новую базу. При этом надо указать в проброшенных в контейнер volumes новое место, чтобы база 8-й версии проинициализировалась и на неё уже накатить бэкап.
Бэкап в mongodb делается просто.
docker compose exec mongodb mongodump --gzip -d rocketchat --archive=/bitnami/mongodb/backup-rchat.gz
, где
mongodb — имя сервиса в композ файле;
-d rocketchat — имя базы данных;
archive= — расположение создаваемого архива. Путь указан в volumes композ файла
Теперь останавливаем контейнер, указываем в композ файле новые данные для образа БД и новые данные. Запускаем контейнер, ждём инициализации новой базы, а затем импортируем из бэкапа:
docker compose exec mongodb mongorestore --gzip --drop --archive=/data/db/backup-rchat.gz
Теперь можно запустить rocketchat новой версии и убедиться, что всё заработало.
Техническая часть
Теперь по шагам опишу, как корректно выполнить обновление.
Вот изначальные файлы сервисов:
volumes:
mongodb_data: { driver: local }
services:
rocketchat:
image: ${IMAGE:-registry.rocket.chat/rocketchat/rocket.chat}:${RELEASE:-latest}
restart: always
labels:
traefik.enable: "true"
traefik.http.routers.rocketchat.rule: Host(`${DOMAIN:-}`)
traefik.http.routers.rocketchat.tls: "true"
traefik.http.routers.rocketchat.entrypoints: https
traefik.http.routers.rocketchat.tls.certresolver: le
environment:
MONGO_URL: "${MONGO_URL:-\
mongodb://${MONGODB_ADVERTISED_HOSTNAME:-mongodb}:${MONGODB_INITIAL_PRIMARY_PORT_NUMBER:-27017}/\
${MONGODB_DATABASE:-rocketchat}?replicaSet=${MONGODB_REPLICA_SET_NAME:-rs0}}"
MONGO_OPLOG_URL: "${MONGO_OPLOG_URL:\
-mongodb://${MONGODB_ADVERTISED_HOSTNAME:-mongodb}:${MONGODB_INITIAL_PRIMARY_PORT_NUMBER:-27017}/\
local?replicaSet=${MONGODB_REPLICA_SET_NAME:-rs0}}"
ROOT_URL: ${ROOT_URL:-http://localhost:${HOST_PORT:-3000}}
PORT: ${PORT:-3000}
DEPLOY_METHOD: docker
DEPLOY_PLATFORM: ${DEPLOY_PLATFORM:-}
REG_TOKEN: ${REG_TOKEN:-}
depends_on:
- mongodb
expose:
- ${PORT:-3000}
ports:
- "${BIND_IP:-0.0.0.0}:${HOST_PORT:-3000}:${PORT:-3000}"
mongodb:
image: docker.io/bitnamilegacy/mongodb:${MONGODB_VERSION:-6.0}
restart: always
volumes:
- ${MONGODB_HOST_PATH:-mongodb_data}:/bitnami/mongodb
environment:
MONGODB_REPLICA_SET_MODE: primary
MONGODB_REPLICA_SET_NAME: ${MONGODB_REPLICA_SET_NAME:-rs0}
MONGODB_PORT_NUMBER: ${MONGODB_PORT_NUMBER:-27017}
MONGODB_INITIAL_PRIMARY_HOST: ${MONGODB_INITIAL_PRIMARY_HOST:-mongodb}
MONGODB_INITIAL_PRIMARY_PORT_NUMBER: ${MONGODB_INITIAL_PRIMARY_PORT_NUMBER:-27017}
MONGODB_ADVERTISED_HOSTNAME: ${MONGODB_ADVERTISED_HOSTNAME:-mongodb}
MONGODB_ENABLE_JOURNAL: ${MONGODB_ENABLE_JOURNAL:-true}
ALLOW_EMPTY_PASSWORD: ${ALLOW_EMPTY_PASSWORD:-yes}
Содержимое файла .env
RELEASE=7.13.1
MONGODB_VERSION=6.0
MONGODB_HOST_PATH=/mnt/rocketchat/mongodb
Сначала останавливаем rocketchat, чтобы никаких изменений в базу не было внесено.
docker compose stop rocketchat
Делаем дамп БД
docker compose exec mongodb mongodump --gzip -d rocketchat --archive=/bitnami/mongodb/backup-rchat.gz
Останавливаем всё
docker compose down
Меняем владельца файлов
chown -R 998:998 /mnt/rocketchat/mongodb
Меняем содержимое композ файла на новое (выделил жирным)
volumes:
mongodb_data: { driver: local }
services:
rocketchat:
image: ${IMAGE:-registry.rocket.chat/rocketchat/rocket.chat}:${RELEASE:-latest}
restart: always
labels:
traefik.enable: "true"
traefik.http.routers.rocketchat.rule: Host(`${DOMAIN:-}`)
traefik.http.routers.rocketchat.tls: "true"
traefik.http.routers.rocketchat.entrypoints: https
traefik.http.routers.rocketchat.tls.certresolver: le
environment:
MONGO_URL: mongodb://mongodb:27017/rocketchat?replicaSet=rs0
ROOT_URL: ${ROOT_URL:-http://localhost:3000}
PORT: ${PORT:-3000}
DEPLOY_METHOD: docker
DEPLOY_PLATFORM: ${DEPLOY_PLATFORM:-}
REG_TOKEN: ${REG_TOKEN:-}
depends_on:
- mongodb
expose:
- ${PORT:-3000}
ports:
- "${BIND_IP:-0.0.0.0}:${HOST_PORT:-3000}:${PORT:-3000}"
mongodb:
image: mongodb/mongodb-community-server:${MONGODB_VERSION:-8.2}-ubi8
restart: on-failure
volumes:
- ${MONGODB_HOST_PATH:-mongodb_data}:/data/db
environment:
MONGODB_REPLICA_SET_NAME: ${MONGODB_REPLICA_SET_NAME:-rs0}
MONGODB_PORT_NUMBER: ${MONGODB_PORT_NUMBER:-27017}
MONGODB_INITIAL_PRIMARY_HOST: ${MONGODB_INITIAL_PRIMARY_HOST:-mongodb}
entrypoint: |
bash -c
"mongod --replSet $$MONGODB_REPLICA_SET_NAME --bind_ip_all &
sleep 2;
until mongosh --eval \"db.adminCommand('ping')\"; do
echo '=====> Waiting for Mongo...';
sleep 1;
done;
echo \"=====> Initiating ReplSet $$MONGODB_REPLICA_SET_NAME at $$MONGODB_INITIAL_PRIMARY_HOST:$$MONGODB_PORT_NUMBER...\";
mongosh --eval \"rs.initiate({_id: '$$MONGODB_REPLICA_SET_NAME', members: [{ _id: 0, host: '$$MONGODB_INITIAL_PRIMARY_HOST:$$MONGODB_PORT_NUMBER' }]})\";
echo '=====> Initiating ReplSet done...';
wait"
Меняем содержимое файла .env
RELEASE=8.0.1
MONGODB_VERSION=8.2
MONGODB_HOST_PATH=/mnt/rocketchat/mongodb
Запускаем только базу данных
docker compose up mongodb -d
Ждём запуска и инициализации и заливаем бэкап
docker compose exec mongodb mongorestore --gzip --drop --archive=/data/db/backup-rchat.gz
Запускаем rocketchat
docker compose up -d
PROFIT
Эпилог
Вот так, спустя неделю с перерывом на чай, нашёлся рецепт успешного обновления rocketchat. И таким образом обновление, когда знаешь что и как делать, занимает не пять дней, а пять минут.
В 8-й версии rocketchat как обычно убрали старые баги, добавили новые, поменяли интерфейс и сделали всё, чтобы пользователи привыкали к новому, а иначе мозг застаивается и костенеет.
На самом деле, смотря новые репозиторий можно заметить, что работа кипит, ведь чтобы сделать процесс перехода безболезненным и простым, надо не вручную делать все шаги, как я описал выше, а описать всё в файлах, чтобы автоматически система пришла в необходимое состояние. А пока — закат солнца вручную.
И неужели нельзя было сначала подготовить репозиторий с уже внедрённым способом автоматического обновления, чем вот так вот гнать пользователей\админов на новый репозиторий, при этом не ведя там никакой работы. Бардак. И людей мало. Opensource же.
