بلاگ/راه‌اندازی Replication و Failover خودکار در PostgreSQL روی دو سرور مجازی و اختصاصی
به‌روز شده

راه‌اندازی Replication و Failover خودکار در PostgreSQL روی دو سرور مجازی و اختصاصی

1405/06/080 بازدید
راه‌اندازی Replication و Failover خودکار در PostgreSQL روی دو سرور مجازی و اختصاصی

راه‌اندازی Replication و Failover خودکار در PostgreSQL روی دو سرور مجازی و اختصاصی

نمای معماری Primary و Standby در PostgreSQL

خرابی یک سرور نباید لزوماً به معنی توقف پایگاه داده باشد. PostgreSQL با قابلیت Streaming Replication می‌تواند تغییرات ثبت‌شده روی سرور اصلی را به یک سرور آماده‌به‌کار منتقل کند. ابزار repmgr نیز وضعیت نودها را زیر نظر می‌گیرد و در صورت قطع‌شدن سرور اصلی، نود standby را به primary جدید ارتقا می‌دهد. در این راهنما، راه‌اندازی replication در PostgreSQL را روی دو سرور مبتنی بر Ubuntu یا Debian بررسی می‌کنیم. سرور اول primary و سرور دوم standby خواهد بود. سپس repmgr را برای failover خودکار پیکربندی می‌کنیم. این معماری نقطه شروع مناسبی برای سرویس‌هایی است که به زمان بازیابی کوتاه نیاز دارند، اما یک محدودیت مهم دارد: در کلاستر دو نودی، قطع ارتباط شبکه می‌تواند با خرابی واقعی primary اشتباه گرفته شود. بنابراین برای محیط حساس باید fencing، یک witness یا سامانه هماهنگ‌کننده دیگری نیز در نظر گرفته شود.

معماری و پیش‌نیازها

در مثال‌های این مقاله از مشخصات زیر استفاده می‌کنیم:

نقشنام میزبانآدرس خصوصیسیستم‌عامل
Primarypg-primary10.10.0.11Ubuntu 24.04
Standbypg-standby10.10.0.12Ubuntu 24.04
پورت PostgreSQL5432

هر دو سرور باید نسخه اصلی یکسانی از PostgreSQL داشته باشند. دستورها با PostgreSQL 16 نوشته شده‌اند؛ اگر نسخه دیگری نصب کرده‌اید، عدد 16 را در مسیرها و نام سرویس‌ها تغییر دهید. پیش از شروع این موارد را بررسی کنید: - ساعت دو سرور با NTP همگام باشد. - نام میزبان‌ها به آدرس خصوصی درست resolve شود. - پورت 5432 فقط میان نودهای مجاز باز باشد. - دیسک standby حداقل به اندازه فضای موردنیاز primary ظرفیت داشته باشد. - روی هر دو نود دسترسی کاربر دارای مجوز sudo داشته باشید. - از داده‌های مهم نسخه پشتیبان مستقل تهیه شده باشد. Replication جای backup را نمی‌گیرد. حذف اشتباه یک جدول یا داده روی primary معمولاً روی standby نیز تکرار می‌شود. برای پشتیبان مستقل و رمزنگاری‌شده می‌توانید از پشتیبان‌گیری خودکار با Restic استفاده کنید.

انتخاب بین Replication همگام و ناهمگام

Streaming Replication در حالت پیش‌فرض ناهمگام است. primary پس از ثبت محلی تراکنش، پاسخ موفق می‌دهد و منتظر تأیید standby نمی‌ماند. این حالت تأخیر کمتری دارد، اما هنگام خرابی ناگهانی ممکن است آخرین تراکنش‌هایی که هنوز منتقل نشده‌اند از دست بروند.

حالتمزیتمحدودیتکاربرد متداول
ناهمگامتأخیر کمتر و تحمل اختلال standbyاحتمال ازدست‌رفتن چند تراکنش آخروب‌سایت‌ها و بارهای عمومی
همگامکاهش احتمال ازدست‌رفتن تراکنش تأییدشدهافزایش تأخیر و احتمال توقف نوشتنداده‌های حساس به RPO
همگام با چند standbyانعطاف بیشتر در تأیید تراکنشنیازمند حداقل سه نود و طراحی پیچیده‌ترکلاسترهای حساس

در معماری دو سروری، فعال‌کردن replication همگام می‌تواند هنگام از دسترس خارج‌شدن standby باعث توقف عملیات نوشتن شود. به همین دلیل این راهنما ابتدا حالت ناهمگام را پیاده می‌کند.

نصب PostgreSQL و repmgr

این راهنما توزیع‌های مبتنی بر Debian را در نظر گرفته است؛ روی خانوادهٔ RHEL مسیر نصب PostgreSQL کمی فرق دارد و نمونه‌اش در نصب PostgreSQL روی CentOS ۷ آمده است. روی هر دو سرور، PostgreSQL و بسته repmgr سازگار با نسخه نصب‌شده را نصب کنید. نام دقیق بسته ممکن است به مخزن توزیع یا مخزن رسمی PostgreSQL وابسته باشد:

sudo apt update
sudo apt install postgresql-16 postgresql-16-repmgr

نسخه‌ها را کنترل کنید:

psql --version
repmgr --version

نام نودها را در /etc/hosts هر دو سرور ثبت کنید تا ارتباط به DNS خارجی وابسته نباشد:

10.10.0.11 pg-primary
10.10.0.12 pg-standby

با دستورهای زیر برقراری ارتباط را آزمایش کنید:

getent hosts pg-primary
getent hosts pg-standby

آماده‌سازی نود Primary

تنظیم پارامترهای PostgreSQL

روی pg-primary فایل /etc/postgresql/16/main/postgresql.conf را ویرایش کنید:

listen_addresses = '*'
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
wal_keep_size = '1GB'
hot_standby = on
archive_mode = on
archive_command = '/bin/true'

مقدار wal_keep_size باید با نرخ تولید WAL و مدت احتمالی قطع ارتباط هماهنگ شود. برای جلوگیری مطمئن‌تر از حذف زودهنگام WAL می‌توان از replication slot استفاده کرد، ولی slot بدون مانیتورینگ ممکن است در زمان قطع طولانی standby دیسک primary را پر کند. در فایل /etc/postgresql/16/main/pg_hba.conf دسترسی شبکه خصوصی را اضافه کنید:

host    replication    repmgr    10.10.0.11/32    scram-sha-256
host    replication    repmgr    10.10.0.12/32    scram-sha-256
host    repmgr          repmgr    10.10.0.11/32    scram-sha-256
host    repmgr          repmgr    10.10.0.12/32    scram-sha-256

اگر شبکه خصوصی بزرگ‌تری دارید، به‌جای بازکردن کل subnet فقط آدرس نودهای شناخته‌شده را مجاز کنید.

ساخت کاربر و دیتابیس repmgr

وارد psql شوید:

sudo -u postgres psql

کاربر و دیتابیس مدیریتی را بسازید:

CREATE ROLE repmgr WITH LOGIN REPLICATION SUPERUSER PASSWORD 'یک-رمز-طولانی-و-تصادفی';
CREATE DATABASE repmgr OWNER repmgr;
\q

در بسیاری از نصب‌ها repmgr برای انجام عملیات مدیریتی به دسترسی سطح بالا نیاز دارد. رمز را در shell history یا فایل قابل‌خواندن برای همه کاربران قرار ندهید. می‌توانید آن را در فایل .pgpass متعلق به کاربر سیستم‌عامل postgres ذخیره کنید:

pg-primary:5432:repmgr:repmgr:رمز
pg-standby:5432:repmgr:repmgr:رمز

مجوز فایل باید محدود باشد:

sudo chmod 600 /var/lib/postgresql/.pgpass
sudo chown postgres:postgres /var/lib/postgresql/.pgpass

سرویس را راه‌اندازی مجدد کنید:

sudo systemctl restart postgresql

پیکربندی repmgr روی Primary

فایل /etc/repmgr.conf را روی primary بسازید یا ویرایش کنید:

node_id=1
node_name='pg-primary'
conninfo='host=pg-primary user=repmgr dbname=repmgr connect_timeout=2'
data_directory='/var/lib/postgresql/16/main'
failover='automatic'
promote_command='/usr/bin/repmgr standby promote -f /etc/repmgr.conf --log-to-file'
follow_command='/usr/bin/repmgr standby follow -f /etc/repmgr.conf --log-to-file --upstream-node-id=%n'
monitoring_history=yes
reconnect_attempts=6
reconnect_interval=10
log_level='INFO'
log_file='/var/log/postgresql/repmgr.log'

سپس primary را در متادیتای کلاستر ثبت کنید:

sudo -u postgres repmgr -f /etc/repmgr.conf primary register
sudo -u postgres repmgr -f /etc/repmgr.conf cluster show

خروجی باید نود pg-primary را با نقش primary نشان دهد.

ساخت Standby از روی Primary

جریان یک‌طرفهٔ داده از سرور primary به سرور standby در Streaming Replication پستگرس

روی pg-standby ابتدا سرویس PostgreSQL را متوقف کنید:

sudo systemctl stop postgresql

پیکربندی /etc/repmgr.conf را با شناسه متفاوت قرار دهید:

node_id=2
node_name='pg-standby'
conninfo='host=pg-standby user=repmgr dbname=repmgr connect_timeout=2'
data_directory='/var/lib/postgresql/16/main'
failover='automatic'
promote_command='/usr/bin/repmgr standby promote -f /etc/repmgr.conf --log-to-file'
follow_command='/usr/bin/repmgr standby follow -f /etc/repmgr.conf --log-to-file --upstream-node-id=%n'
monitoring_history=yes
reconnect_attempts=6
reconnect_interval=10
log_level='INFO'
log_file='/var/log/postgresql/repmgr.log'

پیش از clone، اتصال را بدون ایجاد تغییر آزمایش کنید:

sudo -u postgres repmgr -h pg-primary -U repmgr -d repmgr \
  -f /etc/repmgr.conf standby clone --dry-run

اگر آزمایش موفق بود، clone واقعی را اجرا کنید:

sudo -u postgres repmgr -h pg-primary -U repmgr -d repmgr \
  -f /etc/repmgr.conf standby clone

سرویس را بالا بیاورید و standby را ثبت کنید:

sudo systemctl start postgresql
sudo -u postgres repmgr -f /etc/repmgr.conf standby register
sudo -u postgres repmgr -f /etc/repmgr.conf cluster show

برای اطمینان از قرارداشتن سرور در حالت بازیابی، روی standby اجرا کنید:

sudo -u postgres psql -tAc "SELECT pg_is_in_recovery();"

خروجی باید t باشد. روی primary نیز وضعیت جریان replication را ببینید:

sudo -u postgres psql -x -c "
SELECT application_name, client_addr, state, sync_state,
       write_lag, flush_lag, replay_lag
FROM pg_stat_replication;"

مقدار state در شرایط عادی باید streaming باشد.

آزمایش انتقال داده

روی primary یک جدول آزمایشی بسازید:

sudo -u postgres psql -d postgres -c "
CREATE TABLE replication_test (
  id bigserial PRIMARY KEY,
  created_at timestamptz DEFAULT now()
);
INSERT INTO replication_test DEFAULT VALUES;"

سپس روی standby داده را بخوانید:

sudo -u postgres psql -d postgres -c "TABLE replication_test;"

اگر رکورد نمایش داده شد، جریان WAL کار می‌کند. برای سنجش عقب‌ماندگی نیز می‌توانید اختلاف LSNها را بررسی کنید:

SELECT pg_size_pretty(
  pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn)
) AS replication_lag
FROM pg_stat_replication;

فعال‌کردن Failover خودکار با repmgrd

روی هر دو سرور، سرویس daemon را فعال کنید. نام سرویس ممکن است در بسته توزیع شما repmgrd یا نسخه‌دار باشد:

sudo systemctl enable --now repmgrd
sudo systemctl status repmgrd

وضعیت را دوباره کنترل کنید:

sudo -u postgres repmgr -f /etc/repmgr.conf service status
sudo -u postgres repmgr -f /etc/repmgr.conf cluster show

برای آزمون برنامه‌ریزی‌شده، ابتدا مطمئن شوید standby عقب‌ماندگی قابل‌توجهی ندارد. سپس PostgreSQL را روی primary متوقف کنید:

sudo systemctl stop postgresql

پس از گذشت زمان تشخیص، روی standby اجرا کنید:

sudo -u postgres repmgr -f /etc/repmgr.conf cluster show
sudo -u postgres psql -tAc "SELECT pg_is_in_recovery();"

اگر failover موفق باشد، pg-standby با نقش primary نمایش داده می‌شود و تابع pg_is_in_recovery() مقدار f برمی‌گرداند.

چرا Failover به‌تنهایی کافی نیست؟

repmgr نود standby را ارتقا می‌دهد، اما الزاماً ترافیک برنامه را به primary جدید هدایت نمی‌کند. برنامه باید از یک endpoint ثابت مانند HAProxy، PgBouncer همراه با health check، DNS مدیریت‌شده یا یک virtual IP استفاده کند. موضوع مهم‌تر، جلوگیری از split-brain است. اگر primary هنوز روشن باشد ولی ارتباط میان دو سرور قطع شود، standby ممکن است خود را primary کند و هر دو نود نوشتن بپذیرند. در محیط عملیاتی یکی از این راهکارها را اضافه کنید: - یک نود witness در محل یا شبکه‌ای مستقل - fencing از طریق API زیرساخت برای خاموش‌کردن نود مشکوک - watchdog یا سامانه اجماع مناسب - محدودکردن مسیر نوشتن از طریق load balancer و health check دقیق در معماری دو نودی، failover خودکار بدون fencing انتخاب امنی برای داده‌های حساس نیست. اگر witness ندارید، failover دستی با رویه مشخص گاهی ریسک کمتری دارد.

بازگرداندن Primary قدیمی به کلاستر

پس از failover، primary قبلی را بدون بررسی دوباره روشن و وارد مدار نکنید. timeline آن با primary جدید متفاوت شده است و ممکن است داده‌های قدیمی داشته باشد. اگر WAL لازم هنوز موجود باشد، می‌توان از pg_rewind از طریق repmgr استفاده کرد:

sudo -u postgres repmgr -f /etc/repmgr.conf node rejoin \
  -d 'host=pg-standby user=repmgr dbname=repmgr' \
  --force-rewind

اگر rewind ممکن نبود، نود قدیمی را پاک‌سازی و دوباره از primary فعلی clone کنید. پیش از هرکدام از این عملیات، جهت replication و هویت primary جدید را با repmgr cluster show کنترل کنید.

مانیتورینگ و نگهداری

بعد از راه‌اندازی replication در PostgreSQL، این شاخص‌ها را مانیتور کنید: - وضعیت streaming و زمان آخرین دریافت WAL - فاصله LSN میان primary و standby - مصرف دیسک پوشه WAL - تعداد و وضعیت replication slotها - دسترسی به هر دو نود و endpoint برنامه - رویدادهای promotion، follow و rejoin در لاگ repmgr - اعتبار backup و امکان restore واقعی این شاخص‌ها را می‌توانید با Prometheus Node Exporter جمع‌آوری و برای آن‌ها آستانهٔ هشدار تعریف کنید. هشدار باید پیش از پرشدن دیسک یا افزایش شدید replication lag صادر شود. آزمون failover نیز باید دوره‌ای و در بازه نگهداری انجام شود؛ پیکربندی‌ای که هیچ‌وقت آزمایش نشده، تضمینی برای بازیابی سرویس نیست.

انتخاب زیرساخت مناسب برای PostgreSQL

برای بارهای سبک تا متوسط، دو VPS با شبکه خصوصی و دیسک سریع می‌توانند شروع مناسبی باشند. در بارهای سنگین، دیتابیس‌های پرتراکنش یا زمانی که رفتار پایدار I/O اهمیت زیادی دارد، سرور اختصاصی کنترل بیشتری روی منابع می‌دهد. بهتر است دو نود در میزبان فیزیکی یا دامنه خرابی یکسان قرار نگیرند. همچنین کیفیت شبکه میان primary و standby مستقیماً بر replication lag اثر دارد. برای ساخت نودهای این معماری می‌توانید مشخصات و گزینه‌های سرور ابری آریاسرویس را بررسی کنید و منابع CPU، RAM و فضای ذخیره‌سازی را بر اساس بار واقعی PostgreSQL انتخاب کنید.

جمع‌بندی

Streaming Replication نسخه‌ای نزدیک به لحظه از داده‌های primary را روی standby نگه می‌دارد و repmgr ثبت نودها، مانیتورینگ و promotion را ساده‌تر می‌کند. اجرای درست این معماری به تنظیم PostgreSQL محدود نیست؛ مسیر اتصال برنامه، مانیتورینگ WAL، backup، آزمون بازیابی و کنترل split-brain نیز بخشی از کار هستند. برای سرویس غیرحساس می‌توان کار را با دو نود و failover کنترل‌شده آغاز کرد. برای محیط production حساس، افزودن witness یا fencing و استفاده از endpoint ثابت برای اتصال برنامه ضروری است.