یادداشتها راهنما
زندهبودن با سالمبودن فرق دارد
بدترین اختلال کوبرنتیزی که مدام میبینم، از کار افتادن یک نود نیست. یک پایگاهدادهی کُند است، یک پروب liveness که با همان پایگاهداده حرف میزند، و کلاستری که بعد هر پادی را که هنوز کار مفید میکرد میکُشد.
liveness یعنی «این برنامه سالم است» نیست. یعنی «این فرایند آنقدر گیر کرده که کوبرنتیز باید خلاصش کند». اگر این اسلحه را به سمت یک وابستگی بگیرید، کل ناوگان را خواهید زد.
آنچه باور داشتم ← آنچه حالا میکنم
قبلاً یک نقطهپایانی /health منتشر میکردم و هم liveness و هم readiness را به آن آویزان میکردم، چون Spring Actuator این کار را آسان کرده بود. حالا جدایشان میکنم: liveness محدود به خود فرایند است، readiness یعنی «فرستادن ترافیک امن است»، و startup یعنی «JVM هنوز در حال بوت است، کاریاش نداشته باشید».
سه پروب، سه پرسش
Startup. «آیا فرایند هنوز در حال شروع است؟» یک JVM اسپرینگ بوت ممکن است یک دقیقه طول بکشد تا به main و تازهسازی کانتکست برسد. اگر liveness در این بازه اجرا شود، کوبرنتیز پادی را ریاستارت میکند که اصلاً ناسالم نبوده است. startupProbe هست تا بتوانید یک بار کُند باشید.
Liveness. «آیا این فرایند دچار بنبست شده یا برای همیشه گیر کرده؟» این بررسی نباید به پایگاهداده، Redis، یک API پاییندستی HTTP، یا دیسکی که ممکن است لنگ بزند وابسته باشد. اگر آنها از کار افتادهاند، شما پاد را زنده میخواهید که readiness را رد کند، نه اینکه ریاستارت شود.
Readiness. «آیا این پاد باید ترافیک بگیرد؟» اینجا جایی است که پایگاهداده، بروکر یا کش گرمشده را بررسی میکنید. اگر رد شود، کوبرنتیز پاد را از Service برمیدارد. فرایند به کارش ادامه میدهد. وقتی وابستگی برگشت، پاد بدون یک JVM سرد دوباره ترافیک میگیرد.
استفاده از همان /health اکچواتور برای هر دوی liveness و readiness، این پرسشها را در هم میکوبد. آنوقت یک لغزش کوتاه در Postgres شبیه حلقهی کرش به نظر میرسد.
توفان ریاستارت
- Postgres کُند میشود (بار، failover، شبکه).
- پروب liveness هر پاد منتظر میماند، بعد رد میشود.
- کوبرنتیز پادها را ریاستارت میکند.
- JVMهای جدید همگی یکباره وصل میشوند.
- Postgres کُندتر میشود. پروبها دوباره رد میشوند.
شما باگ برنامه نداشتید. به ارکستریتور یاد دادید که خرابی یک وابستگی را تقویت کند.
راهحل کسلکننده است: liveness را روی /livez یا گروه liveness اکچواتور بگذارید بدون هیچ بررسی پاییندستی. readiness را روی /readyz با همان بررسیهایی بگذارید که جلوی ترافیک را میگیرند. زمانهای انتظار پروبها کوتاه باشد. آستانههای خطا هم «تلاش مجدد تا پایان جهان» نباشد.
تلههای مخصوص JVM
- محدودیت CPU بههمراه یک سرور HTTP برای liveness: نخ پروب گرسنه میماند، پروب رد میشود، پاد میمیرد، و مسئله شبیه نشت حافظه به نظر میرسد. من در گذاشتن محدودیت CPU روی سرویسهای جاوای حساس به تأخیر دستبهعصا هستم.
- هیپهای بزرگ و نبودِ پروب startup: اولین GC هنگام بوت بهعلاوهی یک زمان انتظار تنگ برای liveness، شبیه استقرار ناموفق به نظر میرسد.
- اکچواتور روی همان پورت ترافیک عمومی: برای خیلی جاها مشکلی ندارد؛ ولی باز هم نگذارید مسیر عمومی همان مسیر liveness باشد، اگر آن مسیر میتواند روی ورودی/خروجی مسدود شود.
چه چیزی در مانیفست میگذارم
دوست دارم در همان PRی که سرویس هست، اینها را ببینم:
- یک
startupProbeباfailureThresholdبلند برای بوت. - یک
livenessProbeروی هندلری که فقط جواب میدهد «این فرایند هنوز میتواند روی این پورت یک درخواست را اجرا کند». - یک
readinessProbeروی هندلری که وابستگیهایی را بررسی میکند که این نمونه پیش از گرفتن درخواست کاربر باید داشته باشد. - زمانهای انتظار بر حسب میلیثانیه که بتوانید در جلسهی روزانه توضیحشان دهید، نه کپیشده از یک وبلاگ.
اگر این چهار مورد نباشند، برایم مهم نیست چارت Helm چقدر خوشگل است.
نکتههای کلیدی
- liveness یعنی «این فرایند را بکُش». آن را به سمت پایگاهداده نشانه نروید.
- readiness یعنی «فرستادن ترافیک را متوقف کن». جای درست بررسی وابستگیها همینجاست.
- بوت کُند یک JVM به پروب startup نیاز دارد، نه به زمان انتظار کوتاهتر برای liveness.
- یک
/healthواحد برای هر دو پروب، همان راهی است که یک لغزش وابستگی را به ریاستارت سراسری کلاستر تبدیل میکند.