Přeskočit obsah

Replikace databáze

Popis systému

Tento dokument popisuje konfiguraci replikace databáze PMC. Data v záložní databázi se replikují pomocí physical streaming replication. Konfigurace samotné replikace se nedělá manuálně. Zajištuje ji aplikace patroni. Celý systém je navržen tak, aby v případě nedostupnosti hlavní databáze automaticky došlo k přepnutí na záložní. Aplikace PMC se do databáze připojuje přes HAProxy, které kontroluje stav obou databází a přesměrovává připojení na databázi, která je v danou chvíli leader. Patroni pro svůj běh potřebuje key-value store (ETCD), ten běží ve třech instancích. Databáze bude dostupná při výpadku kteréhokoli z připojených serverů. V případě výpadku hlavního serveru je potřeba manuálně spustit záložní instanci PMC a na routeru přesměrovat připojení na záložní server. V základním stavu (kdy je hlavní databáze dostupná a na záložní se replikují data) se lze na záložní databázi připojit v read-only módu - toho by se dalo využít k vytváření záloh, reportingu atp.

V konfiguračních souborech a v popisu instalace se objevují návy a adresy tří serverů. Tyto adresy bude třeba změnit podle prostředí, kam se bude systém instalovat (stejně jako hesla atp...). Adresy v příkladech níže jsou následující:

  • PMC01: 10.120.1.2
  • PMC02: 10.120.1.3
  • ETCD03: 10.120.0.10
  • Virtuální adresa: 10.120.1.4

Popis jednotlivých částí

Haproxy - Pravidelně posílá dotazy na REST api Patroni (obě instance) a přesměrovává požadavky na ten server, který je aktuálně leader. Veškerá připojení do databáze budou vedena touto cestou.

Patroni - Zajišťuje běh databáze, stará se o to, aby byl vždy právě jeden leader a v případě výpadku provede failover. Vystavuje REST api, pomocí kterého se lze dozvědět jaká instance databáze je v danou chvíli leader.

ETCD - key-value store, který slouží k ukládání provozních dat Patroni. Aby celý systém fungoval je třeba mít tři instance. V jeden moment může být nedostupná maximálně jedna instance.

PMC - jelikož PMC nedovoluje běh ve více instancích, může být v jednu chvíli spuštěna pouze jedna instance. Pokud budeme druhou instanci udržovat a pravidelně aktualizovat, významně tím zkrátíme dobu, kdy je systém po výpadku hlavního serveru v nefunkčním stavu.

Keepalived - Virtuální ip adresa. Přednost má primární server. Při jeho výpadku je přesunuta na záložní. PMC bude nastaveno tak, aby komunikovalo právě s touto adresou. Keepalived samo o sobě není doporučeno používat na nedůvěryhodných sítích (mezi které patří privátní síť v Hetzneru). Proto bude pro komunikaci mezi servery použita VPN (wireguard).

schéma architektury systému

---
title: Architektura systému
---
block-beta
    columns 3

    srv1>"PMC01 10.120.1.2"]
    block:a:2
        columns 8
        pmc_main("PMC MAIN")
        space:5
        db_main[("DB MAIN")]
        space:3
        haproxy1("HAProxy")
        space:5
        etcd1("ETCD")
        space:3
        patroni_main("Patroni")
        space:2
        keepalived1("Keepalived")
  end
  srv2>"ETCD03 10.120.0.10"]

    block:b:1
        columns 3
          space:7
          etcd3("ETCD"):2
          space:4
  end
space:1

    srv3>"PMC02 10.120.1.3"]
    block:c:2
        columns 8
        etcd2("ETCD")
        space:3
        patroni_backup("Patroni")
        space:2
        keepalived2("Keepalived")
        space:2
        haproxy2("HAProxy") 
        space:5
        pmc_backup("PMC BKP")
        space:5
        db_backup[("DB Backup")]
  end

    patroni_main <--> db_main
    patroni_main <--> etcd3
    patroni_backup <--> db_backup
    patroni_backup <--> etcd3
    etcd1 <--> etcd2
    etcd1 <--> etcd3
    etcd2 <--> etcd1
    etcd2 <--> etcd3
    etcd3 <--> etcd1
    etcd3 <--> etcd2
    haproxy1 <--> patroni_main
    haproxy1 <--> patroni_backup
    haproxy1 <--> db_main
    haproxy1 <--> db_backup
    haproxy2 <--> patroni_main
    haproxy2 <--> patroni_backup
    haproxy2 <--> db_backup
    haproxy2 <--> db_main
    pmc_main <--> haproxy1
    pmc_backup <--> haproxy2
    db_backup --> db_main
    keepalived1 <--> keepalived2
    keepalived2 <--> keepalived1


    style etcd1 fill:#0088FF, stroke:#333333;
    style etcd2 fill:#0088FF, stroke:#333333;
    style etcd3 fill:#0088FF, stroke:#333333;
    style pmc_main fill:#55BB55, stroke:#333333;
    style pmc_backup fill:#55BB5533,stroke:#333333;
    style haproxy1 fill:#4444EE77, stroke:#333333;
    style haproxy2 fill:#4444EE77, stroke:#333333;
    style patroni_main fill:#8844EE, stroke:#333333;
    style patroni_backup fill:#8844EE, stroke:#333333;
    style db_main fill:#FF3333, stroke:#333333;
    style db_backup fill:#FF3333, stroke:#333333;
    style a fill:#88442235, stroke:#333333, stroke-width: 3px, stroke-dasharray: 10 10;
    style b fill:#88442235, stroke:#333333, stroke-width: 3px, stroke-dasharray: 10 10;
    style c fill:#88442235, stroke:#333333, stroke-width: 3px, stroke-dasharray: 10 10;
    style srv1 fill:#88442235, stroke:#333333;
    style srv2 fill:#88442235, stroke:#333333;
    style srv3 fill:#88442235, stroke:#333333;

Instalace a konfigurace

Certifikáty

Veškerá komunikace mezi servery je šifrovaná. Níže seznam certifikátů, které jsou pro nastavení potřeba. Názvy souborů se objevují níže v textu.

Soubory Typ certifikátu Popis
etcd.pem, etcd.key Serverový certifikát Certifikát pro ETCD. Všechny instance mají stejný.
patroni.pem, patroni.key Serverový certifikát Certifikát pro Patroni REST API, Využit také pro SSL připojení do databáze.
haproxy.pem Klientský certifikát Certifikát pro ověření HAProxy při dotazech na REST API Patroni. Soubor obsahuje certifikát i privátní klíč.
pmc2t-ca.cert.pem Certifikát CA Certifikát certifikační autority, která vydala všechny zmíněné certifikáty. Slouží k ověření vydavatele certifikátů.

Připojení do databáze z aplikace PMC

V případě výpadku hlavní databáze se bude PMC připojovat do databáze přes síť. Proto je třeba v konfiguraci zapnout parametr ssl (implicitně je vypnutý). Doporučeno je také nastavit parametr sslmode minimálně na hodnotu verify-ca. Všechny možné parametry driveru je možné najít zde. Parametry související s ssl jsou blíže popsány zde. URL pro PMC může vypadat např. takto:

spring.datasource.jdbcUrl=jdbc:postgresql://10.120.1.4:5433/pmc?ssl=true&sslmode=verify-ca&sslrootcert=/opt/pmc/etl/pmc2t-ca.cert.pem
kde 10.120.1.4:5433 je třeba vyměnit za ip a port připojení do HAProxy a /opt/pmc/etl/pmc2t-ca.cert.pem za cestu k certifikátu CA. To stejné platí také pro připojení datové pumpy.

Vytvoření certifikátů

Certifikáty se vytváří standardním způsobem. Příklad certifikátu pro Patroni

openssl req \
    -new \
    -subj "/C=CZ/ST=Czechia/O=Green Center/CN=Green Center PMC TEST PATRONI cert" \ #  (1)!
    -addext "subjectAltName=IP:127.0.0.1,IP:10.120.1.2,IP:10.120.1.3,IP:10.120.1.4" \
    -newkey rsa:4096 \
    -keyout ./newkey.pem \
    -out ./newreq.pem

openssl ca \
    -config ./pmc2t-ca.cnf \
    -policy policy \
    -out ./newcert.pem  \
    -extensions server_ext \
    -infiles newreq.pem

mkdir patroni
cp ./newcert.pem ./patroni/patroni.pem
cp ./newreq.pem ./patroni/patroni.req.pem
openssl rsa -in newkey.pem -out ./patroni/patroni.key
  1. CN

    Uživatel v databázi se musí jmenovat podle CN certifikátu.
openssl req \
    -new \
    -subj "/C=CZ/ST=Czechia/O=Green Center/CN=Green Center PMC TEST haproxy client" \
    -addext "subjectAltName=IP:127.0.0.1,IP:10.120.1.2,IP:10.120.1.3,IP:10.120.1.4" \
    -newkey rsa:4096 \
    -keyout ./newkey.pem \
    -out ./newreq.pem

openssl ca \
    -config ./pmc2t-ca.cnf \
    -policy policy \
    -out ./newcert.pem  \
    -extensions client_ext \
    -infiles newreq.pem

mkdir haproxy
cp ./newcert.pem ./haproxy/haproxy.pem
cp ./newreq.pem ./haproxy/haproxy.req.pem
openssl rsa -in newkey.pem -out ./haproxy/haproxy.key

Instalace potřebných balíků

sudo apt-get install patroni etcd etcd-server haproxy keepalived wireguard wireguard-tools
sudo apt-get install etcd etcd-server

Konfigurace a spuštění etcd

Systemd service
/etc/systemd/system/etcd.service
[Unit]
Description=etcd key-value store
Documentation=https://github.com/etcd-io/etcd
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
WorkingDirectory=/var/lib/etcd
EnvironmentFile=/etc/etcd/etcd.env
ExecStart=/usr/bin/etcd
Restart=always
RestartSec=10s
LimitNOFILE=40000
User=etcd
Group=etcd

[Install]
WantedBy=multi-user.target
Soubor .env

Jako konfigurační soubor nám poslouží .env file, na který se odkazujeme výše v systemd service souboru (EnvironmentFile).

/etc/etcd/etcd.env
ETCD_NAME="postgres-patroni-etcd-01"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_INITIAL_CLUSTER="postgres-patroni-etcd-01=https://10.120.1.2:2380,postgres-patroni-etcd-02=https://10.120.1.3:2380,postgres-patroni-etcd-03=https://10.120.0.10:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.120.1.2:2380"
ETCD_LISTEN_PEER_URLS="https://10.120.1.2:2380,https://127.0.0.1:2380"
ETCD_LISTEN_CLIENT_URLS="https://10.120.1.2:2379,https://127.0.0.1:2379"
ETCD_ADVERTISE_CLIENT_URLS="https://10.120.1.2:2379"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_TRUSTED_CA_FILE="/etc/etcd/pmc2t-ca.cert.pem"
ETCD_CERT_FILE="/etc/etcd/etcd.pem"
ETCD_KEY_FILE="/etc/etcd/etcd.key"
ETCD_PEER_CLIENT_CERT_AUTH="true"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/pmc2t-ca.cert.pem"
ETCD_PEER_CERT_FILE="/etc/etcd/etcd.pem"
ETCD_PEER_KEY_FILE="/etc/etcd/etcd.key"
ETCDCTL_API=3
/etc/etcd/etcd.env
ETCD_NAME="postgres-patroni-etcd-02"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_INITIAL_CLUSTER="postgres-patroni-etcd-01=https://10.120.1.2:2380,postgres-patroni-etcd-02=https://10.120.1.3:2380,postgres-patroni-etcd-03=https://10.120.0.10:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.120.1.3:2380"
ETCD_LISTEN_PEER_URLS="https://10.120.1.3:2380,https://127.0.0.1:2380"
ETCD_LISTEN_CLIENT_URLS="https://10.120.1.3:2379,https://127.0.0.1:2379"
ETCD_ADVERTISE_CLIENT_URLS="https://10.120.1.3:2379"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_TRUSTED_CA_FILE="/etc/etcd/pmc2t-ca.cert.pem"
ETCD_CERT_FILE="/etc/etcd/etcd.pem"
ETCD_KEY_FILE="/etc/etcd/etcd.key"
ETCD_PEER_CLIENT_CERT_AUTH="true"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/pmc2t-ca.cert.pem"
ETCD_PEER_CERT_FILE="/etc/etcd/etcd.pem"
ETCD_PEER_KEY_FILE="/etc/etcd/etcd.key"
ETCDCTL_API=3
/etc/etcd/etcd.env
ETCD_NAME="postgres-patroni-etcd-03"
ETCD_DATA_DIR="/var/lib/etcd"
ETCD_INITIAL_CLUSTER="postgres-patroni-etcd-01=https://10.120.1.2:2380,postgres-patroni-etcd-02=https://10.120.1.3:2380,postgres-patroni-etcd-03=https://10.120.0.10:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://10.120.0.10:2380"
ETCD_LISTEN_PEER_URLS="https://10.120.0.10:2380,https://127.0.0.1:2380"
ETCD_LISTEN_CLIENT_URLS="https://10.120.0.10:2379,https://127.0.0.1:2379"
ETCD_ADVERTISE_CLIENT_URLS="https://10.120.0.10:2379"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_TRUSTED_CA_FILE="/etc/etcd/pmc2t-ca.cert.pem"
ETCD_CERT_FILE="/etc/etcd/etcd.pem"
ETCD_KEY_FILE="/etc/etcd/etcd.key"
ETCD_PEER_CLIENT_CERT_AUTH="true"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/pmc2t-ca.cert.pem"
ETCD_PEER_CERT_FILE="/etc/etcd/etcd.pem"
ETCD_PEER_KEY_FILE="/etc/etcd/etcd.key"
ETCDCTL_API=3
Spuštění
sudo systemctl enable --now etcd

dokumentace k etcd

Informace k provozování etcd lze dohledat v oficiální dokumentaci. K vyřešení problémů s šifrováním mi také posloužil tento dokument.

Poznámka k serverům

Další kroky už provádíme výhradne na serverech PMC01 a PMC02. Server ETCD03 slouží pouze jako node v ETCD clusteru. Neběží na něm HAProxy, Postgres, Patroni, ani Keepalived.

Konfigurace a spuštění HAProxy

Je třeba využít funkce HAProxy, které nejsou dostupné ve verzi distribuované na Ubuntu 22.04, proto spustíme HAProxy v dockeru.

Docker-compose
/opt/haproxy/docker-compose.yml
version: '3.9'

services:
  haproxy:
    image: haproxy:lts-alpine3.22
    restart: always
    environment:
      - TZ=Europe/Prague
    ports:
      - '7000:7000'
      - '5433:5433'
    volumes:
     - ./haproxy:/usr/local/etc/haproxy
Konfigurace haproxy
/opt/haproxy/haproxy/haproxy.cfg
global
    maxconn 100
    log stdout local0  debug

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 boorvar_cluster
    bind *:5433
    option httpchk

    # (1)!
    http-check send hdr Authorization 'Basic aGFwcm94eTp2RG1GbnBiOWtjZ2pFZmJzeHh5YjVoN3ZkRGVzMmtvbnFFZmI5cTRqajNvbmN2RWQ='
    http-check expect status 200 # (2)!
    default-server inter 3s fall 2 rise 1 on-marked-down shutdown-sessions

    # (3)!
    server pgdb1_5432 10.120.1.2:5432 maxconn 100 check check-ssl port 8008 verify required ca-file /usr/local/etc/haproxy/pmc2t-ca.cert.pem crt /usr/local/etc/haproxy/haproxy.pem
    server pgdb2_5432 10.120.1.3:5432 maxconn 100 check check-ssl port 8008 verify required ca-file /usr/local/etc/haproxy/pmc2t-ca.cert.pem crt /usr/local/etc/haproxy/haproxy.pem
  1. Authorization header vytvoříme následujícím způsobem:
    echo -n "username:password" | base64
  2. Patroni se chová tak, že když je daná instance leader, tak vrátí status 200 (OK). V opačném případě vrátí 503 (Service Unavailable). Podle toho HAProxy pozná na který server má přesměrovávat připojení.
    • server <name> <ip:port>
      • Název serveru (pouze pro informaci) a připojení k postgresql databázi
    • port <port>
      • Port patroni REST API
    • ca-file <filename>
    • crt <filename>

        Oba tyto certifikáty se uloží do stejného adresáře jako konfigurace HAProxy.

Spuštění kontejneru
cd /opt/haproxy
sudo docker-compose up -d

Konfigurace a spuštění keepalived

Nastavení VPN
Vytvoření privátního a veřejného klíče
umask 077
wg genkey | tee privatekey | wg pubkey > publickey

# Soubor privatekey obsahuje privátní klíč. Ten se do lokální konfigurace (viz níže) zadá jako PrivateKey.
# Soubor publickey obsahuje veřejný klíč. Ten se do vloží do konfigurace protější strany jako PublicKey.
# Po nastavení VPN je dobré tyto soubory smazat.
Vytvoření konfiguračního souboru pro wireguard
/etc/wireguard/wg1.conf
[Interface]
ListenPort = 51829
PrivateKey = <privatekey> # (1)!
Address = 10.30.0.1/24

PostUp = systemctl start keepalived
PostDown = systemctl stop keepalived

[Peer]
PublicKey = <publickey> # (2)!
AllowedIPs = 10.30.0.0/24
Endpoint = 10.120.1.3:51829
PersistentKeepalive = 25
  1. Nahradit obsahem souboru privatekey
  2. Nahradit obsahem souboru publickey protější strany
/etc/wireguard/wg1.conf
[Interface]
ListenPort = 51829
PrivateKey = <privatekey> # (1)!
Address = 10.30.0.2/24

PostUp = systemctl start keepalived
PostDown = systemctl stop keepalived

[Peer]
PublicKey = <publickey> # (2)!
AllowedIPs = 10.30.0.0/24
Endpoint = 10.120.1.2:51829
PersistentKeepalive = 25
  1. Nahradit obsahem souboru privatekey
  2. Nahradit obsahem souboru publickey protější strany
Konfigurace keepalived

V souboru je třeba na základě prostředí upravit několik věcí.

  • auth_pass - Heslo pro přenos dat - maximálně 8 znaků. Toto je jeden z důvodů proč jako dodatečné zabezpečení přenášíme data přes VPN.
  • interface - Zadat podle VPN interface. Pokud budete postupovat podle návodu výše, wg1 by mělo být v pořádku.
  • virtual_ipaddress - V této sekci je třeba změnit samotnou adresu a také interface, na kterém bude adresa nastavena.
  • unicast_peer, unicast_src_ip - Nastavit podle IP v rámci VPN.
/etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
        state MASTER
        interface wg1
        virtual_router_id 51
        priority 255
        advert_int 1
        authentication {
              auth_type PASS
              auth_pass eEdD39ky
        }

        unicast_src_ip 10.30.0.1

        unicast_peer {
              10.30.0.2
        }

        virtual_ipaddress {
              10.120.1.4/24 dev eno1
        }
}
/etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
        state BACKUP
        interface wg1
        virtual_router_id 51
        priority 254
        advert_int 1
        authentication {
              auth_type PASS
              auth_pass eEdD39ky
        }

        unicast_src_ip 10.30.0.2

        unicast_peer {
              10.30.0.1
        }

        virtual_ipaddress {
              10.120.1.4/24 dev enp0s3
        }
}

Jako další krok musíme nastavit závislost keepalived na VPN

/etc/systemd/system/keepalived.service.d/override.conf
[Unit]
Requires=wg-quick@wg1.service
Spuštění
sudo systemctl enable --now wg-quick@wg1.service keepalived

Konfigurace a spuštění patrioni

Konfigurace

dokumentace

Všechny konfigurační parametry jsou detailně popsány v dokumentaci.

Přednost parametrů

Existují tři způsoby jak upravit konfiguraci patroni. Níže je jejich výpis seřazený od nejnižší priority po nejvyšší (metody níže přepíší ty nahoře):

  1. Globální konfigurace
    • Tato konfigurace je uložena v DCS (Distributed Configuration Store) a je společná pro celý cluster. První zápis do ní je vytvořen ze sekce bootstrap v konfiguračním souboru (viz níže).
    • Další zápisy se provádí výhradně přes utilitu patronictl edit-config.
  2. Konfigurační soubor yml (níže).
  3. Proměnné prostředí
Příklad konfiguračního souboru
/etc/patroni/config.yml
scope: postgresql-cluster
namespace: /service/
name: postgres-patroni-etcd-01  # (18)!

etcd3:
  hosts: 10.120.1.2:2379,10.120.1.3:2379,10.120.0.10:2379  # (12)!
  protocol: https  # (13)!
  cacert: /etc/patroni/pmc2t-ca.cert.pem  # (14)!
  cert: /etc/patroni/patroni.pem  # (15)!
  key: /etc/patroni/patroni.key  # (16)!

log:
  dir: /var/log/patroni
  format: '%(asctime)s %(levelname)s: %(message)s'
  level: DEBUG
  max_queue_size: 1000
  traceback_level: ERROR
  type: plain

restapi:
  listen: 0.0.0.0:8008  # (10)!
  connect_address: 10.120.1.2:8008  # (11)!
  authentication:
    username: haproxy
    password: vDmFnpb9kcgjEfbsxxyb5h7vdDes2konqEfb9q4jj3oncvEd
  certfile: /etc/patroni/patroni.pem
  keyfile: /etc/patroni/patroni.key
  cafile: /etc/patroni/pmc2t-ca.cert.pem
  verify_client: required

ctl:  # (19)!
  authentication:
    username: haproxy
    password: vDmFnpb9kcgjEfbsxxyb5h7vdDes2konqEfb9q4jj3oncvEd
  certfile: /etc/patroni/patroni.pem
  keyfile: /etc/patroni/patroni.key
  cafile: /etc/patroni/pmc2t-ca.cert.pem

postgresql:
  listen: 0.0.0.0:5432  # (5)!
  connect_address: 10.120.1.2:5432  # (6)!
  data_dir: /var/lib/postgresql/15/main  # (4)!
  bin_dir: /usr/lib/postgresql/15/bin  # (3)!
  authentication:
    replication:
      username: Green Center PMC TEST PATRONI cert
      sslmode: prefer
      sslcert: /etc/patroni/patroni.pem
      sslkey: /etc/patroni/patroni.key
      sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
    rewind:
      username: Green Center PMC TEST PATRONI cert
      sslmode: prefer
      sslcert: /etc/patroni/patroni.pem
      sslkey: /etc/patroni/patroni.key
      sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
    superuser:
      username: Green Center PMC TEST PATRONI cert
      sslmode: prefer
      sslcert: /etc/patroni/patroni.pem
      sslkey: /etc/patroni/patroni.key
      sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
  pg_hba:  # (1)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.2/32 cert # (8)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.3/32 cert # (9)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.4/32 cert # (20)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 127.0.0.1/32 cert # (7)!
    - host all all 127.0.0.1/32 md5
    - host all all 0.0.0.0/0 md5
    - local all all trust
  use_pg_rewind: true
  parameters:
    ssl: 'on'
    ssl_cert_file: /etc/patroni/patroni.pem
    ssl_key_file: /etc/patroni/patroni.key
    ssl_ca_file: /etc/patroni/pmc2t-ca.cert.pem
    archive_mode: always
    archive_command: 'test ! -f /var/lib/postgresql/15/wal_archive/%f && cp %p /var/lib/postgresql/15/wal_archive/%f'
    logging_collector: on
    log_destination: 'stderr'
    log_truncate_on_rotation: on
    log_checkpoints: on
    log_connections: on
    log_disconnections: on
    log_error_verbosity: default
    log_lock_waits: on
    log_temp_files: 0
    log_autovacuum_min_duration: 0
    log_min_duration_statement: 50
    log_timezone: 'Europe/Prague'
    log_filename: postgresql-%Y-%m-%d_%H.log
    log_line_prefix: '%t [%p]: db=%d,user=%u,app=%a,client=%h '
    unix_socket_permissions: 0700 # (21)!

bootstrap:  # (17)!
  initdb:
    - encoding: UTF8
    - data-checksums
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576  # Failover parameters
    postgresql:
      pg_hba:  # (2)!
        - hostssl replication "Green Center PMC TEST PATRONI cert" 10.120.1.2/32 cert
        - hostssl replication "Green Center PMC TEST PATRONI cert" 10.120.1.3/32 cert
        - hostssl replication "Green Center PMC TEST PATRONI cert" 10.120.1.4/32 cert
        - hostssl replication "Green Center PMC TEST PATRONI cert" 127.0.0.1/32 cert
        - host all all 127.0.0.1/32 md5
        - host all all 0.0.0.0/0 md5
        - local all all trust
      use_slots: true
      use_pg_rewind: true
      parameters:
        hot_standby: 'on'
        max_connections: 1000
        max_locks_per_transaction: 64
        max_worker_processes: 8
        max_prepared_transactions: 0
        wal_level: replica
        track_commit_timestamp: 'off'
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_segments: 8
        wal_keep_size: 1024MB
        wal_log_hints: 'on'
        shared_buffers: 1024MB
        ssl: 'on'
        ssl_cert_file: /etc/patroni/patroni.pem
        ssl_key_file: /etc/patroni/patroni.key
        ssl_ca_file: /etc/patroni/pmc2t-ca.cert.pem
        logging_collector: on
        log_destination: 'stderr'
        log_truncate_on_rotation: on
        log_checkpoints: on
        log_connections: on
        log_disconnections: on
        log_error_verbosity: default
        log_lock_waits: on
        log_temp_files: 0
        log_autovacuum_min_duration: 0
        log_min_duration_statement: 50
        log_timezone: 'Europe/Prague'
        log_filename: postgresql-%Y-%m-%d_%H.log
        log_line_prefix: '%t [%p]: db=%d,user=%u,app=%a,client=%h '
        unix_socket_permissions: 0700

      authentication:
        replication:
          username: Green Center PMC TEST PATRONI cert
          sslmode: prefer
          sslcert: /etc/patroni/patroni.pem
          sslkey: /etc/patroni/patroni.key
          sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
        rewind:
          username: Green Center PMC TEST PATRONI cert
          sslmode: prefer
          sslcert: /etc/patroni/patroni.pem
          sslkey: /etc/patroni/patroni.key
          sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
        superuser:
          username: Green Center PMC TEST PATRONI cert
          sslmode: prefer
          sslcert: /etc/patroni/patroni.pem
          sslkey: /etc/patroni/patroni.key
          sslrootcert: /etc/patroni/pmc2t-ca.cert.pem

tags:
  nofailover: false
  noloadbalance: false
  clonefrom: true
  1. Pozor, pravidla pg_hba se čtou sekvenčně a použije se první, které odpovídá databázi, uživateli a způsobu připojení (hostssl vs host). Pokud je více takových pod sebou, použije se první. Např při nastavení:
    hostssl all all 10.0.1.2/32 cert
    hostssl all all 10.0.1.2/32 md5
    nebude fungovat přihlášení heslem.
  2. Pozor, pravidla pg_hba se čtou sekvenčně a použije se první, které odpovídá databázi, uživateli a způsobu připojení (hostssl vs host). Pokud je více takových pod sebou, použije se první. Např při nastavení:
    hostssl all all 10.0.1.2/32 cert
    hostssl all all 10.0.1.2/32 md5
    nebude fungovat přihlášení heslem.
  3. Cesta k adresáři se spustitelnými soubory postgresql.
  4. Adresář, ve kterém se budou nacházet data.
  5. IP a port na kterém bude databáze dostupná. Lze zadat více adres. V takovém případě se port musí nacházet za tou poslední. Např.:
    listen: 127.0.0.1,10.0.1.2:5432
    Pokud chceme povolit připojení na všech rozhraních, nastavíme:
    listen: 0.0.0.0:5432
  6. Adresa a port, na které bude databáze dostupná ostatním nodům.
  7. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  8. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  9. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  10. Adresa a port, na které bude REST api dostupné pro ostatní nody
  11. Adresa a port, na které bude poslouchat REST api - musí být dostupná z ostatních nodů a také ze všech instancí HAProxy
  12. Všechny nody ETCD clusteru
  13. Protokol pro připojení do ETCD clusteru
  14. Certifikát CA
  15. Klientský certifikát pro připojení do ETCD clusteru
  16. Klíč ke klientskému certifikátu pro připojení do ETCD clusteru
  17. bootstrap

    Tato sekce se čte pouze při vytváření clusteru. Jakmile je cluster vytvořen, je její obsah ingorován. Není tedy třeba ji aktualizovat při změně konfiguračních parametrů. Ke změně konfiguračních parametrů pro celý cluster je určen příkaz patronictl edit-config viz výše.
  18. Název hosta. Musí být unikátní v celém clusteru.
  19. Parametry upravující chování aplikace patronictl
  20. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  21. Pouze uživatel postgres se může připojit k databázi přes unix socket - díky záznamu local all all trust v pg_hba nebude pro připojení vyžadováno heslo. Toto je primárně kvůli zálohám.
/etc/patroni/config.yml
scope: postgresql-cluster
namespace: /service/
name: postgres-patroni-etcd-02  # (18)!

etcd3:
  hosts: 10.120.1.2:2379,10.120.1.3:2379,10.120.0.10:2379  # (12)!
  protocol: https  # (13)!
  cacert: /etc/patroni/pmc2t-ca.cert.pem  # (14)!
  cert: /etc/patroni/patroni.pem  # (15)!
  key: /etc/patroni/patroni.key  # (16)!

log:
  dir: /var/log/patroni
  format: '%(asctime)s %(levelname)s: %(message)s'
  level: DEBUG
  max_queue_size: 1000
  traceback_level: ERROR
  type: plain

restapi:
  listen: 0.0.0.0:8008  # (10)!
  connect_address: 10.120.1.3:8008  # (11)!
  authentication:
    username: haproxy
    password: vDmFnpb9kcgjEfbsxxyb5h7vdDes2konqEfb9q4jj3oncvEd
  certfile: /etc/patroni/patroni.pem
  keyfile: /etc/patroni/patroni.key
  cafile: /etc/patroni/pmc2t-ca.cert.pem
  verify_client: required

ctl:  # (19)!
  authentication:
    username: haproxy
    password: vDmFnpb9kcgjEfbsxxyb5h7vdDes2konqEfb9q4jj3oncvEd
  certfile: /etc/patroni/patroni.pem
  keyfile: /etc/patroni/patroni.key
  cafile: /etc/patroni/pmc2t-ca.cert.pem

postgresql:
  listen: 0.0.0.0:5432  # (5)!
  connect_address: 10.120.1.3:5432  # (6)!
  data_dir: /var/lib/postgresql/15/main  # (4)!
  bin_dir: /usr/lib/postgresql/15/bin  # (3)!
  authentication:
    replication:
      username: Green Center PMC TEST PATRONI cert
      sslmode: prefer
      sslcert: /etc/patroni/patroni.pem
      sslkey: /etc/patroni/patroni.key
      sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
    rewind:
      username: Green Center PMC TEST PATRONI cert
      sslmode: prefer
      sslcert: /etc/patroni/patroni.pem
      sslkey: /etc/patroni/patroni.key
      sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
    superuser:
      username: Green Center PMC TEST PATRONI cert
      sslmode: prefer
      sslcert: /etc/patroni/patroni.pem
      sslkey: /etc/patroni/patroni.key
      sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
  pg_hba:  # (1)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.2/32 cert # (8)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.3/32 cert # (7)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.4/32 cert # (20)!
    - hostssl all "Green Center PMC TEST PATRONI cert" 127.0.0.1/32 cert # (9)!
    - hostssl all all 0.0.0.0/0 md5
    - hostssl all all 0.0.0.0/0 cert
    - host all all 127.0.0.1/32 md5
    - host all all 0.0.0.0/0 md5
    - local all all trust
  use_pg_rewind: true
  parameters:
    ssl: 'on'
    ssl_cert_file: /etc/patroni/patroni.pem
    ssl_key_file: /etc/patroni/patroni.key
    ssl_ca_file: /etc/patroni/pmc2t-ca.cert.pem
    archive_mode: always
    archive_command: 'test ! -f /var/lib/postgresql/15/wal_archive/%f && cp %p /var/lib/postgresql/15/wal_archive/%f'
    logging_collector: on
    log_destination: 'stderr'
    log_truncate_on_rotation: on
    log_checkpoints: on
    log_connections: on
    log_disconnections: on
    log_error_verbosity: default
    log_lock_waits: on
    log_temp_files: 0
    log_autovacuum_min_duration: 0
    log_min_duration_statement: 50
    log_timezone: 'Europe/Prague'
    log_filename: postgresql-%Y-%m-%d_%H.log
    log_line_prefix: '%t [%p]: db=%d,user=%u,app=%a,client=%h '
    unix_socket_permissions: 0700 # (21)!

bootstrap:  # (17)!
  initdb:
    - encoding: UTF8
    - data-checksums
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    maximum_lag_on_failover: 1048576  # Failover parameters
    postgresql:
      pg_hba:  # (2)!
        - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.2/32 cert
        - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.3/32 cert
        - hostssl all "Green Center PMC TEST PATRONI cert" 10.120.1.4/32 cert
        - hostssl all "Green Center PMC TEST PATRONI cert" 127.0.0.1/32 cert
        - hostssl all all 0.0.0.0/0 md5
        - hostssl all all 0.0.0.0/0 cert
        - host all all 127.0.0.1/32 md5
        - host all all 0.0.0.0/0 md5
        - local all all trust
      use_slots: true
      use_pg_rewind: true
      parameters:
        max_connections: 1000
        max_locks_per_transaction: 64
        max_worker_processes: 8
        max_prepared_transactions: 0
        wal_level: replica
        track_commit_timestamp: 'off'
        max_wal_senders: 10
        max_replication_slots: 10
        wal_keep_segments: 8
        wal_keep_size: 1024MB
        wal_log_hints: 'on'
        hot_standby: 'on'
        shared_buffers: 1024MB
        ssl: 'on'
        ssl_cert_file: /etc/patroni/patroni.pem
        ssl_key_file: /etc/patroni/patroni.key
        ssl_ca_file: /etc/patroni/pmc2t-ca.cert.pem
        logging_collector: on
        log_destination: 'stderr'
        log_truncate_on_rotation: on
        log_checkpoints: on
        log_connections: on
        log_disconnections: on
        log_error_verbosity: default
        log_lock_waits: on
        log_temp_files: 0
        log_autovacuum_min_duration: 0
        log_min_duration_statement: 50
        log_timezone: 'Europe/Prague'
        log_filename: postgresql-%Y-%m-%d_%H.log
        log_line_prefix: '%t [%p]: db=%d,user=%u,app=%a,client=%h '
        unix_socket_permissions: 0700

      authentication:
        replication:
          username: Green Center PMC TEST PATRONI cert
          sslmode: prefer
          sslcert: /etc/patroni/patroni.pem
          sslkey: /etc/patroni/patroni.key
          sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
        rewind:
          username: Green Center PMC TEST PATRONI cert
          sslmode: prefer
          sslcert: /etc/patroni/patroni.pem
          sslkey: /etc/patroni/patroni.key
          sslrootcert: /etc/patroni/pmc2t-ca.cert.pem
        superuser:
          username: Green Center PMC TEST PATRONI cert
          sslmode: prefer
          sslcert: /etc/patroni/patroni.pem
          sslkey: /etc/patroni/patroni.key
          sslrootcert: /etc/patroni/pmc2t-ca.cert.pem

tags:
  nofailover: false
  noloadbalance: false
  clonefrom: false
  1. Pozor, pravidla pg_hba se čtou sekvenčně a použije se první, které odpovídá databázi, uživateli a způsobu připojení (hostssl vs host). Pokud je více takových pod sebou, použije se první. Např při nastavení:
    hostssl all all 10.0.1.2/32 cert
    hostssl all all 10.0.1.2/32 md5
    nebude fungovat přihlášení heslem.
  2. Pozor, pravidla pg_hba se čtou sekvenčně a použije se první, které odpovídá databázi, uživateli a způsobu připojení (hostssl vs host). Pokud je více takových pod sebou, použije se první. Např při nastavení:
    hostssl all all 10.0.1.2/32 cert
    hostssl all all 10.0.1.2/32 md5
    nebude fungovat přihlášení heslem.
  3. Cesta k adresáři se spustitelnými soubory postgresql.
  4. Adresář, ve kterém se budou nacházet data.
  5. IP a port na kterém bude databáze dostupná. Lze zadat více adres. V takovém případě se port musí nacházet za tou poslední. Např.:
    listen: 127.0.0.1,10.0.1.2:5432
    Pokud chceme povolit připojení na všech rozhraních, nastavíme:
    listen: 0.0.0.0:5432
  6. Adresa a port, na které bude databáze dostupná ostatním nodům.
  7. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  8. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  9. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  10. Adresa a port, na které bude REST api dostupné pro ostatní nody
  11. Adresa a port, na které bude poslouchat REST api - musí být dostupná z ostatních nodů a také ze všech instancí HAProxy
  12. Všechny nody ETCD clusteru
  13. Protokol pro připojení do ETCD clusteru
  14. Certifikát CA
  15. Klientský certifikát pro připojení do ETCD clusteru
  16. Klíč ke klientskému certifikátu pro připojení do ETCD clusteru
  17. bootstrap

    Tato sekce se čte pouze při vytváření clusteru. Jakmile je cluster vytvořen, je její obsah ingorován. Není tedy třeba ji aktualizovat při změně konfiguračních parametrů. Ke změně konfiguračních parametrů pro celý cluster je určen příkaz patronictl edit-config viz výše.
  18. Název hosta. Musí být unikátní v celém clusteru.
  19. Parametry upravující chování aplikace patronictl
  20. ověření certifikátem

    Pokud je použito ověření do databáze pomocí certifikátu, musí být název uživatele shodný s CN (Common Name) klientského certifikátu.
  21. Pouze uživatel postgres se může připojit k databázi přes unix socket - díky záznamu local all all trust v pg_hba nebude pro připojení vyžadováno heslo. Toto je primárně kvůli zálohám.
Příprava na spuštění
sudo mkdir /var/lib/postgresql/15/wal_archive /var/log/patroni/
sudo chown -R postgres:postgres /var/lib/postgresql/15/wal_archive /var/log/patroni/
Spuštění
# Jelikož bude postgres ovládat patroni, je nutné zastavit běžící instanci
sudo systemctl disable --now postgresql@15-main.service

# Spuštění patroni
sudo systemctl enable --now patroni
sudo patronictl -c /etc/patroni/config.yml list

# Vypíše stav clusteru. výstup by měl vypadat takto:

# + Cluster: postgresql-cluster (7514618054609667890) ----------+----+-----------+-----------------+
# | Member                   | Host       | Role    | State     | TL | Lag in MB | Tags            |
# +--------------------------+------------+---------+-----------+----+-----------+-----------------+
# | postgres-patroni-etcd-01 | 10.120.1.2 | Leader  | running   | 13 |           | clonefrom: true |
# +--------------------------+------------+---------+-----------+----+-----------+-----------------+

Pokud výpis vypadá jako výše, spustíme patroni na záložním serveru

# Jelikož bude postgres ovládat patroni, je nutné zastavit běžící instanci
sudo systemctl disable --now postgresql@15-main.service

# Spuštění patroni
sudo systemctl enable --now patroni
sudo patronictl -c /etc/patroni/config.yml list

# Vypíše stav clusteru. výstup by měl vypadat takto:

# + Cluster: postgresql-cluster (7514618054609667890) -----------------+----+-----------+-----------------+
# | Member                   | Host       | Role    | State            | TL | Lag in MB | Tags            |
# +--------------------------+------------+---------+------------------+----+-----------+-----------------+
# | postgres-patroni-etcd-01 | 10.120.1.2 | Leader  | running          | 13 |           | clonefrom: true |
# | postgres-patroni-etcd-02 | 10.120.1.3 | Replica | creating replica |    |   unknown |                 |
# +--------------------------+------------+---------+------------------+----+-----------+-----------------+

# Pokud je State jiný, než creating replica, spustit následující příkaz:

# sudo patronictl -c /etc/patroni/config.yml reinit postgresql-cluster postgres-patroni-etcd-02

# Přepnout na záložní server můžeme příkazem

# sudo patronictl -c /etc/patroni/config.yml switchover

Umístění logů

Po spuštění služby patroni se již databázové logy nebudou nacházet ve standardním umístění /var/log/postgresql, ale v adresáři /var/lib/postgresql/15/main/log.

Monitoring

Pro monitoring replikace vznikla šablona v Zabbixu. Její detailní popis je dostupný v repozitáři.