Implementasi referensi database PostgreSQL di GDC dengan air gap

Panduan ini memberikan panduan komprehensif untuk men-deploy stack PostgreSQL yang sangat tersedia di tiga zona dalam lingkungan terisolasi Google Distributed Cloud (GDC). Anda akan mempelajari cara menyiapkan artefak software yang diperlukan, mem-bootstrap VM target, dan menggunakan Autobase untuk mengotomatiskan seluruh proses penyediaan. Di seluruh panduan ini, Patroni digunakan sebagai lapisan pengelolaan utama untuk mengatur siklus proses PostgreSQL dan menangani failover otomatis.

Arsitektur

Arsitektur ini terdiri dari lingkungan tiga VM yang didistribusikan di tiga zona ketersediaan.

Arsitektur tiga VM yang menjalankan tumpukan layanan yang ditempatkan bersama.

Setiap VM identik dan menjalankan stack layanan yang ditempatkan bersama:

  • PostgreSQL 17: Mesin database relasional inti.
  • Patroni: Pengelola Ketersediaan Tinggi. Pengelola ini menangani siklus proses proses PostgreSQL dan melakukan failover otomatis. Pengelola ini mengekspos HTTPS REST API di port 8008 (endpoint /primary) yang digunakan oleh load balancer untuk mengidentifikasi pemimpin saat ini.
  • etcd: Penyimpanan Konfigurasi Terdistribusi (DCS). Penyimpanan ini menyediakan lapisan konsensus untuk pemilihan pemimpin dan menyimpan konfigurasi Patroni.
  • PgBouncer: Pooler koneksi yang berada di depan PostgreSQL untuk menstabilkan overhead koneksi. Pooler ini menyediakan titik entri yang direkomendasikan untuk traffic aplikasi di port 6432.

Stack ini juga mencakup Load Balancer L4 Global GDC dengan air gap, yang merupakan layanan terkelola platform yang menyediakan IP Virtual (VIP) yang stabil. Aplikasi terhubung ke VIP stabil di port 6432, yang dirutekan oleh load balancer ke PgBouncer di VM pemimpin saat ini. PgBouncer kemudian memproxy permintaan ke instance PostgreSQL lokal. Untuk mengelola alur traffic, load balancer terus melakukan polling endpoint HTTPS Patroni sebagai health check.

Health check di VM pemimpin menampilkan HTTP 200 OK untuk memberi sinyal bahwa VM siap untuk traffic, sedangkan health check di VM replika menampilkan HTTP 503 Service Unavailable untuk memberi sinyal kepada load balancer agar melewatinya. Jika pemimpin gagal, pemimpin baru akan dipilih dan instance Patroni-nya mulai menampilkan HTTP 200 OK, sehingga load balancer otomatis mengalihkan traffic ke port PgBouncer VM baru.

Untuk memastikan ketersediaan tinggi dan mencegah kehilangan data, stack mengandalkan konsep kuorum. Dengan 3 VM, sistem memerlukan mayoritas minimal dua anggota yang responsif dan berkomunikasi untuk memilih pemimpin dan tetap beroperasi. Konsensus berbasis mayoritas ini, yang dikelola oleh etcd dan Patroni, memungkinkan stack secara otomatis mentoleransi kegagalan total dari satu VM atau zona.

Pertimbangan performa

Saat merencanakan deployment, pertimbangkan faktor konkret berikut untuk mengoptimalkan performa dan keandalan:

  • Ukuran Hardware: Meskipun persyaratan bervariasi menurut workload, gunakan profil standar ini sebagai titik awal untuk setiap VM:
    • Pengembangan/Proof-of-Concept: 2 vCPU, RAM 8 GB (Minimum untuk operasi yang stabil).
    • Produksi Kecil: 4 vCPU, RAM 16 GB. Cocok untuk alat internal dengan konkurensi sedang.
    • Produksi Standar: 8 vCPU, RAM 32 GB. Baseline yang direkomendasikan untuk aplikasi penting.
    • Throughput Tinggi: 16+ vCPU, RAM 64 GB+. Untuk workload yang memerlukan caching data yang luas dalam memori (buffer bersama PostgreSQL).
  • Performa Penyimpanan: Penyimpanan berperforma tinggi sangat penting. Disk SSD sangat direkomendasikan untuk stabilitas etcd. etcd sangat sensitif terhadap latensi penulisan disk; panduan hardware etcd resmi merekomendasikan latensi fdatasync WAL disk p99 < 10 md.
  • Latensi Jaringan: Latensi antara VM secara langsung memengaruhi performa replikasi:
    • Kuorum etcd: Waktu round-trip (RTT) rata-rata harus < 50 md (ideal < 10 md) untuk mencegah waktu tunggu pemilihan dan ketidakstabilan cluster.
    • Replikasi Sinkron: Jika dikonfigurasi, setiap transaksi tulis harus menunggu konfirmasi replika. Latensi antar-zona di GDC dengan air gap biasanya < 1 md, yang sangat baik untuk menjaga overhead tulis tetap minimal (biasanya 10-30%).
  • Peran PgBouncer: PostgreSQL membuat proses OS baru untuk setiap koneksi, yang menggunakan ~10 MB RAM dan menimbulkan biaya pengalihan konteks CPU. PgBouncer mengurangi overhead ini dengan mempertahankan kumpulan koneksi persisten, sehingga database dapat menangani ribuan koneksi aplikasi dengan proses backend yang jauh lebih sedikit.
  • Komponen Sidecar: Patroni dan etcd ringan, tetapi memerlukan ketersediaan CPU yang konsisten. Dalam skenario beban tinggi, pastikan VM tidak terlalu banyak berlangganan di tingkat hypervisor untuk menghindari "pencurian" siklus CPU yang diperlukan untuk heartbeat dan pemeliharaan pemimpin.
  • Penyesuaian Kernel: Otomatisasi Autobase secara otomatis menerapkan pengoptimalan yang bermanfaat untuk PostgreSQL, seperti mengonfigurasi sysctl parameter (misalnya, vm.swappiness, net.core.somaxconn) dan menonaktifkan Transparent Huge Pages (THP). Perubahan ini mengurangi overhead pengelolaan memori dan meningkatkan throughput jaringan untuk instance database dengan traffic tinggi.

Sebelum memulai

Sebelum memulai deployment, Anda harus memastikan bahwa lingkungan Anda memenuhi persyaratan berikut.

Meninjau persyaratan VM

Untuk tujuan tutorial ini, Anda harus membuat tiga VM di project GDC dengan air gap. Anda harus mempertimbangkan poin dan persyaratan berikut untuk VM:

  • Distribusi zona: Agar deployment ini benar-benar tahan terhadap kegagalan zona, Anda harus mendistribusikan VM di tiga zona ketersediaan yang berbeda. Namun, deployment tetap identik jika VM berada di dua zona atau bahkan satu zona. Yang paling penting adalah semua VM dapat berkomunikasi satu sama lain melalui jaringan dengan alamat IP internalnya.
  • Sistem Operasi: Tutorial ini mengasumsikan Anda menggunakan image Ubuntu 22.04. Langkah-langkah lebih lanjut dalam panduan ini mungkin berbeda jika Anda menggunakan distribusi yang berbeda.
  • Resource: Anda harus menyediakan minimal 2 CPU dan memori 8 GB per VM untuk tutorial ini. Dalam produksi, Anda harus menyediakan resource yang sesuai untuk workload tertentu (Lihat Pertimbangan performa).
  • IP Jaringan: Anda harus mencatat alamat IP internal dan eksternal untuk setiap VM. Dalam panduan ini, Anda menggunakan IP eksternal untuk kontrol Ansible karena Anda menjalankan perintah dari workstation eksternal. IP internal digunakan untuk komunikasi dan binding antarlayanan. Jika Anda menyediakan VM bootstrapper di dalam jaringan, Anda hanya memerlukan IP internal.
  • Akses: Akses sudo tanpa sandi untuk pengguna deployment diperlukan karena otomatisasi Ansible perlu melakukan tugas administratif (menginstal paket, mengubah konfigurasi sistem) tanpa diblokir oleh perintah sandi.
  • SSH: Autentikasi berbasis kunci harus diaktifkan agar Ansible dapat terhubung ke VM target dengan aman dan non-interaktif.

Menyiapkan software workstation lokal

Untuk mengelola deployment dan menyiapkan artefak terisolasi, Anda memerlukan serangkaian alat otomatisasi dan containerization yang diinstal di workstation lokal.

  • Ansible 2.17.0+: Mesin otomatisasi yang menjalankan playbook dan peran deployment.
  • Docker: Digunakan untuk menarik dan mengemas dependensi OS dalam lingkungan yang identik dengan VM target (Ubuntu 22.04).
  • Klien PostgreSQL (psql): Diperlukan untuk menjalankan kueri pengujian dan memverifikasi replikasi data dari workstation lokal.
  • Repositori Autobase:

    • Clone repositori untuk mengakses playbook dan peran otomatisasi: https://github.com/vitabaks/autobase
    • Checkout rilis tertentu (panduan ini menggunakan versi 2.5.2):

      git checkout 2.5.2

      Langkah-langkah lebih lanjut dalam panduan ini mungkin berbeda jika Anda menggunakan distribusi yang berbeda.

    • Untuk menjalankan playbook dalam panduan ini, Anda harus menginstal kode sumber autobase lokal sebagai koleksi Ansible sehingga awalan peran dapat diselesaikan:

      cd autobase/automation
      ansible-galaxy collection install . --force
      

Membuat beberapa variabel lingkungan

Di seluruh panduan ini, Anda akan menggunakan variabel lingkungan berikut untuk menyederhanakan perintah. Variabel ini menyimpan parameter penting seperti project ID, zona ketersediaan untuk VM, nama host, dan label yang digunakan oleh load balancer untuk mengidentifikasi cluster Anda. Tetapkan variabel ini di sesi shell saat ini dengan nilai sebenarnya untuk lingkungan Anda (Pastikan zona dipisahkan dengan spasi).

Perhatikan bahwa meskipun Anda dapat menetapkan nama apa pun yang Anda inginkan untuk VM, panduan ini menggunakan postgres-vm-1, postgres-vm-2, dan postgres-vm-3 sebagai nama contoh arbitrer untuk node cluster:

export PROJECT_ID="your-project-id"
export ZONES="zone1 zone2 zone3"
export VM1_NAME="postgres-vm-1"
export VM2_NAME="postgres-vm-2"
export VM3_NAME="postgres-vm-3"
export VM_LABEL="app=my-postgres-cluster"
export CLUSTER_NAME="my-postgres-cluster"

Mengonfigurasi Ansible

Tentukan lingkungan VM Anda dalam file inventory.ini menggunakan template berikut:

[master]
postgres-vm-1 ansible_host=XX.XX.XX.XX hostname=postgres-vm-1 bind_address=XX.XX.XX.XX

[replica]
postgres-vm-2 ansible_host=XX.XX.XX.XX hostname=postgres-vm-2 bind_address=XX.XX.XX.XX
postgres-vm-3 ansible_host=XX.XX.XX.XX hostname=postgres-vm-3 bind_address=XX.XX.XX.XX

[postgres_cluster:children]
master
replica

[etcd_cluster]
postgres-vm-1
postgres-vm-2
postgres-vm-3

[all:vars]
ansible_user=...
ansible_ssh_private_key_file=~/.ssh/...
postgresql_version=17
with_haproxy_load_balancing=false
patroni_superuser_password=...
etcd_package_repo="file:///tmp/packages/etcd-v3.5.25-linux-amd64.tar.gz"
installation_method="packages"
install_postgresql_repo=false
install_timescale_repo=false
install_citus_repo=false
apt_repository=[]
yum_repository=[]
install_system_packages=false
patroni_installation_method=deb

Memahami konfigurasi:

  • [master] dan [replica]: Menentukan VM database utama dan sekunder. Pastikan Anda menggunakan nama VM sebenarnya yang ditetapkan dalam variabel lingkungan VM1_NAME, VM2_NAME, dan VM3_NAME.
  • [postgres_cluster:children]: Grup yang menggabungkan node master dan replika, sehingga Ansible dapat menargetkan seluruh cluster database dengan satu perintah.
  • [etcd_cluster]: Menentukan node yang akan berpartisipasi dalam cluster konsensus etcd. Ini mencakup ketiga node database untuk memastikan ketersediaan tinggi.
  • ansible_host: (Untuk setiap VM) IP masuk eksternal VM yang digunakan oleh Ansible untuk terhubung ke VM tersebut. Ganti XX.XX.XX.XX dengan IP eksternal sebenarnya.
  • bind_address: (Untuk setiap VM) Alamat IP internal VM. Ganti XX.XX.XX.XX dengan IP internal sebenarnya.
  • ansible_user: Pengguna jarak jauh yang digunakan Ansible untuk terhubung ke VM target dengan SSH. Ganti ... dengan nama pengguna sebenarnya.
  • ansible_ssh_private_key_file: Jalur lokal ke kunci SSH pribadi yang digunakan untuk autentikasi ke VM target. Ganti ~/.ssh/... dengan jalur sebenarnya.
  • patroni_superuser_password: Sandi untuk pengguna postgres. Pastikan Anda menggunakan sandi yang kuat dan aman di sini.
  • with_haproxy_load_balancing=false: Menonaktifkan HAProxy lokal karena Anda menggunakan load balancer L4 native platform.
  • etcd_package_repo: Menunjuk ke jalur lokal biner etcd di dalam direktori bootstrap VM.
  • installation_method="packages": Menginstruksikan otomatisasi untuk menginstal komponen dengan paket OS, bukan mengompilasi dari sumber atau menggunakan pip Python.
  • install_..._repo=false dan _repository=[]: Penggantian ini mencegah Ansible mencoba menjangkau internet untuk menambahkan repositori eksternal atau memperbarui daftar paket.
  • install_system_packages=false: Mencegah otomatisasi mencoba mendownload dan menginstal paket yang telah Anda sediakan selama fase inisialisasi atau bootstrap.
  • patroni_installation_method=deb: Secara khusus memberi tahu peran untuk menggunakan paket .deb yang Anda instal.

Menginisialisasi VM

Stack database memerlukan beberapa paket dan library OS yang mungkin tidak disertakan dalam image Ubuntu dasar Anda. Karena VM berada di lingkungan terisolasi tanpa akses internet, VM tidak dapat mendownload dependensi ini sendiri.

Untuk mengatasi masalah ini, ikuti langkah-langkah berikut:

  • Gunakan container Docker di workstation lokal untuk mendownload semua file yang diperlukan. Perintah berikut menggunakan apt-rdepends untuk mengidentifikasi secara rekursif setiap library bersama dan dependensi yang diperlukan oleh aplikasi target. Perintah ini mengonfigurasi repositori PostgreSQL resmi dalam container untuk mengambil artefak versi 17, lalu melakukan iterasi melalui daftar dependensi untuk mendownload file .deb individual sambil memfilter library sistem inti (seperti libc6 atau hostname) untuk menghindari konflik versi pada VM target. Terakhir, perintah ini mengambil biner etcd mandiri langsung dari GitHub.

    Pertama, buat direktori untuk menyimpan paket:

    mkdir -p ./packages
    

    Kemudian, jalankan perintah Docker untuk mendownload semua paket yang diperlukan dan biner etcd:

    docker run --rm --platform linux/amd64 -v "$(pwd)/packages:/packages" \
      ubuntu:22.04 bash -c "
      set -e
      apt-get update
      apt-get install -y ca-certificates curl gnupg apt-rdepends
    
      # Add PostgreSQL Repository
      curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
        gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg
      echo 'deb http://apt.postgresql.org/pub/repos/apt jammy-pgdg main' > \
        /etc/apt/sources.list.d/pgdg.list
      apt-get update
    
      # Define Application Targets + Explicit dependencies needed for air-gap
      TARGETS='unzip tar pgbouncer patroni netdata postgresql-17 \
        postgresql-client-17 postgresql-contrib-17 \
        postgresql-server-dev-17 postgresql-17-dbgsym \
        python3-psycopg2 python3-click python3-yaml python3-prettytable \
        python3-urllib3 python3-tz python3-pip python3-setuptools \
        python3-cryptography moreutils vim jq acl zstd libjq1 \
        libpython3.10-stdlib libexpat1-dev zlib1g-dev libipc-run-perl \
        libtime-duration-perl libjson-perl libpython3-dev \
        libjs-sphinxdoc python3-wheel'
    
      # Resolve all recursive dependencies
      ALL_DEPS=\$(apt-cache depends --recurse --no-recommends --no-suggests \
        --no-conflicts --no-breaks --no-replaces --no-enhances \$TARGETS | \
        grep '^\w' | sort -u)
    
      cd /packages
      for pkg in \$ALL_DEPS; do
        if apt-cache show \"\$pkg\" > /dev/null 2>&1; then
          # Filter system core to avoid VM conflicts/breaks
          # We exclude core OS libraries (libc, systemd, etc.) because these
          # often cause version conflicts if the VM's patch level differs
          # from the online container.
          FILTER='base-files|debianutils|coreutils|findutils|diffutils|sed'
          FILTER+='|grep|gzip|hostname|ncurses|perl-base|libc6|binutils'
          FILTER+='|linux-libc|libc-bin|libc-dev-bin|systemd|dpkg|init'
          if [[ ! \"\$pkg\" =~ \$FILTER ]]; then
            apt-get download \"\$pkg\" || echo \"Failed \$pkg\"
          fi
        fi
      done
    
      # Download etcd binary
      if [ ! -f etcd-v3.5.25-linux-amd64.tar.gz ]; then
        curl -L https://github.com/etcd-io/etcd/releases/download/v3.5.25/\
    etcd-v3.5.25-linux-amd64.tar.gz -o etcd-v3.5.25-linux-amd64.tar.gz
      fi
    "
    
  • Gunakan Ansible untuk mengupload arsip ke ketiga VM target secara bersamaan:

    ansible all -i inventory.ini -m copy -a "src=packages.tar.gz dest=/tmp/" -b
    
  • Hapus data paket yang ada di VM dan ekstrak file tar baru:

    ansible all -i inventory.ini -m shell -a "rm -rf /tmp/packages && \
      mkdir -p /tmp/packages && tar -xzf /tmp/packages.tar.gz -C /tmp/packages" -b
    
  • Lakukan penginstalan non-interaktif dari semua paket .deb yang didownload. Untuk menghindari masalah dengan pra-dependensi tertentu di lingkungan terisolasi, gunakan flag --force-depends yang diikuti oleh apt-get install -fy untuk menyelesaikan pohon dependensi secara lokal:

    ansible all -i inventory.ini -m shell -a "DEBIAN_FRONTEND=noninteractive \
      NEEDRESTART_MODE=a dpkg -i --force-depends /tmp/packages/*.deb" -b
    
    ansible all -i inventory.ini -m shell -a "DEBIAN_FRONTEND=noninteractive \
      NEEDRESTART_MODE=a apt-get install -fy" -b
    
  • Segera hentikan semua layanan untuk mencegahnya dimulai dengan status default yang tidak dikonfigurasi sebelum otomatisasi siap:

    ansible all -i inventory.ini -m shell -a \
      "systemctl stop patroni etcd pgbouncer postgresql || true" -b
    
  • Terakhir, hapus cluster PostgreSQL default dan data etcd yang ada untuk memungkinkan inisialisasi yang bersih:

    ansible all -i inventory.ini -m shell -a \
      "pg_dropcluster 17 main --stop || true" -b
    ansible all -i inventory.ini -m shell -a \
      "rm -rf /var/lib/postgresql/17/main/* /var/lib/etcd/default.etcd/*" -b
    

Menyediakan infrastruktur database

Dengan VM yang di-bootstrap dan inventaris yang dikonfigurasi, Anda kini dapat menggunakan playbook otomatisasi Autobase dengan Ansible untuk men-deploy stack PostgreSQL yang sangat tersedia.

Pertama, jalankan pemeriksaan pra-penerbangan untuk memastikan lingkungan siap:

ansible-playbook vitabaks.autobase.deploy_pgcluster -i inventory.ini \
  --tags pre_checks

Jika pemeriksaan lulus, lanjutkan dengan deployment penuh:

ansible-playbook vitabaks.autobase.deploy_pgcluster -i inventory.ini

Output yang diharapkan: Playbook harus selesai dengan "PLAY RECAP" yang berhasil dan menampilkan semua VM target sebagai tercapai dan diperbarui:

PLAY RECAP ********************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=254  rescued=0    ignored=0
postgres-vm-1              : ok=160  changed=53   unreachable=0    failed=0    skipped=514  rescued=0    ignored=2
postgres-vm-2              : ok=116  changed=40   unreachable=0    failed=0    skipped=505  rescued=0    ignored=2
postgres-vm-3              : ok=116  changed=40   unreachable=0    failed=0    skipped=505  rescued=0    ignored=2

Memverifikasi deployment

Setelah deployment selesai, Anda harus melakukan beberapa pemeriksaan untuk memastikan semua komponen berfungsi dengan benar.

Memeriksa status HA

Periksa status pengelola ketersediaan tinggi untuk melihat peran yang ditetapkan ke setiap VM:

ansible master -i inventory.ini -m shell -a "patronictl list" -b

Contoh output:

+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member        | Host         | Role    | State     | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Leader  | running   |  1 |             |     |            |     |
| postgres-vm-2 | 10.253.1.253 | Replica | streaming |  1 |   0/6000000 |   0 |  0/6000000 |   0 |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming |  1 |   0/6000000 |   0 |  0/6000000 |   0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+

Memverifikasi endpoint health check

Uji apakah Patroni mengidentifikasi pemimpin dan replika dengan benar menggunakan REST API-nya. Health check VM pemimpin harus menampilkan 200 OK, sedangkan health check VM replika harus menampilkan 503 Service Unavailable:

ansible ${VM1_NAME} -i inventory.ini -m shell -a \
  "curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b

ansible ${VM2_NAME} -i inventory.ini -m shell -a \
  "curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b

ansible ${VM3_NAME} -i inventory.ini -m shell -a \
  "curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b

Memverifikasi kesehatan VM individual

Periksa kesiapan semua instance PostgreSQL:

ansible postgres_cluster -i inventory.ini -m shell -a "pg_isready -p 5432" -b

Contoh output:

postgres-vm-1 | CHANGED | rc=0 >>
/var/run/postgresql:5432 - accepting connections

postgres-vm-2 | CHANGED | rc=0 >>
/var/run/postgresql:5432 - accepting connections

postgres-vm-3 | CHANGED | rc=0 >>
/var/run/postgresql:5432 - accepting connections

Memverifikasi kesehatan etcd

Verifikasi kesehatan lapisan konsensus di semua VM menggunakan localhost sebagai endpoint:

ansible all -i inventory.ini -m shell -a "ETCDCTL_API=3 etcdctl \
  --endpoints=https://localhost:2379 \
  --cacert=/etc/etcd/tls/ca.crt \
  --cert=/etc/etcd/tls/server.crt \
  --key=/etc/etcd/tls/server.key \
  endpoint health" -b

Contoh output:

postgres-vm-1 | CHANGED | rc=0 >>
https://localhost:2379 is healthy: successfully committed proposal: \
took = 28.78311ms

postgres-vm-2 | CHANGED | rc=0 >>
https://localhost:2379 is healthy: successfully committed proposal: \
took = 33.081265ms

postgres-vm-3 | CHANGED | rc=0 >>
https://localhost:2379 is healthy: successfully committed proposal: \
took = 24.414291ms

Mengonfigurasi load balancer global

Untuk menyediakan IP virtual (VIP) yang stabil untuk stack database, konfigurasikan Load Balancer L4 Global native platform menggunakan gdcloud CLI.

  • Prasyarat:

    • Pastikan Anda memiliki peran load-balancer-admin di project Anda.
    • Terapkan label ke VM Anda sehingga load balancer dapat menargetkan instance yang perlu ditayangkan dengan benar (Ganti nilai parameter kubeconfig dengan file kubeconfig API pengelolaan yang sesuai untuk setiap zona):

      kubectl --kubeconfig=ZONE_A_MANAGEMENT_API label VirtualMachine \
        -n ${PROJECT_ID} \
        ${VM1_NAME} \
        ${VM_LABEL}
      
      kubectl --kubeconfig=ZONE_B_MANAGEMENT_API label VirtualMachine \
        -n ${PROJECT_ID} \
        ${VM2_NAME} \
        ${VM_LABEL}
      
      kubectl --kubeconfig=ZONE_C_MANAGEMENT_API label VirtualMachine \
        -n ${PROJECT_ID} \
        ${VM3_NAME} \
        ${VM_LABEL}
      
  • Tentukan tingkat akses load balancing. Tetapkan EXTERNAL jika Anda perlu terhubung dari luar jaringan project, atau INTERNAL jika akses hanya diperlukan dari dalam VPC. Untuk tutorial ini, kita akan menggunakan penyiapan eksternal:

    export LB_SCHEME=EXTERNAL
    
  • Buat health check. Load balancer menggunakan REST API Patroni untuk mengidentifikasi pemimpin:

    gdcloud compute health-checks create https ${CLUSTER_NAME}-hc \
      --project=${PROJECT_ID} \
      --port=8008 \
      --request-path="/primary" \
      --check-interval=10 \
      --timeout=5 \
      --healthy-threshold=2 \
      --unhealthy-threshold=3 \
      --global
    
  • Buat backend zonal terpisah untuk setiap zona tempat VM Anda berada:

    for zone in $(echo $ZONES); do
      gdcloud compute backends create ${CLUSTER_NAME}-backend-${zone} \
        --project=${PROJECT_ID} \
        --zone=${zone} \
        --labels="${VM_LABEL}"
    done
    
  • Buat layanan backend global:

    gdcloud compute backend-services create ${CLUSTER_NAME}-bes \
      --project=${PROJECT_ID} \
      --health-check="${CLUSTER_NAME}-hc" \
      --global
    
  • Tambahkan backend zonal ke layanan global:

    for zone in $(echo $ZONES); do
      gdcloud compute backend-services add-backend ${CLUSTER_NAME}-bes \
        --project=${PROJECT_ID} \
        --backend=${CLUSTER_NAME}-backend-${zone} \
        --backend-zone=${zone} \
        --global
    done
    
  • Buat aturan penerusan global (VIP). Aturan ini mengekspos database di Port 6432:

    gdcloud compute forwarding-rules create ${CLUSTER_NAME}-fr \
      --project=${PROJECT_ID} \
      --load-balancing-scheme=${LB_SCHEME} \
      --backend-service=${CLUSTER_NAME}-bes \
      --ip-protocol-port="TCP:6432" \
      --global
    
  • Ambil alamat VIP:

    LB_IP=$(gdcloud compute forwarding-rules describe ${CLUSTER_NAME}-fr \
      --project=${PROJECT_ID} \
      --load-balancing-scheme=${LB_SCHEME} \
      --global \
      --format=json \
      | jq -r '.metadata.annotations["networking.gke.io/forwardingRuleCIDR"]'  \
      | cut -d '/' -f 1)
    
    echo "The load balancer IP is: ${LB_IP}"
    
  • Buat ProjectNetworkPolicy (PNP) untuk mengizinkan traffic masuk ke port PgBouncer (Ganti nilai parameter kubeconfig dengan file kubeconfig API global lingkungan Anda yang sesuai).

    kubectl --kubeconfig=GLOBAL_API_KUBECONFIG apply -f - <<EOF
    apiVersion: networking.global.gdc.goog/v1
    kind: ProjectNetworkPolicy
    metadata:
      name: allow-pgbouncer
      namespace: ${PROJECT_ID}
    spec:
      ingress:
      - ports:
        - port: 6432
          protocol: TCP
      policyType: Ingress
      subject:
        subjectType: UserWorkload
    EOF
    

Memverifikasi replikasi data

Untuk mengonfirmasi bahwa stack ketersediaan tinggi berfungsi seperti yang diharapkan, Anda dapat membuat data sampel di pemimpin dan memverifikasi keberadaannya di replika.

Menyisipkan data sampel

Otomatisasi menghasilkan sandi acak untuk pengguna postgres selama deployment pertama jika tidak ada yang diberikan di inventory.ini. Anda dapat mengambilnya dari VM mana pun:

export PG_PASSWORD=$(ansible master -i inventory.ini -m shell -a \
  "grep -A10 'authentication:' /etc/patroni/patroni.yml | \
  grep -A3 'superuser' | grep 'password:' | awk '{ print \$2 }'" -b | \
  tail -n 1)
echo $PG_PASSWORD

Hubungkan ke VIP load balancer di port PgBouncer (6432) dan buat tabel sampel:

PGPASSWORD="${PG_PASSWORD}" psql -h ${LB_IP} -p 6432 -U postgres -c "
  CREATE TABLE employees (first_name TEXT, last_name TEXT);
  INSERT INTO employees (first_name, last_name) VALUES ('John', 'Doe');
"

Output yang Diharapkan:

INSERT 0 1

Memverifikasi status replikasi

Jalankan kueri SELECT di semua VM untuk memastikan data telah direplikasi dari pemimpin ke semua replika:

ansible postgres_cluster -i inventory.ini -m shell -a "psql -U postgres -c \
  'SELECT * FROM employees;'" -b

Contoh output:

postgres-vm-1 | CHANGED | rc=0 >>
 first_name | last_name
------------+-----------
 John       | Doe
(1 row)

postgres-vm-2 | CHANGED | rc=0 >>
 first_name | last_name
------------+-----------
 John       | Doe
(1 row)

postgres-vm-3 | CHANGED | rc=0 >>
 first_name | last_name
------------+-----------
 John       | Doe
(1 row)

Menguji pengalihan manual

Pengalihan manual memungkinkan Anda memindahkan peran pemimpin ke VM kandidat tertentu dengan lancar. Hal ini biasanya dilakukan untuk pemeliharaan terencana, upgrade software, atau untuk menyeimbangkan penggunaan resource di seluruh zona.

Mengidentifikasi pemimpin saat ini

Verifikasi peran dan status VM saat ini:

ansible master -i inventory.ini -m shell -a "patronictl list" -b

Contoh output:

+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member        | Host         | Role    | State     | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Leader  | running   |  1 |             |     |            |     |
| postgres-vm-2 | 10.253.1.253 | Replica | streaming |  1 |   0/6000000 |   0 |  0/6000000 |   0 |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming |  1 |   0/6000000 |   0 |  0/6000000 |   0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+

Melakukan pengalihan

Picu pengalihan dari pemimpin saat ini ke VM lain (dalam hal ini masing-masing dari postgres-vm-1 ke postgres-vm-2). Perintah ini menggunakan --force untuk melewati perintah konfirmasi manual:

ansible master -i inventory.ini -m shell -a "patronictl switchover \
  --leader ${VM1_NAME} --candidate ${VM2_NAME} --force" -b

Contoh output:

Successfully switched over to "postgres-vm-2"
+ Cluster: postgres-cluster (7607933704953386478) -+----+-------------+-----+------------+-----+
| Member        | Host         | Role    | State   | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+---------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Replica | stopped |    |     unknown |     |    unknown |     |
| postgres-vm-2 | 10.253.1.253 | Leader  | running |  1 |             |     |            |     |
| postgres-vm-3 | 10.253.1.252 | Replica | running |  1 |   0/70000A0 |   0 |  0/70000A0 |   0 |
+---------------+--------------+---------+---------+----+-------------+-----+------------+-----+

Memverifikasi pergeseran health check

Setelah pengalihan, verifikasi bahwa status health check telah beralih ke pemimpin baru:

ansible ${VM1_NAME} -i inventory.ini -m shell -a \
  "curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b

ansible ${VM2_NAME} -i inventory.ini -m shell -a \
  "curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b

Pemimpin lama (postgres-vm-1) harus menampilkan 503, sedangkan pemimpin baru (postgres-vm-2) harus menampilkan 200.

Menguji failover otomatis

Tidak seperti pengalihan manual, failover otomatis terjadi saat VM pemimpin tidak tersedia. Pengujian ini mengonfirmasi bahwa Patroni memilih pemimpin baru dan load balancer mengalihkan traffic tanpa intervensi manual. Asumsikan bahwa postgres-vm-2 adalah pemimpin saat ini setelah pengalihan manual yang dilakukan sebelumnya.

Mengidentifikasi pemimpin saat ini

Verifikasi peran dan status VM saat ini:

ansible master -i inventory.ini -m shell -a "patronictl list" -b

Contoh output:

+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member        | Host         | Role    | State     | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Replica | streaming |  2 |   0/8000000 |   0 |  0/8000000 |   0 |
| postgres-vm-2 | 10.253.1.253 | Leader  | running   |  2 |             |     |            |     |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming |  2 |   0/8000000 |   0 |  0/8000000 |   0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+

Menyimulasikan kegagalan VM

Hentikan layanan patroni di VM pemimpin untuk menyimulasikan error atau kegagalan yang parah:

ansible master -i inventory.ini -m shell -a "systemctl stop patroni" -b

Mengamati pemilihan baru

Tunggu 10-20 detik dan periksa status dari VM lain untuk melihat promosi pemimpin baru:

ansible replica -i inventory.ini -m shell -a "patronictl list" -b

Contoh output:

postgres-vm-3 | CHANGED | rc=0 >>
+ Cluster: postgres-cluster (7607933704953386478) ---+----+-------------+-----+------------+-----+
| Member        | Host         | Role    | State     | TL | Receive LSN | Lag | Replay LSN | Lag |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+
| postgres-vm-1 | 10.253.1.254 | Leader  | running   |  3 |             |     |            |     |
| postgres-vm-2 | 10.253.1.253 | Replica | stopped   |    |     unknown |     |    unknown |     |
| postgres-vm-3 | 10.253.1.252 | Replica | streaming |  3 |   0/90003F8 |   0 |  0/90003F8 |   0 |
+---------------+--------------+---------+-----------+----+-------------+-----+------------+-----+

Anda akan melihat bahwa salah satu VM lainnya (postgres-vm-1 atau postgres-vm-3) telah menjadi pemimpin dan pemimpin lama (postgres-vm-2) ditandai sebagai dihentikan.

Memverifikasi pergeseran health check

Konfirmasi bahwa health check load balancer kini akan mengidentifikasi pemimpin yang baru terpilih dengan benar:

ansible ${VM1_NAME} -i inventory.ini -m shell -a \
  "curl -ks -o /dev/null -w '%{http_code}' https://localhost:8008/primary" -b

Pemimpin baru harus menampilkan 200 OK.

Memulihkan VM yang gagal

Mulai ulang layanan patroni di VM asli agar dapat bergabung kembali dengan stack sebagai replika dan mengejar data yang terlewat:

ansible ${VM2_NAME} -i inventory.ini -m shell -a \
  "systemctl start patroni" -b