سلام
بعد از مدت ها با یه تجربه نسبتا سخت در بخش کار با دیتابیس ها یه مطلب چند قسمتی اماده کردم.
بی مقدمه میرم سر اصل مطلب. قراره در مورد دو تا دیتابیس influxdb و postgres بنویسم و تجربه کار باهاشون رو برای بحث های HA و رپلیکا بهتون بگم.
برای این که پست خیلی حوصله سر بر و خسته کننده نباشه اول میریم سراغ پیاده سازی یه رپلیکیشن ساده از پستگرس. دلیلی که من این پستو مینویسم اینه که توی نسخه های جدیدتر پستگرس مدل رپلیکیشن عوض شده و من خودم مطلب خوب راجبش حتی به زبان انگلیسی پیدا نکردم و دوست داشتم تجربه ای که داشتم رو در اختیارتون بزارم.
در ادامه در مورد تئوری و مقدماتش هم مینویسم و مدل های مختلفی که میشه انجامش داد رو هم میگم.
اول بریم سراغ اینکه چرا پایگاه داده ما نیاز به رپلیکیشن پیدا کرد. ما به صورت عادی از رپلیکیشن دیتابیس برای سرویس هامون استفاده نمیکنیم و این در واقع یک سولوشن موقت برای یه مشکل جابجایی سریع بود. ما بحث down time برامون خیلی مهم بود و توی یک فرصت خیلی محدود مجبور بودیم زیر ساختمونو جابجا کنیم.
ما چند تا راه حل داشتیم. یکی از راه حل ها این بود که سرور مبدا رو خاموش بکنیم و دیسک رو کامل منتقل بکنیم و سرور مقصد رو روشن بکنیم که این حداقل چند ساعت زمان میبرد و البته ریسک این هم وجود داشت که پروسه جابجایی به هر دلیلی به مشکل بخوره و عملا فقط ما چند ساعت سیستممون پایین بود و کمکی بهمون نشده بود.
یه راه حل بهتر این بود که ما فقط بکاپ دیتابیس رو داشته باشیم و اون قاعدتا خیلی سریع تر جابجا میشه و بکاپ رو ریستور بکنیم و این هم حدودا نیم ساعت زمان میبرد.
شاید با خودتون فکر کنید که خب نیم ساعت که چیزی نیست ولی دو تا نکته مهم وجود داره . برای بعضی کسب و کار ها نیم ساعت خیلی مهمه و نکته دیگه اینه که فقط این سرویس نیست . ما بخش های دیگه و اجزای دیگه ای از سرویس هامون هم وجود داشتن که اگر با این رویکرد میخواستیم جابجاشون کنیم در نهایت یک روز کامل مجبور بودیم سیستممون رو خاموش کنیم یا یه اختلال ناخوشاید داشته باشیم.
من دنبال یه راه حل بهتر بودم که اصلا down time نداشته باشیم. قبلا با بحث های رپلیکیشن یه اشنایی مختصر داشتم ولی هیچ وقت به صورت جدی این کار رو انجام نداده بودم و مجبور شدم که توی این فرصت کوتاه و پر استرس این کار رو انجام بدم.
برای بحث رپلیکیشن نسخه پستگرسی که ازش استفاده میکنید خیلی مهمه و تغییرات جدی توی نسخه های مختلف داشته.
اول از همه باید ببینیم که رپلیکیشنی که من استفاده کردم چجوری کار میکنه. به دیاگرام دقت کنید
همونطور که میبینید تمام درخواست های write باید به primary بره ولی برای read محدودیتی نداریم. به هر دو سرور میتونیم درخواست بزنیم
البته چون مدل async رو داریم اجرا میکنیم ممکنه رپلیکا دیرتر اپدیت بشه.
این مدل برای کاری که من میخواستم انجام بدم خوب بود. چون من نمیخواستم هیچ رایتی روی رپلیکا انجام بدم تا زمانی که سرور پرایمری رو خاموش بکنم و از طرفی تاخیر اپدیت شدن هم برام اهمیتی نداشت(در حد اینکه رپلیکا تبدیل به پرایمری بشه و سرویس ها اپدیت بشن)
خب بریم سراغ اینکه این کار رو چجوری باید انجام بدیم. من چون تو بستر داکر این کار رو میخواستم بکنم چالش هایی که احتمال داره با داکر داشته باشید هم بهتون میگم و فرض میکنیم که دو تا کانتینر به نام های primary-psql , replica-psql داریم. خب اولین کاری که باید انجام بدیم باز کردن دسترسی های سرور پرایمری هستش. و تنظیم یک یوزر با رول replica که بتونه از سرور پرایمری دیتا بگیره.
با فرض اینکه primary-psql یک کانتینر در حال اجرا هستش یوزر ریپلیکا رو براش میسازیم و پالیسیش رو تغییر میدیم.
#/bin/bash docker exec -it primary-psql bash
#inside docker container ## run postgres shell psql -U postgres ## create replica user CREATE ROLE replica_user WITH REPLICATION LOGIN ENCRYPTED PASSWORD 'REPLICADIFFICULTPASSWORD';
برای تغییر در فایل pg_hba.conf چون داخل کانتینتر ویرایشگر متنی نداریم از خارج کانتینتر فایلی که داخل والیومی که برای دیتای پستگرس ساخته شده رو ادیت میکنیم. فرض کنید اسم والیوم من primary-psql-data باشه. اگر روی سیستم خودتون برای تست این کار رو انجام میدید میتونید از قابلیت های داکر دسکتاپ استفاده بکنید.
sudo vim /var/lib/docker/volumes/primary-psql-data/_data/pg_hba.conf
خط پایین رو به این فایل اضافه میکنیم. هدف از اینکار اینه که دسترسی استریم کردن دیتا رو به یوزری که الان ساختیم اضافه بکنیم.
host replication replica_user 0.0.0.0/0 md5
کانتینر primary-psql رو ریستارت میکنیم.
حالا باید بریم سراغ کانتینر رپلیکا. فرض کنید یه کانتینر در حال اجرا به نام secondary-psql داریم. اول باید کانتینر رو استاپ بکنیم چون قراره پوشه /var/lib/postgresql/data رو با base_backup از نو بسازیم و یک فایل به نام standby.signal در دایرکتوری /var/lib/postgresql/data ایجاد بکنیم. البته برای فایل standby.signal میتونیم از سوییچ R استفاده بکنیم که خودش اتوماتیک این فایل رو بسازه. ولی در کل باید حواسمون باشه که این فایل حتما وجود داشته باشه.
در مورد سوییچ R این نکته رو هم اضافه بکنم که توی فایل postgresql.auto.conf میاد و اطلاعات کانکت شدن رو هم اضافه میکنه و نیازی نیست دیگه دستی اینو توی فایل postgresql.conf تغییرش بدیم.
docker stop secondary-psql docker run --rm --volume postgres_secondary-psql-data:/secondary-data --network=postgres_default -it postgres:15 bash
pg_basebackup -D /secondary-data -P -R -h primary-psql -U replica_user -W # Enter replica_user password (REPLICADIFFICULTPASSWORD) touch /secondary-data/standby.signal (run above command without -R switch) exit
اگر از سوییچ -R استفاده نکردیم باید یک سری تغییرات روی این فایل ها بدیم و کانتینر رو ران بکنیم
فایل postgresql.conf رو باید ادیت بکنیم و primary_conninfo رو ویرایش بکنیم.
# primary_conninfo = '' primary_conninfo = 'host=primary-psql port=5432 user=replica_user password=REPLICADIFFICULTPASSWORD'
خب الان باید کانتینتر رپلیکا رو استارت بکنیم.
docker start secondary-psql
برای این که مطمئن بشیم رپلیکا تونسته به پرایمری وصل بشه میتونیم از دستور زیر داخل کانتینتر پرایمری استفاده بکنیم.
select * from pg_stat_replication;
اگر هیچ سطری وجود نداشته باشه یعنی سرور رپلیکا نتونسته وصل بشه اما اگر یک سطر مطابق با اطلاعات سرور رپلیکا وجود داشته باشه فرایند درست انجام شده.
pid | usesysid | usename | application_name | client_addr | client_hostname | client_port | backend_start | backend_xmin | state | sent_lsn | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state | reply_time
-----+----------+--------------+------------------+-------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+-----------+------------+-----------------+-----------------+-----------------+---------------+------------+------------------------------
34 | 16388 | replica_user | walreceiver | 172.18.0.2 | | 37088 | 2024-05-31 08:24:12.693549+00 | | streaming | 0/B000850 | 0/B000850 | 0/B000850 | 0/B000850 | 00:00:00.000284 | 00:00:00.003262 | 00:00:00.003376 | 0 | async | 2024-05-31 08:57:14.61691+00
همچنین داخل سرور رپلیکا هم میتونیم چک بکنیم که برای اتصال به سرور پرایمری داره از چه کانفیگ هایی استفاده میکنه. با دستور زیر میتونیم چک بکنیم.
select * from pg_stat_wal_receiver;
pid | status | receive_start_lsn | receive_start_tli | written_lsn | flushed_lsn | received_tli | last_msg_send_time | last_msg_receipt_time | latest_end_lsn | latest_end_time | slot_name | sender_host | sender_port | conninfo
-----+-----------+-------------------+-------------------+-------------+-------------+--------------+-------------------------------+------------------------------+----------------+-------------------------------+-----------+--------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
38 | streaming | 0/F000000 | 1 | 0/F000C40 | 0/F000C40 | 1 | 2024-06-06 15:56:28.035265+00 | 2024-06-06 15:56:28.035474+00 | 0/F000C40 | 2024-06-06 15:54:57.999413+00 | | primary-psql | 5432 | user=replica_user password=******** channel_binding=prefer dbname=replication host=primary-psql port=5432 fallback_application_name=walreceiver sslmode=prefer sslcompression=0 sslcertmode=allow sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres gssdelegation=0 target_session_attrs=any load_balance_hosts=disable
(1 row)
خب تا اینجا ما الان دو تا کانتینر داریم که دیتاهاشون به صورت async سینک میشه.
این برای نیاز من میتونم بگم اوکی بوده. چون خودم قرار بود سرور پرایمری رو خاموش کنم و سریع بین دیتابیس ها سوییچ بکنم. قاعدتا من توی این یه سری پست ها نمیخوام فقط به حالتی که خودم انجام داده بودم بسنده بکنم و میخوام یه کنجکاوی درباره روش های دیگری که میشد انجام داد انجام بدم و سعی بکنم عمق دانشم توی این حوزه رو بیشتر بکنم.
برای چک کردن این که الان توی حالت رپلیکا هستیم یا نه میتونیم از دستور زیر استفاده بکنیم.
SELECT pg_is_in_recovery(); ------------------------------------------------------------- pg_is_in_recovery
-------------------
t
(1 row)
حالا فرض کنید سرور پرایمری ما به هر دلیل مشکل میخوره یا توی سناریویی که من داشتم میخواستم خودم خاموشش بکنم. الان ما دسترسی نوشتن روی سرور رپلیکا رو نداریم و خطا میگیریم. با دستور زیر میتونیم سرور رو تبدیل به سرور پرایمری بکنیم و اگر اینکار رو انجام بدیم دیگه راه برگشت به حالت قبل رو نداریم. در مورد اینکه بعد از اجرا کردن این دستور چه اتفاقی میوفته توی پست های بعدی بیشتر توضیح میدم و روند برگشتنش رو هم بررسی خواهیم کرد
SELECT pg_promote();
قاعدتا این چیزی که بالا نوشتم روش اول و ساده ترین راه بود که میتونستیم توی اون زمان انجام بدیم. دوست دارم در ادامه این سری پست ها درباره ی هر کدوم بیشتر اطلاعات کسب کنم و نتیجه اش رو با شما در میون بزارم.
چند نکته:
در پست بعدی میخوام برم سراغ تغییر وضعیت از async به sync و یه مقدار با پارامتر هایی که داره بازی بکنیم و ببینیم چه اتفاقی توی هر کدوم از حالت ها اتفاق میوفته و چالش هاش چیه.
ممنون که تا اینجا همراه بودید.
خوشحال میشم اگر سوالی داشتید در کامنت ها بپرسید و یا اشتباهی وجود داشت بهم بگید که اصلاحش بکنم.
من زمانی که قرار بود این کار رو انجام بدم از چند منبع که در ادامه اوردم استفاده کردم ولی برای نوشتن این پست رفتم سراغ داک رسمی خود پستگرس که خیلی کامل توضیح داده . لینکشو میزارم حتما یه سر بهش بزنید.