Pulse

توسعه: شروع - به‌روزرسانی

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

مسئله

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

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

معماری

Pulse Server تعریف بررسی‌ها را با HTTP به ایجنت‌های شبکه‌های جدا می‌فرستد و ایجنت‌ها نتیجه‌ها را برمی‌گردانند.

این راهکار چهار بخش دارد:

چرا این طراحی؟

چرا ایجنت؟ بررسی باید همان‌جا اجرا شود که مشاهده اهمیت دارد. نصب یک worker روی ویندوز یا لینوکس به Pulse اجازه می‌دهد یک سرویس را از شبکه‌های جدا مقایسه کند، نه اینکه هر خطا را از موقعیت سرور حدس بزند.

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

چرا HTTP، نه WebSocket؟ سامانه درخواست‌هایی با محدوده‌ی روشن ردوبدل می‌کند: تعریف‌ها در هر heartbeat و نتیجه‌های batchشده پس از پایان بررسی‌ها. یک کانال پایدار و دوطرفه، مدیریت اتصال را اضافه می‌کرد بی‌آنکه این جریان را بهتر کند.

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

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

چرا Blazor و قراردادهای مشترک؟ Blazor رابط عملیاتی را در همان stack .NET نگه می‌دارد و Pulse.Contracts نمی‌گذارد سرور، worker و dashboard شکل درخواست و نتیجه را جداگانه تعریف کنند.

رفتار مانیتورینگ

ایجنت‌ها بررسی‌های HTTP، ping و TCP را اجرا می‌کنند. HTTP پاسخ 2xx را سالم می‌داند و اگر مقصد HEAD را نپذیرد، به GET برمی‌گردد. هر بررسی فاصله و timeout خودش را دارد؛ شکست‌ها به‌جای اینکه در یک وضعیت بولی گم شوند، زمان سپری‌شده و جزئیات خطا را نگه می‌دارند.

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

ارسال نتیجه‌ها در batch‌های حداکثر ۵۰تایی یا هر پنج ثانیه بافر می‌شود. اگر batch ارسال نشود، ایجنت نتیجه‌ها را دوباره در channel حافظه می‌گذارد و پس از مکثی کوتاه دوباره تلاش می‌کند. ثبت ایجنت هم با تأخیرهای افزایشی تکرار می‌شود و worker پس از خطاهای عادی، حلقه‌ی اصلی heartbeat را زنده نگه می‌دارد.

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

گام بعدی برای بهبود

Pulse عمداً کوچک مانده و محدودیت‌هایش برای استفاده‌ی production روشن است. پیش از اینکه جدول نتیجه‌ها به مخزن بلندمدت داده‌های time-series تبدیل شود، SQLite تک‌استقراری را با یک پایگاه‌داده‌ی رابطه‌ای مدیریت‌شده جایگزین می‌کردم و برای نتیجه‌های قدیمی retention یا rollup اضافه می‌کردم.

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

مشاهده‌ی کد منبع در گیت‌هاب