Содержание
Схема
Описание
Архитектура рассчитана на использование одного сервера как основной и второго сервера как запасной с постоянной репликацией. В случае отказа работы основного возможность ручного переключения на запасного с помощью скрипта и изменении IP-адреса указывающего на основную ноду.
В архитектуре используются следующие компоненты и входят в состав стандартной установки Корпоративного сервера 2024:
- PostgreSQL 11;
- RabbitMQ-server 3.13.7;
- Redis server 7.2.7;
- glusterfs 5.5;
Проверка осуществлялась на версии 2.0.2024.14752 Корпоративного сервера 2024 и 2025.2.1.801 Сервера документов.
Условия эксплуатации
1. При использовании одной доменной А записи с несколькими IP-адресами (с возможностью включения и выключения IP) или изменение доменной записи для Корпоративного сервера вида *.domain.ru
, например: *.test2.s7-office.site
, и отдельными записями для двух почтовых серверов (*опционально).
2. В случае если почтовый сервер не нужен, то возможна установка без почтового сервера. Предварительно ознакомьтесь с инструкцией чтобы исключить почтовый сервер из установки.
Команды связанные с почтовым сервером отмечены в статье.
3. При ошибках работы приложений на сервере master потребуется переключение сервера slave в режим master с помощью скрипта.
Технические требования
- 2 Виртуальные машины;
- Для хранения данных glusterfs;
- ТХ Машин, для тестирования, возможно использовать:
от 4 CPU;
от 8Гб RAM;
от 50Гб свободного пространства на диске; - Более конкретные данные рассчитываются по обращению в ТП;
- Отключение или перевод selinux в режим permissive для корректной работы сервисов.
1. Установка и настройка сервиса хранения glusterfs
1.1. Описание
Файловое хранилище:
- Для Почтового сервера будет использоваться каталог:
/mail
;
- Для Корпоративного сервера 2024:
/var/r7-office/filestorage
/var/r7-office/searchindex
;
- Для Сервера документов:
/var/www/r7-office/Data
/var/lib/r7-office/documentserver/App_Data/cache
.
Нет необходимости синхронизировать временные каталоги:
filestorage_temp filestorage_temp_proc
БД:
Используется PostgreSQL версии 11 или более новая в режиме master-slave.
1.2. Установка и настройка glusterfs
Добавьте запись в /etc/hosts
:
192.168.27.10 gluster1 192.168.27.36 gluster2
Рекомендуется использовать запись 127.0.0.1 localhost
как стандартную и без использования имени сервера, так как в дальнейшем будут добавлены другие записи для работы почтового сервера.
Укажите соответствующее имя (поправить на свой домен и IP-адрес сервера), вместо mx1
можно использовать любое необходимое имя, например: mail
:
На сервере №1
hostnamectl set-hostname mx1.your-domain.ru
Hostname сервера не совпадает с его доменом. Например, server.example.com
— полное имя домена, а server
— его hostname. Данная запись необходима для почтового сервера.
Без использования почтового сервера возможно задать любое имя сервера.
Укажите соответствующее имя на сервере №2 для почтового сервера (поправить на свой домен):
На сервере №2
hostnamectl set-hostname mx2.your-domain.ru
Добавьте запись в /etc/hosts
:
192.168.27.10 mx1.your-domain.ru 192.168.27.36 mx2.your-domain.ru
Укажите имя и IP ваших серверов.
Для корректной установки корпоративного сервера требуется наличие перевода строки в конце файла /etc/hosts
.
Без использования почтового сервера возможно задать любое имя сервера.
Для применения параметров выполните перезапуск серверов.
1.3. На всех нодах установить, запустить и добавить в автозагрузку
apt install -y glusterfs-server systemctl start glusterd.service systemctl enable glusterd.service
Не имеет значения, какой из узлов вы будете использовать, но в следующем примере команда запускается на gluster1:
gluster peer probe gluster2
Фактически эта команда сообщает gluster1 доверять gluster2 и регистрирует его как часть пула хранения данных.
Если зондирование пройдет успешно, вы получите следующий вывод:
peer probe: success
Вы можете проверить связь узлов в любое время путем запуска команды:
gluster peer status
Если вы запустите эту команду из gluster2, вы увидите следующий вывод:
Number of Peers: 1 Hostname: gluster1 Uuid: 7ecfa2d1-3394-4f15-a1f5-bba484f2bbef State: Peer in Cluster (Connected)
Рекомендуется на время установки/настройки отключить firewalld.
На этом этапе два ваших сервера взаимодействуют и готовы к созданию томов хранения друг с другом.
Создание томов
Выполняется на первом сервере.
Для создания тома вы будете использовать команду gluster volume create с таким общим синтаксисом:
для почтового сервера sudo gluster volume create mail_volume replica 2 gluster{1,2}:/mail_volume force sudo gluster volume create cddisk-filestorage replica 2 gluster{1,2}:/cddisk-filestorage force sudo gluster volume create cddisk-searchindex replica 2 gluster{1,2}:/cddisk-searchindex force sudo gluster volume create ds-data replica 2 gluster{1,2}:/ds-data force sudo gluster volume create ds-cache replica 2 gluster{1,2}:/ds-cache force
Если том был создан успешно, вы увидите следующий вывод:
volume create: mail_volume: success: please start the volume to access data
На этом этапе ваш том создан, но еще не активирован. Вы можете запустить том и сделать его доступным для использования путем выполнения следующей команды с любого сервера Gluster:
для почтового сервера sudo gluster volume start mail_volume sudo gluster volume start cddisk-filestorage sudo gluster volume start cddisk-searchindex sudo gluster volume start ds-data sudo gluster volume start ds-cache
Вы получите следующий вывод, если том запущен корректно:
volume start: mail_volume: success volume start: cddisk-filestorage: success volume start: cddisk-searchindex: success volume start: ds-data: success volume start: ds-cache: success
Затем проверьте, находится ли том в сети. Запустите следующую команду с любого из ваших узлов:
sudo gluster volume status
В результате вы увидите вывод, аналогичный данному:
Status of volume: mail Gluster process TCP Port RDMA Port Online Pid ------------------------------------------------------------------------------ Brick gluster1:/mail 49152 0 Y 4495 Brick gluster2:/mail 49152 0 Y 20192 Self-heal Daemon on localhost N/A N/A Y 4518 Self-heal Daemon on gluster2 N/A N/A Y 20215 Task Status of Volume mail_volume ------------------------------------------------------------------------------ There are no active volume tasks
Создайте каталог и смонтируйте
Инструкция ниже выполняется на всех серверах.
#для почтового сервера mkdir /mail mkdir -p /var/r7-office/filestorage mkdir -p /var/r7-office/searchindex mkdir -p /var/www/r7-office/Data mkdir -p /var/lib/r7-office/documentserver/App_Data/cache #для почтового сервера mount.glusterfs localhost:/mail_volume /mail mount.glusterfs localhost:/cddisk-filestorage /var/r7-office/filestorage mount.glusterfs localhost:/cddisk-searchindex /var/r7-office/searchindex mount.glusterfs localhost:/ds-data /var/www/r7-office/Data mount.glusterfs localhost:/ds-cache /var/lib/r7-office/documentserver/App_Data/cache
Добавьте в автозагрузку:
#для почтового сервера { echo 'localhost:/mail_volume /mail glusterfs defaults,_netdev,backupvolfile-server=localhost 0 0' echo 'localhost:/cddisk-filestorage /var/r7-office/filestorage glusterfs defaults,_netdev,backupvolfile-server=localhost 0 0' echo 'localhost:/cddisk-searchindex /var/r7-office/searchindex glusterfs defaults,_netdev,backupvolfile-server=localhost 0 0' echo 'localhost:/ds-data /var/www/r7-office/Data glusterfs defaults,_netdev,backupvolfile-server=localhost 0 0' echo 'localhost:/ds-cache /var/lib/r7-office/documentserver/App_Data/cache glusterfs defaults,_netdev,backupvolfile-server=localhost 0 0' } | sudo tee -a /etc/fstab
Добавьте задание для перезапуска glusterfs, в случае перезагрузки или выключения питания:
Инструкция ниже выполняется на всех серверах.
После перезагрузки сервера, могут наблюдаться проблемы с синхронизацией glusterfs. Поэтому данный метод решает эту проблему:
echo "@reboot sleep 30 && systemctl restart glusterd.service" | crontab -
Проверка монтирования:
df -h mount -a
Проверьте корректность синхронизации каталогов между серверами, создавая тестовые файлы или каталоги в монтированных каталогах.
При настройке разрешенных портов рекомендуется проверить все задействованные порты на двух серверах:
ss -tulnp | grep gluster
2. Подготовка к установке почтового сервера (*опционально)
2.1. Записи в DNS и необходимые конфигурации
Для почтового сервера необходимо сделать A и MX записи в виде:
MX записи:
mx1 - 192.168.27.186 mx2 - 192.168.26.253
А записи:
smtp - 192.168.27.186, 192.168.26.253 imap - 192.168.27.186, 192.168.26.253
Примеры А записей:
А также TXT запись:
v=spf1 +mx ~all
TXT v=spf1 +a +mx -all
говорит о том, что отправлять письма от имени домена your-domain.ru
могут сервера, указанные в A и MX-записях этого домена, а письма, отправленные от других серверов должны быть удалены (Fail). Важно понимать: SPF-запись не наследуется на поддомены.
3. Установка и настройка БД
Установите следующие пакеты на оба сервера:
sudo apt update && sudo apt install postgresql -y
3.1. БД
Инструкция ниже выполняется на всех серверах.
Отредактируйте postgresql.conf
:
sudo nano /etc/postgresql/14/main/postgresql.conf #в зависимости от версии установленной postgresql сервера, путь к конфигурационному файлу может отличаться
Приведите параметры к виду:
listen_addresses = 'localhost,192.168.26.48' # what IP address(es) to listen on; port = 5432
Где:
localhost
,192.168.26.48
— адреса, которые слушает сервис;5432
— порт, который сервис прослушивает.
В файле postgresql.conf
разрешите логическую репликацию. Для этого отредактируйте /etc/postgresql/14/main/postgresql.conf
:
listen_addresses = 'localhost,ip_srv,ip_srv2' # Слушать на адресах port = 5432 # Задать порт БД wal_level = replica # Включить репликацию archive_mode = on # Включить архивирование WAL archive_command = 'cp %p /var/lib/postgresql/14/main/archive/%f' # Команда для архивирования WAL, в зависимости от версии установленной postgresql сервера, путь к файлу может отличаться max_wal_senders = 5 # Максимальное количество одновременных подключений репликации max_replication_slots = 5 # Количество слотов для репликации hot_standby = on # Разрешить подключения в режиме hot standby hot_standby_feedback = on # Определяет, будет или нет сервер slave сообщать мастеру о запросах, которые он выполняет.
Где:
ip_srv1
, ip_srv2
— адреса внутренней сети первого и второго почтового сервера.
Инструкция ниже выполняется на всех серверах.
Настройте аутентификацию пользователей в базе данных. Приведите к следующему виду файл /etc/postgresql/14/main/pg_hba.conf
:
# Разрешить локальные подключения для всех пользователей # "local" is for Unix domain socket connections only local all all trust # IPv4 local connections: host all all 127.0.0.1/32 trust host all all ip_srv1/32 trust host all all ip_srv2/32 trust
Вместо ip_srv1/32
и ip_srv2/32
укажите IP-адрес двух серверов. Режим trust
необходим для установки Корпоративного сервера.
И добавьте строки в конце для возможности репликации БД:
# Разрешить репликацию с обоих серверов для пользователя replication_user host replication replication_user ip_srv1/32 md5 host replication replication_user ip_srv2/32 md5
Где:
ip_another_srv/32
— адрес первого или второго почтового сервера, в зависимости от сервера, где производится настройка.
Выполните перезапуск сервиса БД:
sudo systemctl restart postgresql
Инструкция ниже выполняется на сервере №1.
После базовой настройки базы данных, создаем необходимую структуру в БД, создаем пользователей и настраиваем репликацию:
sudo -u postgres psql
В консоли psql>
выполните следующие команды (вместо паролей password
и replication_password
, cddisk
и ds
задайте свои пароли и в поле insert into
вместо YOUR_DOMAIN
укажите реальный адрес вашего домена), подтверждая каждую нажатием Enter:
Не рекомендуется использовать другие имена для баз данных cddisk и ds.
CREATE USER cddisk WITH password 'cddisk'; CREATE USER ds WITH password 'ds'; CREATE DATABASE cddisk OWNER cddisk; CREATE DATABASE ds OWNER ds; GRANT ALL privileges ON DATABASE cddisk TO cddisk; GRANT ALL privileges ON DATABASE ds TO ds; CREATE USER replication_user WITH REPLICATION LOGIN PASSWORD 'replication_password'; GRANT CONNECT ON DATABASE cddisk TO replication_user; GRANT CONNECT ON DATABASE ds TO replication_user; \c cddisk GRANT USAGE ON SCHEMA public TO replication_user; GRANT SELECT ON ALL TABLES IN SCHEMA public TO replication_user; GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO replication_user; \c ds GRANT USAGE ON SCHEMA public TO replication_user; GRANT SELECT ON ALL TABLES IN SCHEMA public TO replication_user; GRANT SELECT ON ALL SEQUENCES IN SCHEMA public TO replication_user; Для выхода из БД \q
Инструкция ниже выполняется на сервере №1.
Создайте директорию для архива:
#в зависимости от версии установленной postgresql сервера, путь к каталогу может отличаться sudo mkdir -p /var/lib/postgresql/14/main/archive sudo chown -R postgres:postgres /var/lib/postgresql/14/main/archive sudo chmod -R 750 /var/lib/postgresql/14/main/archive
Инструкция ниже выполняется на сервере №1.
Перезагрузите службу PostgreSQL для применения изменений:
systemctl restart postgresql
3.2. Настройка репликации БД
Инструкция ниже выполняется на сервере №2.
Удалите существующую директорию данных:
#в зависимости от версии установленной postgresql сервера, путь к каталогу может отличаться rm -rf /var/lib/postgresql/14/main
Создайте базовую резервную копию с мастера:
#в зависимости от версии установленной postgresql сервера, путь pgdata=/var/lib/postgresql/14/main может отличаться su - postgres -c "pg_basebackup --host=ip_srv1 --username=replication_user --pgdata=/var/lib/postgresql/14/main --wal-method=stream --write-recovery-conf"
Где:
ip_srv1
— адрес внутренней сети первого сервера, с master БД.
Перезапустите сервис и проверьте его статус:
systemctl restart postgresql.service systemctl status postgresql.service
4. Установка Корпоративного сервера
Выполнение установки по инструкции Установка Корпоративный сервер 2024 на ОС Астра Линукс.
4.1. Предварительная подготовка
Инструкция ниже выполняется на сервере №1.
Для корректной установки, разместите архив в директории, отличной от /root
, например в /mnt
или /tmp
.
Перейдите в каталог:
cd /mnt
Распакуйте архив:
unzip CDinstall_*.zip
Для корректной работы Корпоративного сервера 2024 обязательно требуется настройка HTTPS. Перед установкой скопируйте crt
и key
файлы в папку sslcert
.
Предоставьте права на скрипт установки:
chmod +x online_installer.sh
Создайте DNS запись для Корпоративного сервера, например:
Если Offline установка
chmod +x offline_installer.sh
Для offline установки требуется установить пакеты в систему и подключить ISO-образ установочного диска операционный системы в папку distr
.
Скачать пакеты можно по данной ссылке.
Архив cddisk.zip
содержит необходимые пакеты для Корпоративного сервера 2024.
1. Установить wget;
2. Скачать архив cddisk.zip
, распаковать и установить пакеты командой
sh install.sh
.
В папку ./distr
положите ISO-образ установочного диска операционный системы. Файл должен быть с расширением .iso. Данный образ вы можете скачать с официального сайта производителя.
4.2. Запуск установки первого сервера
Инструкция ниже выполняется на сервере №1.
Если установка online
Выполните команду:
./online_installer.sh
Если установка offline
Убедитесь, что все репозитории отключены, чтобы установка пакетов происходила исключительно из offline архива. Для этого удалите или закомментируйте (добавив символ # в начало строки) все строки в файле /etc/apt/sources.list
, а также очистите содержимое папки /etc/apt/sources.list.d
.
После этого запустите установку:
./offline_installer.sh
Выберите Нет, иначе произойдет удаление PostgreSQL:
Выберите Нет:
Выберите Да:
Задайте secret. Необходимо ввести секрет (Набор цифр, букв и спецсимволов. Длина от 8 символов) для защищённого доступа Р7-Диска и Сервера документов:
Укажите пароль к БД ds, ранее созданный в пункте 3.1 (из примера: ds):
Выберите Да:
Выберите PostgreSQL:
Выберите Нет:
Укажите БД master и IP-адрес текущего первого сервера:
Укажите по умолчанию порт 5432
:
Укажите БД cddisk из пункта 3.1 (из примера: cddisk
):
Укажите пароль к БД cddisk из пункта 3.1 (из примера: cddisk
):
Измените на актуальный, если есть Корпоративный сервер 2019 и нажмите ОК:
Выберите Да:
Необходимо указать домен, в котором у вас созданы записи из пункта подготовки:
Далее укажите необходимые префиксы для всех модулей.
Выберите Да (если требуется установка почтового сервера):
Дальнейшие действия нужно выполнять при установке Корпоративного сервера 2024 с Почтовым сервером, иначе перезагрузите сервер.
Выберите PostgreSQL:
Необходимо указать имя сервера:
Укажите IP-адрес сервера:
Укажите пароль для пользователя postfix:
Если требуется установка SpamAssassin:
- Выберите 1
Если не требуется установка SpamAssassin:
- Выберите 2
Если требуется установка:
- Выберите Да
Если не требуется установка:
- Выберите Нет
После инсталляции в консоли будет предложено сделать TXT-запись:
Перезапустите сервер.
4.3. Установка второго сервера
Инструкция ниже выполняется на сервере №2.
Скачайте дистрибутив.
Для корректной установки, разместите архив в директории, отличной от /root
, например в /mnt
или /tmp
.
Перейдите в каталог:
cd /mnt
Распакуйте архив:
unzip CDinstall_*.zip
Для корректной работы Корпоративного сервера 2024 обязательно требуется настройка HTTPS. Перед установкой скопируйте crt
и key
файлы в папку sslcert
.
Предоставьте права на скрипт установки:
chmod +x online_installer.sh
Если Offline установка
chmod +x offline_installer.sh
Для offline установки требуется установить пакеты в систему и подключить ISO-образ установочного диска операционный системы в папку distr
.
Скачать пакеты можно по данной ссылке.
Архив cddisk.zip
содержит необходимые пакеты для Корпоративного сервера 2024.
1. Установить wget;
2. Скачать архив cddisk.zip
, распаковать и установить пакеты командой
sh install.sh
.
В папку ./distr
положите ISO-образ установочного диска операционный системы. Файл должен быть с расширением .iso. Данный образ вы можете скачать с официального сайта производителя.
4.4. Запуск установки
Если установка online
Выполните команду:
./online_installer.sh
Если установка offline
Убедитесь, что все репозитории отключены, чтобы установка пакетов происходила исключительно из offline архива. Для этого удалите или закомментируйте (добавив символ # в начало строки) все строки в файле /etc/apt/sources.list
, а также очистите содержимое папки /etc/apt/sources.list.d
.
После этого запустите установку:
./offline_installer.sh
Выберите Нет:
Выберите Нет:
Выберите Да:
Укажите такой же secret
как для первого сервера:
Укажите пароль к БД ds из пункта 3.1 (из примера ds):
Выберите Да:
Выберите PostgreSQL:
Выберите Нет:
Укажите IP-адрес первого сервера master:
Укажите порт 5432
:
Укажите БД cddisk из пункта 3.1 (из примера: cddisk):
Укажите пароль к БД cddisk из пункта 3.1 (из примера: cddisk):
Измените на актуальный, если есть Корпоративный сервер 2019 и нажмите ОК:
Выберите Да:
Необходимо указать домен, в котором у вас созданы записи из пункта подготовки:
Далее укажите необходимые префиксы для всех модулей, одинаковые с первым сервером.
Выберите Да (если требуется установка почтового сервера):
Дальнейшие действия нужно выполнять при установке Корпоративного сервера 2024 с Почтовым сервером, иначе перезагрузите сервер.
Выберите PostgreSQL:
Необходимо указать имя для второго сервера:
Укажите IP-адрес сервера:
Укажите пароль для пользователя postfix (нужно указать такой же пароль, как на первом сервере):
Если требуется установка SpamAssassin (выберите тот же вариант, который указали при установке первого сервера):
- Выберите 1
Если не требуется установка SpamAssassin:
- Выберите 2
Если требуется установка (выберите тот же вариант, которые указали при установке первого сервера):
- Выберите Да
Если не требуется установка:
- Выберите Нет
После инсталляции в консоли будет предложено сделать TXT запись:
Перезапустите сервер.
После установки корпоративного сервера на втором сервере в базу добавляется вторая корневая организация с id = 2.
Для его удаления требуется в таблице Customers изменить IsRoot на false
.
Инструкция ниже выполняется на сервере №1.
Выполните:
sudo -u postgres psql
\c cddisk UPDATE "Customers" SET "IsRoot"=false WHERE "Id"=2; # Для выхода из БД \q
Затем удалите вторую корневую организацию через веб-интерфейс.
5. Выполните дополнительную настройку Nginx и Сервер документов
Выведите значение параметра secure_link_secret
на первом сервере:
grep -rn "set \$secure_link_secret" /etc/r7-office/documentserver/nginx/ds.conf
Пример вывода:
13: set $secure_link_secret kCj7RMAAYCNtOh6KiGin;
Для второго сервера:
sudo sed -i "s/set \$secure_link_secret [^;]*/set \$secure_link_secret НОВОЕ_ЗНАЧЕНИЕ/" $(grep -rl "set \$secure_link_secret" /etc/r7-office/documentserver/) sudo sed -i -E "s/\"secretString\": .+/\"secretString\": \"НОВОЕ_ЗНАЧЕНИЕ\"/" $(grep -rl "secretString" /etc/r7-office/documentserver/)
Где, замените НОВОЕ_ЗНАЧЕНИЕ
на необходимый параметр c вывода предыдущей команды (пример, kCj7RMAAYCNtOh6KiGin
).
Выполните команды по перезапуску Nginx и Сервера документов:
systemctl restart nginx ds-converter.service ds-docservice.service ds-metrics.service
6. Настройка подключения почтового сервера (*опционально)
Перейдите в https://admin.test2.s7-office.site/
Организации — Выберите организацию, в которую добавлены пользователи — Почтовые серверы:
Укажите ранее добавленные данные по почтовому серверу:
Скопируйте папку /home/mail_cddisk/.ssh
из первого сервера во второй сервер.
Выполните на втором сервере:
chown -R mail_cddisk:mail_cddisk /home/mail_cddisk/.ssh
7. Управление переключением БД при неработоспособности первого сервера
7.1. Создайте скрипт управления
Готовый скрипт можно скачать по данной ссылке.
Укажите необходимые константы в начале скрипта. Потребуется указать необходимые параметры, что является master (первый сервер) и slave (второй сервер).
Параметры указанные ранее в пункте 3.1.
Константы для подключения к БД
MASTER_IP="192.168.27.186"
— укажите адрес master БД;SLAVE_IP="192.168.26.253"
— укажите адрес replica БД;REPLICATION_USER="replication_user"
— укажите пользователя для репликации;REPLICATION_PASS="replication_password"
— пароль от пользователя для репликации;LOG_FILE="/var/log/switch_postgres.log"
— расположение файла логирования скрипта;PG_DATA="/var/lib/postgresql/11/main"
— расположение каталога с базой;PG_BIN="/usr/lib/postgresql/11/bin"
— расположение исполняемых файлов PostgreSQL.
Константы для cddisk (appsettings.json)
CDDISK_DB_NAME="cddisk"
— не рекомендуется изменять;CDDISK_DB_USER="cddisk"
— укажите пользователя Корпоративного сервера 2024;CDDISK_DB_PASS="cddisk"
— укажите пароль для пользователя Корпоративного сервера 2024;CDDISK_DB_PORT="5432"
— укажите порт БД.
Константы для documentserver (local.json)
DS_DB_NAME="ds"
— не рекомендуется изменять;DS_DB_USER="ds"
— укажите пользователя ДС;DS_DB_PASS="ds"
— укажите пароль для пользователя ДС;DS_DB_PORT="5432"
— укажите порт БД.
Константы для почтового сервера (postfix, dovecot)
MAIL_DB_NAME="postfix"
— не рекомендуется изменять;MAIL_DB_USER="postfix"
— укажите пользователя Postfix;MAIL_DB_PASS="postfix"
— укажите пароль пользователя Postfix;MAIL_DB_PORT="5432"
— укажите порт БД.
Содержимое файла готового скрипта по ссылке указанной выше:
#!/bin/bash # === Константы для подключения к БД === MASTER_IP="192.168.99.67" SLAVE_IP="192.168.99.240" REPLICATION_USER="replication_user" REPLICATION_PASS="replication_password" LOG_FILE="/mail/log/switch_postgres.log" PG_DATA="/var/lib/postgresql/11/main" PG_BIN="/usr/lib/postgresql/11/bin" # === Константы для cddisk (appsettings.json) === CDDISK_DB_NAME="cddisk" CDDISK_DB_USER="cddisk" CDDISK_DB_PASS="cddisk" CDDISK_DB_PORT="5432" # === Константы для documentserver (local.json) === DS_DB_NAME="ds" DS_DB_USER="ds" DS_DB_PASS="ds" DS_DB_PORT="5432" # === Константы для почтового сервера (postfix, dovecot) === MAIL_DB_NAME="postfix" MAIL_DB_USER="postfix" MAIL_DB_PASS="postfix" MAIL_DB_PORT="5432" declare -a postfix_configs=( "/etc/postfix/pgsql/virtual_alias_maps.cf" "/etc/postfix/pgsql/virtual_mailbox_domains.cf" "/etc/postfix/pgsql/virtual_mailbox_maps.cf" ) declare -a dovecot_configs=( "/etc/dovecot/dovecot-pgsql.conf" ) declare -a cs_ds_configs=( "/opt/r7-office/Api/appsettings.json" "/opt/r7-office/Sso.Api/appsettings.json" "/opt/r7-office/Processing/appsettings.json" "/etc/r7-office/documentserver/local.json" ) declare -a cs_configs=( "/opt/r7-office/Api/appsettings.json" "/opt/r7-office/Sso.Api/appsettings.json" "/opt/r7-office/Processing/appsettings.json" ) function log() { echo "$(date '+%Y-%m-%d %H:%M:%S') — $1" | tee -a "$LOG_FILE" } function check_superuser() { if [[ $EUID -ne 0 ]]; then log "Ошибка: Скрипт должен запускаться от root." exit 1 fi } function get_local_ip() { hostname -I | awk '{print $1}' } function get_local_hostname() { hostname | awk '{print $1}' } function is_master() { [[ "$(get_postgres_role)" == "master" ]] } function is_slave() { [[ "$(get_postgres_role)" == "slave" ]] } function backup_file_if_needed() { local file="$1" local bak_file="${file}.bak" if [[ ! -f "$bak_file" ]]; then cp "$file" "$bak_file" log "Создана резервная копия: $bak_file" fi } function restore_from_backup() { local file="$1" local bak_file="${file}.bak" if [[ -f "$bak_file" ]]; then log "Восстановление файла $file из резервной копии..." cp "$bak_file" "$file" else log "⚠️ Резервная копия для $file не найдена. Пропущено." fi } function restore_all_configs() { log "Начало восстановления конфигураций из резервных копий..." for file in "${cs_ds_configs[@]}"; do restore_from_backup "$file" done for file in "${postfix_configs[@]}"; do restore_from_backup "$file" done for file in "${dovecot_configs[@]}"; do restore_from_backup "$file" done log "✅ Восстановление завершено." } function extract_cddisk_db_credentials() { local file="/opt/r7-office/Api/appsettings.json" if [[ ! -f "$file" ]]; then log "Файл $file не найден." return 1 fi # Извлекаем строку подключения local line line=$(grep -A2 '"R7StorageServerUserActions"' "$file" | tr -d ' ') if [[ -z "$line" ]]; then log "Стoрока R7StorageServerUserActions не найдена в $file" return 1 fi local conn_str conn_str=$(echo "$line" | sed -E 's/.*"R7StorageServerUserActions"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/') if [[ -z "$conn_str" ]]; then log "Не удалось извлечь строку подключения из $file" return 1 fi DB_NAME=$(echo "$conn_str" | grep -oP 'Database=\K[^;]+') DB_USER=$(echo "$conn_str" | grep -oP 'Username=\K[^;]+') DB_PASS=$(echo "$conn_str" | grep -oP 'Password=\K[^;]+') DB_HOST=$(echo "$conn_str" | grep -oP 'Host=\K[^;]+') DB_PORT=$(echo "$conn_str" | grep -oP 'Port=\K[^;]*') DB_PORT="${DB_PORT:-5432}" if [[ -z "$DB_NAME" || -z "$DB_HOST" ]]; then log "В строке подключения отсутствуют необходимые параметры (Database и Host)." return 1 fi export DB_NAME DB_USER DB_PASS DB_HOST DB_PORT } function extract_documentserver_db_credentials() { local config="/etc/r7-office/documentserver/local.json" if [[ ! -f "$config" ]]; then log "⚠️ Файл $config не найден." return 1 fi DS_DB_HOST=$(grep '"dbHost"' "$config" | awk -F '"' '{print $4}') DS_DB_NAME=$(grep '"dbName"' "$config" | awk -F '"' '{print $4}') DS_DB_USER=$(grep '"dbUser"' "$config" | awk -F '"' '{print $4}') DS_DB_PASS=$(grep '"dbPass"' "$config" | awk -F '"' '{print $4}') DS_DB_PORT=$(grep '"dbPort"' "$config" | awk -F '"' '{print $4}') DS_DB_PORT="${DS_DB_PORT:-5432}" if [[ -z "$DS_DB_HOST" || -z "$DS_DB_USER" || -z "$DS_DB_PASS" ]]; then log "⚠️ В файле local.json отсутствуют данные для подключения к БД." return 1 fi export DS_DB_HOST DS_DB_NAME DS_DB_USER DS_DB_PASS DS_DB_PORT } function check_db_connection_after_change() { local new_master="$1" log "Проверка подключения к БД с новыми параметрами..." # === cddisk: используем константы с обновлённым Host === local db_name="$CDDISK_DB_NAME" local db_user="$CDDISK_DB_USER" local db_pass="$CDDISK_DB_PASS" local db_host="$new_master" local db_port="${CDDISK_DB_PORT:-5432}" log "Попытка подключения (cddisk): Host=$db_host, User=$db_user, Database=$db_name, Port=$db_port" echo "$db_pass" | PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -p "$db_port" -d "$db_name" -c "\q" > /dev/null 2>&1 if [ $? -eq 0 ]; then log "✅ Успешное подключение к cddisk" else log "❌ Не удалось подключиться к cddisk" return 1 fi # === documentserver: используем DS_* константы с обновлённым Host === local ds_name="$DS_DB_NAME" local ds_user="$DS_DB_USER" local ds_pass="$DS_DB_PASS" local ds_host="$new_master" local ds_port="${DS_DB_PORT:-5432}" log "Попытка подключения (documentserver): Host=$ds_host, User=$ds_user, Database=$ds_name, Port=$ds_port" echo "$ds_pass" | PGPASSWORD="$ds_pass" psql -h "$ds_host" -U "$ds_user" -p "$ds_port" -d "$ds_name" -c "\q" > /dev/null 2>&1 if [ $? -eq 0 ]; then log "✅ Успешное подключение к documentserver" else log "❌ Не удалось подключиться к documentserver" return 1 fi # === mail: используем MAIL_* константы с обновлённым Host === local ds_name="$MAIL_DB_NAME" local ds_user="$MAIL_DB_USER" local ds_pass="$MAIL_DB_PASS" local ds_host="$new_master" local ds_port="${MAIL_DB_PORT:-5432}" log "Попытка подключения (postfix): Host=$ds_host, User=$ds_user, Database=$ds_name, Port=$ds_port" echo "$ds_pass" | PGPASSWORD="$ds_pass" psql -h "$ds_host" -U "$ds_user" -p "$ds_port" -d "$ds_name" -c "\q" > /dev/null 2>&1 if [ $? -eq 0 ]; then log "✅ Успешное подключение к postfix" else log "❌ Не удалось подключиться к postfix" return 1 fi return 0 } function extract_mail_db_credentials() { local file="/etc/dovecot/dovecot-pgsql.conf" if [[ ! -f "$file" ]]; then log "Файл $file не найден." return 1 fi DB_NAME=$(grep -E "dbname\s*=\s*+" "$file" | awk -F"=" '{print $2}') | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' if [[ -z "$DB_NAME" ]]; then log "Стoрока dbname = не найдена в $file" return 1 fi DB_USER=$(grep -E "user\s*=\s*+" "$file" | awk -F"=" '{print $2}') | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' if [[ -z "$DB_USER" ]]; then log "Стoрока user = не найдена в $file" return 1 fi DB_PASS=$(grep -E "password\s*=\s*+" "$file" | awk -F"=" '{print $2}') | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' if [[ -z "$DB_PASS" ]]; then log "Стoрока password = не найдена в $file" return 1 fi DB_HOST=$(grep -E "hosts\s*=\s*[^\:]+:" "$file" | awk '{print $3}'| awk -F":" '{print $1}') | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' if [[ -z "$DB_HOST" ]]; then log "Стoрока hosts = не найдена в $file" return 1 fi DB_PORT=$(grep -E "hosts\s*=\s*[^\:]+:" "$file" | awk '{print $3}'| awk -F":" '{print $2}') | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' if [[ -z "$DB_PORT" ]]; then log "Стoрока hosts = : не найдена в $file" return 1 fi DB_PORT="${DB_PORT:-5432}" if [[ -z "$DB_NAME" || -z "$DB_HOST" ]]; then log "В строке подключения отсутствуют необходимые параметры (Database и Host)." return 1 fi export DB_NAME DB_USER DB_PASS DB_HOST DB_PORT } function check_db_connection() { log "Проверка подключения к БД с текущими параметрами..." # === cddisk === if extract_cddisk_db_credentials; then local db_host="$DB_HOST" local db_name="$DB_NAME" local db_user="$DB_USER" local db_pass="$DB_PASS" local db_port="${DB_PORT:-5432}" log "Попытка подключения (cddisk): Host=$db_host, User=$db_user, Database=$db_name, Port=$db_port" echo "$db_pass" | PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -p "$db_port" -d "$db_name" -c "SELECT version();" > /dev/null 2>&1 if [ $? -eq 0 ]; then log "Успешное подключение к БД от пользователя $db_user" else log "Не удалось подключиться к БД от пользователя $db_user. Проверьте настройки доступа или пароль." fi else log "⚠️ Пропущена проверка cddisk: не удалось получить данные из appsettings.json" fi # === documentserver === if extract_documentserver_db_credentials; then local ds_host="$DS_DB_HOST" local ds_name="$DS_DB_NAME" local ds_user="$DS_DB_USER" local ds_pass="$DS_DB_PASS" local ds_port="$DS_DB_PORT" log "Попытка подключения (documentserver): Host=$ds_host, User=$ds_user, Database=$ds_name, Port=$ds_port" echo "$ds_pass" | PGPASSWORD="$ds_pass" psql -h "$ds_host" -U "$ds_user" -p "$ds_port" -d "$ds_name" -c "SELECT version();" > /dev/null 2>&1 if [ $? -eq 0 ]; then log "Успешное подключение к БД от пользователя $ds_user" else log "Не удалось подключиться к БД от пользователя $ds_user. Проверьте настройки доступа или пароль." fi else log "⚠️ Пропущена проверка documentserver: не удалось получить данные из local.json" fi # === mail === if extract_mail_db_credentials; then local db_host="$DB_HOST" local db_name="$DB_NAME" local db_user="$DB_USER" local db_pass="$DB_PASS" local db_port="${DB_PORT:-5432}" log "Попытка подключения (postfix): Host=$db_host, User=$db_user, Database=$db_name, Port=$db_port" echo "$db_pass" | PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -p "$db_port" -d "$db_name" -c "SELECT version();" > /dev/null 2>&1 if [ $? -eq 0 ]; then log "Успешное подключение к БД от пользователя $db_user" else log "Не удалось подключиться к БД от пользователя $db_user. Проверьте настройки доступа или пароль." fi else log "⚠️ Пропущена проверка postfix: не удалось получить данные из virtual_alias_maps.cf" fi return 0 } function update_cddisk_configs() { local new_master="$1" log "Обновление appsettings.json для cddisk на новый мастер: $new_master..." # Формируем новые строки подключения с новым IP local conn_user_actions="Database=$CDDISK_DB_NAME;Username=$CDDISK_DB_USER;Password=$CDDISK_DB_PASS;Host=$new_master;Port=$CDDISK_DB_PORT;" local conn_server="Database=$CDDISK_DB_NAME;Username=$CDDISK_DB_USER;Password=$CDDISK_DB_PASS;Host=$new_master;Port=$CDDISK_DB_PORT;" for file in "${cs_configs[@]}"; do if [[ ! -f "$file" ]]; then log "⚠️ Файл $file не найден, пропущено." continue fi backup_file_if_needed "$file" # === R7StorageServerUserActions === local old_line=$(grep -A2 '"R7StorageServerUserActions"' "$file" | tr -d ' ') if [[ -n "$old_line" ]]; then log "Старая строка (R7StorageServerUserActions): $old_line" sed -i "s|\"R7StorageServerUserActions\": *\"[^\"]*\"|\"R7StorageServerUserActions\": \"$conn_user_actions\"|" "$file" if [[ $? -eq 0 ]]; then log "✅ R7StorageServerUserActions в $file обновлён" else log "❌ Ошибка при обновлении R7StorageServerUserActions в $file" fi else log "⚠️ R7StorageServerUserActions не найден в $file" fi # === R7StorageServer === local old_line=$(grep -A2 '"R7StorageServer"' "$file" | tr -d ' ') if [[ -n "$old_line" ]]; then log "Старая строка (R7StorageServer): $old_line" sed -i "s|\"R7StorageServer\": *\"[^\"]*\"|\"R7StorageServer\": \"$conn_server\"|" "$file" if [[ $? -eq 0 ]]; then log "✅ R7StorageServer в $file обновлён" else log "❌ Ошибка при обновлении R7StorageServer в $file" fi else log "⚠️ R7StorageServer не найден в $file" fi done } function update_documentserver_config() { local new_master="$1" local config="/etc/r7-office/documentserver/local.json" if [[ ! -f "$config" ]]; then log "⚠️ Файл $config не найден, пропущено." return 1 fi backup_file_if_needed "$config" current_host=$(grep '"dbHost"' "$config" | awk -F '"' '{print $4}') if [[ "$current_host" == "$new_master" ]]; then log "IP-адрес в $config уже актуален ($new_master). Изменение не требуется." return 0 fi log "Старый dbHost: $current_host" sed -i "s/\"dbHost[^\"]*\": *\"[^\"]*\"/\"dbHost\": \"$new_master\"/" "$config" if [[ $? -eq 0 ]]; then log "dbHost в $config успешно обновлён на $new_master" else log "❌ Ошибка обновления $config" fi } function restart_supervisor_services() { log "Перезапуск supervisor-сервисов..." supervisorctl restart all } function restart_documentserver_services() { log "Перезапуск ds-сервисов..." local prepare_script="/usr/bin/documentserver-prepare4shutdown.sh" if [[ -f "$prepare_script" ]]; then log "Выполняется подготовительный скрипт: $prepare_script..." "$prepare_script" if [[ $? -eq 0 ]]; then log "Подготовительный скрипт выполнен успешно." else log "⚠️ Подготовительный скрипт завершился с ошибкой. Продолжить перезапуск? (y/n)" read -p "Продолжить? " confirm [[ "$confirm" != "y" && "$confirm" != "Y" ]] && log "Перезапуск отменён пользователем." && return 1 fi else log "⚠️ Подготовительный скрипт $prepare_script не найден. Пропущено." fi local services=( "ds-converter.service" "ds-docservice.service" "ds-metrics.service" ) for service in "${services[@]}"; do if systemctl is-active --quiet "$service"; then log "Перезапуск $service..." systemctl daemon-reload > /dev/null 2>&1 systemctl restart "$service" log "$service перезапущен." else if systemctl list-units --type=service | grep -q "$service"; then log "⚠️ Сервис $service найден, но не активен. Пропущено." else log "⚠️ Сервис $service не найден. Проверьте установку документ-сервера." fi fi done log "✅ Все актуальные ds-сервисы перезапущены." } function stop_documentserver_services() { log "Останов ds-сервисов..." local prepare_script="/usr/bin/documentserver-prepare4shutdown.sh" if [[ -f "$prepare_script" ]]; then log "Выполняется подготовительный скрипт: $prepare_script..." "$prepare_script" if [[ $? -eq 0 ]]; then log "Подготовительный скрипт выполнен успешно." else log "⚠️ Подготовительный скрипт завершился с ошибкой. Продолжить останов? (y/n)" read -p "Продолжить? " confirm [[ "$confirm" != "y" && "$confirm" != "Y" ]] && log "Остановка сервиса отменён пользователем." && return 1 fi else log "⚠️ Подготовительный скрипт $prepare_script не найден. Пропущено." fi local services=( "ds-converter.service" "ds-docservice.service" "ds-metrics.service" ) for service in "${services[@]}"; do if systemctl is-active --quiet "$service"; then log "Перезапуск $service..." systemctl daemon-reload > /dev/null 2>&1 systemctl restart "$service" log "$service перезапущен." else if systemctl list-units --type=service | grep -q "$service"; then log "⚠️ Сервис $service найден, но не активен. Пропущено." else log "⚠️ Сервис $service не найден. Проверьте установку документ-сервера." fi fi done log "✅ Все актуальные ds-сервисы остановлены." } function update_postfix_configs() { local new_master="$1" log "Обновление файлов /etc/postfix/pgsql/* для postfix на новый мастер: $new_master..." for file in "${postfix_configs[@]}"; do if [[ ! -f "$file" ]]; then log "⚠️ Файл $file не найден, пропущено." continue fi backup_file_if_needed "$file" # === Postfix configs === local old_line=$(grep -E "hosts\s*=\s*[^\:]+:" "$file" | tr -d ' ') if [[ -n "$old_line" ]]; then log "Старая строка (hosts = ): $old_line" sed -i -E "s|hosts\s*=\s*[^\:]+:|hosts = $new_master:|" "$file" if [[ $? -eq 0 ]]; then log "✅ hosts = в $file обновлён" else log "❌ Ошибка при обновлении hosts = в $file" fi else log "⚠️ hosts = не найден в $file" fi done } function update_dovecot_configs() { local new_master="$1" log "Обновление файла /etc/dovecot/dovecot-pgsql.conf для dovecot на новый мастер: $new_master..." for file in "${dovecot_configs[@]}"; do if [[ ! -f "$file" ]]; then log "⚠️ Файл $file не найден, пропущено." continue fi backup_file_if_needed "$file" # === Dovecot configs === local old_line=$(grep -E "host=[^\ ]+" "$file" | tr -d ' ') if [[ -n "$old_line" ]]; then log "Старая строка (host=): $old_line" sed -i -E "s|host=[^\ ]+|host=$new_master|" "$file" if [[ $? -eq 0 ]]; then log "✅ host= в $file обновлён" else log "❌ Ошибка при обновлении host= в $file" fi else log "⚠️ host= не найден в $file" fi done } function restart_mail_services() { local services=( "postfix.service" "dovecot.service" ) for service in "${services[@]}"; do if systemctl is-active --quiet "$service"; then log "Перезапуск $service..." #systemctl daemon-reload > /dev/null 2>&1 systemctl restart "$service" log "$service перезапущен." else if systemctl list-units --type=service | grep -q "$service"; then log "⚠️ Сервис $service найден, но не активен. Пропущено." else log "⚠️ Сервис $service не найден. Проверьте установку документ-сервера." fi fi done } function promote_slave_to_master() { log "Продвижение slave в master..." local pg_data="$PG_DATA" if [[ -f "$pg_data/standby.signal" && -f "$pg_data/recovery.signal" ]]; then log "⚠️ Сервер не находится в режиме standby. Пропущено продвижение в master." return 0 fi sudo -u postgres ${PG_BIN}/pg_ctl promote -D "$pg_data" if [ $? -eq 0 ]; then log "✅ Slave успешно переведён в режим master." else log "⚠️ Не удалось продвинуть в master (возможно, уже является master)." fi } function switch_to_slave_as_master() { log "Начало процесса переключения slave -> master..." log "Используем MASTER_IP = $MASTER_IP из констант." # Проверяем доступность БД с новым IP check_db_connection_after_change "$MASTER_IP" # Продвигаем в master (работает только если standby.signal существует) promote_slave_to_master # Обновляем конфиги приложений update_cddisk_configs "$MASTER_IP" update_documentserver_config "$MASTER_IP" # Перезапуск сервисов restart_supervisor_services restart_documentserver_services log "✅ Сервер переведён в режим master." } function switch_to_master_as_slave() { log "Начало процесса переключения master -> slave..." log "Используем MASTER_IP = $MASTER_IP для создания реплики." read -p "Вы уверены, что хотите сделать этот сервер slave? Это очистит данные. (y/n): " confirm [[ "$confirm" != "y" ]] && log "Операция отменена пользователем." && exit 1 # Убираем запрос пароля — используем REPLICATION_PASS из констант if [[ -z "$REPLICATION_PASS" ]]; then log "❌ Пароль не может быть пустым." exit 1 fi log "Остановка PostgreSQL..." systemctl stop postgresql log "Очистка каталога данных PostgreSQL..." rm -rf ${PG_DATA}/* mkdir -p ${PG_DATA} log "Создание новой реплики с мастера: $MASTER_IP..." # Передаем пароль через PGPASSWORD вместо stdin sudo -u postgres env PGPASSWORD="$REPLICATION_PASS" pg_basebackup \ -h "$MASTER_IP" \ -D ${PG_DATA} \ -U "$REPLICATION_USER" \ -P -R if [ $? -ne 0 ]; then log "❌ Ошибка при создании реплики. Проверьте доступность мастера и учётные данные." exit 1 fi log "Запуск PostgreSQL..." systemctl start postgresql # === Меняем IP на MASTER_IP === check_db_connection_after_change "$MASTER_IP" update_cddisk_configs "$MASTER_IP" update_documentserver_config "$MASTER_IP" restart_supervisor_services restart_documentserver_services log "✅ Сервер успешно переведён в режим slave." } function show_current_configurations() { echo -e " === Текущие конфигурации с подключением к БД ===" echo "Файлы appsettings.json (только актуальные):" for file in "${cs_configs[@]}"; do if [[ ! -f "$file" ]]; then echo " $file — не найден" continue fi echo " === $file ===" local user_actions=$(grep -A2 '"R7StorageServerUserActions"' "$file" | tr -d ' ' | sed 's/[",]//g; s/:[ ]*/:/g') if [[ -n "$user_actions" ]]; then echo " R7StorageServerUserActions: ${user_actions#*R7StorageServerUserActions: }" else echo " R7StorageServerUserActions: не найден" fi local storage_server=$(grep -A2 '"R7StorageServer"' "$file" | tr -d ' ' | sed 's/[",]//g; s/:[ ]*/:/g') if [[ -n "$storage_server" ]]; then echo " R7StorageServer: ${storage_server#*R7StorageServer: }" else echo " R7StorageServer: не найден" fi echo "" done local doc_config="/etc/r7-office/documentserver/local.json" echo "Файл local.json (documentserver):" if [[ -f "$doc_config" ]]; then grep -E '"(dbHost|dbName|dbUser|dbPass|dbPort)"' "$doc_config" | sed 's/^/ /' else echo " Файл не найден" fi } function show_current_configurations_postfix() { echo -e " === Текущие конфигурации POSTFIX, подключение к БД ===" for file in "${postfix_configs[@]}"; do if [[ ! -f "$file" ]]; then echo " $file — не найден" continue fi echo " === $file ===" if [[ -f "$file" ]]; then grep -E "(hosts|user|password|dbname)" "$file" else echo "Файл $file не найден" fi echo "" done } function show_current_configurations_dovecot() { echo -e " === Текущие конфигурации Dovecot, подключение к БД ===" for file in "${dovecot_configs[@]}"; do if [[ ! -f "$file" ]]; then echo " $file — не найден" continue fi echo " === $file ===" if [[ -f "$file" ]]; then grep -E "(connect = )" "$file" else echo "Файл $file не найден" fi echo "" done } function get_postgres_data_dir() { local dir="${PG_DATA}" if [[ -d "$dir" ]]; then echo "$dir" return 0 else log "❌ Каталог данных PostgreSQL не найден." return 1 fi } function get_postgres_role() { local pg_data="${PG_DATA}" # Попробуем через SQL, если PostgreSQL запущен if systemctl is-active --quiet postgresql > /dev/null 2>&1; then local role_sql=$(sudo -u postgres psql -tAc "SELECT pg_is_in_recovery();") if [[ $? -eq 0 ]]; then if [[ "$(echo "$role_sql" | tr '[:upper:]' '[:lower:]')" == "f" ]]; then echo "master" return 0 else echo "slave" return 0 fi else log "⚠️ Не удалось выполнить запрос к PostgreSQL." fi fi # Если БД не запущена — проверяем сигналы if [[ -f "$pg_data/standby.signal" ]]; then echo "slave" return 0 elif [[ -f "$pg_data/postmaster.pid" ]]; then echo "master" return 0 else log "❌ Не удалось определить роль PostgreSQL." echo "unknown" return 1 fi } function is_master() { [[ "$(get_postgres_role)" == "master" ]] } function is_slave() { [[ "$(get_postgres_role)" == "slave" ]] } function show_status_replication() { local local_ip=$(get_local_ip) echo -e " === Текущий статус сервера ===" echo "IP сервера: $local_ip" echo "MASTER_IP (константа): $MASTER_IP" echo "SLAVE_IP (константа): $SLAVE_IP" local role=$(get_postgres_role) echo "Роль: ${role^}" if [[ "$role" == "master" ]]; then echo "Проверка состояния репликации:" env PGHOME=/tmp sudo -u postgres PGOPTIONS="-c search_path=pg_catalog" psql -c "SELECT * FROM pg_stat_replication;" elif [[ "$role" == "slave" ]]; then echo "Проверка состояния восстановления:" env PGHOME=/tmp sudo -u postgres PGOPTIONS="-c search_path=pg_catalog" psql -c "SELECT * FROM pg_stat_wal_receiver;" else echo "Роль: Не определена" fi echo -e "============================== " } function main_menu() { clear local local_ip=$(get_local_ip) local local_hostname=$(get_local_hostname) echo "=== Добро пожаловать в систему управления PostgreSQL ===" echo "Лог-файл: $LOG_FILE" echo "" while true; do log "Скрипт работает на сервере ${local_hostname} с ip - ${local_ip}" echo "=== Меню управления PostgreSQL ===" echo "1. Установка параметров master" echo "2. Установка параметров slave (производиться с удалением базы на текущей машине)" echo "3. Показать текущие конфиги" echo "4. Проверка подключения с новыми параметрами" echo "5. Восстановить конфигурации из .bak" echo "6. Перезапустить сервисы приложений" echo "7. Показать текущий статус сервера" echo "8. Остановка сервисов корпоративного сервера (холодный резерв)" echo "9. Выход" read -p "Выберите действие (1-9): " choice case $choice in 1) switch_to_slave_as_master update_postfix_configs $MASTER_IP update_dovecot_configs $MASTER_IP restart_mail_services show_status_replication read -p "Нажмите Enter для продолжения..." ;; 2) switch_to_master_as_slave update_postfix_configs $MASTER_IP update_dovecot_configs $MASTER_IP restart_mail_services show_status_replication read -p "Нажмите Enter для продолжения..." ;; 3) show_current_configurations show_current_configurations_postfix show_current_configurations_dovecot read -p "Нажмите Enter для продолжения..." ;; 4) check_db_connection_after_change "$MASTER_IP" read -p "Нажмите Enter для продолжения..." ;; 5) restore_all_configs read -p "Нажмите Enter для продолжения..." ;; 6) log "Перезапуск всех сервисов..." supervisorctl restart all restart_documentserver_services restart_mail_services read -p "Нажмите Enter для продолжения..." ;; 7) show_status_replication read -p "Нажмите Enter для продолжения..." ;; 8) log "Остановка всех сервисов корпоративного сервера..." log "Состояние сервисов почтового сервера не меняется!!!" supervisorctl stop all stop_documentserver_services read -p "Нажмите Enter для продолжения..." ;; 9) log "Выход..." exit 0 ;; *) echo "Неверный выбор." sleep 2 ;; esac clear done } # === MAIN === check_superuser main_menu
Описание пунктов:
1. Установка параметров master — устанавливает параметры master для сервера используемого основным (меняет конфигурации приложений на использование БД master) и создает в том же каталоге резервную копию изменяемых файлов;
2. Установка параметров slave — устанавливает параметры slave для сервера используемым запасным (меняет конфигурации приложений на использование БД master и включение репликации на этом сервере) и создает в том же каталоге резервную копию изменяемых файлов;
3. Показать текущие конфиги — показывает текущие параметры приложений;
4. Проверка подключения с новыми параметрами — проверяет доступ для приложений с заданными константами по подключению к master;
5. Восстановить конфигурации из .bak
— при ранее измененных конфигураций приложений может восстановить резервные копии файлов из формата .bak
;
6. Перезапустить сервисы приложений — перезапускает сервисы приложений;
7. Показать текущий статус сервера — указывает состояние БД на текущем сервере;
8. Остановка сервисов корпоративного сервера (холодный резерв) — останавливает сервисы корпоративного сервера для режима «холодный резерв».
7.2. Настройка второго сервера
Укажите в скрипте текущее представление серверов (master и slave) в константах скрипта (и проверьте другие параметры с корректными данными) и запустите скрипт на втором сервере (slave) пункт меню «Установка параметров slave».
7.3. Проверка работы серверов
Для проверки потребуется переключать в DNS А запись ведущая на первый сервер. Перейдите по адресу https://admin.test2.s7-office.site
, убедитесь что используется корректная адресация на первый адрес Корпоративного сервера 2024 (используйте команду ping
) и используйте предустановленный логин\пароль superadmin
. Создайте пользователя и проверьте редактирование файлов — корректное сохранение файлов и повторное открытие файлов.
Далее переключите А запись на второй сервер и так же проверьте его работу.
В DNS используйте установленную текущую ноду как master. Запустите скрипт управления БД и проверьте текущие статусы серверов.
Настоятельно рекомендуется отработать поэтапное переключение из slave в master на втором сервере в случае ошибок на первом сервере.
8. Резервное копирование и восстановление
Для проведения резервирования и восстановления необходимо использовать скрипты из статьи Резервное копирование/восстановление Корпоративный сервер 2024.
Перед использованием скрипта создания бекапа добавьте каталог mail, приведите строку к виду:
# Создание архива с бэкапом tar cf "$backup_dir/cddisk-${backup_date}.tar.bz2" --selinux --use-compress-prog="lbzip2 -k -n$num_cores" /opt/r7-office $supervisor_dir /etc/r7-office /var/r7-office /var/www/r7-office "$backup_dir/cddisk_db.tar.gz /mail"
При восстановлении на мастер ноде рекомендуется остановить службу:
systemctl stop glusterfs
И после завершения восстановления включить:
systemctl start glusterfs
При восстановлении резервной ноды нет необходимости выполнения восстановления, так как скрипт позволяет назначить вторую ноду репликой первой для БД и автоматическую синхронизацию каталогов производится средствами glusterfs.