The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Настройка СУБД PostgreSQL 13 под управлением Pacemaker/Corosync в Debian 11
В статье расскажу про мой опыт настройки Postgresql для работы под контролем
кластерной службы Pacemaker

Под нагрузкой данное решение не проверялось, всегда делайте (и проверяйте) резервные копии.

Для хранения базы данных будет рассмотрен пример использования кластерной файловой системе OCFS2.

Версии ПО, использованные в примере:

  • OCFS2 - драйвер из ядра 5.10, утилиты ocfs2-tools - 1.8.6
  • Corosync - 3.1.2
  • Pacemaker - 2.0.5
  • Postgresql 13 В статье будет три типа кластеров:
  • кластер файловой системы OCFS2 - обеспечивает хранение файлов на общем диске и согласованную работу с ними
  • кластер Corosync/Pacemaker - обеспечивает отслеживание работы процессов СУБД, запуск виртуального ip-адреса СУБД
  • кластер баз данных Postgresql - набор баз, управляемых одним экземпляром работающего сервера СУБД Подготовка операционных систем Для устойчивой работы кластеров (OCFS2, Pacemaker/Corosync) необходимо как минимум три сервера. Сервера могут быть как физические так и виртуальные. Желательно, чтобы сервера имели одинаковые характеристики производительности. Я для демонстрации подготовил три виртуальные машины с помощью Qemu-KVM. Устанавливаем ОС Debian 11 на каждый из серверов в минимальной конфигурации. Настройка сети В примере у меня будут сервера с адресами:
  • node1 - ip 192.168.1.11
  • node2 - ip 192.168.1.12
  • node3 - ip 192.168.1.13 Имена узлов должны разрешаться в IP-адреса на каждом из серверов, для этого необходимо прописать сопоставление в файле /etc/hosts или создать записи на DNS-сервере. root:~# cat /etc/hosts 127.0.0.1 localhost 192.168.1.11 node1.local node1 192.168.1.12 node2.local node2 192.168.1.13 node3.local node3 В случае реальной реализации кластера, сетевых карт на каждом из серверов должно быть как минимум две - карты необходимо объединить в логическое устройство bonding или teaming. Физически карты должны подключаться к двум независимым коммутаторам. В примере у меня будет по одной сетевой карте на сервер. Настройка сетевого экрана Выполняем настройку экрана на каждом узле:
  • устанавливаем пакет для управления брандмауэром ufw
  • создаём разрешающие правила для ssh, postgres, узлов кластера
  • активируем правила root# apt install ufw root# ufw allow ssh root# ufw allow postgres root# ufw allow from 192.168.1.11 to any root# ufw allow from 192.168.1.12 to any root# ufw allow from 192.168.1.13 to any root# ufw enable Кластер OCFS2 для отслеживания работы узлов по-умолчанию использует протокол TCP порт 7777 (задаётся в файле /etc/ocfs2/cluster.conf), а Corosync - протокол UDP, порт 5405 (задаётся в файле /etc/corosync/corosync.conf), с учётом этого, можно настроить более тонкие правила брандмауэра: root# ufw allow proto tcp from 192.168.1.11 to any port 7777 root# ufw allow proto tcp from 192.168.1.12 to any port 7777 root# ufw allow proto tcp from 192.168.1.13 to any port 7777 root# ufw allow proto udp from 192.168.1.11 to any port 5405 root# ufw allow proto udp from 192.168.1.12 to any port 5405 root# ufw allow proto udp from 192.168.1.13 to any port 5405 Настройка дисковой системы На каждом сервере будет индивидуальный диск для системы (/dev/vda, 20 Гб) и общий диск на все сервера для хранения БД (/dev/vdb, 5 Гб): root# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT vda 254:0 0 20G 0 disk ├─vda1 254:1 0 512M 0 part /boot/efi ├─vda2 254:2 0 18,5G 0 part / └─vda3 254:3 0 976M 0 part [SWAP] vdb 254:16 0 5G 0 disk /mnt/ocfs2clst Настройка кластерной файловой системы Для реального использования общий диск должен быть расположен на системе хранения данных, и подключатся к серверам по нескольким путям. Для демонстрации общий диск будет реализован с помощью средств Qemu-KVM. Настройка службы кластера ФС OCFS2 Настройка ядра Linux Необходимо изменить параметры ядра, чтобы сервер автоматически перезагружался при сбое кластерной ФС, для это нужно создать файл /etc/sysctl.d/10-ocfs2.conf kernel.panic_on_oops = 1 kernel.panic = 30 После применить параметры: root# systemctl restart systemd-sysctl Данные настройки указывают ядру Linux при возникновении сбоя (когда связь по сети пропала, но узел продолжает запись heartbeat сообщений на общий диск) автоматически перезагрузить узел через 30 секунд. Установка пакетов OCFS2 Устанавливаем пакеты на каждом из узлов root# apt install ocfs2-tools Настройка кластера ФС OCFS2 Все настройки кластера OCFS2 хранятся в файле /etc/ocfs2/cluster.conf. Нужно либо выполнить команды на каждом узле кластера, либо выполнить на одном узле и после скопировать файл /etc/ocfs2/cluster.conf на каждый узел, а после выполнить регистрацию и запуск. Создаём кластер (выполнить на каждом узле кластера) root# o2cb add-cluster ocfs2clst Добавляем узлы в кластер (выполнить на каждом узле кластера), имя узла должно совпадать с тем, что выдаёт команда hostname root# o2cb add-node --ip 192.168.1.11 --port 7777 --number 1 ocfs2clst node1 root# o2cb add-node --ip 192.168.1.12 --port 7777 --number 2 ocfs2clst node2 root# o2cb add-node --ip 192.168.1.13 --port 7777 --number 3 ocfs2clst node3 Регистрируем кластер (выполнить на каждом узле кластера) root# o2cb register-cluster ocfs2clst Включаем кластер (выполнить на каждом узле кластера) root# o2cb start-heartbeat ocfs2clst Выполняем настройку драйвера ФС (обязательно выполнить на каждом узле кластера) root# dpkg-reconfigure ocfs2-tools Запускать кластер OCFS2 (O2CB) во время загрузки компьютера?: Y Имя кластера, запускаемого во время загрузки компьютера: ocfs2clst Настройки драйвера хранятся в файле /etc/default/o2cb Содержимое файла: # O2CB_ENABLED: 'true' means to load the driver on boot. O2CB_ENABLED=true # O2CB_BOOTCLUSTER: If not empty, the name of a cluster to start. O2CB_BOOTCLUSTER=ocfs2clst # O2CB_HEARTBEAT_THRESHOLD: Iterations before a node is considered dead. O2CB_HEARTBEAT_THRESHOLD=31 # O2CB_IDLE_TIMEOUT_MS: Time in ms before a network connection is considered dead. O2CB_IDLE_TIMEOUT_MS=30000 # O2CB_KEEPALIVE_DELAY_MS: Max. time in ms before a keepalive packet is sent. O2CB_KEEPALIVE_DELAY_MS=2000 # O2CB_RECONNECT_DELAY_MS: Min. time in ms between connection attempts. O2CB_RECONNECT_DELAY_MS=2000 Создание ФС OCFS2 Можно создать разделы на кластерном томе и создавать ФС уже на разделе, но это добавит сложностей при расширении тома, так как придётся вручную править границы раздела с помощью parted/fdisk и после расширять ФС. Но у нас кластерный том планируется целиком отдать под работу СУБД Postgresql, поэтому ФС предлагаю создать сразу на всем томе (в примере это диск /dev/vdb). Выполняем форматирование общего тома на одном из узлов: root# mkfs.ocfs2 -L pg-data --cluster-name=ocfs2clst -N 5 -T datafiles --fs-feature-level=max-features --cluster-stack=o2cb /dev/vdb Описание параметров:
  • -L pg-data - метка ФС
  • --cluster-name=ocfs2clst - имя кластера OCFS2, который управляет ФС
  • -N 5 - максимальное количество узлов, которые могут работать одновременно с ФС, позже можно поменять с помощью tunefs.ocfs2, но рекомендуется создавать структуру заранее
  • -T datafiles - тип хранимых данных, может быть mail, datafiles, vmstore
  • --fs-feature-level=max-features - включаем все доступные возможности ФС, т.к. узлы у нас идентичные
  • --cluster-stack=o2cb - используем для управления ФС стандартный стек o2cb Проверяем, что метки новой ФС видны на всех узлах кластера: root# blkid /dev/vdb /dev/vdb: LABEL="pg-data" UUID="ce92b1e7-30cb-4883-9a92-57c986f76acd" BLOCK_SIZE="4096" TYPE="ocfs2" Вывод команды blkid на всех узлах кластера должен совпадать. Выполняем пробное монтирование Монтирование выполняется 20-30 секунд, так как требует согласования по сети. Выполняем команды на всех узлах кластера. root# mkdir /mnt/ocfs2clst root# mount /dev/disk/by-uuid/ce92b1e7-30cb-4883-9a92-57c986f76acd /mnt/ocfs2clst Команда mounted.ocfs2 показывает на каких узлах смонтирована ФС. root# mounted.ocfs2 -f Device Stack Cluster F Nodes /dev/vdb o2cb ocfs2clst node1, node2, node3 Создаём пробные файлы/папки в каталоге /mnt/ocfs2clst на одном из узлов и проверяем, что они видны на остальных узлах кластера OCFS2. Размонтируем ФС на каждом узле: root# umount /mnt/ocfs2clst Расширение ФС OCFS2 Если потребуется увеличить размер хранилища:
  • Увеличиваем размер тома на СХД
  • Пересканируем том на сервере или перезагружаем узлы кластера
  • Расширяем ФС root# tunefs.ocfs2 -S /dev/vdb Добавление узла в кластер OCFS2 Если потребуется добавить ещё узел (например, node4, с ip 192.168.1.14) в кластер OCFS2, то необходимо выполнить команду на каждом узле: root# o2cb_ctl -C -i -n node4 -t node -a number=4 -a ip_address=192.168.1.14 -a ip_port=7777 -a cluster=ocfs2clst Необходимо заметить, что для обеспечения кворума, количество узлов должно быть нечётным. Установка PostgreSQL Устанавливаем пакеты на все узлы кластера: root# apt install postgresql Отключаем службу на каждом узле кластера, т.к. запуском СУБД будет управлять Pacemaker root# systemctl disable postgresql Настройка Pacemaker/Corosync Установка пакетов root# apt install pacemaker corosync crmsh fence-agents Настройка Corosync Служба Corosync обеспечивает обмен сообщениями между узлами кластера, с помощью неё отслеживается, что узлы работают корректно. А уже на основании информации о том какие узлы доступны, служба Pacemaker принимает решение о запуске сервисов (запуск виртуальных ip-адресов, монтирование файловых систем, запуск процессов СУБД). Настройки Corosync хранятся в файле /etc/corosync/corosync.conf. Рабочий пример файла указан ниже: # Please read the corosync.conf.5 manual page totem { version: 2 cluster_name: pgclst crypto_cipher: aes256 crypto_hash: sha256 } logging { fileline: off to_stderr: yes to_logfile: yes logfile: /var/log/corosync/corosync.log to_syslog: yes debug: off logger_subsys { subsys: QUORUM debug: off } } quorum { provider: corosync_votequorum } nodelist { node { name: node1 nodeid: 1 ring0_addr: 192.168.1.11 } node { name: node2 nodeid: 2 ring0_addr: 192.168.1.12 } node { name: node3 nodeid: 3 ring0_addr: 192.168.1.13 } } Включение шифрования сообщений Corosync Для повышения безопасности можно включить шифрование служебных сообщений при обмене между узлами кластера. Для этого на одном из узлов необходимо выполнить команду: root# corosync-keygen Она создаст файл /etc/corosync/authkey, этот файл необходимо скопировать на другие узлы кластера. root@node1:~# scp /etc/corosync/authkey root@node2:/etc/corosync/authkey root@node1:~# scp /etc/corosync/authkey root@node3:/etc/corosync/authkey В файле настроек /etc/corosync/corosync.conf необходимо задать параметры crypto_cipher и crypto_hash в секции totem: totem { ... crypto_cipher: aes256 crypto_hash: sha256 ... } Если вам необходимо разместить файл-ключ по не стандартному пути, то расположение можно указать с помощью директивы keyfile. После изменений необходимо перезапустить службы на каждом узле: root# systemctl restart corosync pacemaker Параметры узлов кластера Corosync Для работы кластера необходимо указать список узлов. Это делается в секции nodelist. nodelist { node { name: node1 nodeid: 1 ring0_addr: 192.168.1.11 } node { name: node2 nodeid: 2 ring0_addr: 192.168.1.12 } node { name: node3 nodeid: 3 ring0_addr: 192.168.1.13 } } После настройки, копируем файл /etc/corosync/corosync.conf на остальные узлы. Перезагружаем все узлы кластера и проверяем работу службы corosync с помощью команд corosync-quorumtool и crm_mon root@node1:~# corosync-quorumtool -s Quorum information ------------------ Date: Thu Jul 14 21:09:17 2022 Quorum provider: corosync_votequorum Nodes: 3 Node ID: 1 Ring ID: 1.139 Quorate: Yes Votequorum information ---------------------- Expected votes: 3 Highest expected: 3 Total votes: 3 Quorum: 2 Flags: Quorate Membership information ---------------------- Nodeid Votes Name 1 1 node1 (local) 2 1 node2 3 1 node3 root@node1:~# crm_mon -1 Cluster Summary: * Stack: corosync * Current DC: node3 (version 2.0.5-ba59be7122) - partition with quorum * Last updated: Thu Jul 14 21:11:18 2022 * Last change: Thu Jul 14 20:24:25 2022 by root via cibadmin on node1 * 3 nodes configured * 0 resource instances configured Node List: * Online: [ node1 node2 node3 ] Настройка Pacemaker Ресурсы Pacemaker описываются через XML-файлы, я вместо ручного написания xml-объектов буду использовать crm (CRM shell), где параметры ресурсов можно задать в виде аргументов. Смена имени кластера Ранее мы создали кластер OCFS2 с именем ocfs2clst, кластер Corosync с именем pgclst, теперь укажем имя кластера Pacemaker. После установки, имя кластера Pacemaker, обычно debian, поменяем его также на pgclst: root# crm_attribute --query --name=cluster-name scope=crm_config name=cluster-name value=debian root# crm_attribute --type crm_config --name cluster-name --update pgclst Параметры по-умолчанию Меняем параметры по-умолчанию для новых ресурсов:
  • resource-stickiness - "липучесть" ресурса к текущему расположению в кластере (по-умолчанию 0), или "стоимость" переноса ресурса на другой узел. При увеличении значения, pacemaker будет стараться восстановить состояние сбойного ресурса на том же узле, при малом значении - предпочтёт восстановить ресурс запуском на других узлах. root# crm_attribute --type rsc_defaults --name resource-stickiness --update 10
  • migration-threshold - кол-во сбоев ресурса на узле, при превышении которого происходит миграция на другой узел root# crm_attribute --type rsc_defaults --name migration-threshold --update 2 Ассиметричный кластер Можно указать, что для запуска каких-либо ресурсов необходимо наличие явного разрешающего правила. Это может понадобиться если не все узлы кластера идентичны по характеристикам: root# crm_attribute -n symmetric-cluster -v false После включения для каждого ресурса будет необходимо создать правила. crm conf location <имя правила> <имя ресурса> <приоритет>: <узел> Например, для ресурса виртуального ip-адреса (ip-pgclst) можно указать, что c приоритетом 100 он будет размещаться на узле node1, с приоритетом 10 - на узле node2, а на узле node3 его запуск будет запрещён (приоритет -infinity ): root~# crm conf crm(live)configure# location loc-ip-1 ip-pgclst 100: node1 crm(live)configure# location loc-ip-2 ip-pgclst 10: node2 crm(live)configure# location loc-ip-3 ip-pgclst -inf: node3 Изоляция узлов (stonith) В Pacemaker для каждого узла необходимо указать метод изоляции (fencing) в случае сбоя сетевой доступности. Осуществляется изоляция с помощью stonith ресурсов. Это могут быть программы для отключения питания на UPS, программы, которые подключаются к гипервизору и принудительно завершают работу виртуальной машины (нашего узла кластера) и много других вариантов. Без STONITH устройств Pacemaker откажется запускать ресурсы: root:~# crm_verify -L -V (unpack_resources) error: Resource start-up disabled since no STONITH resources have been defined (unpack_resources) error: Either configure some or disable STONITH with the stonith-enabled option (unpack_resources) error: NOTE: Clusters with shared data need STONITH to ensure data integrity Errors found during check: config not valid Список устройств для изоляции можно узнать из команды: root# stonith_admin --list-installed Параметры, необходимые устройству для работы, можно узнать: root# stonith -t <имя устройста stonith> -n Простейшие stonith ресурсы можно создать так. Ресурс-пустышка dummy - ничего не отключает: root# crm conf primitive sh-dummy stonith:null params hostlist="192.168.1.11 192.168.1.12 192.168.1.13" root# crm conf clone fency sh-dummy SSH-stonith - пытается подключиться к сбойному узлу через SSH и запланировать выключение через службу at (должна быть установлена на всех узлах). root# apt install at root# crm conf primitive fence-ssh stonith:ssh params hostlist="192.168.1.11 192.168.1.12 192.168.1.13" root# crm conf clone fency fence-ssh Имитируем сбой на узле node3: root# stonith -t ssh -p "node1 node2 node3" -T reset node3 Для тестирования может понадобится отключение STONITH (НЕ РЕКОМЕНДУЕТСЯ): root~# crm_attribute -n stonith-enabled -v false Очистить ошибки можно командой: root# stonith_admin --cleanup --history=node3 Стоит заметить что, в случае сбоя сети, кроме STONITH устройств, узел кластера может перезагрузить служба OCFS2. Если у какого-либо узла пропадёт связь с другими узлами, но он продолжит посылать heartbeat сообщения на кластерный диск, то через 30 секунд (значение sysctl kernel.panic = 30) этот узел будет перезагружен принудительно. Настройка PostgreSQL для работы под управлением Pacemaker/Corosync Для запуска PostgreSQL необходимо создать три ресурса:
  • Ресурс, который будет монтировать ФС, где расположена БД
  • Ресурс виртуального ip-адреса, по которому будут обращаться клиенты к СУБД
  • Ресурс, запускающий процессы СУБД PostgreSQL После необходимо настроить правила совместного расположения ресурсов и указать порядок запуска. В примере будет созданы ресурсы, обеспечивающие работу экземпляра СУБД. Ресурс, обеспечивающий монтирование ФС с БД Описание ресурса Pacemaker для ФС OCFS2 Ресурс fs-ocfs2 будет монтировать кластерную ФС OCFS2 в каталог /mnt/ocfs2clst на каждом узле. Монтирование будет производится по метке ФС. Том, где расположена БД, у меня имеет UUID метку ce92b1e7-30cb-4883-9a92-57c986f76acd (см. Создание ФС OCFS2). root~# mkdir -p /mnt/ocfs2clst # выполнить на каждом узле root@node1:~# crm conf crm(live/node1)configure# primitive fs-ocfs2 Filesystem \ params device="/dev/disk/by-uuid/ce92b1e7-30cb-4883-9a92-57c986f76acd" \ directory="/mnt/ocfs2clst" \ fstype=ocfs2 options="rw,relatime,commit=5,coherency=buffered" \ op start timeout=60s interval=0 \ op stop timeout=60s interval=0 \ op monitor timeout=40 interval=20 Полный список параметров монтирования можно узнать на странице https://www.kernel.org/doc/html/latest/filesystems/ocfs2.html По-умолчанию, ресурс запускается только на одном узле, но так как у нас ФС кластерная, необходимо запустить ресурс на всех узлах. Это возможно с помощью клона ресурса: crm(live/node1)configure# clone fs-clone-ocfs2 fs-ocfs2 Проверяем конфигурацию и выходим: crm(live/node1)configure# verify crm(live/node1)configure# commit crm(live/node1)configure# quit После создания ресурса, ФС должна автоматически смонтироваться на всех узлах кластера. root@node1:~# mounted.ocfs2 -f Device Stack Cluster F Nodes /dev/vdb o2cb ocfs2clst node1, node3, node2 Правила размещения ресурса на узлах Если указан параметр symmetric-cluster=false, то для запуска ресурсов необходимо указать явные правила, где ресурсы могут запускаться. Указываем, что ресурс кластерной ФС должен запускаться на всех узлах кластера с равным приоритетом 1: root~# crm conf crm(live)configure# location loc-fs-ocfs2-1 fs-clone-ocfs2 1: node1 crm(live)configure# location loc-fs-ocfs2-2 fs-clone-ocfs2 1: node2 crm(live)configure# location loc-fs-ocfs2-3 fs-clone-ocfs2 1: node3 crm(live)configure# verify crm(live)configure# commit crm(live)configure# quit Если symmetric-cluster=true (или параметр не задан), то создавать правила не обязательно. Ресурс виртуального ip-адреса Создаём ресурс ip-pgclst виртуального ip-адреса 192.168.1.10. Именно этот ip-адрес будет использовать СУБД для приёма подключений. root~# crm conf crm(live)configure# primitive ip-pgclst IPaddr \ params ip=192.168.1.10 \ op monitor interval=10s Если в атрибутах кластера Pacemaker указан параметр symmetric-cluster=false, то аналогично ресурсу файловой системы создаём правила размещения. Ресурс ip-адреса будет располагаться совместно с СУБД. Если производительность узлов отличается, то можно указать разные приоритеты для запуска. Предположим, что node1 мощнее, чем node2, а node3 вообще исключим для работы СУБД: root~# crm conf crm(live)configure# location loc-ip-pgclst-1 ip-pgclst 100: node1 crm(live)configure# location loc-ip-pgclst-2 ip-pgclst 10: node2 crm(live)configure# location loc-ip-pgclst-3 ip-pgclst -inf: node3 crm(live)configure# verify crm(live)configure# commit crm(live)configure# quit Если symmetric-cluster=true (или параметр не задан), то создавать правила не обязательно. У меня в примере правила размещения не используются. Ресурс Pacemaker, запускающий процессы СУБД PostgreSQL Инициализация файлов кластера БД PostgreSQL Изменяем владельца и права доступа на каталог с БД: root# chown -R postgres:postgres /mnt/ocfs2clst root# chmod 750 /mnt/ocfs2clst Инициализируем файлы кластера БД PostgreSQL в каталоге /mnt/ocfs2clst/pg-data с включением контроля чётности страниц БД, а после запускаем СУБД: postgres@node1:~$ /usr/lib/postgresql/13/bin/initdb -D /mnt/ocfs2clst/pg-data/ -A peer -k postgres@node1:~$ /usr/lib/postgresql/13/bin/pg_ctl -D /mnt/ocfs2clst/pg-data/ start Настройка конфигурации PostgreSQL Подключаемся к СУБД через unix-сокет: root@node1:~# su - postgres postgres@node1:~$ psql Изменяем параметры БД для возможности работы под управлением Pacemaker: psql> alter system set logging_collector=on; psql> alter system lc_messages = 'C.UTF-8'; psql> alter system set listen_addresses='192.168.1.10'; Здесь я включил сборщик сообщений (logging collector), поменял язык сообщений на английский (при работе по Pacemaker, русские сообщения заменялись вопросами) и указал, что СУБД должна принимать соединения только на кластерном ip-адресе. Если потребуется в кластере Pacemaker запустить несколько экземпляров Postgresql, то необходимо разместить unix-сокет СУБД по отдельным каталогам, так как по-умолчанию все экземпляры будут создавать сокет в каталоге /tmp. psql> alter system set unix_socket_directories = '/mnt/ocfs2clst/pg-data'; Например, если бы у нас было две БД: СУБД 1, кластерный ip 192.168.1.21, каталог /mnt/ocfs2clst/pg-db1 СУБД 2, кластерный ip 192.168.1.22, каталог /mnt/ocfs2clst/pg-db2 то unix_socket_directories необходимо задать: psql db1> alter system set unix_socket_directories = '/mnt/ocfs2clst/pg-db1'; psql db2> alter system set unix_socket_directories = '/mnt/ocfs2clst/pg-db2; В дальнейшем для подключения через unix-сокет необходимо указать путь к нему (команду необходимо выполнять на том узле, где работает СУБД): postgres@node1:~$ psql -h /mnt/ocfs2clst/pg-data/ Редактируем файл /mnt/ocfs2clst/pg-data/pg_hba.conf, разрешаем подключение по сети с паролем: ... # IPv4 network connections host all all all md5 ... После внесения настроек, останавливаем СУБД: postgres@node1:~$ /usr/lib/postgresql/13/bin/pg_ctl -D /mnt/ocfs2clst/pg-data/ stop Создание ресурса СУБД PostgreSQL root~# crm configure crm(live/node1)configure# primitive db-pgclst pgsql \ params pgctl="/usr/lib/postgresql/13/bin/pg_ctl" \ psql="/usr/lib/postgresql/13/bin/psql" \ pgdba=postgres \ pglibs="/usr/lib/postgresql/13/lib" \ pgdata="/mnt/ocfs2clst/pg-data" \ socketdir="/mnt/ocfs2clst/pg-data" \ config="/mnt/ocfs2clst/pg-data/postgresql.conf" \ op start timeout=120s interval=0 \ op stop timeout=120s interval=0 \ op monitor timeout=30 interval=30 Значения параметра socketdir в описании ресурса Pacemaker должно совпадать с параметром unix_socket_directories в файле конфигурации PostgreSQL postgresql.conf/postgresql.auto.conf. Правила размещения ресурса на узлах Аналогично ресурсу ip-адреса, если в атрибутах указан параметр symmetric-cluster=false, то создаём правила размещения ресура: root~# crm conf crm(live)configure# location loc-db-pgclst-1 db-pgclst 100: node1 crm(live)configure# location loc-db-pgclst-2 db-pgclst 10: node2 crm(live)configure# location loc-db-pgclst-3 db-pgclst -inf: node3 crm(live)configure# verify crm(live)configure# commit crm(live)configure# quit Если symmetric-cluster=true (или параметр не задан), то создавать правила не обязательно, тогда СУБД сможет запускаться на любом из узлов, при условии, что ресурсу ip-адреса так же разрешен запуск на всех узлах. У меня в примере правила размещения не используются. Правила, описывающие совместное расположение ресурсов Необходимо, чтобы виртуальный ip-адрес и экземпляра СУБД PostgreSQL располагались на одном узле, иначе СУБД не запустится. crm conf colocation <имя правила> <приоритет>: <ресурс1> <ресурс2> Для этого создаём соответствующее правило: root~# crm conf crm(live/node1)configure# colocation col-ip-pgsql inf: ip-pgclst db-pgclst crm(live/node1)configure# verify crm(live/node1)configure# commit crm(live/node1)configure# quit Правила, описывающие порядок запуска ресурсов Необходимо, чтобы ресурс виртуального ip-адреса и ресурс, монтирующий кластерную ФС OCFS2, запускались раньше ресурса СУБД PostgreSQL. crm conf order <имя правила> <приоритет>: <ресурс1> <ресурс2> Для этого создаём соответсвующие правила: root~# crm conf crm(live/node1)configure# order ord-fs-pgsql Mandatory: fs-clone-ocfs2 db-pgclst crm(live/node1)configure# order ord-ip-pgsql Mandatory: ip-pgclst db-pgclst crm(live/node1)configure# verify crm(live/node1)configure# commit crm(live/node1)configure# quit Проверка работы После настройки ресурсов проверяем, что все они запущены с помощью команды crm_mon: root@node1:~# crm_mon -nr1 Cluster Summary: * Stack: corosync * Current DC: node3 (version 2.0.5-ba59be7122) - partition with quorum * Last updated: Sat Jul 16 18:04:00 2022 * Last change: Sat Jul 16 10:52:30 2022 by root via cibadmin on node2 * 3 nodes configured * 8 resource instances configured Node List: * Node node1: online: * Resources: * fs-ocfs2 (ocf::heartbeat:Filesystem): Started * sh-dummy (stonith:null): Started * Node node2: online: * Resources: * sh-dummy (stonith:null): Started * fs-ocfs2 (ocf::heartbeat:Filesystem): Started * db-pgclst (ocf::heartbeat:pgsql): Started * ip-pgclst (ocf::heartbeat:IPaddr): Started * Node node3: online: * Resources: * sh-dummy (stonith:null): Started * fs-ocfs2 (ocf::heartbeat:Filesystem): Started Inactive Resources: * No inactive resources Из вывода видно, что СУБД запущена на узле node2. Подключимся к нему через ssh и создадим пользователя в Postgresql: user@pc:~$ ssh user@192.168.1.12 user@node2:~$ sudo su - postgres postgres@node2:~$ psql -h /mnt/ocfs2clst/pg-data/ postgres=# create role pguser login encrypted password 'пароль'; postgres=# \q Проверяем подключение к СУБД с клиента: user@pc:~$ psql -h 192.168.1.10 -U pguser postgres postgres=> select count(*) from pg_settings; count ------- 308 (1 строка) С любого узла кластера перемещаем СУБД на другой узел: root@node1:~# crm_resource --move -r db-pgclst -H node1 или с помощью CRM Shell root@node1:~# crm resource move db-pgclst node1 Определяем где запущен ресурс: root@node1:~# crm_resource -W -r db-pgclst resource db-pgclst is running on: node1 Выполняем повторный запрос с клиента: postgres=> select count(*) from pg_settings; FATAL: terminating connection due to administrator command сервер неожиданно закрыл соединение Скорее всего сервер прекратил работу из-за сбоя до или в процессе выполнения запроса. Подключение к серверу потеряно. Попытка восстановления удачна. psql (14.1, сервер 13.7 (Debian 13.7-0+deb11u1)) postgres=> select count(*) from pg_settings; count ------- 308 (1 строка) Как видно, при перемещении ресурса все соединения с СУБД закрылись, но повторный sql-запрос выполнился успешно. Команды перемещения ресуров crm_resource --move или crm resource move на самом делее создают в базе Pacemaker CIB запись: <rsc_location id="cli-prefer-db-pgclst" rsc="db-pgclst" role="Started" node="node1" score="INFINITY"/> Эта запись указывает в дальнейшем запускать ресурс db-pgclst на узле node1. Для того чтобы вернуть возможность запуска ресурса на любом из узлов достаточно выполнить одну из команд: root# crm_resource -r db-pgclst --clear root# crm resource clear db-pgclst Команды управления кластером Pacemaker root# crm_verify -L -V - проверка конфигурации Pacemaker root# crm_mon -rf - отслеживание статуса ресурсов root# crm_resource -W -r db-pgclst - определить расположение ресурса db-pgclst в кластере root# crm node standby - приостановить работу узла root# crm node online - возобновить работу узла root# crm resource status - посмотреть список ресурсов root# crm resource move db-pgclst node2 - мигрировать ресурс ip-pgclst на узел node2 root# crm resource clear db-pgclst - убрать привязку после переноса root# crm resource stop db-pgclst - остановить ресурс db-pgclst root# crm resource start db-pgclst - запустить работу ресурса db-pgclst root# crm resource cleanup db-pgclst или # crm_resource --resource db-pgclst --cleanup - сброс количества ошибок ресурса root# crm configure delete db-pgclst - удаление ресурса root# cibadmin --query > tmp.xml - создать дамп базы Pacemaker CIB
  •  
    09.09.2022 , Автор: Slonik
    Ключи: postgresql, cluster, debian, pacemaker, corosync / Лицензия: CC-BY
    Раздел:    Корень / Программисту и web-разработчику / SQL и базы данных / PostgreSQL специфика / Оптимизация и администрирование PostgreSQL

    Обсуждение [ Линейный режим | Показать все | RSS ]
  • 1.2, Олег (??), 19:40, 12/09/2022 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Тот момент когда вместо типовых решений используют лютый костылинг, после чего имея кучу анального с... Са.
     
     
  • 2.3, vasiukoff (?), 16:19, 13/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    С удовольствием прочитаю Вашу статью как правильно делать. Когда ждать?
     
     
  • 3.4, Олег (??), 14:30, 14/09/2022 [^] [^^] [^^^] [ответить]  
  • +1 +/
    Определили бы задачу вначале чего хотите
    Если просто master-master то вам https://www.percona.com/blog/2020/06/09/multi-master-replication-solutions-for
    Если поиграться с пцмаркерами, бекапами и неработающими БД то пожалуйте инструкции из статьи, а еще совет сразу для пущего эффекта в докер-кубер это завернуть
     
     
  • 4.5, vasiukoff (?), 14:49, 14/09/2022 [^] [^^] [^^^] [ответить]  
  • +1 +/
    > Определили бы задачу вначале чего хотите
    > Если просто master-master то вам https://www.percona.com/blog/2020/06/09/multi-master-replication-solutions-for
    > Если поиграться с пцмаркерами, бекапами и неработающими БД то пожалуйте инструкции из
    > статьи, а еще совет сразу для пущего эффекта в докер-кубер это
    > завернуть

    Олег,
    Я ничего не имею против критики, статей на английском языке и конкретно Вас.
    Но мой вопрос был в том, чтобы Вы показали свою статью на русском языке, если уж критикуете.
    Я в таких случаях либо в частной беседе предлагаю свои правки автору, либо ссылаюсь на то что делал сам.

     
     
  • 5.6, Олег (??), 19:13, 14/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Из моей практики тот костылинг, что предлагает автор статьи, приводит к непонятным последствиям.

    Из моего жизненного опыта, мы живём не в 90х, чтобы придерживаться ещё старомодной этики, которая приводит к ошибкам в реализация уже серьёзных задач.

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

     
     
  • 6.7, casm (ok), 11:47, 15/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    В статье описывается не multi master, в примере выше СУБД работает только на одном узле, при сбое процессов/железа автоматически переезжает на рабочий узел. Это open source аналог oracle rac one node.
     
     
  • 7.9, Олег (??), 19:58, 15/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    И кеши посгрес в курсе этого?
     
     
  • 8.10, casm (ok), 20:15, 15/09/2022 [^] [^^] [^^^] [ответить]  
  • +2 +/
    Если упал сервер, то там никаких кешей уже нет Pacemaker запускает процесс post... текст свёрнут, показать
     
  • 8.20, Аноним (20), 18:30, 19/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Ну какие ещё кэши clog при каждом chekpoint-е в файлы сбрасывается А больше ... текст свёрнут, показать
     
  • 6.13, Онаним. (?), 11:50, 17/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Абсолютно да.
    Причём этот костылинг чреват в случаях развала сети, а не падения ноды.
     
  • 6.22, Аноним (20), 18:46, 19/09/2022 [^] [^^] [^^^] [ответить]  
  • +/

    > Я привёл стандартные механизмы мультимастера

    Да, забавно. Из перечисленного более-менее потребен только BDR, но он платный. И, опять же, его внедрение происходит поверх repmgr или всё того же писмэйкера.

     
  • 4.18, Аноним (20), 18:12, 19/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Олег, а вы сами какое угодно master-master-решение внедряли/эксплуатировали, пусть и не на Слоне, а?
     

  • 1.8, casm (ok), 11:52, 15/09/2022 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Ocfs2 лучше монтировать с опцией coherency=full - так медленнее, но надёжнее.
     
     
  • 2.15, Онаним. (?), 11:58, 17/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > Ocfs2 лучше монтировать с опцией coherency=full - так медленнее, но надёжнее.

    coherency=full так-то дефолт
    Для файлов active-standby, а особенно DBMS - всё правильно, coherency full проложит мелкую запись в лог в разы.

     
     
  • 3.16, Онаним. (?), 12:00, 17/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    (это конечно если у нас direct IO для лога подразумевается)
     

  • 1.11, Dan (??), 21:25, 15/09/2022 [ответить] [﹢﹢﹢] [ · · · ]  
  • +1 +/
    the same thing can be done using postgres streaming replication, without shared FS. there is pacemaker postgres resource, that can take care about node promotion from slave to master. I did it for 3 nodes cluster - 1 active 2 stand-by for zabbix db with timescaledb extension, DB size was close to 2Tb.
     
     
  • 2.24, Аноним (20), 18:56, 19/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > the same thing can be done using postgres streaming replication, without shared
    > FS. there is pacemaker postgres resource, that can take care about
    > node promotion from slave to master. I did it for 3
    > nodes cluster - 1 active 2 stand-by for zabbix db with
    > timescaledb extension, DB size was close to 2Tb.

    Streaming repl needs two time more space.

     

  • 1.12, Онаним. (?), 11:49, 17/09/2022 [ответить] [﹢﹢﹢] [ · · · ]  
  • +1 +/
    Садо-мазо. По набору компонент.
    "Под нагрузкой данное решение не проверялось" - т.е. теория ради теории.
     
     
  • 2.14, Онаним. (?), 11:55, 17/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    - Коросинк развалится там, где возможны непредсказуемые делеи, т.е. решение для двух соседних стоек ДЦ
    - Pacemaker требует STONITH, а реальный STONITH ныне - это очень редкая вещь. Всякие псевдо-варианты на уровне тушения виртуалок встанут колом, как только развалится менеджовая сеть, и когда это всё соберётся взад (скорее всего с раздельным написанием первой буквы) - результат непредсказуем, т.е. опять же максимум годно для полутора стоек в одном ДЦ
    - Из реально работающего cross-DC здесь OCFS2, но её тоже надо уметь готовить - никаких коросинков с пацемакерами, внутренний кластерный стек там достаточно вылизанный, при этом общее дисковое хранилище в качестве дополнительного арбитра работает железно

    В итоге не проще ли взять MySQL с асинхронной репликацией и не извращаться?
    В худшем случае - зайдёт Galera с синхронным коммитом.

     
     
  • 3.21, Аноним (20), 18:36, 19/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > - Коросинк развалится там, где возможны непредсказуемые делеи, т.е. решение для двух
    > соседних стоек ДЦ

    Вы удивитесь, но сбой этот как раз и есть случай "непредсказуемого делея". Подобные схемы и нужны, чтобы в  случае "непредсказуемого делея" поднять новый мастер.

    > - Pacemaker требует STONITH

    Любые схемы с транзакциями требуют STONITH.

    > В итоге не проще ли взять MySQL с асинхронной репликацией и не
    > извращаться?
    > В худшем случае - зайдёт Galera с синхронным коммитом.

    Если данные не важны и модель данных уровня какого-нибудь web-проекта, где данные и время ничего не стоят, да.

     
     
  • 4.25, Онаним. (?), 22:12, 20/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    - Любые схемы с транзакциями требуют STONITH.
    Нет.

    - Если данные не важны и модель данных уровня какого-нибудь web-проекта, где данные и время ничего не стоят, да.
    Надувание щёк не предмет для обсуждения однозначно. И да, возможно тут просто незнание место быть имеет. Потому что репликация в MySQL - это не репликация в постхрюках.

     
     
  • 5.30, Аноним (30), 15:11, 21/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > - Любые схемы с транзакциями требуют STONITH.
    > Нет.

    Было бы интересно почитать что-нибудь о том, как обходиться без STONITH.


     
     
  • 6.33, Онаним. (?), 23:10, 21/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Стандартный нечётный арбитраж с самоустранением (остановкой операций, не обязательно полностью, можно до момента восстановления кворума) нодами, не имеющими кворума. Надёжнее - с дополнительным посредником арбитража, который расположен не на нодах и не на стыках между ними, при правильной конфигурации становится возможен вторичный кворум, и количество нод вполне может быть и чётным без особых последствий, главное, чтобы не было локационного сплита 1/2. Единственным существенным моментом при этом является необходимость предварительной блокировки операций записи, чтобы не дать провести запись в случае потери кворума.
     
     
  • 7.35, НамНам (?), 23:55, 21/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > Стандартный нечётный арбитраж с самоустранением (остановкой операций, не обязательно
    > полностью, можно до момента восстановления кворума) нодами, не имеющими кворума. Надёжнее
    > - с дополнительным посредником арбитража, который расположен не на нодах и
    > не на стыках между ними, при правильной конфигурации становится возможен вторичный
    > кворум, и количество нод вполне может быть и чётным без особых
    > последствий, главное, чтобы не было локационного сплита 1/2. Единственным существенным
    > моментом при этом является необходимость предварительной блокировки операций записи,
    > чтобы не дать провести запись в случае потери кворума.

    Ну а зачем? В чём профит? Относительно простого пристрелить. "Нечёткий арбитраж" -- простите, ржал. Вы на "нечёткий арбитраж" в типично сложных случаях потратите астрономически больше времени, чем просто всё вырубить. И, снова, возращаемся к адекватности угадывания. А угадывание -- всегда угадывание. Но одно дело угадывать как лучше что-то сделать, не теряя ничего, кроме времени, чем угадывать: потерять даные... или не потерять. Причём, ладно бы был выбор, -- потерять немножко данных, но выиграть во времени исполнения -- но нет же -- вы предлагает потерять неизвестно сколько времени взамен на потерять... неизвестно сколько данных )))) Хреновый выбор.

     
     
  • 8.41, Онаним. (?), 00:17, 23/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Эээээ, а можно как-то по буквам читать что-ли Нечётный, а не нечёткий 2N 1... текст свёрнут, показать
     
     
  • 9.43, Аноним (30), 11:15, 23/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Ааа Ну бывает, слепой уже Так работают патрони и repmgr Они оркеструют репли... текст свёрнут, показать
     
  • 5.31, Аноним (30), 15:13, 21/09/2022 [^] [^^] [^^^] [ответить]  
  • +1 +/
    > Потому что репликация в MySQL - это не репликация в постхрюках.

    А что не так с репликацией в Слоне?

     
     
  • 6.32, Онаним. (?), 22:46, 21/09/2022 [^] [^^] [^^^] [ответить]  
  • –1 +/
    Можно односложно?

    Всё.

     
     
  • 7.36, Аноним (30), 10:44, 22/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    А при чём тут репликация? Описанное решение как раз без репликации. Репликация подразумевает, что у вас есть н-узлов, данные на которых идентичны (строго или "в конце концов"). Т.е. каждый узел с экземпляром обладает своей репликой данных, с которой и работает. Тут же, как я понял, узлы с экземпляром используют одни и те же данные, доступ к которым разделён по времени.
     
     
  • 8.38, Онаним. (?), 00:15, 23/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Вопрос был про репликацию, безотносительно ... текст свёрнут, показать
     
  • 6.34, НамНам (?), 23:46, 21/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    А как-то более предметно? В Слоне репликация работает. Она надёжно наблюдаема. И крайне легка в настройке. Что из этого вы способны аргументировано оспорить?
     
     
  • 7.39, Онаним. (?), 00:16, 23/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    И даже в изменения схемы уже умеет?
     
     
  • 8.42, Аноним (30), 11:09, 23/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Не понял вас, не понял, что значит в изменение схемы Понятие схемы очень уж м... текст свёрнут, показать
     
  • 2.17, Онаним. (?), 12:02, 17/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Ну и да, поскольку у нас тут простой active-standby - можно тупо обойтись keepalived и монтированием разделяемого раздела на той ноде, которая должна живой стать, даже с OCFS2 не извращаясь.
     
  • 2.19, Аноним (20), 18:17, 19/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > Садо-мазо. По набору компонент.
    > "Под нагрузкой данное решение не проверялось" - т.е. теория ради теории.

    Набор компонентов один из наиболее тиражных. Большая часть отказоустойчивых кластеров со Слоном реализованы либо на стеке коросинка с писмэйкером, либо на repmrg. Чуть меньше на патрони. При этом именно коросинк с писмэйкером позволяют реализовать сколь угодно сложные и изощрённые решения. Если не стоит задача делать master-master.

     
     
  • 3.26, Онаним. (?), 22:15, 20/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > Большая часть отказоустойчивых кластеров со Слоном реализованы либо на стеке коросинка с писмэйкером, либо

    Вот именно поэтому я ко всему этому счастью (постхрюку) близко не подхожу.
    Сколь угодно сложные и изощрённые решения позволяет MySQL и его вариации. ВПЛОТЬ до master-master.
    Без всяких коросинков. Или с ними, если хочется извращаться.

    Что же до задачи - на коросинках кросс-дц с парой ms делеев уже проблема.


     
     
  • 4.29, Аноним (30), 15:07, 21/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Про MySQL ничего сказать не могу. Мой опыт в пользу того, чтобы MySQL не применять ни для чего сложнее регистратора событий.
    И нет, для MySQL нет и близко решений master-master такого уровня, которые есть для Слона.
     

  • 1.23, Аноним (20), 18:49, 19/09/2022 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Похоже на одноузловой РЭК. Общий кластер БД. Экземпляры монтируют его в разное время. Вполне жизненно.
     
     
  • 2.27, Онаним. (?), 22:17, 20/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Не совсем понятно на фига в этой схеме коросинк и прочее.
    Обычного keepalived и ocfs2 с арбитрацией на разделяемой хранилке будет более, чем достаточно.
     
     
  • 3.28, Онаним. (?), 22:18, 20/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    (причём с арбитрацией на хранилке даже в конфигурации с двумя нодами будет работать, если нужен здоровый минимализм)
     
  • 3.37, Аноним (37), 14:48, 22/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Писмэйкер и коросинком позволяют сделать стэк отказа сколь угодно глубоким. Можно в этот пирог запихнуть реакцию на что угодно, хоть на фазу луны, если это нужно. Другое дело, нужно ли. Но другой вопрос.
     
     
  • 4.40, Онаним. (?), 00:16, 23/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    Писмэйкер и коросинком позволяют сделать проникновение граблей сколь угодно глубоким

    Fixed.

     
     
  • 5.44, Аноним (30), 11:19, 23/09/2022 [^] [^^] [^^^] [ответить]  
  • +/
    > Писмэйкер и коросинком позволяют сделать проникновение граблей сколь угодно глубоким

    Ну и это тоже. Это тёмная сторона гибкости и функциональности.

     

  • 1.45, none (??), 13:45, 10/11/2022 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    Есть же patroni, его проще поднять и это решение работает, проверено:
    https://github.com/zalando/patroni
     
  • 1.46, Легивон (?), 10:57, 15/03/2023 [ответить] [﹢﹢﹢] [ · · · ]  
  • +/
    А какой смысл в кластерной файловой системе? Сэкономить 50% места и в результате получить рост латенси (читай IOPS) в 100 раз из-за сети? Или сколько у вас получилось если учитывать что базовый сервер с SSD.
    В случае с обычной master-slave репликацией и тулзой сбоку переключающей мастера (patroni, stolon, repmgr и т.д.) с реплик еще и читать можно (большинству типовых запросов типовых приложений не важна консистентность) получая производительность х3.
    Если суммировать все потери то статья про то как на ровном месте сделать постгрес в 10 раз медленнее, а конфигурацию запутанее.
     


     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру