注意:

在 Oracle Cloud Infrastructure 上部署高可用性 Postgres 叢集

簡介

本教學課程概述使用 Patroni 和其他元件在 HA 組態中 Postgres 的設計與實作。PostgreSQL 缺少內建的自動容錯移轉和自動機制,可將失敗的主控機制加回叢集。Patroni 是 PostgreSQL 歲的新高可用性 (HA) 解決方案,具備雲端原生功能和進階選項,可進行容錯移轉 / 切換和自動啟動安裝及複本設定。

Patroni 是一個使用 Python 建立您自己的客製化 HA 解決方案的樣板,也是分散式組態存放區 (例如 etcd) 的最大輔助功能。

原生 Postgres 複製和使用 Patroni 的解決方案有下列限制:

原生 Postgres 複製中的限制 以 Patroni 為主的解決方案
預設複製機制不支援自動容錯移轉。 Patroni 提供自動容錯移轉。
使用外部工具進行容錯移轉可能需要額外努力,才能讓它們繼續運作。 帕特羅尼負責容錯移轉。
監督 Postgres 也可以是挑戰性。 Patroni 擁有內建機制,可監控 Postgres 服務。
自動將失敗的節點加回叢集,需要進階命令檔技能。 Patroni 已內建自動化功能,可將失敗的節點回復至叢集。
無法處理分割區塊鏈案例。 在 ETCD 的幫助下,Patroni 將能夠選擇新的領導者。

其他 PostgreSQL HA 解決方案包括:

不過,使用 Patroni 搭配 Postgres 實行,可大幅簡化整個叢集管理生命週期。

目標

本教學課程列出可行的高效能解決方案,可讓客戶將 Postgres 資料庫 (從 AWS 或其他雲端供應商) 移轉至 OCI。HA 和即時資料移轉的主要需求。

架構

下列架構包含 3 個 ETCD 伺服器、3 個 (Postgres + Patroni + Pgbackrest) 伺服器、物件儲存的儲存桶以及網路負載平衡器。

架構圖

建議

  1. 針對 HA:使用 3 個 ETCD 伺服器,即 PostgreSQL 的 3 個節點,並將其置於不同的可用性網域。
  2. 為了得到更佳的傳輸量,請為資料、暫時、Wal 和日誌檔建立個別的區塊磁碟區。
  3. 在建立叢集時,定義 patroni.yaml bootstrap 區段中的所有自訂參數。

設定並安裝 PostgreSQL HA 元件

設定分為兩個部分:

工作 1:佈建基礎架構

  1. 建立運算 VM:

    • 3 ETCD 伺服器
    • Postgres + Patroni 的 3 個伺服器 (1 個領導者和 2 個複本)
  2. 建立用於儲存備份的物件儲存的儲存桶。

  3. 在上述物件存放區儲存桶上建立具備 READ/WRITE 存取權的 OCI 租用戶使用者。

作業 2:安裝並設定軟體

  1. 配置 ETCD。

    • 在 3 台伺服器上安裝 ETCD。

      cd /tmp
      wget https://github.com/etcd-io/etcd/releases/download/v3.5.2/etcd-v3.5.2-linux-amd64.tar.gz
      tar xzvf /tmp/etcd-v3.5.2-linux-amd64.tar.gz
      cd etcd-v3.5.2-linux-amd64
      cp etcdutl etcdctl etcd /usr/local/bin/
      mkdir -p /etc/etcd
      mkdir -p /var/lib/etcd
      groupadd -f -g 1501 etcd
      useradd -c "etcd user" -d /var/lib/etcd -s /bin/false -g etcd -u 1501 etcd
      chown -R etcd:etcd /var/lib/etcd
      
    • 在所有 3 個伺服器上設定並啟動 ETCD。

      以下是 ETCD 伺服器詳細資料:

      | Hostname | IP Address | Availability Domain |
      | --- | --- | --- |    
      | pg-etcd-01 | 192.0.2.4  | AD1 |
      | pg-etcd-02 | 192.0.2.5  | AD2 |
      | pg-etcd-03 | 192.0.2.6  | AD3 |
      

      注意:根據需求修改 IP。

      pg-etcd-01

      vi /etc/etcd/etcd.conf
      
      ###Node1
      ##192.0.2.4
      
      ETCD_NAME="pg-etcd-01"
      ETCD_INITIAL_CLUSTER="pg-etcd-01=http://192.0.2.4:2380"
      ETCD_LISTEN_CLIENT_URLS="http://192.0.2.4:2379,http://127.0.0.1:2379"
      ETCD_ADVERTISE_CLIENT_URLS="http://192.0.2.4:2379"
      ETCD_LISTEN_PEER_URLS="http://192.0.2.4:2380,http://127.0.0.1:7001"
      ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.0.2.4:2380"
      ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
      ETCD_INITIAL_CLUSTER_STATE="new"
      ETCD_DATA_DIR="/var/lib/etcd"
      ETCD_ELECTION_TIMEOUT="5000"
      ETCD_HEARTBEAT_INTERVAL="1000"
      ETCD_ENABLE_V2="true"
      
      

      pg-etcd-02

      vi /etc/etcd/etcd.conf
      
      ##Node2
      ##192.0.2.5
      ETCD_NAME=" pg-etcd-02"
      ETCD_INITIAL_CLUSTER="pg-etcd-01=http://192.0.2.4:2380,pg-etcd-02=http://192.0.2.5:2380"
      ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
      ETCD_INITIAL_CLUSTER_STATE="existing"
      ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.0.2.5:2380"
      ETCD_LISTEN_PEER_URLS="http://192.0.2.5:2380,http://127.0.0.1:7001"
      ETCD_LISTEN_CLIENT_URLS="http://192.0.2.5:2379,http://127.0.0.1:2379"
      ETCD_ADVERTISE_CLIENT_URLS="http://192.0.2.5:2379"
      ETCD_DATA_DIR="/var/lib/etcd"
      ETCD_ELECTION_TIMEOUT="5000"
      ETCD_HEARTBEAT_INTERVAL="1000"
      ETCD_ENABLE_V2="true"
      

      pg-etcd-03

      vi /etc/etcd/etcd.conf
      
      ##Node3
      ##192.0.2.6
      ETCD_NAME="pg-etcd-03"
      ETCD_INITIAL_CLUSTER="pg-etcd-01=http://192.0.2.4:2380,pg-etcd-02=http://192.0.2.5:2380,pg-etcd-03=http://192.0.2.6:2380"
      ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
      ETCD_INITIAL_CLUSTER_STATE="existing"
      ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.0.2.6:2380"
      ETCD_LISTEN_PEER_URLS="http://192.0.2.6:2380,http://127.0.0.1:7001"
      ETCD_LISTEN_CLIENT_URLS="http://192.0.2.6:2379,http://127.0.0.1:2379"
      ETCD_ADVERTISE_CLIENT_URLS="http://192.0.2.6:2379"
      ETCD_DATA_DIR="/var/lib/etcd"
      ETCD_ELECTION_TIMEOUT="5000"
      ETCD_HEARTBEAT_INTERVAL="1000"
      ETCD_ENABLE_V2="true"
      
    • 新增成員至 pg-etcd-01。

      etcdctl member add pg-etcd-02 --peer-urls=http://192.0.2.5:2380
      etcdctl member add pg-etcd-03 --peer-urls=http://192.0.2.6:2380
      
    • 執行下列指令以顯示成員清單。

      etcdctl member list
      
    • 建立服務。

      vi /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"
      Restart=on-failure
      LimitNOFILE=65536
      IOSchedulingClass=best-effort
      IOSchedulingPriority=0
      
      [Install]
      WantedBy=multi-user.target
      
      systemctl daemon-reload
      
      systemctl enable etcd
      
  2. 在所有節點上安裝 Postgres。執行下列程序檔以安裝 Postgres:

    • Postgres 安裝指令碼

      注意:若為 Replica,請停止 postgresql 並刪除當 Patroni 組態完成後,將從領導者複製的資料目錄。

      /opt/pgsql/bin/pg_ctl -D /opt/pgsql/data/$MAJORVERSION stop
      cd /opt/pgsql/data/
      rm *
      
  3. 安裝擴充功能:pg_squeeze 和 pgaudit。

    • 安裝 PGAUDIT

      cd /usr/local/src/postgresql-12.6/contrib/
      wget https://github.com/pgaudit/pgaudit/archive/refs/heads/REL_12_STABLE.zip
      unzip REL_12_STABLE.zip
      make install USE_PGXS=1 PG_CONFIG=/opt/pgsql/bin/pg_config
      
    • 安裝 PG_SQUEEZE

      cd /usr/local/src/postgresql-12.6/contrib/
      wget https://github.com/cybertec-postgresql/pg_squeeze/archive/refs/heads/master.zip
      unzip master.zip
      make install USE_PGXS=1 PG_CONFIG=/opt/pgsql/bin/pg_config
      
  4. 在所有節點上安裝 Patroni。

    yum update –y
    yum -y install epel-release
    yum -y install python3
    yum install -y python3-devel
    yum install -y psutils
    yum install -y gcc
    yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
    yum install python3-psycopg2
    pip3 install python-etcd
    pip3 install patroni
    
  5. 在所有節點上安裝和設定 pgBackrest。

    • 安裝 Pgbackrest

      yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
      sudo yum install pgbackrest -y
      
    • 設定所有節點的 Pgbackrest

      /etc/pgbackrest.conf
      [global]
      repo1-type=s3
      repo1-path=/backup
      repo1-s3-uri-style=path
      repo1-s3-region=us-ashburn-1
      repo1-s3-endpoint=https://xxxxx0011.compat.objectstorage.us-ashburn-1.oraclecloud.com
      repo1-s3-key=0000000a90d57eddexxxxxa3dc7c4f700000ed2e6
      repo1-s3-key-secret=1UhXj69xxxx1e6OyF+00000cccccyyyuuu=
      repo1-s3-bucket=pg_backup
      repo1-retention-full=10
      log-level-console=info
      log-level-file=debug
      log-path=/var/log/pgbackrest/
      
      [stanza-name]
      pg1-path=/opt/pgsql/data
      pg1-user=Postgres
      
    • 在所有節點上建立啟動安裝檔案

      vi /etc/patroni/boot_pgbackrest.sh
      
      #!/ usr / bin /env bash
      while getopts ": -:" optchar ; do
              [[ "${ optchar }" == "-" ]] || continue
              case "${ OPTARG }" in
              datadir =* )
                      DATA_DIR =${ OPTARG #*=}
                      ;;
              scope =* )
                      SCOPE =${ OPTARG #*=}
                      ;;
              esac
      done
      /bin/pgbackrest --stanza=$SCOPE --link-all restore
      
  6. 在所有節點上設定 Patroni。

    主機名稱 IP 位址 可用性網域
    pg-db-01 192.0.2.1 AD1
    pg-db-02 192.0.2.2 AD2
    pg-db-03 192.0.2.3 AD3

    注意:根據需求修改 IP。

    mkdir -p /etc/patroni
    mkdir -p /opt/pgsql/patroni
    chown postgres:postgres -R /opt/pgsql/patroni
    chmod 700 /opt/pgsql/patroni
    
    vi /etc/patroni/patroni.yml
    
    scope:pg-ha-cluster
    name:pg-db-01
    namespace:/opt/pgsql/patroni/
    ###
    restapi:
      listen: "192.0.2.1:8008"
      connect_address: "192.0.2.1:8008"
    ###ETCD Configuration
     etcd:
       hosts: "192.0.2.4:2379,192.0.2.5:2379, 192.0.2.6:2379"
     ###Bootstrap
     bootstrap:
       dcs:
         ttl: 120
         loop_wait: 10
         retry_timeout: 10
         maximum_lag_on_failover: 1048576
         postgresql:
           use_pg_rewind: true
           use_slots: true
         parameters:
           archive_command: "pgbackrest --stanza=<stanza-name> archive-push %p"
           archive_mode: on
           archive_timeout: 900s
           log_file_mode: "0640"
           log_filename: postgresql-%u.log
           log_rotation_age: 1d
           log_truncate_on_rotation: "on"
           logging_collector: "on"
           max_connections: 2000
           max_replication_slots: 10
           max_wal_senders: 10
           max_wal_size: 5GB
           max_worker_processes: 40
           min_wal_size: 1GB
           wal_level: "replica"
           password_encryption: scram-sha-256
           superuser_reserved_connections: 200
           create_replica_methods:
             - pgbackrest
           pgbackrest:
             command: "/usr/bin/pgbackrest --stanza=<stanza-name>  restore --delta --link-all"
             keep_data: True
             no_params: True
         recovery_conf:
           recovery_target_timeline: latest
           restore_command: '/usr/bin/pgbackrest --stanza=<stanza-name>  archive-get %f %P'
        method: pgbackrest
        pgbackrest:
          command: /etc/patroni/boot_pgbackrest.sh
          keep_existing_recovery_conf: False
          recovery_conf:
            recovery_target_timeline: latest
            restore_command: '/usr/bin/pgbackrest --stanza=<stanza-name>  archive-get %f %P'
       initdb:
         - encoding: UTF8
         - data-checksums
       pg_hba:
         - host replication replicator 127.0.0.1/32 md5
         - host replication replicator 192.0.2.1/32 md5
         - host replication replicator 192.0.2.2/32 md5
         - host replication replicator 192.0.2.3/32 md5
         - host all all x.x.x.0/0 md5
       users:
         admin:
           password: admin
           options:
             - createrole
             - createdb
     #####Local Postgresql Parameters
     postgresql:
     listen: "192.0.2.1:5432"
     connect_address: "192.0.2.1:5432"
     data_dir: /opt/pgsql/data
     pgpass: /opt/pgsql/patroni/pgpass
     authentication:
       replication:
         username: replicator
         password: password
       superuser:
         username: postgres
         password: password
     #    rewind:
     #      username: replicator
     #      password: password
     parameters:
       unix_socket_directories: "/var/run/postgresql, /tmp"
     ###Any Tags
     tags:
       nofailover: false
       noloadbalance: false
       clonefrom: false
       nosync: false
    

    注意:分別變更每個節點的 IP。

    chmod 640 /etc/patroni/patroni.yml
    chown postgres:postgres -R /etc/patroni/patroni.yml
    
    • 在所有節點上建立 Patroni 服務
    vi /etc/systemd/system/patroni.service
    
    [Unit]
    Description=Runners to orchestrate a high-availability PostgreSQL - patroni
    After=syslog.target network.target
    
    [Service]
    Type=simple
    
    User=postgres
    Group=postgres
    
    # Read in configuration file if it exists, otherwise proceed
    EnvironmentFile=-/etc/patroni_env.conf
    
    WorkingDirectory=~
    
    # Where to send early-startup messages from the server
    # This is normally controlled by the global default set by systemd
    # StandardOutput=syslog
    
    # Pre-commands to start watchdog device
    # Uncomment if watchdog is part of your patroni setup
    #ExecStartPre=-/usr/bin/sudo /sbin/modprobe softdog
    #ExecStartPre=-/usr/bin/sudo /bin/chown postgres /dev/watchdog
    
    # Start the patroni process
    ExecStart=/usr/local/bin/patroni /etc/patroni/patroni.yml
    
    # Send HUP to reload from patroni.yml
    ExecReload=/bin/kill -s HUP $MAINPID
    
    # Only kill the patroni process, not its children, so it will gracefully stop postgres
    KillMode=process
    
    # Give a reasonable amount of time for the server to start up/shut down
    TimeoutSec=60
    
    # Do not restart the service if it crashes, we want to manually inspect database on failure
    Restart=no
    
    [Install]
    WantedBy=multi-user.target
    
    sudo systemctl daemon-reload
    sudo systemctl enable patroni
    sudo systemctl start patroni
    sudo systemctl status patroni
    

    注意:在 Replica 節點上設定 Patroni,但不要啟動服務。

    • 執行下列命令,查看 Master 上的 Patroni 叢集狀態。

      patronictl -c /etc/patroni/patroni.yml list
      
      [root@pg-db-01 ~]# /usr/local/bin/patronictl -c /etc/patroni/patroni.yml list
      +--------------+-------------+---------+---------+----+-----------+
      | Member       | Host        | Role    | State   | TL | Lag in MB |
      + Cluster: pg-ha-cluster (7089354141421597068) --+----+-----------+
      | pg-db-01 | 192.0.2.1 | Leader  | running | 1 |           |
      +--------------+-------------+---------+---------+----+-----------+
      
  7. 建立 Stanza 並完整備份 。

    pgbackrest --stanza=<stanza-name> stanza-create
    pgbackrest  --type=full --stanza= pgha --process-max=10 backup
    pgbackrest info
    
  8. 在 Replica 節點上啟動 Patroni,而且應該從 Leader 自動擷取。

    sudo systemctl start patroni
    sudo systemctl status patroni
    
    • 執行下列命令以查看叢集狀態:
    patronictl -c /etc/patroni/patroni.yml list
    

    狀態應如下:

    [root@pg-db-01 ~]# /usr/local/bin/patronictl -c /etc/patroni/patroni.yml list
    +--------------+-------------+---------+---------+----+-----------+
    | Member       | Host        | Role    | State   | TL | Lag in MB |
    + Cluster: pg-ha-cluster (7089354141421597068) --+----+-----------+
    | pg-db-01 | 192.0.2.1 | Leader  | running | 2 |           |
    | pg-db-02 | 192.0.2.2  | Replica | running |2 |         0 |
    | pg-db-03 | 192.0.2.3  | Replica | running | 2 |         0 |
    +--------------+-------------+---------+---------+----+-----------+
    
  9. 最後,請設定 cron 工作以清除日誌、備份等等。

    crontab –e
    #Pgbackrest FULL Backup on Sunday @1AM
    00 01 * * 0 postgres pgbackrest  --type=full --stanza=ash1-adeprod1-pgcluster --process-max=10 backup  &> /dev/null
    #Pgbackrest INC Backup on Mon-Sat @1AM
    00 01 * * 1-6 postgres pgbackrest  --type=diff --stanza=ash1-adeprod1-pgcluster --process-max=10 backup &> /dev/null
    #Delete PostgreSQL logs
    08 * * * find /opt/pgsql/log/* -mtime +15 -exec rm {} \; &>/dev/null
    

其他 Patroni 命令

確認

其他學習資源

探索 docs.oracle.com/learn 上的其他實驗室,或存取 Oracle Learning YouTube 通道上的更多免費學習內容。此外,請造訪 education.oracle.com/learning-explorer 以成為 Oracle Learning Explorer。

如需產品文件,請瀏覽 Oracle Help Center