چطور استقرارها را به صفر خطای ۵۰۲ رساندیم
هر بار که بکاند Mythex را مستقر میکردیم، تعدادی درخواست با 502 Bad Gateway شکست میخوردند. زیاد نبودند — اما یک ۵۰۲ در حین استقرار یعنی کلیک کسی هیچ کاری نکرد، یا یک build که داشت تماشایش میکرد لحظهای متوقف شد. آن را به دو مشکل جدا ردیابی کردیم: یک watchdog خودترمیم که با استقرارهای ما میجنگید، و فاصلهای بین زمانی که یک سرور دیگر گوش نمیداد و زمانی که load balancer دیگر ترافیک به آن نمیفرستاد. در سه استقرار متوالی production بعد از رفعها، تعداد از ۱۰ به ۶ و بعد به صفر رسید.
راهاندازی
API و ارکستراتور ما بهصورت کانتینر روی یک گروه کوچک از سرورها پشت یک Application Load Balancer (ALB) در AWS اجرا میشوند. یک استقرار سرورها را یکییکی بهروز میکند: کانتینرهای قدیمی را تخلیه میکند، کانتینرهای جدید را راه میاندازد، و جلو میرود. یک watchdog روی هر سرور بررسی میکند که کانتینرها بالا باشند و هر چیزی را که نیست دوباره راه میاندازد — برای این است که یک پروسهی کرشکرده ساعت ۳ صبح بدون بیدار کردن کسی برگردد.
هر دو بهتنهایی منطقیاند. کنار هم، نبودند.
مشکل ۱: watchdog با استقرار رقابت میکرد
watchdog هر دقیقه یک بار اجرا میشد و با یک بررسی سلامت ناموفق «ترمیم» میکرد. یک استقرار عمداً سرویسها را تا ۱۵۰ ثانیه تخلیه میکند — و در این مدت یک بررسی سلامت باید شکست بخورد. پس watchdog در هر استقرار شلیک میکرد.
بیشتر وقتها در این رقابت بیضرر میباخت. یک بار برد: فایل compose (توصیف اینکه کدام کانتینرها باید اجرا شوند) را وسط یک استقرار بازنویسی کرد، و سروری را بدون هیچ کانتینر gateway رها کرد — در حالی که کار استقرار موفقیت را گزارش میداد.
رفعها:
- یک قفل. استقرار حالا همان قفل فایلی را میگیرد که اسکریپت راهاندازی سرور میگیرد، پس watchdog و استقرار هرگز همزمان اجرا نمیشوند.
- چهار اخطار، نه یک. watchdog قبل از اقدام به چهار بررسی سلامت ناموفق متوالی نیاز دارد. سرویسی که در حال تخلیه است دیگر مرده به نظر نمیرسد.
- یک تعریف. فایل compose حالا یک منبع واحد دارد که به سرورها منتشر میشود، بهجای کپیهایی که ممکن بود از هم دور شوند.
- تأیید کنید، به صرف اعتماد نکنید. کار استقرار دیگر با اینکه یک دستور برگشته موفق نمیشود. قبل از موفقیت، API، ارکستراتور، gateway و worker انتشار هر سرور را بررسی میکند.
مشکل ۲: سرور زودتر از ترافیک رفت
مشکل دوم آرامتر بود. وقتی یک استقرار سروری را تخلیه میکند، سرور شروع به پاسخ دادن 503 به endpoint سلامتش میکند تا load balancer آن را از چرخه خارج کند. ما این کار را ۵ ثانیه انجام میدادیم و بعد خاموش میشدیم.
اما ALB هر چند وقت یک بار سلامت را بررسی میکرد و برای خارج کردن یک هدف از گردش، چند شکست متوالی لازم داشت — تا حدود ۶۰ ثانیه از ابتدا تا انتها. پس برای حدود ۵۵ ثانیه، load balancer همچنان درخواستهای واقعی را به سروری میفرستاد که از قبل گوش دادن را متوقف کرده بود. اینها همان ۵۰۲های ما بودند.
راهحل این بود که دو جدول زمانی را با هم همزمان کنیم:
- گروه هدف حالا هر ۵ ثانیه سلامت را بررسی میکند و بعد از ۲ شکست یک هدف را ناسالم علامت میزند — حدود ۱۰ ثانیه تا فهمیدن.
- تخلیه بعد از شروع شکست بررسیهای سلامت، برای ۱۵ ثانیه ترافیک واقعی را همچنان سرو میکند، تا همیشه همپوشانی وجود داشته باشد.
- این تنظیمات در اسکریپتی زندگی میکنند که زیرساخت را فراهم میکند، نه در یک کنسول که کسی یک بار کلیک کرده باشد.
نگه داشتن اعداد در کنار هم
زمانبندیهایی مثل این به هم وابستهاند. تخلیه باید از زمان لازم برای اینکه balancer متوجه شود طولانیتر باشد؛ تخلیه بهعلاوهی خاموشی باید پیش از کشته شدن اجباری کانتینر تمام شود. آسان است که کسی شش ماه بعد یک عدد را عوض کند و بیسروصدا باگ را دوباره وارد کند.
پس این روابط را بهصورت تست نوشتیم: تخلیه باید از فاصلهی بررسی سلامت ضربدر آستانهی شکست طولانیتر باشد؛ زمان برگشت به حالت سالم باید کوتاه بماند؛ کل تخلیه باید در مهلت توقف کانتینر جا شود. اگر یک عدد را بدون بقیه تغییر دهید، CI شکست میخورد. خود تستها را هم جهشآزمایی (mutation-test) کردیم — هر عدد را به یک مقدار اشتباه تغییر دادیم و بررسی کردیم که یک تست آن را بگیرد.
اندازهگیری یک استقرار آسان است که اشتباه شود
اولین اندازهگیری ما میگفت یک استقرار تمیز حدود ۳٪ خراب است. نبود. ما هر پاسخ غیر-۲۰۰ را میشمردیم، و یک 503 از endpoint سلامت در حین تخلیه شکست نیست — کاری است که تخلیه انجام میدهد و به balancer میگوید آن سرور را وقتی هنوز درخواست واقعی سرو میکند انتخاب نکند.
چیزی که کاربران حس میکنند یک ۵۰۲ است: درخواست به هیچجا نرسیده. فقط شمردن آنها تصویر واقعی را داد — ۱۰، بعد ۶، بعد ۰ در سه استقرار production.
آنچه از آن برداشتیم
- خودترمیمی باید از خرابی برنامهریزیشده خبر داشته باشد. watchdogی که نمیتواند استقرار را از کرش تشخیص دهد، دیر یا زود با استقرار میجنگد.
- تخلیه یک دستدادن است. سرور باید تا وقتی load balancer ثابت کرده ترافیک را فرستادن متوقف کرده به سرو کردن ادامه دهد، و آن زمانبندی روی balancer تنظیم میشود، نه روی سرور.
- ثابتها را بهصورت تست رمزگذاری کنید. اعداد عملیاتی کمکم از هم دور میشوند. تستهایی که آنها را به هم ربط میدهند این کار را نمیکنند.
- پیش از اندازهگیری، تصمیم بگیرید چه چیزی را اندازه میگیرید. «خطاها» معیار اشتباهی بود؛ «درخواستهایی که به هیچجا نرسیدند» معیار درست بود.
اگر روی Mythex اپ منتشر میکنید، هیچکدام از اینها برای شما دیده نمیشود — که هدف همین است. مرتبط: چطور اپ منتشرشدهی شما هنگام انتشار مجدد بالا میماند.