امروزه پایش لحظه ای سرویسهای نرم افزاری و اعلام سریع مشکلات به وجود اومده به گروه هدف (یه چیزی شبیه سیستم های اعلان خطر) جز بخش بسیار مهم سرویس های نرم افزاری هستش چرا که تشخیص سریع مشکل باعث صرفه جویی در وقت و هزینه مالکان و ذینفعان سرویس خواهد شد. البته سیستم های مانیتورینگ مزایای خیلی بیشتری هم دارند که در ادامه باهاشون روبرو خواهیم شد.
کارهایی که در طی این مقاله انجام میدیم اینطوریه که قراره چند تا instance از یه Web API بیاریم بالا و یه سری Metric شبه واقعی برای مانیتورینگ سرویسمون تعریف کنیم، بعدش اونها رو بفرستیم روی Prometheus (در واقع Prometheus اونها رو از ما بگیره و ذخیره کنه) و به کمک Grafana یه سری داشبورد مانیتورینگ برای سرویس(ها) بسازیم. برای اینکه شما درگیر راه اندازی سرویس ها نشید و بعد از مباحث ابتدایی بریم سراغ کوئری و داشبوردهای Prometheus کافیه فایل docker-compose رو بالا بیارید و اتفاقاتی که افتاده رو ببینید، البته سورس کل کارها و توضیحات مراحل کار هم اینجا نوشته میشه که برای درک بهتر کارهای انجام شده میتونه کمک کنه.
همه چیزهایی که گفتم توی گیتهاب این مقاله گذاشتم و میتونید ببینید و اگر براتون مفید بود بهش ستاره بدید.
git clone https://github.com/naeemaei/MonitoringExample
بعد از دریافت سورس از گیت با این دستور از روت پروژه، سرویس ها pull و run خواهند شد. اگه مشکلی با docker-compose داشتید این لینک رو ببینید
docker-compose -f "Prometheus\docker-compose.yml" up -d --build
به چه ابزارهایی نیاز داریم؟
1- VS Code
2- Docker
در طی این مجموعه مقالات با موارد زیر آشنا خواهیم شد:
من راجع به دیتابیس های Time Serie مثل Prometheus و InfluxDB و شبیه اینها زیاد نمینویسم چون راجع بهشون مقاله زیاد نوشته شده ولی به یک سری مسائل اصلی در موردشون اشاره میکنم، البته با محوریت Prometheus
برای چه کارهایی و چه نوع دیتاهایی ازشون استفاده میکنیم؟
قبل از اینکه یکم بحث رو فنی کنیم لطفا به این دو تا مثال توجه کنید:
به ساعت هوشمندی که دارین دقت کنید، اونها ضربان قلب شما رو در هر لحظه نشون میدن، حالا اگر بخوایم مقادیر ضربان رو در بازه زمانی طولانی مدت ذخیره کنیم (مثلا یک ماه)، باید در چه ساختاری ذخیره کنیم؟
یک بُعد اصلی به نام زمان داریم (Key) و یک مقدار داریم به نام تعداد ضربان (Value). در واقع در یک زمان مشخص یک مقدار برای تعداد ضربان وجود داره. احتمالا شما رو به یاد یکی از ساختارهای نگهداری داده میندازه
علاوه بر این شاید بخوایم یک سری اطلاعات دیگه رو در کنارش نگهداری کنیم، مثلا شهری که الان توش هستیم، و اینکه در چه حالی هستم (خواب، بیداری، پیاده روی، ورزش)
و در پایان هم میخوایم گزارش تهیه کنیم یا ماینتور کنیم:
1. میانگین ضربان قلب اون فرد در یک شهر ساحلی یا در یک شهر کوهستانی چقدر هستن.
2. نرخ کاهش یا افزایش ضربان قلب در ساعات شروع خواب، عمق خواب و پایانی خواب به چه صورت هستن.
3. نرخ ضربان قلب در یک ساعت اخیر چقدر بوده است.
امروزه ساعت های هوشمند و حتی گوشی ها قدم های شما را هم ثبت می کنند و به شما گزارش می دهند در طول یک روز چند قدم برداشتید.
به چه روشی این دیتا ها رو ذخیره کنیم که بتونیم هم روی این اطلاعات مانیتورینگ انجام بدیم و هم گزارشاتی مانند مواردی که بالا گفتم رو تهیه کنیم
آیا RDBMS های مثل MySQL و MSSQL برای نگهداری و گزارش گیری این نوع اطلاعات مناسب هستند؟ در واقع Time Serie Database ها برای چنین استفاده هایی طراحی و بهینه سازی شده اند و Prometheus یکی از اونهاست.
دقیقا چه اطلاعاتی در Prometheus ذخیره میشود؟
1 - نام متریک مثلا ضربان قلب
2 - زمان ذخیره سازی مثلا 17:45:46.125 2021/07/14
3 - چه اطلاعات اضافه ای در کنارش ذخیره می شود؟ مثلا شهر، وضعیت فرد (خواب، بیدار و...). به مواردی که در کنار متریک ذخیره می شوند Label یا Tag میگیم.
4 - مقدار متریک، مثلا 103
بطور خلاصه میشه گفت برای یک Value علاوه بر زمان یک سری Tag یا label هم میشه ذخیره کرد.
2021/07/14 14:17:25.1000 => heart_rate{city=”shiraz",state=”sleep”} 103
Metric Name: heart_rate (ضربان قلب)
Timestamp: 2021/07/14 14:17:25.1000 (زمان ثبت مقدار متریک)
Tags: city, state
Value: 103
ضربان قلب در تاریخ 2021/07/14 14:17:25.1000 در شهر شیراز و وضعیت خواب 103 ثبت شده است.
در کل باید گفت در Time Serie Databases محور اصلی و کلید زمان هستش، به این تصویر توجه کنید:
همونطور که در این تصویر میبینید، ما یه سری نقطه زمانی داریم از t0 تا t6 که در هر کدوم از این زمان ها اطلاعات متریک ها ذخیره میشه، مثلا تعداد درخواستهای سرچی که تا لحظه t3 وارد اپلیکیشن اندروید شده (سطر اول از پایین) 18 تا هستش که تا زمان t5 این عدد به 22 میرسه یعنی در (t5 - t3) ثانیه 5 تا درخواست سرچ جدید روی سرویس اندروید اومده.
یه نکته هم که اهمیت داره بدونید اینه که در Prometheus یه مفهومی داریم به اسم Scrape که با کانفیگ کردن اون مشخص میکنیم اطلاعات متریک ها هر چند ثانیه از Exporter ها گرفته بشه و ذخیره بشه. حالا باید ببینیم Exporter ها چیا هستن؟ اما قبلش معماری کلی رو ببینیم:
سرویس Prometheus به دو روش دیتای متریک ها رو جمع آوری و ذخیره مکینه
روش Pull: در این روش هر سرویسی که قراره اطلاعات متریک هاشو در اختیار Prometheus قرار بده به عنوان یک Exporter معرفی میشه و مقادیر متریک ها رو در یک قالب مشخص ایجاد میکنه و Prometheus در بازه های مشخصی اطلاعاتشو میخونه (Scrape) و ذخیره میکنه.
روش Push: در این روش هر سرویس مقادیر متریک هاشو برای Pushgateway میفرسته و Prometheus با Pushgateway هم مثل یک Exporter برخورد میکنه و اطلاعات رو در بازه زمانی مشخص ازش میخونه. البته ما در این پروژه از روش Pull استفاده خواهیم کرد.
نکته جذاب تر اینه که کتابخانه های جانبی زیادی وجود دارند که برای انواع مختلف سرویس ها Exporter ارائه میکنند. مثلا شما اگر بخواین از بخشی اطلاعات موجود در MSSQL در Prometheus استفاده کنید میتونید سرچ کنید و Exporter مناسب رو پیدا کنید، یا اگر بخواین سیستم عامل ویندوز رو مانیتور کنید میتونید از Windows Exporter استفاده کنید. تقریبا برای اکثر سرویس های مهمی که میشناسید Exporter وجود داره و میشه به سادگی به Prometheus متصلش کرد.
یکی از بخش های دیگه Alertmanager هستش که وظیفه ی ارسال هشدار ها از طریق ایمیل، اسلک، اس ام اس و... رو داره.
پرومتئوس از Service Discovery برای شناسایی Target ها و بررسی up یا down بودن اونها استفاده میکنه که امکان Integrate شدنش با Kubernetes، Consul وجود داره.
از ابزارهایی مثل Grafana هم برای نمایش اطلاعات روی نمودار و داشبورد استفاده میشه. Prometheus یه HTTP Server هم داره که از طریق اون کوئریها رو دریافت میکنه و خروجی مورد نیاز رو به گرافانا یا هر API دیگه ای ارائه میده، این کوئری ها به زبان PromQL هستن که در ادامه باهاش آشنا میشیم
متریک ها چند نوع هستن: Counter، Gauge، Histogram و Summaries
یک. Counter یک شمارنده ست که مقدارش در گذر زمان افزایش پیدا میکنه، برای مثال تعداد درخواستها وارد شده به سرویس به ازای هر درخواست جدید که وارد سرویس میشه یک واحد افزایش پیدا میکنه. مقادیر یه شمارنده همیشه افزایشی است: 1-2-3-4-5-6-7-8-9-10.....
# TYPE application_request_counter gauge application_request_counter{endpoint="product-detail-page",responsecode="200"} 532
مثال بالا به این معناست که برای Endpoint جزئیات محصول با پاسخ 200 تا این لحظه 532 تا درخواست وارد سیستم شده و زمانی که درخواست جدیدی بیاد میشه 533
دو. Gauge برای نگهداری مقادیر عددی متغیر استفاده میشه، مثل مصرف CPU در یک زمان خاص یا مدت زمانی که صرف اجرای یک درخواست میشه: 450-320-470-1250-125-400
# TYPE application_request_duration gauge application_request_duration{endpoint="product-list-page"} 845
مثال بالا هم به این معناست که مدت زمان اجرای یک درخواست نمایش محصولات 845 میلی ثانیه است.
سه و چهار. Summary و Histogram متریک های ترکیبی هستند، یعنی اگر بخوایم مقدار یک متریک رو بسنجیم هم تعدادش (count) رو ثبت میکنه و هم مجموع رخداد (sum) این متریک ثبت میشه. که داشتن این دو مقدار به ما برای رسیدن به میانگین اون متریک کمک میکنه، فرض کنید ما تعداد درخواستها رو داریم و در کنارش مجموع Response Time درخواست ها هم هست و با کمک این دوتا مقدار میتونیم به میانگین زمان پاسخ درخواست ها برسیم. حالا تفاوت کجاست.
در Histogram میتونیم در کنار موارد بالا، مجموع تعداد درخواست در دسته های مختلف را هم داشته باشیم، مثلا تعداد درخواستهایی که زمان پاسخ کمتر از 50 میلی ثانیه بوده، تعداد درخواستهایی که زمان پاسخ کمتر از 180 میلی ثانیه و...
# TYPE application_request histogram application_request_bucket{le="50"} 502 application_request_bucket{le="100"} 954 application_request_bucket{le="180"} 1166 application_request_bucket{le="250"} 1296 application_request_bucket{le="400"} 1383 application_request_bucket{le="800"} 1424 application_request_bucket{le="2000"} 1429 application_request_bucket{le="+Inf"} 1429 application_request_sum 25465 application_request_count 1429
در Summary هم مجموع و تعداد رو داریم اما در کنارش یه دسته بندی کمی (quantile) هم داریم که با عددی بین صفر و یک نشون میدیم که تا هر دهک درصدی میانگین زمان پاسخ سرویس چقدره، یکم تعریفش گنگ بود با یک مثال فهمش راحت تره. یک نمونه از Summary برای میانگین زمان پاسخ به این شکل هستش که مثلا ثبت میکنیم میانگین زمان پاسخِ 50 درصد درخواستها حدود 114 میلی ثانیه هستش و میانگین زمان پاسخِ 95 درصد درخواست ها 146 میلی ثانیه.
# TYPE application_request_duration summary application_request_duration_sum{app="MonitoringExample.Api"} 95743 application_request_duration_count{app="MonitoringExample.Api"} 1429 application_request_duration{app="MonitoringExample.Api",quantile="0.5"} 114 application_request_duration{app="MonitoringExample.Api",quantile="0.75"} 132 application_request_duration{app="MonitoringExample.Api",quantile="0.95"} 146 application_request_duration{app="MonitoringExample.Api",quantile="0.99"} 152
توضیحات تکمیلی و مثالهایی از این متریک ها را در قسمتهای بعد سعی میکنم اضافه کنم.
شما بعد از اجرا شدن دستور docker-compose میتونید سرویس ها رو در مرورگر ببینید. اینجا لینک هر سرویس و اطلاعات جانبی برای ورود رو گذاشتم که برای مشاهده داشبورد اولیه لازمه وارد پنل گرافانا بشید. وقتی وارد پنل گرافانا شدید در داشبورد my api dshboard میتونید نمودارهایی که قراره بسازیم رو ببینید تا در قسمت های بعد نحوه ساختن هر کدوم رو توضیح بدیم.
در قسمت های بعد موارد زیر را بررسی میکنیم: