Есть такой тип инфраструктуры, которую администратор замечает только тогда, когда она перестаёт работать. Mattermost в production-среде - именно такой случай. Пока система держит нагрузку и отвечает на запросы, о ней не думают. Но стоит упасть одному узлу в неподходящий момент - и сотни пользователей теряют доступ к рабочим каналам, инцидент-тикетам и оперативной коммуникации. Именно поэтому высокая доступность (High Availability, HA) в Mattermost - не опция для энтузиастов, а архитектурная необходимость для любой организации, где корпоративный мессенджер стал частью критических рабочих процессов.
В этой статье разбирается, как правильно выстроить кластерную конфигурацию Mattermost с PostgreSQL в роли backend, начиная с подготовки базы данных и заканчивая тонкостями балансировки нагрузки и репликации.
Архитектура высокой доступности - три кита, на которых всё держится
Mattermost реализует HA через три независимо избыточных уровня: серверы приложений, серверы баз данных и балансировщики нагрузки. Выход из строя любого отдельного компонента не прерывает работу всей системы - это ключевой принцип. Но здесь важно понять одну вещь, которую упускают при первом развёртывании: каждый оставшийся после сбоя компонент должен быть заранее сконфигурирован на работу под полной нагрузкой. Если это требование не соблюдено, потеря одного сервера просто утащит за собой остальные под весом трафика, превратив частичный отказ в полный.
Типовая схема выглядит следующим образом: два и более сервера Mattermost принимают трафик через NGINX или аппаратный балансировщик, оба обращаются к единому PostgreSQL-кластеру с primary/replica-репликацией, а файловое хранилище вынесено в общий том или S3-совместимое объектное хранилище. Ни один из серверов приложений не хранит состояние локально - это принципиально.
Начиная с версии Mattermost 5.36, gossip-протокол для межузловой коммуникации стал обязательным и не может быть отключён. Все узлы кластера общаются между собой именно через него, используя порты 8074 (UDP и TCP) и 8075. Это нужно учитывать при настройке файерволов между серверами - порты должны быть открыты внутри кластерной сети.
Подготовка PostgreSQL - фундамент, который не должен качаться
PostgreSQL версии 14 и выше является обязательным требованием для актуальных релизов Mattermost. Поддержка MySQL официально устаревает начиная с версии Mattermost v11, и это не просто рекомендация - часть функциональности, включая отдельные возможности AI-расширений, доступна исключительно на PostgreSQL-backend.
Создание базы данных и пользователя выглядит стандартно, но с важным нюансом по локали:
CREATE DATABASE mattermost
WITH ENCODING 'UTF8'
LC_COLLATE='en_US.UTF-8'
LC_CTYPE='en_US.UTF-8'
TEMPLATE=template0;
CREATE USER mmuser WITH PASSWORD 'надёжный-пароль';
ALTER DATABASE mattermost OWNER TO mmuser;
ALTER SCHEMA public OWNER TO mmuser;
GRANT USAGE, CREATE ON SCHEMA public TO mmuser;
Если при создании базы возникает ошибка invalid LC_COLLATE locale name, нужно сначала сгенерировать локаль командой locale-gen en_US.UTF-8 и только потом повторить операцию.
После создания базы PostgreSQL нужно разрешить удалённые подключения. В файле postgresql.conf параметр listen_addresses должен принимать трафик с нужных интерфейсов, а в pg_hba.conf добавляется строка для mmuser с IP-адресом серверов Mattermost.
Для production-нагрузок под Mattermost официальная документация приводит набор параметров, протестированных на AWS Aurora r5.xlarge. Ключевые из них:
# postgresql.conf
max_connections = 1024
random_page_cost = 1.1
Значение max_connections = 1024 согласуется с параметром MaxOpenConns в конфигурации Mattermost, который рекомендуется держать в районе 100 при соотношении MaxIdleConns 2:1, то есть около 50. Если сервер менее мощный, чем r5.xlarge, оба значения нужно пропорционально снизить. random_page_cost = 1.1 выставляется для SSD-хранилищ; для дисков с вращающимися пластинами оставляйте дефолтное значение 4.0.
Потоковая репликация PostgreSQL - как данные копируют себя без вашего участия
Streaming replication в PostgreSQL работает на базе механизма Write-Ahead Log (WAL). Каждая транзакция в базе сначала записывается в журнал упреждающей записи, и именно эти записи в режиме реального времени передаются с primary-сервера на standby-узлы. На primary работает процесс wal_sender, на standby - wal_receiver. Когда они установили соединение, репликация идёт непрерывно, а не сегментами.
Настройка primary-узла сводится к правке postgresql.conf:
wal_level = replica
max_wal_senders = 5
wal_keep_size = 1024
hot_standby = on
archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/wal_archive/%f'
Параметр wal_level = replica - минимальный уровень для поддержки standby-серверов. max_wal_senders определяет максимальное количество одновременных WAL-соединений со standby-узлами; рекомендуется ставить с небольшим запасом сверх числа реплик, потому что внезапный разрыв соединения может оставить "призрачный" слот до истечения таймаута. archive_command позволяет архивировать WAL-сегменты - это страховка на случай, если standby отстаёт и primary успел удалить нужные сегменты.
Пользователь репликации создаётся отдельно:
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'repl-password';
В pg_hba.conf primary-сервера добавляется разрешение для этого пользователя с IP standby-узла:
host replication replicator 10.10.10.3/32 md5
На standby-сервере инициализация реплики выполняется через pg_basebackup:
pg_basebackup -h 10.10.10.2 -U replicator -D /var/lib/postgresql/14/main \
-P -Xs -R
Флаг -R автоматически создаёт файл standby.signal и вписывает строку подключения к primary в postgresql.auto.conf. После запуска PostgreSQL на standby репликация стартует автоматически.
Проверить, что репликация работает, можно на primary:
SELECT client_addr, state, replay_lsn
FROM pg_stat_replication;
Строка в состоянии streaming означает, что всё в порядке.
Конфигурация Mattermost для работы с кластером
После того как база данных поднята и реплицируется, настраивается сам Mattermost. Файл config.json на всех узлах кластера должен быть идентичным - это требование, а не рекомендация. Расхождение конфигов между узлами приводит к непредсказуемому поведению.
Секция SqlSettings для работы с primary и репликой:
"SqlSettings": {
"DriverName": "postgres",
"DataSource": "postgres://mmuser:Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. .2:5432/mattermost?sslmode=disable&connect_timeout=10",
"DataSourceReplicas": [
"postgres://mmuser:Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. .3:5432/mattermost?sslmode=disable&connect_timeout=10"
],
"MaxOpenConns": 100,
"MaxIdleConns": 50,
"ConnMaxLifetimeMilliseconds": 3600000,
"ReplicaLagSettings": [
{
"DataSource": "postgres://mmuser:Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. .2:5432/mattermost?sslmode=disable&connect_timeout=10",
"QueryAbsoluteLag": "select usename, pg_wal_lsn_diff(pg_current_wal_lsn(),replay_lsn) as metric from pg_stat_replication;",
"QueryTimeLag": null
}
]
}
DataSourceReplicas содержит список строк подключения к read-репликам. Mattermost автоматически направляет read-запросы на реплики, разгружая primary. ReplicaLagSettings позволяет Mattermost мониторить отставание реплики через разницу WAL LSN - если реплика слишком сильно отстала, сервер перестаёт направлять на неё запросы.
Кластерная секция активирует gossip-механизм:
"ClusterSettings": {
"Enable": true,
"ClusterName": "production",
"OverrideHostname": "",
"UseIpAddress": true,
"UseGossip": true,
"ReadOnlyConfig": true,
"GossipPort": 8074,
"StreamingPort": 8075
}
ReadOnlyConfig: true - критически важный параметр для кластера. Он запрещает запись изменений из System Console обратно в config.json, что при нескольких узлах могло бы привести к рассинхронизации конфигов. Все изменения при включённом параметре применяются в оперативной памяти, но не сохраняются на диск.
Оптимальное решение для HA-деплоя - хранить конфигурацию в самой базе данных, а не в файле. Для этого в mattermost.environment прописывается переменная окружения:
MM_CONFIG='postgres://mmuser:Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. .2:5432/mattermost?sslmode=disable&connect_timeout=10'
В этом режиме все узлы кластера автоматически получают одинаковую конфигурацию, а изменения через System Console распространяются на весь кластер мгновенно без ручной синхронизации файлов.
NGINX как балансировщик - не просто прокси, а умный распределитель
Балансировщик нагрузки - это не только распределение HTTP-трафика. В контексте Mattermost он обязан корректно проксировать WebSocket-соединения, которые используются для real-time обмена сообщениями. Если WebSocket работает неправильно, пользователи видят сообщения только после ручного обновления страницы.
upstream mattermost_backend {
least_conn;
server 10.10.10.2:8065;
server 10.10.10.4:8065;
keepalive 256;
}
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mattermost_cache:50m
max_size=16g inactive=60m use_temp_path=off;
server {
listen 443 ssl http2;
server_name mattermost.example.com;
location ~ /api/v[0-9]+/(users/)?websocket$ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_pass http://mattermost_backend;
proxy_http_version 1.1;
proxy_read_timeout 86400;
}
location / {
proxy_pass http://mattermost_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Алгоритм least_conn направляет каждый новый запрос на сервер с наименьшим числом активных соединений. Это важнее, чем кажется: алгоритм ip_hash, который часто используют по привычке, распределяет клиентов по серверам на основе их IP-адреса и при неравномерном распределении пользователей может перегрузить один узел при практически пустом другом.
Для проверки работоспособности узлов NGINX может использовать health check endpoint - http://10.10.10.2/api/v4/system/ping. Ответ со статусом 200 означает, что сервер принимает трафик. Именно этот endpoint используется для автоматической маркировки узла как "в строю" или "вне строя".
Patroni и автоматический failover - когда PostgreSQL принимает решения сам
Нативная streaming replication PostgreSQL не включает автоматическое переключение на реплику при падении primary. Это важно понимать: без дополнительного инструмента администратор должен вручную промотировать standby в primary при аварии. В производственной среде это неприемлемо.
Patroni решает эту задачу. Это шаблон HA для PostgreSQL с автоматическим failover, который работает поверх DCS-систем (etcd, Consul, ZooKeeper) для хранения состояния кластера и выбора лидера. Принцип работы прост: Patroni на каждом узле постоянно опрашивает DCS, и при потере лидером своей "аренды" (lease) узел с наименьшим отставанием репликации автоматически промотируется в primary.
Конфигурация Patroni на primary-узле (patroni.yml):
scope: mattermost-cluster
namespace: /db/
name: node1
restapi:
listen: 0.0.0.0:8008
connect_address: 10.10.10.2:8008
etcd:
hosts: 10.10.10.10:2379,10.10.10.11:2379,10.10.10.12:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 30
maximum_lag_on_failover: 1048576
synchronous_mode: false
pg_hba:
- host replication replicator 10.10.10.0/24 md5
- host all mmuser 10.10.10.0/24 md5
postgresql:
listen: 0.0.0.0:5432
connect_address: 10.10.10.2:5432
data_dir: /var/lib/postgresql/14/main
bin_dir: /usr/lib/postgresql/14/bin
authentication:
replication:
username: replicator
password: repl-password
superuser:
username: postgres
password: postgres-password
После запуска Patroni-кластера строку подключения в Mattermost нужно направлять не напрямую на конкретный IP, а через HAProxy или pgBouncer, которые знают, какой узел сейчас является primary. Patroni предоставляет REST API на порту 8008, через который HAProxy может проверять роль каждого узла.
Мониторинг и обслуживание кластера - то, о чём думают в последнюю очередь
Кластер без мониторинга - это иллюзия надёжности. Проверить состояние всех узлов Mattermost-кластера можно через System Console в разделе Environment > High Availability. Зелёные индикаторы напротив каждого узла означают, что gossip-коммуникация работает нормально. Любой жёлтый или красный статус - сигнал к немедленному изучению логов.
Для PostgreSQL-репликации критичен мониторинг отставания replicа от primary. Запрос, который показывает текущее состояние:
SELECT
client_addr,
state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes
FROM pg_stat_replication;
Отдельного внимания требуют replication slots. Неактивный слот продолжает удерживать WAL-сегменты на primary, и при длительном простое это может привести к полному заполнению дискового пространства - причём PostgreSQL не выдаст предупреждение в логах. Найти неактивные слоты:
SELECT slot_name, active FROM pg_replication_slots WHERE NOT active;
При обновлении кластера Mattermost порядок действий строго регламентирован. Нельзя иметь в кластере узлы с разницей версий больше одного минорного релиза одновременно. Процедура rolling upgrade выглядит так: проксирование трафика переводится на один сервер, остальные поочерёдно обновляются и возвращаются в ротацию. Между перезапусками серверов рекомендуется выдерживать паузу не менее 10 секунд - именно столько занимает инициализация gossip-соединений.
Autovacuum PostgreSQL при работе с Mattermost заслуживает отдельного внимания. Mattermost генерирует интенсивный поток UPDATE и DELETE операций в таблицах сообщений и каналов, и при неправильной настройке autovacuum таблицы начинают раздуваться, а запросы замедляться. Официальная документация рекомендует периодически перезапускать начальный SQL-запрос для расчёта параметров вакуумирования и корректировать значения по результатам. Главное правило - автовакуум должен успевать обрабатывать таблицы быстрее, чем в них накапливается "мёртвый" мусор.
Правильно выстроенный HA-кластер Mattermost с PostgreSQL backend - это не разовая задача настройки, а живая архитектура, которая требует понимания на всех уровнях: от механики WAL-репликации до алгоритмов балансировки HTTP-трафика. Те, кто вложил время в корректную начальную конфигурацию, получают систему, которую не замечают - в самом лучшем смысле этого слова.