Верил в мечту и в счастливые грёзы. День за день я жил - улетели те годы.
В мире корпоративных коммуникаций надёжность — это крайне важное преимущество, особенно в условиях, когда компании активно переходят на отечественное ПО. Если топ‑менеджер не может проверить почту перед важным совещанием или выездной инженер теряет доступ к критической документации из‑за отказа инфраструктуры, бизнес несёт прямые финансовые потери.
Мы решили эту задачу кардинально, в духе технологического суверенитета: вместо того чтобы бесконечно «латать» устаревающую или импортную инфраструктуру, мы спроектировали и развернули отказоустойчивый кластер специально для отечественной системы мобильных рабочих мест WorksPad.
В этой статье я расскажу, как этот проект вписывается в программу импортозамещения, почему для нас критически важен был принцип High Availability (высокая доступность), какие Open Source технологии мы выбрали для обеспечения независимости и как нам удалось заставить все компоненты работать как единый, бесперебойный механизм.
По официальной документации архитектура представлена в таком варианте:

Но мы пойдем иным путём, более сложным и более надежным.
В нашей инсталляции задействовано 11 виртуальных машин (ВМ) на платформе виртуализации ISPsystem VMmanager (https://www.ispsystem.ru/vmmanager), разделенных на логические слои. Такая сегментация позволяет гибко управлять нагрузкой и обеспечивает высокую доступность сервиса.
1. Слой балансировки и доступа (Frontend)
Это "ворота" в нашу систему. Все запросы от мобильных клиентов приходят сюда.
ВМ: nginx (192.168.1.127)
Роль: Обратный прокси (Reverse Proxy).
Задача: Принимает HTTPS-трафик извне и распределяет его между тремя серверами WorksPad. Также может выполнять терминацию SSL.
2. Слой приложений (Application Layer)
Здесь работает логика WorksPad. Мы используем кластер из трех узлов для отказоустойчивости.
ВМ: node1 (192.168.1.122) — WorksPad Server (Gateway, Assistant)
ВМ: node2 (192.168.1.123) — WorksPad Server
ВМ: node3 (192.168.1.124) — WorksPad Server
Задача: Обработка запросов пользователей, взаимодействие с Exchange и Active Directory.
Особенность: Эти серверы не хранят данные локально. Для работы им нужна связь с БД через слой Middleware.
3. Слой Middleware (Отказоустойчивая маршрутизация БД)
Прослойка, которая делает работу с базой данных надежной.
ВМ: proxy1 (192.168.1.140) — PgBouncer + Keepalived (Master)
ВМ: proxy2 (192.168.1.141) — PgBouncer + Keepalived (Backup)
Задача:
VIP (192.168.1.200): Keepalived держит этот «плавающий» IP. Все ноды WorksPad стучатся именно на 192.168.1.200:6432.
Пулинг: PgBouncer принимает соединения и перенаправляет их на текущего лидера кластера Patroni.
Результат: При падении proxy1 адрес.200 мгновенно переезжает на proxy2. Сервис не останавливается.
4. Слой данных (Data Layer)
Кластер хранения данных под управлением Patroni.
ВМ: psql1 (192.168.1.130) — PostgreSQL + Patroni
ВМ: psql2 (192.168.1.131) — PostgreSQL + Patroni (Текущий Лидер в нашем примере)
ВМ: psql3 (192.168.1.132) — PostgreSQL + Patroni
Задача: Надежное хранение данных WorksPad.
Механика: Patroni следит за состоянием PostgreSQL. Если Лидер (.131) упадет, Patroni выберет нового лидера (например,.130), и PgBouncer начнет слать запросы туда.
5. Инфраструктурные сервисы (Infrastructure)
Внешние системы, необходимые для работы корпоративной среды.
ВМ: msad (192.168.1.128) — Microsoft Active Directory 2016
Задача: Аутентификация пользователей WorksPad.
ВМ: exchange (192.168.1.205) — Microsoft Exchange 2016
Задача: Источник корпоративной почты, календарей и контактов, с которыми работает WorksPad.
Как проходит запрос пользователя (Flow):
Пользователь открывает приложение WorksPad.
Запрос летит на Nginx (
.127).Nginx пересылает его на одну из нод, например, node2 (
.123).node2 понимает, что нужно проверить данные в базе. Он делает запрос на VIP Middleware (
.200).Текущий активный прокси (proxy1,
.140) принимает запрос через PgBouncer.PgBouncer пересылает запрос на реального лидера БД (psql2,
.131).psql2 отдает данные, и цепочка возвращается обратно пользователю.
Эта схема гарантирует, что падение любого одного сервера (nginx, node, proxy, psql) не приведет к остановке сервиса.
И так, а теперь перейдем к этапам установки и настройки:
Предполагаем, что ОС (мы использовали Astra Linux) уже установлена.
Этап 1. Создаем "Сердце" системы — Кластер PostgreSQL + Patroni
Patroni — это не просто красивое слово. Это технология, которая превращает обычную базу данных PostgreSQL в самовосстанавливающийся кластер. Без него наша архитектура — просто куча серверов, которые нужно чинить вручную.
Зачем нужен Patroni?
Представьте: у вас есть база данных PostgreSQL с репликацией Master → Slave. Всё работает отлично, пока Мастер не упадет. Что дальше?
Без Patroni: Вы (администратор) должны в 3 часа ночи зайти на Slave-сервер и вручную выполнить команды, чтобы "повысить" его до Мастера. Это часы простоя.
С Patroni: Система сама обнаруживает смерть Мастера за 5 секунд, проводит "выборы" среди живых реплик и автоматически назначает нового лидера. Человек может спокойно спать.
В нашем проекте мы используем 3 сервера PostgreSQL под управлением Patroni:
psql1 (192.168.1.130)
psql2 (192.168.1.131) — будет Мастером изначально
psql3 (192.168.1.132)
Шаг 1.1. Устанавливаем PostgreSQL
На всех трех серверах (psql1, psql2, psql3) выполняем:
sudo apt updatesudo apt install postgresql postgresql-contrib
Проверяем, что PostgreSQL запустился:
systemctl status postgresql
Важно: Мы не будем настраивать репликацию вручную. Patroni сделает это сам.
Шаг 1.2. Устанавливаем Patroni и etcd
Patroni нужен "координатор" — сервис, который хранит информацию о том, кто сейчас Мастер. Мы используем etcd (легковесная распределенная база данных для конфигов).
На всех трех серверах БД:
sudo apt install patroni etcd python3-etcd
Шаг 1.3. Настраиваем etcd (Coordinator)
Для простоты мы развернем etcd на тех же серверах, где PostgreSQL.
Редактируем /etc/default/etcd на каждом узле.
На psql1:
ETCD_NAME="psql1"
ETCD_INITIAL_CLUSTER="psql1=http://192.168.1.130:2380,psql2=http://192.168.1.131:2380,psql3=http://192.168.1.132:2380"ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="patroni-cluster"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.1.130:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.1.130:2379" ETCD_LISTEN_PEER_URLS="http://192.168.1.130:2380" ETCD_LISTEN_CLIENT_URLS="http://192.168.1.130:2379,http://127.0.0.1:2379"
На psql2 и psql3 меняем только IP и имя узла.
Запускаем etcd на всех узлах:
systemctl start etcdsystemctl enable etcd
Проверяем кластер:
etcdctl member list
Должны увидеть три узла.

Шаг 1.4. Настраиваем Patroni
Теперь учим Patroni управлять PostgreSQL.
Создаем конфиг /etc/patroni/patroni.yml на каждом сервере.
Пример для psql2 (будущий Мастер):
scope: workspad-cluster
namespace: /db/
name: psql2
restapi:
listen: 192.168.1.131:8008
connect_address: 192.168.1.131:8008
etcd:
hosts: 192.168.1.130:2379,192.168.1.131:2379,192.168.1.132:2379
bootstrap:
dcs:
ttl: 30
loop_wait: 10
retry_timeout: 10
maximum_lag_on_failover: 1048576
postgresql:
use_pg_rewind: true
parameters:
max_connections: 500
shared_buffers: 256MB
initdb:
- encoding: UTF8
- data-checksums
pg_hba:
- host replication replicator 192.168.1.0/24 md5
- host all all 192.168.1.0/24 md5
postgresql:
listen: 192.168.1.131:5432
connect_address: 192.168.1.131:5432
data_dir: /var/lib/postgresql/15/main
bin_dir: /usr/lib/postgresql/15/bin
authentication:
replication:
username: replicator
password: RepliPass123
superuser:
username: postgres
password: StrongPass
На psql1 и psql3 конфиг аналогичный, но меняем:
name: psql1/psql3Все упоминания IP на соответствующий (
.130и.132).
Шаг 1.5. Запускаем Patroni
На всех трех серверах:
systemctl start patronisystemctl enable patroni
Проверяем статус кластера:
patronictl -c /etc/patroni/patroni.yml list

Шаг 1.6. Создаем базу данных WorksPad
Заходим на текущего лидера (в нашем случае psql2):
sudo -u postgres psql
Создаем базу и пользователя:
CREATE DATABASE workspad;
CREATE USER workspad WITH PASSWORD 'jhYg/=\F';
GRANT ALL PRIVILEGES ON DATABASE workspad TO workspad;
\q
Patroni будет автоматически реплицировать эту базу на остальные узлы.
Часть 2. Установка и настройка WorksPad
Теперь переходим к установке самого приложения WorksPad. Этот процесс нужно повторить на всех трех узлах: node1, node2 и node3.
Этап 2.1. Подготовка зависимостей
WorksPad работает на платформе .NET Core. Прежде чем ставить сервер, нужно подготовить почву в ОС (мы используем Astra Linux или Debian).
Устанавливаем ASP.NET Core Runtime 6.0
Это "движок", на котором работает приложение.
# Скачиваем ключи Microsoft (для доступа к репозиториям)
wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt update
# Устанавливаем сам рантайм
sudo apt install -y aspnetcore-runtime-6.0
Устанавливаем системные библиотеки и GSS-NTLMSSP WorksPad активно работает с графикой (для предпросмотра документов) и аутентификацией Windows.
sudo apt install -y libgdiplus libc6-dev libicu-dev gss-ntlmssp
Этап 2.2 Установка пакета WorksPad Server
В отличие от многих Linux-сервисов, WorksPad имеет удобный инсталлятор, который сам создает службы и папки.
Запускаем установку Копируем .deb пакет на сервер и запускаем:
sudo dpkg -i workspad_amd64.deb
Мастер настройки (прямо в консоли) Инсталлятор задаст вам 4 вопроса. Важно отвечать правильно, с учетом нашей архитектуры!
Вопрос 1: Database Server host (Адрес сервера БД)
Здесь мы вводим наш VIP-адрес PgBouncer: 192.168.1.200:6432
Почему: Мы не указываем адреса реальных PostgreSQL серверов. WorksPad должен ходить только через прокси (в нашем проекте).
Вопрос 2: Database name (Имя базы)
Пишем: workspad
Вопрос 3: Database User (Пользователь)
Пишем: workspad
Вопрос 4: Password (Пароль)
Вводим пароль пользователя workspad (который мы прописываем в userlist.txt на pgbouncer).
После этого инсталлятор распакует файлы в /opt/workspad и автоматически создаст systemd-службы (Gateway, Assistant, AdminAPI и др.).
Этап 3. Настройка кластеризации WorksPad
Инсталлятор настроил всё для одиночного сервера. Но у нас кластер из трех нод.
Этап 3.1. Синхронизация сертификатов шифрования
При первой установке на node1 WorksPad сгенерировал уникальный сертификат защиты данных . Если его не скопировать на node2 и node3, то пользователь, переключившийся между нодами, "вылетит" из сессии.
Зайдите на node1.
И на этом этапе я продемонстрирую как это описано в фоициальной документации

Этап 4. Готовим "Регулировщика" (PgBouncer + Keepalived)
Мы работаем на серверах proxy1 и proxy2.
Этап 4.1. Устанавливаем софт:
sudo apt update
sudo apt install pgbouncer keepalived
Этап 4.2. Настраиваем Keepalived (Виртуальный IP):
Нам нужно, чтобы адрес 192.168.1.200 жил на этих серверах. Открываем конфиг
nano /etc/keepalived/keepalived.conf.
На proxy1 (Master):
vrrp_instance VIP_GPROXY {
state MASTER
interface eth0
virtual_router_id 70
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass SecretPass
}
virtual_ipaddress {
192.168.1.200/24
}
}
На proxy2 (Backup): конфиг тот же, но state BACKUP и priority 90.
Запускаем: systemctl start keepalived. Проверяем ip a — адрес 192.168.1.200 должен появиться на мастере.
Этап 4.3. Настраиваем PgBouncer
Редактируем /etc/pgbouncer/pgbouncer.ini.
[databases]
workspad = host=192.168.1.131 port=5432 dbname=workspad
[pgbouncer]
listen_addr = *
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
Самый хитрый момент: Пароли PgBouncer не хранит пароли в открытом виде. Ему нужен MD5-хэш. Генерируем его в консоли (формат: пароль + логин):
echo -n "МойПарольworkspad" | md5sum
Результат вставляем в /etc/pgbouncer/userlist.txt:
"workspad" "md5<ваш_полученный_хэш>"
Перезапускаем: systemctl restart pgbouncer.
Этап 5. Настраиваем WorksPad
Теперь идем на сервера приложений: node1, node2, node3.
WorksPad уже установлен в /opt/workspad. Нам нужно научить его ходить через наш новый отказоустойчивый шлюз.
Открываем файл настроек:
/opt/workspad/GatewayService/appsettings.json
Нас интересует секция ConnectionStrings.
Было (напрямую к базе):
"Host=psql2;Database=workspad;..."
Стало (через наш кластер):
"ConnectionStrings": { "WorksPadDatabase": "Host=192.168.1.200:6432;Database=workspad;User Id=workspad;Password='jhYg/=\\F';"}
Перезапускаем сервис:
systemctl restart workspad-gateway
Часть 5. Входные ворота: Настройка Nginx Load Balancer
Это последний недостающий пазл вашей статьи. Nginx здесь выполняет роль входных ворот (Load Balancer). Именно он принимает пользователей из интернета и распределяет их между тремя нодами WorksPad (node1, node2, node3).
У нас есть три мощных сервера WorksPad, но пользователи о них не знают. Они стучатся по одному адресу (например, workspad.company.com). Нам нужен "швейцар", который встретит гостя и проводит к свободному серверу. Эту роль выполняет Nginx.
Мы работаем на сервере nginx1 (IP: 192.168.1.127).
5.1. Установка
Всё стандартно:
sudo apt updatesudo apt install nginx
5.2. Настройка балансировки (Upstream)
Nginx должен знать обо всех наших трех узлах. Открываем конфиг (обычно создаем новый /etc/nginx/conf.d/workspad.conf или редактируем default):
# Группа серверов WorksPad (Backends)
upstream workspad_cluster {
# Метод балансировки: ip_hash важен!
# Он "привязывает" пользователя к одному серверу на время сессии.
# Без этого пользователя будет выкидывать при каждом запросе.
ip_hash;
server 192.168.1.122:4430; # Node1
server 192.168.1.123:4430; # Node2
server 192.168.1.124:4430; # Node3
}
5.3. Настройка проксирования (Server Block)
Теперь настраиваем сам сервер, который слушает порт 443 (HTTPS) и пересылает запросы в workspad_cluster.
server {
listen 443 ssl;
server_name workspad.company.com; # Ваше доменное имя
# SSL сертификаты (те же, что и на самих нодах WorksPad)
ssl_certificate /etc/nginx/ssl/workspad.crt;
ssl_certificate_key /etc/nginx/ssl/workspad.key;
# Основные настройки SSL
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Локация для перенаправления
location / {
proxy_pass https://workspad\_cluster; # Шлем трафик в наш кластер
# Важные заголовки, чтобы WorksPad понимал, кто реальный клиент
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;
# Поддержка WebSocket (критично для WorksPad!)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Увеличиваем таймауты для тяжелых файлов
proxy_read_timeout 300s;
}
}
5.4. Проверка и запуск
Проверяем, что мы не наделали ошибок в конфиге:
sudo nginx -t
Если видите syntax is ok — вы молодец.
Перезагружаем Nginx:
sudo systemctl reload nginx
Если node1 упадет, Nginx (благодаря пассивным проверкам) поймет это и перестанет слать туда трафик. Пользователи автоматически пойдут на node2 и node3. Если нагрузка вырастет, мы просто поднимем node4, добавим одну строку в upstream и перезагрузим Nginx.
Итог: Что мы построили?
Потратив время на настройку этой архитектуры, мы ушли от "ремесленного" подхода ("лишь бы работало") к промышленному стандарту надежности.


Мы собрали систему из 11 серверов, которая работает как единое целое:
Nginx встречает пользователя и отправляет его к свободному серверу.
Кластер WorksPad (3 ноды) обрабатывает запросы. Если даже один сервер остался, работа продолжится.
PgBouncer + Keepalived работа с СУБД продолжится, даже если будет происходить аварийное переключение.
Patroni + PostgreSQL гарантируют, что данные (самое ценное, что у нас есть) не пропадут и будут доступны 24/7.
d3d11
Как вы решили проблему доступности в условии белых списков?
Самый актуальный сегодня вопрос.