پرش به مطلب اصلی

قابلیت اداره‌پذیری سیستم‌ها چیست و چرا فقط با مانیتورینگ به دست نمی‌آید؟

· ۷ دقیقه مطالعه
مهدی مالوردی
مهندس نرم‌افزار و نویسندهٔ این سایت

فرض کن نسخه‌ی تازه‌ی سرویس سفارش را مستقر کرده‌ایم. چند دقیقه‌ی اول، همه‌چیز عادی به نظر می‌رسد: نمودارها وضعیت بدی نشان نمی‌دهند، نرخ خطا بالا نرفته، و از بیرون انگار سامانه دارد کار می‌کند. کمی بعد، پشتیبانی خبر می‌دهد که چند پرداخت موفق بوده، اما سفارش در پنل کاربر دیده نمی‌شود. لاگ‌ها را باز می‌کنیم، متریک‌ها را می‌بینیم، تریس‌ها را دنبال می‌کنیم؛ داده کم نیست، اما جواب روشن پیدا نمی‌شود. معلوم نیست مشکل از پرداخت است، صف پیام، موجودی، نسخه‌ی تازه، یا داده‌ای که در میانه‌ی مسیر نیمه‌کاره مانده است.

من معمولاً همین‌جا تفاوت میان «کار کردن» و «قابل اداره بودن» را می‌بینم. سامانه ممکن است تا حدی زنده باشد، درخواست بگیرد و حتی بیشتر مسیرها را درست پاسخ بدهد، اما در لحظه‌ی تغییر یا اختلال، برای تیم قابل فهم نباشد. قابلیت اداره‌پذیری، یا operability، درباره‌ی همین لحظه‌هاست: لحظه‌هایی که باید بفهمیم چه رخ داده، آسیب را محدود کنیم، تصمیمی امن بگیریم، و اگر لازم شد از مسیر اشتباه برگردیم.

تصویر مفهومی درباره‌ی قابلیت اداره‌پذیری سامانه‌ها

حرف اصلی

مانیتورینگ می‌گوید چیزی از آستانه گذشته است. مشاهده‌پذیری کمک می‌کند بفهمیم چه رخ داده است. قابلیت اداره‌پذیری باید کمک کند بفهمیم حالا چه کار امنی می‌توان انجام داد.

وقتی می‌گویم سامانه قابل اداره است، منظورم فقط این نیست که داشبورد دارد. حتی منظورم این نیست که لاگ، متریک و تریس فراوان تولید می‌کند. منظورم سامانه‌ای است که در زمان ابهام، تیم را با حدس، حافظه‌ی افراد و دستورهای دستی تنها نمی‌گذارد. خطاهایش معنا دارند، وابستگی‌هایش قابل ردگیری‌اند، مسیر بازگشتش تمرین شده، و عملیات تکراری‌اش تا جای ممکن قابل بازبینی و امن شده است.

برای همین من اداره‌پذیری را با مفاهیم نزدیک به آن یکی نمی‌گیرم:

مفهومپرسش اصلیچرا کافی نیست؟
پایایی، یا reliabilityآیا سامانه معمولاً درست کار می‌کند؟سامانه‌ی پایا هم ممکن است در زمان خرابی سخت بازیابی شود.
مشاهده‌پذیری، یا observabilityآیا می‌توان رفتار سامانه را از بیرون فهمید؟فهمیدن رخداد کافی نیست؛ باید مسیر اقدام هم داشته باشیم.
نگهداشت‌پذیریآیا تغییر کد با هزینه‌ی معقول ممکن است؟عملیات زنده فقط تغییر کد نیست؛ استقرار، rollback، داده و وابستگی هم هست.
استفاده‌پذیریآیا کاربر با محصول راحت کار می‌کند؟این مفهوم درباره‌ی کاربر محصول است، نه تیمی که سامانه را اداره می‌کند.

ممکن است سرویس پرداخت پایا باشد و متریک‌های خوبی هم داشته باشد، اما اگر نتوانیم یک درگاه معیوب را موقتاً غیرفعال کنیم، درگاه جایگزین را امن فعال کنیم، یا سفارش‌های نامشخص را بازپردازش کنیم، هنوز با سامانه‌ای سخت‌اداره طرفیم. مشاهده‌پذیری به ما نشان می‌دهد کجا را باید نگاه کنیم؛ اداره‌پذیری می‌پرسد بعد از دیدن، دستمان برای چه کاری باز است.

مشکل بسیاری از سامانه‌ها این نیست که هیچ نشانه‌ای تولید نمی‌کنند؛ مشکل این است که نشانه‌ها به تصمیم وصل نمی‌شوند. هشداری که فقط می‌گوید «خطا زیاد شده است» من را جلو نمی‌برد. من از هشدار خوب انتظار دارم بگوید چه چیزی آسیب دیده، دامنه‌ی اثر کجاست، اقدام نخست چیست، و از کجا می‌فهمیم رخداد تمام شده است.

این نمونه‌ی کوچک، پیکربندی واقعی یک ابزار هشدار نیست. فقط می‌خواهم با آن تفاوت هشدار مبهم و هشدار قابل اقدام را نشان بدهم:

noisy_alert = {
"name": "errors_are_high",
"condition": "error_count > 100",
"suggested_action": "investigate",
}

actionable_alert = {
"name": "checkout_payment_error_rate_is_high",
"condition": "checkout_payment_error_rate > 0.05 for 10m",
"context": {
"service": "checkout",
"dependency": "payment_gateway",
"region": "tehran",
},
"user_impact": "users may fail to complete payment",
"first_safe_action": "enable_fallback_payment_provider",
"runbook": "runbooks/checkout/payment_degradation.md",
"recovery_condition": "checkout_payment_error_rate < 0.01 for 20m",
}

در نمونه‌ی دوم، هشدار فقط پرجزئیات‌تر نیست؛ تصمیم‌پذیرتر است. کسی که آن را می‌بیند، بهتر می‌فهمد مشکل به کدام سرویس و وابستگی مربوط است، اثر احتمالی روی کاربر چیست، اقدام نخست کدام است، و چه زمانی می‌توان رخداد را پایان‌یافته دانست.

سوءبرداشت رایج

زیاد کردن لاگ، متریک و تریس به‌تنهایی اداره‌پذیری نمی‌سازد. اگر این نشانه‌ها به فهم و اقدام امن وصل نشوند، فقط نویز بیشتری تولید می‌کنند.

حالا دوباره به همان سفارش برگردیم. پرداخت موفق شده، اما سفارش در پنل نیست. اگر مسیر داده روشن نباشد، باید حدس بزنیم: آیا پیام order_created دیر به صف رسیده؟ آیا موجودی هنوز به‌روز نشده؟ آیا گزارش مالی پرداخت را دیده اما پنل پشتیبانی سفارش را نه؟ آیا نسخه‌ی تازه قرارداد پیام را تغییر داده و مصرف‌کننده‌ی قدیمی هنوز با شکل جدید پیام سازگار نیست؟

در چنین رخدادی هیچ سرویس واحدی الزاماً «خاموش» نیست. ممکن است همه‌چیز از دید مانیتورینگ زنده باشد، اما روایت کلی سامانه ناقص باشد. این همان جایی است که سامانه‌های داده‌محور سخت‌تر اداره می‌شوند. داده ممکن است دیر برسد، دوبار پردازش شود، از ترتیب خارج شود، یا در چند بخش سامانه چند روایت متفاوت بسازد. کد را شاید بتوان سریع rollback کرد، اما داده‌ی نیمه‌کاره یا ناسازگار ممکن است ساعت‌ها اثر بگذارد.

نکته‌ی داده‌ای

برای سامانه‌ی داده‌محور، زنده بودن سرویس کافی نیست. باید تازگی داده، تأخیر صف‌ها، امکان بازپردازش، و مسیر حرکت داده هم قابل فهم باشد.

اینجاست که می‌شود فهمید اداره‌پذیری از طراحی شروع می‌شود، نه از داشبورد. اگر مرز سرویس‌ها مبهم باشد، خرابی راحت از یک بخش به بخش دیگر سرایت می‌کند. اگر خطاها فقط failed یا internal error باشند، نمی‌فهمیم خطا گذراست یا پایدار، تکرار درخواست امن است یا نه، و کدام کاربر آسیب دیده است. اگر rollback فقط روی کاغذ وجود داشته باشد، در زمان بحران به کار نمی‌آید؛ چون باید با داده، صف، کش و نسخه‌های هم‌زمان هم سازگار باشد.

حتی تصمیم‌هایی که ظاهراً کوچک‌اند، در عملیات اثر بزرگ دارند. مهلت انتظار نامناسب می‌تواند زنجیره‌ی درخواست‌ها را قفل کند. تلاش دوباره‌ی بی‌مهار هنگام کندی، بار سامانه را بیشتر می‌کند. نبود اجرای دوباره‌ی امن می‌تواند یک درخواست تکراری را به سفارش یا پرداخت تکراری تبدیل کند. گاهی هم بهترین تصمیم این است که بخشی از قابلیت را موقتاً کم کنیم تا کل مسیر کاربر خراب نشود؛ یعنی کاهش سطح سرویس باید از قبل طراحی شده باشد.

پرسش کاربردی

برای هر قابلیت تازه بپرسید: اگر این بخش کند شد، نصفه اجرا شد، دوبار اجرا شد، یا لازم شد برگردد، تیم دقیقاً چه چیزی را می‌بیند و چه کاری می‌تواند امن انجام دهد؟

اگر بخواهم این حرف‌ها را به چند عادت عملی تبدیل کنم، از همین‌جا شروع می‌کنم: هر هشدار باید مالک، اثر کاربری، اقدام نخست و معیار پایان داشته باشد. هر استقرار باید مسیر بازگشت تمرین‌شده داشته باشد. هر وابستگی بیرونی باید رفتار مشخصی در کندی، قطعی و پاسخ ناسازگار داشته باشد. هر مسیر داده باید داستان روشنی برای تازگی داده، بازپردازش و اصلاح داده‌ی خراب داشته باشد. هر کار دستی تکراری هم باید یا حذف شود، یا به ابزار امن و قابل بازبینی تبدیل شود.

این‌ها فقط وظیفه‌ی تیم عملیات نیست. تیم مهندسی باید خطا، rollback، تلاش دوباره و رفتار وابستگی‌ها را در طراحی ببیند. تیم پلتفرم باید استقرار، مانیتورینگ، پیکربندی و بازیابی را ساده و امن کند. تیم داده باید تازگی، کیفیت و مسیر داده را قابل پیگیری کند. معماری هم باید مرزها و الگوهای خرابی را در سطح کل سامانه ببیند. اگر این تیم‌ها زبان مشترک نداشته باشند، هرکدام از زاویه‌ی خودشان حرف درستی می‌زنند، اما سامانه همچنان سخت اداره می‌شود.

در پایان، اگر بخواهم همه‌ی بحث را به یک جمله برگردانم، می‌گویم سامانه‌ی قابل اداره الزاماً بی‌خطا نیست؛ تفاوتش این است که وقتی خطا رخ می‌دهد، تیم می‌داند چه چیزی را ببیند، چه چیزی را دست نزند، چه اقدامی امن‌تر است، و از کجا باید برگردد. مانیتورینگ و مشاهده‌پذیری برای رسیدن به این نقطه لازم‌اند، اما کافی نیستند. سامانه‌ی خوب فقط سامانه‌ای نیست که در روزهای عادی کار کند؛ سامانه‌ی خوب سامانه‌ای است که هنگام تغییر و خرابی هم قابل فهم و قابل اداره بماند.

اگر قرار باشد بعد از خواندن این متن فقط چند چیز در ذهن بماند، یکی این است که اداره‌پذیری یعنی توان فهم، تغییر، عیب‌یابی، مهار آسیب و بازیابی سامانه در محیط واقعی. دیگری این است که هشدار خوب باید اثر، دامنه، اقدام نخست و معیار پایان داشته باشد. و شاید مهم‌تر از همه: در سامانه‌های داده‌محور، مسیر داده به‌اندازه‌ی زنده بودن سرویس مهم است.

برای ادامه‌ی مطالعه، کتاب‌ها و نوشته‌هایی مثل Designing Data-Intensive Applications، Site Reliability Engineering، The Site Reliability Workbook، Release It!، Team Guide to Software Operability و Team Topologies نقطه‌ی شروع خوبی هستند.