Настройка отказоустойчивого кластера 1C + PostgreSQL (etcd+patroni+haproxy) на Centos 8

19.12.22

База данных - Администрирование СУБД

Настройка отказоустойчивого кластера PostgreSQL для сервера приложений 1С на операционной системе Centos 8.

Здравствуйте.

Мне необходимо осветить реализацию на основе инструкции от издателя 1С, по причине обновлённого подхода к реализации решения отказоустойчивого кластера для баз данных под системой управления PostgreSQL.

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

Собирал кластер первый раз, некоторые терминологические выводы основываются на гипотезах, признателен буду в случае обратной связи о том, верна ли гипотеза, а если нет, то какая истина ?

Имеется

  1. 5 виртуальных машин на "Centos-8"
  2. 3 машины идут под базы данных, можно и две, но в целях пресечения так называемого явления "split-brain"(где обе машины      начнут считать себя мастером) вводим третью машину и на каждую из них будет установлена служба "Patroni".
  3. 2 машины уходят под сервера приложений "1С", на каждую из них мы установим помимо сервера приложения "1С", "HAproxy", как предполагается для сбора статистики от службы "patroni" и однозначности обращения со стороны сервера приложения "1С" к одному, а один ко многим.
  4. На всех 5 машинах будут установлены сервисы "etcd" посредством которого, узлы будут введены в один общий кластер, как описано в инструкции от специалистов "1С", "etcd" будет использоваться службой "Patroni" в целях хранения своей конфигурации, но как на практике не совсем понятная суть изложенного, так как каждый конфигурационный файл "patroni.yml", будет создан на каждом узле хранения баз данных в ручную, с почти идентичным настройками.
  5. Таблица игроков
    NS имя сервера БД-1 pg1.local *.1 четвёртый октет адреса
    NS имя сервера БД-2 pg2.local *.2
    NS имя сервера БД-3 pg3.local *.3
    NS имя сервера 1C 1c.local *.4
    NS имя сервера 1C-R 1cr.local *.5
  6. Предварительно на серверах "pg-1", "pg-2", "pg-3" установлены системы управления баз данных "PostgreSQL" но не запущенные и без первичной инициализации баз данных, так же служба "PostgreSQL" снята с автозапуска, "patroni" самостоятельно будет управлять а так же конфигурировать "PostgreSQL".
  7. На серверах "1c" и "1cr", предварительно необходимо установить платформу приложений "1С".  

Установка "etcd" на "Centos-8"(все операции на всех узлах, выполняются под суперпользователем root):

  1. Качаем релиз в виде архива, распаковываем, перемещаем распакованную библиотеку в окружение не системных коллекций
    ~$ export RELEASE="3.3.11"
    ~$ wget https://github.com/etcd-io/etcd/releases/download/v${RELEASE}
    ~$ tar xvf etcd-v${RELEASE}-linux-amd64.tar.gz
    ~$ cd etcd-v${RELEASE}-linux-amd64
    ~$ mv etcd etcdctl /usr/local/bin
  2. Проверяем версию:
    ~$ etcd --version
    etcd Version: 3.3.11
    Git SHA: 2cf9e51d2
    Go Version: go1.10.7
    Go OS/Arch: linux/amd64
  3. Создаём окружение для работы сервиса "etcd":
    ~$ mkdir -p /var/lib/etcd/
    ~$ mkdir /etc/etcd
    ~$ groupadd --system etcd
    ~$ useradd -s /sbin/nologin --system -g etcd etcd
    ~$ chown -R etcd:etcd /var/lib/etcd/
  4. Создаём для "etcd" как службы, служебный файл:
    ~$ nano /etc/systemd/system/etcd.service
    [Unit]
    Description=Etcd Server
    After=network.target
    After=network-online.target
    Wants=network-online.target
    [Service]
    Type=notify
    WorkingDirectory=/var/lib/etcd/
    EnvironmentFile=-/etc/etcd/etcd.conf
    User=etcd
    # set GOMAXPROCS to number of processors
    ExecStart=/bin/bash -c "GOMAXPROCS=$(nproc) /usr/local/bin/etcd --name=\"${ETCD_NAME}\" --data-dir=\"${ETCD_DATA_DIR}\" --listen-client-urls=\"${ETCD_LISTEN_CLIENT_URLS}\""
    Restart=on-failure
    LimitNOFILE=65536
    [Install]
    WantedBy=multi-user.target
  5. Создаём конфигурацию начального/мастер узла кластера:
    ~$ nano /etc/etcd/etcd.conf
    #[Member]
    ETCD_DATA_DIR="/var/lib/etcd"
    ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
    ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
    ETCD_NAME="1c"
    ETCD_HEARTBEAT_INTERVAL="1000"
    ETCD_ELECTION_TIMEOUT="5000"
    #[Clustering]
    ETCD_INITIAL_ADVERTISE_PEER_URLS="http://1c.local:2380"
    ETCD_ADVERTISE_CLIENT_URLS="http://1c.local:2379"
    ETCD_INITIAL_CLUSTER="1c=http://1c.local:2380"
    ETCD_INITIAL_CLUSTER_TOKEN="Ваш токен, любым генератором создаваемый"
    ETCD_INITIAL_CLUSTER_STATE="new"
  6. Запускаем "etcd" службу и открываем порты:
    ~$ systemctl daemon-reload
    ~$ firewall-cmd -zone=public --permanent --add-port=2379-2380/tcp
    ~$ systemctl start etcd.service
  7. Проверяем статус службы, в случае без ошибочного статуса, включаем службу "etcd" в автозапуск:
    ~$ systemctl status etcd.service
    `79; etcd.service - Etcd Server
    Loaded: loaded (/etc/systemd/system/etcd.service; disabled; vendor preset:
    Active: active (running) since Thu 2022-08-20 15:15:31 MSK; 3s
    Main PID: 5551 (etcd)
    Tasks: 18 (limit: 302041)
    Memory: 6.7M
    CGroup: /system.slice/etcd.service
    ^92;^72;4454 /usr/local/bin/etcd --name=agg-server-1 --data-dir=/var/lib/etcd
    авг 20 15:15:31 1c.local etcd[5551]: uid_сервера_1c.local received MsgVoteResp from uid_сервера_1c.local at term 2
    авг 20 15:15:31 1c.local etcd[5551]: uid_сервера_1c.local became leader at term 2
    авг 20 15:15:31 1c.local etcd[5551]: raft.node: uid_сервера_1c.local leader uid_сервера_1c.local at term 2
    авг 20 15:15:31 1c.local etcd[5551]: setting up the initial cluster version to 3.3
    авг 20 15:15:31 1c.local etcd[5551]: set the initial cluster version to 3.3
    авг 20 15:15:31 1c.local etcd[5551]: enabled capabilities for version 3.3
    авг 20 15:15:31 1c.local etcd[5551]: ready to serve client requests
    авг 20 15:15:31 1c.local etcd[5551]: published {Name:1c.local ClientURLs:[http://1c.local:2379]} to cluster uid_cluster
    авг 20 15:15:31 1c.local etcd[5551]: Started Etcd Server.
    авг 20 15:15:31 1c.local etcd[5551]: serving insecure client requests on [::]:2379, this is strongly discouraged!
    
    ~$ systemctl enable etcd.service
  8. Проверяем состояние кластера:
    ~$ etcdctl cluster-health
    member uid_1c.local is healthy: got healthy result from http://1c.local:2379
    cluster is healthy
  9. Добавляем второй узел:
    ~$ etcdctl member add 1cr.local http://1cr.local:2380
    Added member named 1cr.local with ID uid_1cr.local to cluster
    
    ETCD_NAME="1cr.local"
    ETCD_INITIAL_CLUSTER="1c=http://1c.local:2380,1cr=http://1cr.local:2380"
    ETCD_INITIAL_CLUSTER_STATE="existing"
  10. Устанавливаем в аналогичном порядке "etcd" на втором узле, но изменяем конфигурацию под атрибуты данного узла:
    #[Member]
    ETCD_DATA_DIR="/var/lib/etcd"
    ETCD_LISTEN_PEER_URLS="http://0.0.0.0:2380"
    ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
    ETCD_NAME="1cr"
    ETCD_HEARTBEAT_INTERVAL="1000"
    ETCD_ELECTION_TIMEOUT="5000"
    #[Clustering]
    ETCD_INITIAL_ADVERTISE_PEER_URLS="http://1cr.local:2380"
    ETCD_ADVERTISE_CLIENT_URLS="http://1cr.local:2379"
    ETCD_INITIAL_CLUSTER="1c=http://1c.local:2380,1cr=http://1cr.local:2380"
    ETCD_INITIAL_CLUSTER_TOKEN="Ваш_токен"
    ETCD_INITIAL_CLUSTER_STATE="existing" <- "Обратите внимание на флаг, он отличается так же"
  11. Запускаем службу и проверяем состав кластера на состояние:
    ~$ etcdctl cluster-health 
    member uid_1cr is healthy: got healthy result from http://1cr.local:2379
    member uid_1c is healthy: got healthy result from http://1c.local:2379
    cluster is healthy
  12. Порядок дальнейшего добавления узлов в кластер опускаю, он идентичен на добавлением второго узла и после добавления Всех необходимых узлов, нужно привести конфигурацию на всех участниках к единому виду по следующим переменным:
    ETCD_INITIAL_CLUSTER="1c=http://1c.local:2380,1cr=http://1cr.local:2380,pg1=http://pg1.local:2380,pg2=http://pg2.local:2380,pg3=http://pg3.local:2380"
    ETCD_INITIAL_CLUSTER_STATE="existing"
  13. Далее нам необходимо создать пользователя кластера с высокими правами, для внутренней смежной коммуникацией между "patroni" и кластером "etcd":
    ~$ etcdctl user add root
    New password: 
    User root created
    
    ~$ etcdctl user get root
    User: root
    Roles:  root
    
    ~$ etcdctl auth enable
    Authentication Enabled
    
    ~$ etcdctl --username root user get root
    Password: 
    User: root
    Roles:  root

     

Установка и настройка сервиса "patroni" на "Centos-8":

  1. Устанавливаем "Python-3":
    ~$ yum install -y python3
    ~$ python3 -m pip install --upgrade pip
    ~$ python3 --version
    Python 3.6.8
  2. Устанавливаем необходимые зависимости:
    ~$ yum install -y gcc python3-devel
    ~$ python3 -m pip install psycopg2-binary
  3. Устанавливаем "patroni":
    ~$ python3 -m pip install patroni[etcd]
    ~$ patroni --version
    patroni 2.1.4
  4. Настраиваем первый узел "pg1.local", настроем окружение для службы "patroni":
    ~$ mkdir /etc/patroni
    ~$ chown postgres:postgres /etc/patroni
    ~$ chmod 700 /etc/patroni
  5. Создаём файл настроек для службы "patroni", файл имеет мои собственные параметры для моего окружения, мною было принято решение оставить их для наглядности того, в каком блоке необходимо выполнять параметризацию "PostgreSQL" посредством "patroni", описание/понимание каждой строки как в этом так и в предыдущих примерах, оставляю на совесть испытуемого, большинство настроек имеет конфигурацию по умолчанию, то как они были указаны в инструкциях от 1с и других источников:
    ~$ nano /etc/patroni/patroni.yml
    name: pg1
    namespace: /db/
    scope: postgres
    restapi:
      listen: 0.0.0.0:8008
      connect_address: pg1.local:8008
      authentication:
       username: patroni
       password: patroni
    etcd:
     hosts: localhost:2379
     username: root
     password: Ваш_пароль_кластера_etcd
    bootstrap:
     dcs:
      ttl: 30
      loop_wait: 10
      retry_timeout: 10
      maximum_lag_on_failover: 1048576
      master_start_timeout: 10
      postgresql:
       use_pg_rewind: true
       use_slots: true
       parameters:
        wal_level: replica
        hot_standby: "on"
        wal_keep_segments: 8
        max_wal_senders: 5
        max_replication_slots: 5
        checkpoint_timeout: 30
     initdb:
     - auth-host: md5
     - auth-local: peer
     - encoding: UTF8
     - data-checksums
     - locale: ru_RU.UTF-8
     pg_hba:
     - host replication replicator samenet md5
     - host replication all 127.0.0.1/32 md5
     - host replication all ::1/128 md5
     users:
      usr1cv8:
       password: Сгенерированный_Вами_пароль
       options:
        - superuser
    postgresql:
     listen: 0.0.0.0:5432
     connect_address: pg1.local:5432
     config_dir: /var/lib/pgsql/14/data
     bin_dir: /usr/pgsql-14/bin/
     data_dir: /var/lib/pgsql/14/data
     pgpass: /tmp/pgpass
     authentication:
       superuser:
         username: postgres
         password: Сгенерированный_Вами_пароль
       replication:
         username: replicator
         password: Сгенерированный_Вами_пароль
       rewind:
         username: rewind_user
         password: Сгенерированный_Вами_пароль
     parameters:
        max_connections: 181
        dynamic_shared_memory_type: posix
        seq_page_cost: 0.1
        random_page_cost: 0.1
        cpu_operator_cost: 0.0025
        logging_collector: on
        log_timezone: 'Europe/Moscow'
        datestyle: 'iso, dmy'
        timezone: 'Europe/Moscow'
        lc_messages: 'ru_RU.UTF-8'
        lc_monetary: 'ru_RU.UTF-8'
        lc_numeric: 'ru_RU.UTF-8'
        lc_time: 'ru_RU.UTF-8'
        default_text_search_config: 'pg_catalog.russian'
        temp_buffers: 128MB
        max_files_per_process: 10000
        commit_delay: 1000
        from_collapse_limit: 8
        join_collapse_limit: 8
        autovacuum_max_workers: 4 
        vacuum_cost_limit: 200 
        autovacuum_naptime: 10s
        autovacuum_vacuum_scale_factor: 0.01
        autovacuum_analyze_scale_factor: 0.005
        max_locks_per_transaction: 512
        escape_string_warning: off
        standard_conforming_strings: off
        shared_preload_libraries: 'online_analyze, plantuner'
        online_analyze.threshold: 50
        online_analyze.scale_factor: 0.1
        online_analyze.enable: on
        online_analyze.verbose: off
        online_analyze.min_interval: 10000
        online_analyze.table_type: 'temporary'
        plantuner.fix_empty_table: on
        shared_buffers: 8GB
        effective_cache_size: 16GB
        maintenance_work_mem: 1GB
        checkpoint_completion_target: 0.9
        wal_buffers: 16MB
        default_statistics_target: 500
        effective_io_concurrency: 200
        work_mem: 13981kB
        min_wal_size: 2GB
        max_wal_size: 8GB
        max_worker_processes: 12
        max_parallel_workers_per_gather: 6
        max_parallel_workers: 12
        max_parallel_maintenance_workers: 4
    tags:
      nofailover: false
      noloadbalance: false
      clonefrom: false
      nosync: false
    
    
  6. Создаём сервис для запуска службы "patroni":
    ~$ nano /etc/systemd/system/patroni.service
    [Unit]
    Description=Runners to orchestrate a high-availability PostgreSQL
    After=syslog.target network.target
    [Service]
    Type=simple
    User=postgres
    Group=postgres
    ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml
    ExecReload=/bin/kill -s HUP $MAINPID
    KillMode=process
    TimeoutSec=10
    Restart=no
    [Install]
    WantedBy=multi-user.target
  7. Запускаем службу "patroni" и открываем порты:
    ~$ systemctl daemon-reload
    ~$ firewall-cmd -zone=public --permanent --add-port=8008/tcp
    ~$ firewall-cmd -zone=public --permanent --add-port=5432/tcp
    ~$ systemctl start patroni.service
    
  8. Проверяем статус службы "patroni":
    ~$ systemctl status patroni.service
    ? patroni.service - Runners to orchestrate a high-availability PostgreSQL
    Loaded: loaded (/etc/systemd/system/patroni.service; disabled; vendor preset: disabled)
    Active: active (running) since Wed 2022-08-20 16:01:12 UTC; 2s ago
    Main PID: 1029 (patroni)
    CGroup: /system.slice/patroni.service
    1715 /opt/pgpro/1c-14/bin/postgres -D /var/lib/pgpro/1c-14/data --config-file=/var/lib/pgpro/1c-14/data/postgresql.conf --listen_addresses=0.0.0.0 --port=5432 --cluster_name=postgres --wal_level=replica --hot_standby=on --max_connections=180 --max_wal_senders=5 --max_prepared_transactions=0 --max_locks_per_transaction=64 
    --track_commit_timestamp=off --max_replication_slots=5 --max_worker_processes=8 --wal_log_hints=on
    авг 20 16:01:13 pg1.local patroni[1029]: Data page checksums are enabled.
    авн 20 16:01:13 pg1.local patroni[1029]: fixing permissions on existing directory /var/lib/pgpro/1c-14/data ... ok
    авг 20 16:01:13 pg1.local patroni[1029]: creating configuration files ... ok
    авг 20 16:01:13 pg1.local patroni[1029]: running bootstrap script ... ok
    авг 20 16:01:13 pg1.local patroni[1029]: performing post-bootstrap initialization ... ok
  9. Проверьте каталог по пути "/var/lib/pgpro/1c-14/data"он должен быть полным.
    Так же проверьте, слушает ли "PostgreSQL" порт 5432?:
    ~$ ss -ltn | grep 5432
    LISTEN 0 128 0.0.0.0:5432 0.0.0.0:*
  10. Добавляем "patroni" в автозапуск:
    ~$ systemctl enable patroni.service
  11. Для проверки состояния кластера службы "patroni", выполняем следующую команду:
    ~$ patronictl -c /etc/patroni/patroni.yml list
    +--------+-----------------+---------+---------+----+-----------+
    | Member | Host            | Role    | State   | TL | Lag in MB |
    + Cluster: postgres (*******************) -----+----+-----------+
    | pg1  | pg1.local | Leader  | running |  1 |           |
    +--------+-----------------+---------+---------+----+-----------+
    
  12. Добавление нового узла. 
    На данном этапе, настройка похожа как и в случае конфигурирования кластера "etcd", то есть, выполняем те же этапы установки и создаём тот же файл конфигурации "patroni.yml" с некоторыми поправками:
    ~$ nano /etc/patroni/patroni.yml
    name: pg2
    namespace: /db/
    scope: postgres
    restapi:
      listen: 0.0.0.0:8008
      connect_address: pg2.local:8008
      authentication:
       username: patroni
       password: patroni
    etcd:
     hosts: localhost:2379
     username: root
     password: Ваш_такой_же_пароль_что_и_в_первой_конфигурации
    bootstrap:
     dcs:
      ttl: 30
      loop_wait: 10
      retry_timeout: 10
      maximum_lag_on_failover: 1048576
      master_start_timeout: 10
      postgresql:
       use_pg_rewind: true
       use_slots: true
       parameters:
        wal_level: replica
        hot_standby: "on"
        wal_keep_segments: 8
        max_wal_senders: 5
        max_replication_slots: 5
        checkpoint_timeout: 30
     initdb:
     - auth-host: md5
     - auth-local: peer
     - encoding: UTF8
     - data-checksums
     - locale: ru_RU.UTF-8
     pg_hba:
     - host replication replicator samenet md5
     - host replication all 127.0.0.1/32 md5
     - host replication all ::1/128 md5
     users:
      usr1cv8:
       password: Ваш_такой_же_пароль_что_и_в_первой_конфигурации
       options:
        - superuser
    postgresql:
     listen: 0.0.0.0:5432
     connect_address: pg2.local:5432
     config_dir: /var/lib/pgsql/14/data
     bin_dir: /usr/pgsql-14/bin/
     data_dir: /var/lib/pgsql/14/data
     pgpass: /tmp/pgpass
     authentication:
       superuser:
         username: postgres
         password: Ваш_такой_же_пароль_что_и_в_первой_конфигурации
       replication:
         username: replicator
         password: Ваш_такой_же_пароль_что_и_в_первой_конфигурации
       rewind:
         username: rewind_user
         password: Ваш_такой_же_пароль_что_и_в_первой_конфигурации
     parameters:
        max_connections: 181
        dynamic_shared_memory_type: posix
        seq_page_cost: 0.1
        random_page_cost: 0.1
        cpu_operator_cost: 0.0025
        logging_collector: on
        log_timezone: 'Europe/Moscow'
        datestyle: 'iso, dmy'
        timezone: 'Europe/Moscow'
        lc_messages: 'ru_RU.UTF-8'
        lc_monetary: 'ru_RU.UTF-8'
        lc_numeric: 'ru_RU.UTF-8'
        lc_time: 'ru_RU.UTF-8'
        default_text_search_config: 'pg_catalog.russian'
        temp_buffers: 128MB
        max_files_per_process: 10000
        commit_delay: 1000
        from_collapse_limit: 8
        join_collapse_limit: 8
        autovacuum_max_workers: 4 
        vacuum_cost_limit: 200 
        autovacuum_naptime: 10s
        autovacuum_vacuum_scale_factor: 0.01
        autovacuum_analyze_scale_factor: 0.005
        max_locks_per_transaction: 512
        escape_string_warning: off
        standard_conforming_strings: off
        shared_preload_libraries: 'online_analyze, plantuner'
        online_analyze.threshold: 50
        online_analyze.scale_factor: 0.1
        online_analyze.enable: on
        online_analyze.verbose: off
        online_analyze.min_interval: 10000
        online_analyze.table_type: 'temporary'
        plantuner.fix_empty_table: on
        shared_buffers: 8GB
        effective_cache_size: 16GB
        maintenance_work_mem: 1GB
        checkpoint_completion_target: 0.9
        wal_buffers: 16MB
        default_statistics_target: 500
        effective_io_concurrency: 200
        work_mem: 13981kB
        min_wal_size: 2GB
        max_wal_size: 8GB
        max_worker_processes: 12
        max_parallel_workers_per_gather: 6
        max_parallel_workers: 12
        max_parallel_maintenance_workers: 4
    tags:
      nofailover: false
      noloadbalance: false
      clonefrom: false
      nosync: false
    
    
  13. В результате, после настройки и запуска всех узлов, должно быть три узла в кластере "patroni":
    ~$ patronictl -c /etc/patroni/patroni.yml list
    +--------+-----------------+---------+---------+----+-----------+
    | Member | Host            | Role    | State   | TL | Lag in MB |
    + Cluster: postgres (*******************) -----+----+-----------+
    | pg1  | pg1.local | Leader  | running |  1 |           |
    | pg2  | pg2.local | Replica | running |  1 |         0 |
    | pg3  | pg3.local | Replica | running |  1 |         0 |
    +--------+-----------------+---------+---------+----+-----------+
    

Установка балансировщика "HAproxy" на сервер "1c.local":
 

  1. Устанавливаем "HAproxy", так же перемещаем настройки по умолчанию в сторону:
    ~$ yum install -y haproxy
    ~$ mv /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.conf.def
  2. Создаём конфигурационный файл:
    ~$ nano /etc/haproxy/haproxy.cfg
    global
     maxconn 100
    defaults
     log global
     mode tcp
     retries 2
     timeout client 30m
     timeout connect 4s
     timeout server 30m
     timeout check 5s
    listen stats
     mode http
     bind *:7000
     stats enable
     stats uri /
    listen postgres
     bind *:5432
     option httpchk
     http-check expect status 200
     default-server inter 3s fastinter 1s fall 2 rise 2 on-marked-down shutdown-sessions
     server pg1 pg1.local:5432 maxconn 100 check port 8008
     server pg2 pg2.local:5432 maxconn 100 check port 8008
     server pg3 pg3.local:5432 maxconn 100 check port 8008
    
    
    
    
  3. Запускаем службу "HAproxy", и открываем порты:
    ~$ setsebool -P haproxy_connect_any=1  # в случае включенного SELinux
    ~$ systemctl start haproxy.service
    ~$ firewall-cmd -zone=public --permanent --add-port=7000/tcp
  4. Если запуск прошёл успешно, добавляем службу "HAproxy" в автозапуск:
    ~$ systemctl enable haproxy.service

 

 

В целом на этом настройка закончена, по адресу "http://1c.local:7000" можно проверить состояние узлов кластера "Patroni".
При добавлении базы данных, обращение в менеджере кластера "1C", выполняется на "localhost", так как HAproxy вещает на "*:5432" возможность подключения к системе управления баз данных "PostgreSQL", но в моём случае было необходимо поправить конфигурацию файла "/var/lib/pgpro/1c-14/data/pg_hba.conf" в ручную, в целях предоставления возможности подключения с узла "1c.local" к "PostgreSQL", эти изменения мною проводились на всех узлах кластера Patroni и после выполнялась порядовая перезагрузка узлов кластера:

~$ patronictl -c /etc/patroni/patroni.yml reload postgres pg1
~$ patronictl -c /etc/patroni/patroni.yml restart postgres pg1
~$ patronictl -c /etc/patroni/patroni.yml reload postgres pg2
~$ patronictl -c /etc/patroni/patroni.yml restart postgres pg2
~$ patronictl -c /etc/patroni/patroni.yml reload postgres pg3
~$ patronictl -c /etc/patroni/patroni.yml restart postgres pg3

Дальше задумка следующая, запустить второй "1cr.local" сервер приложения , который уже в кластере и котором я умолчал, выполнить установку/настройку службы "HAproxy", добавить этот сервер посредством менеджера кластера "1С" как второй рабочий, с последующей настройкой его как резервный и тестировать отказоустойчивость на уровне сервера приложения "1С".

Обновление:

По результатам настройки кластера приложений "1С" посредством консоли управления кластером "1С" было выявлено не адекватное и не предсказуемое поведения самого кластера при переключениях с ведущего сервера приложений на повторитель, порог ожидания 10 минут после введения резервного сервера в кластер.
Что было выявлено:

  1. Зависает на 10-15 секунд, после переключает на резервный в случае если сам узел ведущего в сети а сама служба "" на ведущем сервера остановлена, то есть срабатывает некое условие перенаправления. Работает подключение к базам при таких условиях на всех узлах интрасети.
  2. В случае если физически не доступен узел ведущего сервера "", то тип модели меняется на ошибку, что узел не доступен, при том что на том узле, где выполнялась настройка кластера посредством консоли, появляется так же ошибка но сообщает уже о не доступности баз данных, но через примерно 15-30 секунд, подключение работает, ссылаясь на сетевое имя узла "1c.local" с установлением сессии на "1cr.local", но в интрасети, на других узлах, подключение не работает.
    Возможно эта особенность проблемы кроется в сети, так как локально работает "IDS\IPS" система. 
  3. На момент 15-30 секундного переключения с ведущего сервера на резервный, в этот момент в консоли кластера "1С", наблюдалось поведение оповещающие о том что резервный не является центральным сервером, показывая при этом в ошибке хэш кластера, соответствующая галочка была установлена.
  4. При настройке силами консоли управления кластера "1С", посредством изучения тем, форумов, на вопрос настройки кластера отказоустойчивости, было выявлено, что поведения повторителя должно быть с моделью поведения как у повторителя, то есть может быть ситуация при той концепции о которой написана инструкция, когда ведущий сервер делает запись в БД а повторитель повторяет эту запись в туже БД, что приведёт к коллизиям и проблемам.  

Было предпринято решение на основании указанных пунктов (не исключается тот факт, что автору этой статьи не хватает опыта работы с настройкой отказоустойчивого кластера силами инструментов самой платформы) выполнить кластер используя "HAproxy" перед сервером "" ведущим и сервером "" резервным, под именем "srv1c.local", без ввода этого узла в кластер "etcd".
Этапы установки на новый узел, не отличаются от указанных мною этапах данной инструкции, по этому приведу сразу работающий пример конфигурации:
 

~$ nano /etc/haproxy/haproxy.cfg
global
 maxconn 100
defaults
 log global
 mode tcp
 retries 2
 timeout client 3s
 timeout connect 5s
 timeout server 3s
 timeout check 2s
listen stats
 mode http
 bind *:7000
 stats enable
 stats uri /
listen 1c
 bind *:1540-1541
 bind *:1560-1691
 option tcp-check
 balance first
 server 1c 1c.local maxconn 3000 check port 1541
 server 1rc 1rc.local maxconn 3000 check port 1541

Какие изменения коснулись конфигурации:

  1. Мы провели смену проверки на tcp "option tcp-check"
  2. bind поставили прослушивать диапазоны нужных нам портов
  3. Роль балансировки выставлена "first", политика этой роли в том, что на каждый сервер выдаётся свой предел соединений и осуществляется это по декларативному порядку, то есть по порядку следования, тот кто первый указан в списке, тот и будет первым, тот кто вторым, тот будет вторым после первого и т.д. 
    Определяется порог установленных сессий по количеству переменной "maxconn

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

  

Схема:





Аннотированный Список:

https://its.1c.ru/db/content/metod8dev/src/developers/scalability/administration/postgresql/i8105971.htm#_top

https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-using-firewalld-on-centos-7

https://www.devmind.ru/%D0%B2%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5%20%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D1%8B/razvoracivaem-ha-cluster-postgresql-11

Отказоустойчивость Кластер 1C PostgreSQL Patroni HAproxy Etcd

См. также

Администрирование СУБД Системный администратор Платформа 1С v8.3 Бесплатно (free)

Пользовался ранее https://infostart.ru/1c/articles/1120161/#, но она устарела, т.к. службы запускаются через systemctl, да и сами службы слегка изменились. Возможно, где-то на ИТС уже есть нужная инструкция, но мне не попалась.

15.11.2024    301    Baser    2    

1

HighLoad оптимизация Администрирование СУБД Системный администратор Программист Платформа 1С v8.3 Россия Бесплатно (free)

Мы исследуем проблему долгого выполнения запросов PostgreSQL при использовании конструкции VALUES: когда она возникает, как на нее можно повлиять, а главное, почему ее продуманная отработка важна для более быстрого функционирования решений на базе 1С

12.11.2024    830    Tantor    19    

14

HighLoad оптимизация Администрирование СУБД Механизмы платформы 1С Программист Платформа 1С v8.3 ИТ-компания Россия Бесплатно (free)

В данной статье мы рассмотрим, как работает механизм временных таблиц на postgres на платформе 8.3.23 и что изменилось в нем при добавлении новых возможностей в платформе 8.3.25. А также на примере покажу, как понимание работы платформы позволяет оптимизировать СУБД для работы с 1С.

29.10.2024    3148    Tantor    38    

34

Администрирование СУБД Системный администратор Программист Бесплатно (free)

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

08.10.2024    734    AlexSvoykin    1    

7

Администрирование СУБД Системный администратор Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Анализ и решение ошибок СУБД. Во время реиндексации базы Ошибка СУБД: Microsoft SQL Server Native Client 11.0: Не удалось найти объект "ИмяБазы.dbo._RefSInf21806", так как он не существует, или отсутствуют разрешения. Во время проверки целостности Ошибка СУБД: Microsoft SQL Server Native Client 11.0: Недопустимое имя объекта "dbo._RefSInf21806".

19.09.2024    4349    Xershi    10    

17

HighLoad оптимизация Администрирование СУБД Архивирование (backup) Системный администратор Программист Платформа 1С v8.3 Бесплатно (free)

Бэкап в Postgres состоит из набора граблей, которые нужно обойти для успешного восстановления. Они заложены в самых неожиданных местах от предмета резервного копирования (база или кластер) до структуры каталогов. Один неверный шаг и восстановление будет невозможным. Почему нельзя было сделать проще, как в MS SQL или Oracle? Почему бэкап в Postgres оставляет впечатление чьей-то лабораторной работы? Статья адресована прежде всего специалистам 1С, избалованным комфортом в MS SQL, в суровых буднях импортозамещения на Postgres.

13.08.2024    2971    1CUnlimited    9    

4
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. vld1973 90 22.08.22 22:48 Сейчас в теме
Спасибо, напиши как прошло тестирование отказоустойчивости
user1332168; +1 Ответить
4. user1332168 27 23.08.22 09:38 Сейчас в теме
7. user1332168 27 27.08.22 12:13 Сейчас в теме
(1) Здравствуйте.
Обновил статью
2. redfred 23.08.22 08:40 Сейчас в теме
3 машины идут под базы данных, можно и две, но в целях пресечения так называемого явления "split-brain"(где обе машины начнут считать себя мастером) вводим третью машину и на каждую из них будет установлена служба "Patroni".


В третьей машине под бд, по большому счёту, нет особой необходимости, кворум обеспечивается через etcd

"etcd" будет использоваться службой "Patroni" в целях хранения своей конфигурации, но как на практике не совсем понятная суть изложенного, так как каждый конфигурационный файл "patroni.yml", будет создан на каждом узле хранения баз данных в ручную, с почти идентичным настройками.


В etcd хранится конфигурация общая для всех нод кластера, в локальном yml файле - локальная для конкретной ноды, настройки оттуда имеют больший приоритет. Т.е. предполагается что всё лежит в etcd и при редактировании через patronictl edit-config автоматически применяется на всех нодах, но если есть настройка, которая должна быть применена только на одной конкретной ноде (памяти на ней, например, меньше, и нужно только там shared_buffers уменьшить) то можете добавить её в локальный yml в секцию postgresql.parameters. В вашем варианте для изменения настроек вам придётся править конфиги на всех нодах
user1332168; +1 Ответить
3. user1332168 27 23.08.22 09:35 Сейчас в теме
(2)
В третьей машине под бд, по большому счёту, нет особой необходимости, кворум обеспечивается через etcd


Я полагаю не правильно Вас понял.
Так как Ваш ответ воспринял так: если к примеру мы перезагрузим уже сконфигурированный etcd, первым станет к примеру 3-й узел и при повторной перезагрузки как на практике показало, он остаётся им дальше, но к примеру 3 узел, является 2-ым держателем баз данных из 3ёх, где первый ведущий. Такое поведение не совсем расценивается скажем систематизированным, так как на примере patroni, после перезагрузки ведущего узла, им станет 2-ой, но, после того, как узел первый возобновит своё состояние до активного, при перезагрузке 2-го, первый самостоятельно становится ведущим, а не 3-ий.

В etcd хранится конфигурация общая для всех нод кластера, в локальном yml файле - локальная для конкретной ноды, настройки оттуда имеют больший приоритет. Т.е. предполагается что всё лежит в etcd и при редактировании через patronictl edit-config автоматически применяется на всех нодах, но если есть настройка, которая должна быть применена только на одной конкретной ноде (памяти на ней, например, меньше, и нужно только там shared_buffers уменьшить) то можете добавить её в локальный yml в секцию postgresql.parameters. В вашем варианте для изменения настроек вам придётся править конфиги на всех нодах


То есть, в мною описанной концепции, не имеет смысла использовать, изменять конфигурацию patroni в одном месте (где Вы сказали лежит в etcd), и править только отдельно каждую?
5. redfred 23.08.22 10:18 Сейчас в теме
(3)
Так как Ваш ответ воспринял так: если к примеру мы перезагрузим уже сконфигурированный etcd, первым станет к примеру 3-й узел и при повторной перезагрузки как на практике показало, он остаётся им дальше, но к примеру 3 узел, является 2-ым держателем баз данных из 3ёх, где первый ведущий. Такое поведение не совсем расценивается скажем систематизированным, так как на примере patroni, после перезагрузки ведущего узла, им станет 2-ой, но, после того, как узел первый возобновит своё состояние до активного, при перезагрузке 2-го, первый самостоятельно становится ведущим, а не 3-ий


Признаться я вообще не понял, что вы пытались мне донести. Наверное мне пора уже прекратить пить коньяк по утрам.
Смотрите - патрони в своей работе полностью зависит от dcs (в вашем случае это etcd). Помимо хранения конфигурации, там есть такая штука, как ключ лидера, у которого есть ttl, т.е. он действителен сколько-то времени, потом "протухает". Патрони, запущенный на мастер ноде постгреса, периодически обновляет этот ключ. Патрони на реплике периодически из этого ключа считывает кто сейчас мастер и реплицируется с него. Если мастер отваливается от сети, то, соотв. ключ в dcs обновить не может. Патрони на реплике видит пропажу ключа и, очень обобщенно говоря, промоутит пг до мастера и начинает сам обновлять этот ключ. Если вдруг старый мастер вернётся в строй, он увидит в dcs ключ нового мастера и, опять же очень обобщенно, через pg_rewind отмотается назад до момента расхождения и станет новой репликой.
Т.е. для предотвращение split-brain вам не нужно иметь нечётное количество нод с постгресом, на двух всё будет нормально. А вот etcd нужен надёжный, это да. И в patroni.yml вам надо указать все ваши ноды etcd, а не только локалхост

(3)
То есть, в мною описанной концепции, не имеет смысла использовать, изменять конфигурацию patroni в одном месте (где Вы сказали лежит в etcd), и править только отдельно каждую?


Имеет смысл иметь общую конфигурацию в etcd, а через локальные файлы применять настройки точечно, только в случае реальной необходимости
user1332168; +1 Ответить
6. user1332168 27 23.08.22 10:53 Сейчас в теме
(5)
ризнаться я вообще не понял, что вы пытались мне донести. Наверное мне пора уже прекратить пить коньяк по утрам.
Смотрите - патрони в своей работе полностью зависит от dcs (в вашем случае это etcd). Помимо хранения конфигурации, там есть такая штука, как ключ лидера, у которого есть ttl, т.е. он действителен сколько-то времени, потом "протухает". Патрони, запущенный на мастер ноде постгреса, периодически обновляет этот ключ. Патрони на реплике периодически из этого ключа считывает кто сейчас мастер и реплицируется с него. Если мастер отваливается от сети, то, соотв. ключ в dcs обновить не может. Патрони на реплике видит пропажу ключа и, очень обобщенно говоря, промоутит пг до мастера и начинает сам обновлять этот ключ. Если вдруг старый мастер вернётся в строй, он увидит в dcs ключ нового мастера и, опять же очень обобщенно, через pg_rewind отмотается назад до момента расхождения и станет новой репликой.
Т.е. для предотвращение split-brain вам не нужно иметь нечётное количество нод с постгресом, на двух всё будет нормально. А вот etcd нужен надёжный, это да. И в patroni.yml вам надо указать все ваши ноды etcd, а не только локалхост


Спасибо за подробное разъяснение, при изучении Вашего ответа, действия по проверки и обновления ttl в patroni у etcd, напомнили чем-то схему работы kerberos по получение билета на доступ к сервису и его обновления, но это уже ответвление а Вам спасибо большое за подробное изложение и теоретическую поддержку.

Имеет смысл иметь общую конфигурацию в etcd, а через локальные файлы применять настройки точечно, только в случае реальной необходимости


Принял, спасибо.
8. FreedomOfChoice 18.12.22 12:54 Сейчас в теме
на схеме машина srv1c.local как бы ла использована в итоге? я не нашел в статье про нее ничего.
10. user1332168 27 19.12.22 16:24 Сейчас в теме
(8)Добавил в описание объявление о "srv1c.local".
9. user1332168 27 19.12.22 16:20 Сейчас в теме
(8) Здравствуйте.
Да, Вы правы, явно об этом не указал, но о ней написано и больше того, описана её конфигурация, в блоке "Обновление"
11. пользователь 25.05.23 21:04
Сообщение было скрыто модератором.
...
12. Selfdefens 01.03.24 17:58 Сейчас в теме
Схема с проблемами, которые сильно влияют на производительность.

Разница в сетевых задержках и локальными запросами исчисляется порядками. в последнем проекте у меня были сетевые задержки около 0,6 мс, а время выполнения 99% запросов на чтение 0.001 мс и около 0.2 мс на запись. Эти 0,6 мс это NetIO.
Теперь смотрите как на Вашей схеме это выглядит:
1) запрос от 1С к HAProxy 0.6 мс + haproxy смотрит запрос
2) От HAProxy до одного из Postgres 0,6 мс + 0.001(если чтение) / +0.2 если запись
3) Если запрост на запись настроенна синхронная запись + 0,6 мс + 0,2
Теперь посчитайте во сколько база замедлилась из-за лишних элементов в схеме.

У 1С большинство запросов достаточно примитивные (Как правило это куча простых селектов). Грубо говоря, когда 1С нужно посчитать сумму по отчёту, он не попросит СУБД посчитать эту сумму, он запросит все значения и сложит сам. А если в отчёте какой-нибудь баланс по 1000 контрагентов, тогда он даже названия контрагентов будет запрашивать отдельными селектами. т.е. если Вам нужно построить отчёт - вы примерно половину времени потеряете на NetIO с HAProxy, а если какие-нибудь массовые проводки - около 75% на HAProxy и вторую и третью запись.

Я бы:
1) убрал из схемы HAProxy и сделал VIP-адрес, который бы передавался между нодами постгресс. (это технология примерно как MSSQL AlwaysON). Этим вы уберёте один из NetIO.
2) Убрал третью ноду постгреса.
13. Selfdefens 01.03.24 18:01 Сейчас в теме
Схема с проблемами, которые сильно влияют на производительность.

Разница в сетевых задержках и локальными запросами исчисляется порядками. в последнем проекте у меня были сетевые задержки около 0,6 мс, а время выполнения 99% запросов на чтение 0.001 мс и около 0.2 мс на запись. Эти 0,6 мс это NetIO.
Теперь смотрите как на Вашей схеме это выглядит:
1) запрос от 1С к HAProxy 0.6 мс + haproxy смотрит запрос
2) От HAProxy до одного из Postgres 0,6 мс + 0.001(если чтение) / +0.2 если запись
3) Если запрост на запись настроенна синхронная запись + 0,6 мс + 0,2
Теперь посчитайте во сколько база замедлилась из-за лишних элементов в схеме.

У 1С большинство запросов достаточно примитивные (Как правило это куча простых селектов). Грубо говоря, когда 1С нужно посчитать сумму по отчёту, он не попросит СУБД посчитать эту сумму, он запросит все значения и сложит сам. А если в отчёте какой-нибудь баланс по 1000 контрагентов, тогда он даже названия контрагентов будет запрашивать отдельными селектами. т.е. если Вам нужно построить отчёт - вы примерно половину времени потеряете на NetIO с HAProxy, а если какие-нибудь массовые проводки - около 75% на HAProxy и вторую и третью запись.

Я бы:
1) убрал из схемы HAProxy и сделал VIP-адрес, который бы передавался между нодами постгресс. (это технология примерно как MSSQL AlwaysON). Этим вы уберёте один из NetIO.
2) Убрал третью ноду постгреса.
14. AlekseyBelyy 12 31.05.24 16:03 Сейчас в теме
Добрый день, спасибо за статью.

На одном из проектов использовали такой же подход и словили очень интересную ошибку - при отмене проведения документа случался таймаут на СУБД (postgresql) на таблице документа. При этом таймаут был даже когда в базе ты один. После анализа удалось выяснить в чем было дело:
Отмена проведения большого документа, ~3-4 тыс. строк в ТЧ, ввод начальных остатков. В рамках транзакции случался длительный запрос на СУБД на 48 минут. Но haproxy срубал соединение 1С с СУБД по таймауту (30 мин). При этом платформа устанавливала новое соединение с СУБД и повторяла вызов, все согласно документации с ИТС:
Если клиентское приложение вызвало сервер вне транзакции и ошибка передачи данных получена клиентом до фиксации первой за вызов сервера транзакции, то клиент автоматически выполняет процедуру установки соединения с сервером и повторяет тот же самый вызов

Но первая транзакция на СУБД никуда не делась, в ней по прежнему выполнялся длительный update таблицы итогов одного из регистров накопления. А перед этим был выполнен update самого документа.
Вторая транзакция пыталась так же выполнить update документа и через 20 сек. случался таймаут. Когда длительный запрос первой транзакции выполнился, то процесс на СУБД завершался с ошибкой вида "не удалось отправить результат клиенту, обрыв канала".
Таким образом сессия 1С одна, но два соединения с СУБД которые блокировали друг друга.

Получается у haproxy таймаут работает только в одну сторону - клиента (1с), но не сервера (постгрес). Заставить его работать в обе стороны - завершать соединение и на СУБД - у нас пока не получилось.

Кто-нибудь сталкивался с подобным поведением?

ПС: только не надо советовать ускорить запроса, это и так понятно. Как и то, что запросы дольше заданного таймаута на haproxy могут появиться в любой момент.
Оставьте свое сообщение