در مقاله به شما خواهم گفت که چگونه به مسئله تحمل خطا PostgreSQL برخورد کردیم، چرا برای ما مهم شد و در نهایت چه اتفاقی افتاد.
ما یک سرویس پر بار داریم: 2,5 میلیون کاربر در سراسر جهان، 50 هزار کاربر فعال هر روز. سرورها در آمازون در یک منطقه از ایرلند قرار دارند: بیش از 100 سرور مختلف دائماً کار می کنند که تقریباً 50 مورد آن با پایگاه داده هستند.
کل باطن یک برنامه جاوا حالت یکپارچه بزرگ است که یک اتصال وب سوکت ثابت را با مشتری حفظ می کند. وقتی چندین کاربر به طور همزمان روی یک برد کار می کنند، همه آنها تغییرات را در زمان واقعی مشاهده می کنند، زیرا ما هر تغییر را در پایگاه داده می نویسیم. ما در هر ثانیه حدود 10 هزار درخواست به پایگاه داده خود داریم. در اوج بار در Redis، ما 80-100 هزار درخواست در ثانیه می نویسیم.
چرا ما از Redis به PostgreSQL تغییر مکان دادیم
در ابتدا، سرویس ما با Redis کار می کرد، یک فروشگاه با ارزش کلید که تمام داده ها را در RAM سرور ذخیره می کند.
مزایای Redis:
- سرعت پاسخگویی بالا، زیرا همه چیز در حافظه ذخیره می شود.
- سهولت پشتیبان گیری و تکرار.
معایب Redis برای ما:
- هیچ معامله واقعی وجود ندارد. ما سعی کردیم آنها را در سطح برنامه خود شبیه سازی کنیم. متأسفانه، این همیشه خوب کار نمی کرد و نیاز به نوشتن کد بسیار پیچیده داشت.
- مقدار داده به مقدار حافظه محدود می شود. با افزایش مقدار داده، حافظه افزایش می یابد، و در پایان، با ویژگی های نمونه انتخاب شده مواجه می شویم، که در AWS نیاز به توقف سرویس ما برای تغییر نوع نمونه دارد.
- لازم است دائماً سطح تأخیر پایینی حفظ شود، زیرا. ما تعداد بسیار زیادی درخواست داریم. سطح تاخیر بهینه برای ما 17-20 میلی ثانیه است. در سطح 30-40 میلیثانیه، ما پاسخهای طولانی به درخواستهای برنامه کاربردی خود و کاهش خدمات دریافت میکنیم. متأسفانه، این اتفاق در سپتامبر 2018 برای ما افتاد، زمانی که یکی از موارد با Redis به دلایلی 2 برابر بیشتر از حد معمول تاخیر دریافت کرد. برای حل این مشکل، سرویس را در اواسط روز به دلیل تعمیر و نگهداری برنامهریزی نشده متوقف کردیم و نمونه مشکلساز Redis را جایگزین کردیم.
- به راحتی می توان ناسازگاری داده ها را حتی با اشتباهات جزئی در کد دریافت کرد و سپس زمان زیادی را صرف نوشتن کد برای تصحیح این داده ها کرد.
ما معایب را در نظر گرفتیم و متوجه شدیم که باید به سمت چیزی راحت تر، با تراکنش های معمولی و وابستگی کمتر به تاخیر حرکت کنیم. تحقیقات انجام داد، بسیاری از گزینه ها را تجزیه و تحلیل کرد و PostgreSQL را انتخاب کرد.
ما 1,5 سال است که به یک پایگاه داده جدید نقل مکان کرده ایم و تنها بخش کوچکی از داده ها را منتقل کرده ایم، بنابراین اکنون به طور همزمان با Redis و PostgreSQL کار می کنیم. اطلاعات بیشتر در مورد مراحل جابجایی و جابجایی داده ها بین پایگاه های داده در نوشته شده است
وقتی برای اولین بار حرکت را شروع کردیم، برنامه ما مستقیماً با پایگاه داده کار می کرد و به Master Redis و PostgreSQL دسترسی داشت. خوشه PostgreSQL از یک Master و یک Replica با تکرار ناهمزمان تشکیل شده است. طرح پایگاه داده به این صورت بود:
پیاده سازی PgBouncer
در حالی که ما در حال حرکت بودیم، محصول نیز در حال توسعه بود: تعداد کاربران و تعداد سرورهایی که با PostgreSQL کار می کردند افزایش یافت و ما شروع به کمبود اتصال کردیم. PostgreSQL یک فرآیند جداگانه برای هر اتصال ایجاد می کند و منابع را مصرف می کند. شما می توانید تعداد اتصالات را تا یک نقطه خاص افزایش دهید، در غیر این صورت این شانس وجود دارد که عملکرد پایگاه داده کمتر از حد مطلوب باشد. گزینه ایده آل در چنین شرایطی انتخاب یک مدیر اتصال است که در مقابل پایه قرار می گیرد.
ما دو گزینه برای مدیر اتصال داشتیم: Pgpool و PgBouncer. اما اولی حالت تراکنشی کار با پایگاه داده را پشتیبانی نمی کند، بنابراین PgBouncer را انتخاب کردیم.
ما طرح کار زیر را تنظیم کردهایم: برنامه ما به یک PgBouncer دسترسی دارد که در پشت آن Masters PostgreSQL قرار دارد و پشت هر Master یک Replica با تکرار ناهمزمان وجود دارد.
در عین حال نمیتوانستیم کل اطلاعات را در PostgreSQL ذخیره کنیم و سرعت کار با پایگاه داده برای ما مهم بود، بنابراین شروع به اشتراک گذاری PostgreSQL در سطح برنامه کردیم. طرحی که در بالا توضیح داده شد برای این کار نسبتاً راحت است: هنگام اضافه کردن یک قطعه جدید PostgreSQL، کافی است پیکربندی PgBouncer را به روز کنید و برنامه می تواند بلافاصله با قطعه جدید کار کند.
خطای PgBouncer
این طرح تا لحظه ای که تنها نمونه PgBouncer از بین رفت کار کرد. ما در AWS هستیم، جایی که همه نمونهها روی سختافزاری اجرا میشوند که بهطور دورهای از بین میروند. در چنین مواردی، نمونه به سادگی به سخت افزار جدید منتقل می شود و دوباره کار می کند. این اتفاق با PgBouncer رخ داد، اما در دسترس نبود. نتیجه این پاییز عدم دسترسی به سرویس ما به مدت 25 دقیقه بود. AWS استفاده از افزونگی سمت کاربر را برای چنین مواقعی توصیه می کند که در آن زمان در کشور ما پیاده سازی نشده بود.
پس از آن، ما به طور جدی در مورد تحمل خطای خوشه های PgBouncer و PostgreSQL فکر کردیم، زیرا وضعیت مشابهی ممکن است با هر نمونه ای در حساب AWS ما اتفاق بیفتد.
ما طرح تحمل خطا PgBouncer را به صورت زیر ساختیم: همه سرورهای برنامه به Network Load Balancer دسترسی دارند که پشت آن دو PgBouncer وجود دارد. هر PgBouncer به همان Master PostgreSQL هر قطعه نگاه می کند. اگر یک تصادف نمونه AWS دوباره رخ دهد، تمام ترافیک از طریق PgBouncer دیگر هدایت می شود. شکست شبکه Load Balancer توسط AWS ارائه شده است.
این طرح افزودن سرورهای جدید PgBouncer را آسان می کند.
یک خوشه Failover PostgreSQL ایجاد کنید
هنگام حل این مشکل، گزینه های مختلفی را در نظر گرفتیم: خطای خودکار، repmgr، AWS RDS، Patroni.
فیلمنامه های خودنویس
آنها می توانند کار استاد را زیر نظر داشته باشند و در صورت عدم موفقیت، ماکت را به استاد ارتقا دهند و پیکربندی PgBouncer را به روز کنند.
مزایای این رویکرد حداکثر سادگی است، زیرا شما خودتان اسکریپت ها را می نویسید و دقیقاً نحوه کار آنها را درک می کنید.
منفی:
- ممکن است استاد نمرده باشد، در عوض ممکن است خرابی شبکه رخ داده باشد. Failover، غافل از این، ماکت را برای استاد تبلیغ می کند، در حالی که استاد قدیمی به کار خود ادامه می دهد. در نتیجه ما دو سرور در نقش مستر دریافت می کنیم و نمی دانیم کدام یک از آنها آخرین داده های به روز را دارد. این وضعیت را تقسیم مغز نیز می نامند.
- ما بدون پاسخ ماندیم. در پیکربندی ما، master و one replica، پس از تعویض، ماکت به سمت Master حرکت میکند و دیگر ماکتی نداریم، بنابراین باید به صورت دستی یک replica جدید اضافه کنیم.
- ما نیاز به نظارت اضافی بر عملیات failover داریم، در حالی که ما 12 قطعه PostgreSQL داریم، به این معنی که باید 12 کلاستر را نظارت کنیم. با افزایش تعداد خرده ها، باید به یاد داشته باشید که failover را نیز به روز کنید.
failover خود نوشته بسیار پیچیده به نظر می رسد و نیاز به پشتیبانی غیر ضروری دارد. با یک خوشه PostgreSQL، این ساده ترین گزینه خواهد بود، اما مقیاس بندی نمی شود، بنابراین برای ما مناسب نیست.
Repmgr
Replication Manager برای خوشه های PostgreSQL، که می تواند عملکرد یک خوشه PostgreSQL را مدیریت کند. در عین حال، از جعبه آن یک شکست خودکار وجود ندارد، بنابراین برای کار، باید "لفاف بندی" خود را در بالای محلول تمام شده بنویسید. بنابراین همه چیز می تواند حتی پیچیده تر از فیلمنامه های خودنویس باشد، بنابراین ما حتی Repmgr را امتحان نکردیم.
AWS RDS
از همه چیزهایی که نیاز داریم پشتیبانی می کند، می داند چگونه پشتیبان تهیه کند و مجموعه ای از اتصالات را حفظ می کند. دارای سوئیچینگ خودکار: هنگامی که استاد می میرد، ماکت به استاد جدید تبدیل می شود، و AWS رکورد dns را به اصلی جدید تغییر می دهد، در حالی که کپی ها را می توان در AZ های مختلف قرار داد.
معایب شامل عدم تنظیمات خوب است. به عنوان نمونه ای از تنظیم دقیق: نمونه های ما محدودیت هایی برای اتصالات tcp دارند که متأسفانه در RDS قابل انجام نیستند:
net.ipv4.tcp_keepalive_time=10
net.ipv4.tcp_keepalive_intvl=1
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_retries2=3
علاوه بر این، AWS RDS تقریبا دو برابر قیمت نمونه معمولی گران است، که دلیل اصلی کنار گذاشتن این راه حل بود.
حامی
این یک قالب پایتون برای مدیریت PostgreSQL با اسناد خوب، failover خودکار و کد منبع در github است.
نکات مثبت Patroni:
- هر پارامتر پیکربندی توضیح داده شده است، نحوه عملکرد آن واضح است.
- failover خودکار خارج از جعبه کار می کند.
- نوشته شده در پایتون، و از آنجایی که ما خودمان در پایتون زیاد می نویسیم، مقابله با مشکلات و شاید حتی کمک به توسعه پروژه برای ما آسان تر خواهد بود.
- PostgreSQL را به طور کامل مدیریت می کند، به شما امکان می دهد پیکربندی را در تمام گره های خوشه به طور همزمان تغییر دهید، و اگر برای اعمال پیکربندی جدید نیاز به راه اندازی مجدد خوشه باشد، می توان دوباره با استفاده از Patroni این کار را انجام داد.
منفی:
- در مستندات نحوه صحیح کار با PgBouncer مشخص نیست. اگرچه به سختی می توان آن را منهای نامید، زیرا وظیفه Patroni مدیریت PostgreSQL است و اینکه چگونه اتصالات به Patroni انجام می شود مشکل ما است.
- نمونه های کمی از اجرای Patroni در حجم های بزرگ وجود دارد، در حالی که نمونه های زیادی از پیاده سازی از ابتدا وجود دارد.
در نتیجه، Patroni را برای ایجاد یک خوشه شکستی انتخاب کردیم.
فرآیند اجرای Patroni
قبل از Patroni، ما 12 قطعه PostgreSQL در یک پیکربندی از یک Master و یک Replica با تکرار ناهمزمان داشتیم. سرورهای برنامه از طریق Network Load Balancer به پایگاههای داده دسترسی پیدا میکردند که در پشت آن دو نمونه با PgBouncer و پشت آنها همه سرورهای PostgreSQL قرار داشتند.
برای پیاده سازی Patroni، باید یک پیکربندی خوشه ذخیره سازی توزیع شده را انتخاب کنیم. Patroni با سیستم های ذخیره سازی پیکربندی توزیع شده مانند etcd، Zookeeper، Consul کار می کند. ما فقط یک کلاستر Consul کامل در بازار داریم که در ارتباط با Vault کار می کند و ما دیگر از آن استفاده نمی کنیم. یک دلیل عالی برای شروع استفاده از Consul برای هدف مورد نظر خود.
نحوه کار پاترونی با کنسول
ما یک خوشه Consul داریم که از سه گره تشکیل شده است و یک خوشه Patroni که از یک رهبر و یک ماکت تشکیل شده است (در Patroni به master می گویند cluster leader و slave ها replicas). هر نمونه از خوشه Patroni دائماً اطلاعاتی در مورد وضعیت خوشه به کنسول ارسال می کند. بنابراین، از Consul همیشه می توانید پیکربندی فعلی خوشه Patroni و اینکه در حال حاضر رهبر است را دریابید.
برای اتصال Patroni به کنسول، کافی است اسناد رسمی را مطالعه کنید، که می گوید بسته به نحوه کار ما با کنسول و طرح اتصال، به صورت اختیاری باید یک هاست را در قالب http یا https مشخص کنید:
host: the host:port for the Consul endpoint, in format: http(s)://host:port
scheme: (optional) http or https, defaults to http
ساده به نظر می رسد، اما مشکلات از اینجا شروع می شود. با Consul، ما روی یک اتصال امن از طریق https کار می کنیم و پیکربندی اتصال ما به این صورت خواهد بود:
consul:
host: https://server.production.consul:8080
verify: true
cacert: {{ consul_cacert }}
cert: {{ consul_cert }}
key: {{ consul_key }}
اما این کار نمی کند. هنگام راه اندازی، Patroni نمی تواند به کنسول متصل شود، زیرا به هر حال سعی می کند از طریق http عبور کند.
کد منبع Patroni به حل مشکل کمک کرد. خوب است که با پایتون نوشته شده است. به نظر می رسد که پارامتر میزبان به هیچ وجه تجزیه نشده است و پروتکل باید در طرح مشخص شود. بلوک پیکربندی کاری برای کار با کنسول برای ما به این صورت است:
consul:
host: server.production.consul:8080
scheme: https
verify: true
cacert: {{ consul_cacert }}
cert: {{ consul_cert }}
key: {{ consul_key }}
کنسول-قالب
بنابراین، ما فضای ذخیره سازی را برای پیکربندی انتخاب کرده ایم. اکنون باید بفهمیم که چگونه PgBouncer پیکربندی خود را هنگام تغییر رهبر در خوشه Patroni تغییر میدهد. هیچ پاسخی برای این سوال در اسناد وجود ندارد، زیرا. در آنجا، در اصل، کار با PgBouncer شرح داده نشده است.
در جستجوی راهحل، مقالهای پیدا کردیم (متاسفانه عنوانش را به خاطر ندارم) که در آن نوشته شده بود Сonsul-template در جفت کردن PgBouncer و Patroni کمک زیادی کرده است. این ما را بر آن داشت تا نحوه عملکرد Consul-template را بررسی کنیم.
معلوم شد که Consul-template به طور مداوم پیکربندی خوشه PostgreSQL را در کنسول نظارت می کند. هنگامی که رهبر تغییر می کند، پیکربندی PgBouncer را به روز می کند و دستوری برای بارگذاری مجدد آن ارسال می کند.
مزیت بزرگ قالب این است که به عنوان کد ذخیره می شود، بنابراین هنگام اضافه کردن یک قطعه جدید، کافی است یک commit جدید ایجاد کنید و قالب را به طور خودکار به روز کنید و از اصل زیرساخت به عنوان کد پشتیبانی کنید.
معماری جدید با Patroni
در نتیجه، ما طرح کار زیر را دریافت کردیم:
همه سرورهای برنامه به متعادل کننده دسترسی دارند → دو نمونه از PgBouncer در پشت آن وجود دارد → در هر نمونه، Consul-template راه اندازی می شود که وضعیت هر خوشه Patroni را نظارت می کند و ارتباط پیکربندی PgBouncer را نظارت می کند که درخواست ها را به رهبر فعلی ارسال می کند. از هر خوشه
تست دستی
ما این طرح را قبل از راهاندازی آن در یک محیط آزمایشی کوچک اجرا کردیم و عملکرد سوئیچینگ خودکار را بررسی کردیم. آنها تخته را باز کردند، برچسب را حرکت دادند و در آن لحظه رهبر خوشه را "کشتند". در AWS، این کار به سادگی خاموش کردن نمونه از طریق کنسول است.
برچسب در عرض 10-20 ثانیه برگشت و سپس دوباره شروع به حرکت عادی کرد. این بدان معنی است که خوشه Patroni به درستی کار می کرد: رهبر را تغییر داد، اطلاعات را به Сonsul ارسال کرد و Сonsul-template بلافاصله این اطلاعات را دریافت کرد، پیکربندی PgBouncer را جایگزین کرد و دستور بارگذاری مجدد را ارسال کرد.
چگونه در زیر بار زیاد زنده بمانیم و زمان خرابی را به حداقل برسانیم؟
همه چیز عالی کار می کند! اما سؤالات جدیدی وجود دارد: چگونه تحت بار زیاد کار می کند؟ چگونه به سرعت و با خیال راحت همه چیز را در تولید عرضه کنیم؟
محیط آزمایشی که در آن تست بار را انجام می دهیم به ما کمک می کند تا به سوال اول پاسخ دهیم. از نظر معماری کاملاً مشابه تولید است و داده های آزمایشی را تولید کرده است که تقریباً از نظر حجم برابر با تولید است. ما تصمیم میگیریم فقط یکی از استادان PostgreSQL را در طول آزمایش "کشتیم" و ببینیم چه اتفاقی میافتد. اما قبل از آن، بررسی نورد خودکار مهم است، زیرا در این محیط چندین خرده PostgreSQL داریم، بنابراین قبل از تولید، تست عالی اسکریپت های پیکربندی را دریافت خواهیم کرد.
هر دو کار بلندپروازانه به نظر می رسند، اما ما PostgreSQL 9.6 را داریم. آیا می توانیم بلافاصله به 11.2 ارتقا دهیم؟
تصمیم داریم این کار را در 2 مرحله انجام دهیم: ابتدا به 11.2 ارتقا دهید، سپس Patroni را راه اندازی کنید.
به روز رسانی PostgreSQL
برای به روز رسانی سریع نسخه PostgreSQL، از گزینه استفاده کنید -k، که در آن لینک های سخت روی دیسک ایجاد می شود و نیازی به کپی کردن اطلاعات شما نیست. در پایگاه های 300-400 گیگابایت، به روز رسانی 1 ثانیه طول می کشد.
ما خرده های زیادی داریم، بنابراین به روز رسانی باید به صورت خودکار انجام شود. برای انجام این کار، یک کتاب بازی Ansible نوشتیم که کل فرآیند بهروزرسانی را برای ما انجام میدهد:
/usr/lib/postgresql/11/bin/pg_upgrade
<b>--link </b>
--old-datadir='' --new-datadir=''
--old-bindir='' --new-bindir=''
--old-options=' -c config_file='
--new-options=' -c config_file='
در اینجا ذکر این نکته ضروری است که قبل از شروع ارتقا، باید آن را با پارامتر انجام دهید --بررسیتا مطمئن شوید که می توانید ارتقا دهید. اسکریپت ما همچنین تنظیمات را برای مدت زمان ارتقا جایگزین می کند. اسکریپت ما در 30 ثانیه کامل شد که یک نتیجه عالی است.
Patroni را راه اندازی کنید
برای حل مشکل دوم، فقط به تنظیمات Patroni نگاه کنید. مخزن رسمی دارای یک پیکربندی نمونه با initdb است که مسئول اولیه سازی یک پایگاه داده جدید در اولین راه اندازی Patroni است. اما از آنجایی که ما قبلا یک پایگاه داده آماده داریم، به سادگی این بخش را از پیکربندی حذف کردیم.
هنگامی که شروع به نصب Patroni روی یک خوشه PostgreSQL از قبل و اجرای آن کردیم، با مشکل جدیدی مواجه شدیم: هر دو سرور به عنوان یک رهبر شروع به کار کردند. Patroni چیزی در مورد وضعیت اولیه خوشه نمی داند و سعی می کند هر دو سرور را به عنوان دو خوشه مجزا با یک نام راه اندازی کند. برای حل این مشکل، باید دایرکتوری با داده های موجود در برده را حذف کنید:
rm -rf /var/lib/postgresql/
این کار فقط روی برده باید انجام شود!
هنگامی که یک ماکت تمیز متصل می شود، Patroni یک رهبر پایه پشتیبان ایجاد می کند و آن را به ماکت بازیابی می کند و سپس طبق wal log به وضعیت فعلی می رسد.
مشکل دیگری که با آن مواجه شدیم این است که همه خوشه های PostgreSQL به طور پیش فرض main نامگذاری شده اند. وقتی هر خوشه چیزی در مورد دیگری نمی داند، این طبیعی است. اما وقتی می خواهید از Patroni استفاده کنید، همه خوشه ها باید یک نام منحصر به فرد داشته باشند. راه حل این است که نام خوشه را در پیکربندی PostgreSQL تغییر دهید.
تست بار
ما آزمایشی را راه اندازی کرده ایم که تجربه کاربر را در بردها شبیه سازی می کند. وقتی بار به مقدار متوسط روزانه ما رسید، دقیقاً همان آزمایش را تکرار کردیم، یک نمونه را با رهبر PostgreSQL خاموش کردیم. failover خودکار همانطور که انتظار داشتیم کار کرد: Patroni رهبر را تغییر داد، Consul-template پیکربندی PgBouncer را به روز کرد و دستوری برای بارگذاری مجدد ارسال کرد. با توجه به نمودارهای ما در گرافانا، مشخص بود که تاخیرهای 20-30 ثانیه ای و مقدار کمی خطا از سرورهای مرتبط با اتصال به پایگاه داده وجود دارد. این یک وضعیت عادی است، چنین مقادیری برای Failover ما قابل قبول است و قطعاً بهتر از زمان خرابی سرویس است.
آوردن Patroni به تولید
در نتیجه ما به طرح زیر رسیدیم:
- Consul-template را در سرورهای PgBouncer مستقر کرده و راه اندازی کنید.
- به روز رسانی PostgreSQL به نسخه 11.2.
- تغییر نام خوشه؛
- راه اندازی خوشه Patroni.
در عین حال، طرح ما به ما اجازه میدهد که تقریباً در هر زمان اولین نکته را بیان کنیم، میتوانیم هر PgBouncer را به نوبه خود از کار حذف کنیم و Consul-template را روی آن مستقر کرده و اجرا کنیم. بنابراین ما انجام دادیم.
برای استقرار سریع، از Ansible استفاده کردیم، زیرا قبلاً همه کتابهای بازی را در یک محیط آزمایشی آزمایش کردهایم و زمان اجرای اسکریپت کامل از 1,5 تا 2 دقیقه برای هر قطعه بود. ما میتوانیم همه چیز را به نوبه خود در هر قطعه بدون توقف سرویسمان پخش کنیم، اما باید هر PostgreSQL را برای چند دقیقه خاموش کنیم. در این حالت، کاربرانی که اطلاعات آنها روی این خرده است در حال حاضر نمی توانند به طور کامل کار کنند و این برای ما غیر قابل قبول است.
راه برون رفت از این وضعیت تعمیر و نگهداری برنامه ریزی شده بود که هر 3 ماه یکبار انجام می شود. این یک پنجره برای کار برنامه ریزی شده است، زمانی که ما به طور کامل سرویس خود را خاموش می کنیم و نمونه های پایگاه داده خود را ارتقا می دهیم. یک هفته تا پنجره بعدی باقی مانده بود و ما تصمیم گرفتیم فقط منتظر بمانیم و بیشتر آماده شویم. در طول زمان انتظار، علاوه بر این، خودمان را ایمن کردیم: برای هر قطعه PostgreSQL، در صورت عدم حفظ آخرین دادهها، یک ماکت یدکی ایجاد کردیم، و یک نمونه جدید برای هر قطعه اضافه کردیم، که باید به یک کپی جدید در خوشه Patroni تبدیل شود. تا دستوری برای حذف داده ها اجرا نشود. همه اینها کمک کرد تا خطر خطا به حداقل برسد.
ما سرویس خود را مجدداً راه اندازی کردیم، همه چیز همانطور که باید کار کرد، کاربران به کار خود ادامه دادند، اما در نمودارها متوجه بار غیرعادی بالای سرورهای کنسول شدیم.
چرا ما این را در محیط تست ندیدیم؟ این مشکل به خوبی نشان می دهد که پیروی از زیرساخت به عنوان اصل کد و اصلاح کل زیرساخت، از محیط های آزمایشی تا تولید، ضروری است. در غیر این صورت، به راحتی میتوان به مشکلی که داشتیم پی برد. چی شد؟ Consul ابتدا در تولید ظاهر شد و سپس در محیط های آزمایشی، در نتیجه در محیط های آزمایشی، نسخه Consul بالاتر از تولید بود. فقط در یکی از نسخه ها، نشت CPU هنگام کار با consul-template حل شد. بنابراین، ما به سادگی کنسول را به روز کردیم، بنابراین مشکل حل شد.
خوشه Patroni را مجددا راه اندازی کنید
با این حال، ما با یک مشکل جدید مواجه شدیم که حتی به آن مشکوک نبودیم. هنگام به روز رسانی Consul، ما به سادگی با استفاده از دستور consul leave → Patroni به سرور کنسول دیگری متصل می شود → همه چیز کار می کند، گره Consul را از خوشه حذف می کنیم. اما وقتی به آخرین نمونه از Consul Cluster رسیدیم و دستور consul leave را برای آن ارسال کردیم، تمام خوشههای Patroni به سادگی دوباره راهاندازی شدند و در لاگها خطای زیر را مشاهده کردیم:
ERROR: get_cluster
Traceback (most recent call last):
...
RetryFailedError: 'Exceeded retry deadline'
ERROR: Error communicating with DCS
<b>LOG: database system is shut down</b>
خوشه Patroni نتوانست اطلاعات مربوط به خوشه خود را بازیابی کند و دوباره راه اندازی شد.
برای یافتن راه حل، از طریق مشکلی در github با نویسندگان Patroni تماس گرفتیم. آنها بهبودهایی را برای فایل های پیکربندی ما پیشنهاد کردند:
consul:
consul.checks: []
bootstrap:
dcs:
retry_timeout: 8
ما توانستیم مشکل را در یک محیط آزمایشی تکرار کنیم و این گزینه ها را در آنجا آزمایش کردیم، اما متاسفانه کار نکردند.
مشکل هنوز حل نشده باقی مانده است. ما قصد داریم راه حل های زیر را امتحان کنیم:
- از Consul-agent در هر نمونه خوشه Patroni استفاده کنید.
- مشکل را در کد حل کنید.
ما متوجه شدیم که خطا کجا رخ داده است: مشکل احتمالاً استفاده از مهلت زمانی پیشفرض است که از طریق فایل پیکربندی لغو نمیشود. هنگامی که آخرین سرور کنسول از خوشه حذف می شود، کل کلاستر کنسول برای بیش از یک ثانیه هنگ می کند، به همین دلیل، Patroni نمی تواند وضعیت خوشه را دریافت کند و کل کلاستر را به طور کامل راه اندازی مجدد می کند.
خوشبختانه دیگر با خطا مواجه نشدیم.
نتایج استفاده از Patroni
پس از راهاندازی موفقیتآمیز Patroni، ما یک ماکت اضافی در هر خوشه اضافه کردیم. در حال حاضر در هر خوشه شباهتی از حد نصاب وجود دارد: یک رهبر و دو نسخه تکراری، برای شبکه ایمنی در صورت تقسیم مغز هنگام تعویض.
پاترونی بیش از سه ماه است که روی تولید کار می کند. در این مدت او قبلاً توانسته به ما کمک کند. اخیراً رهبر یکی از خوشه ها در AWS درگذشت، failover خودکار کار کرد و کاربران به کار خود ادامه دادند. پاترونی وظیفه اصلی خود را انجام داد.
خلاصه ای کوچک از استفاده از Patroni:
- سهولت در تغییرات پیکربندی کافی است پیکربندی را در یک نمونه تغییر دهید و تا کل خوشه بالا کشیده شود. اگر برای اعمال پیکربندی جدید نیاز به راه اندازی مجدد باشد، Patroni به شما اطلاع خواهد داد. Patroni می تواند کل خوشه را با یک فرمان راه اندازی مجدد کند که این نیز بسیار راحت است.
- خرابی خودکار کار می کند و قبلاً به ما کمک کرده است.
- به روز رسانی PostgreSQL بدون توقف برنامه. ابتدا باید کپی ها را به نسخه جدید به روز کنید، سپس لیدر خوشه Patroni را تغییر دهید و لیدر قدیمی را به روز کنید. در این صورت، تست لازم از خطای خودکار رخ می دهد.
منبع: www.habr.com