بطريقة ما كان عليّ أن أتعامل مع مقاييس واجهة برمجة التطبيقات الخاصة بنا ، كما هو الحال دائمًا (لا وقت؟) لإضافتها لاحقًا - إنها صعبة للغاية ولم يتم تنفيذها بعد - وهذا يعني أن الوقت قد حان لتطبيقها. بعد بعض التجوال على الشبكة ، كان نظام الرصد الأكثر شعبية ، كما يبدو لي ، بروميثيوس.
باستخدام Prometheus ، يمكننا مراقبة موارد الكمبيوتر المختلفة ، مثل: الذاكرة والمعالج والقرص وتحميل الشبكة. قد يكون من المهم بالنسبة لنا أيضًا حساب عدد المكالمات لطرق واجهة برمجة التطبيقات الخاصة بنا أو لقياس وقت تنفيذها ، لأنه كلما زاد الحمل على النظام ، زاد تعطله. وهنا تأتي بروميثيوس لمساعدتنا. يبدو أن هذا المقال يوفر النقاط الرئيسية لفهم عمل Prometheus ولإضافة مجموعة المقاييس إلى API. لذلك ، نبدأ مع أكثر عاديا ، مع وصف صغير.
بروميثيوس هو نظام مفتوح المصدر و Time Series DBMS مكتوب في Go والذي طورته SoundCloud. لديه وثائق رسمية ودعم لغات مثل: Go ، Java أو Scala ، Python ، Ruby. يوجد دعم غير رسمي للغات أخرى ، مثل: C # و C ++ و C و Bash و Lua for Nginx و Lua for Tarantool وغيرها ، والقائمة بأكملها موجودة على موقع Prometheus الرسمي.
تتوفر جميع خدمات Prometheus كصور Docker على Docker Hub أو Quay.io.
يتم docker run -p 9090:9090 prom/prometheus
Prometheus بواسطة الأمر docker run -p 9090:9090 prom/prometheus
، والذي يبدأ تشغيله بالتكوين الافتراضي ويقوم بتعيين المنفذ localhost:9090
، وبعد ذلك ، ستكون واجهة المستخدم Prometheus متاحة في localhost:9090
.
بروميثيوس هو نظام مراقبة يتضمن أدوات متنوعة لتكوين مراقبة التطبيقات (نقاط النهاية) باستخدام بروتوكول HTTP. عند الاتصال بـ Prometheus ، لا يدعم HTTP API "المصادقة الأساسية". إذا كنت تريد استخدام المصادقة الأساسية للاتصال بـ Prometheus ، نوصيك باستخدام Prometheus بالاقتران مع خادم وكيل عكسي واستخدام المصادقة على مستوى الوكيل. يمكنك استخدام أي وكيل عكسي مع بروميثيوس.
المكونات الرئيسية لبروميثيوس:
- خادم يجمع المقاييس ويحفظها في قاعدة البيانات وينظفها ؛
- حزم لجمع المقاييس في API ؛
- Pushgateway - مكون لتلقي المقاييس من التطبيقات التي لا يمكن استخدام طلب السحب ؛
- المصدرين - أدوات لتصدير المقاييس من تطبيقات وخدمات الطرف الثالث ، المثبتة على الأجهزة المستهدفة ؛
- AlertManager - مدير الإخطار (التنبيهات) ، يتم تعريف التنبيهات في ملف التكوين وتعيينها بواسطة مجموعة من القواعد للمقاييس.
إذا كان هناك التزام أثناء العملية بالقاعدة ، يتم تشغيل التنبيه وإرساله إلى المستلمين المحددين عبر البريد الإلكتروني أو Slack أو غيرهم.
تسمى الكائنات التي يعمل معها بروميثيوس المقاييس التي تم تلقيها من الأهداف إما من خلال بوابة الدفع أو من خلال المصدرين.
عند جمع المقاييس ، يتم استخدام عدة طرق لنقلها:
- تطلب Prometheus مقاييس من الهدف من خلال طلب سحب ، يتم تحديد إعداداتها في ملف التكوين في قسم scrape_config لكل مهمة.
عندما يجمع النظام البيانات ، يمكنك التحكم في وتيرة التجميع وإنشاء تكوينات متعددة لجمع البيانات لتحديد تردد مختلف لكائنات مختلفة ؛ - يسمح لك المصدرون بجمع المقاييس من كائنات مختلفة ، على سبيل المثال: قواعد البيانات (MongoDB ، SQL ، إلخ) ، وسماسرة الرسائل (RabbitMQ ، EMQ ، NSQ ، وما إلى ذلك) ، موازنات تحميل HTTP ، وما إلى ذلك ؛
- Pushgateway. يمكن استخدامه عند الضرورة ، عندما يتعذر على التطبيق توفير القدرة على إعطاء المقاييس مباشرةً لـ Prometheus ؛ أو عند استخدام مهام الدُفعات التي ليس لديها القدرة على استخدام طلب سحب Prometheus.
وبالتالي ، سيتم تخزين جميع المقاييس المستلمة من قبل بروميثيوس في قاعدة بيانات مع طوابع زمنية.
ترتيب
يتم تكوين Prometheus باستخدام علامات سطر الأوامر وملفات التكوين المتوفرة بتنسيق YAML. تتيح لك علامات سطر الأوامر تكوين معلمات غير قابلة للتغيير ، مثل: المسارات ، وحدات تخزين البيانات المخزنة على القرص وفي الذاكرة ، إلخ. يسمح لك ملف التكوين بتكوين كل ما يتعلق بالوظائف وإعداد ملفات yaml التي تم تحميلها. كل شيء مكتوب في ملف التكوين العام ، فهو يتيح لك ضبط الإعدادات العامة للجميع وتمييز الإعدادات لمقاطع التكوين المختلفة بشكل منفصل. يتم تكوين الإعدادات التي يستطلعها Prometheus في ملف التكوين في قسم scrape_configs.
يمكن لـ Prometheus إعادة تحميل ملفات التكوين أثناء العملية ، إذا كان التكوين الجديد غير صالح ، فلن يتم تطبيقه. يتم تشغيل إعادة تشغيل ملف التكوين عن طريق إرسال أمر SIGHUP Prometheus أو إرسال طلب HTTP POST إلى /-/reload
، بشرط أن يتم --web.enable-lifecycle
علامة --web.enable-lifecycle
. سيتم أيضًا إعادة تحميل جميع ملفات القواعد المكونة.
ما هي أنواع البيانات المستخدمة
تقوم بروميثيوس بتخزين نموذج بيانات متعدد الأبعاد مخصص وتستخدم لغة استعلام للبيانات متعددة الأبعاد تسمى PromQL. تقوم بروميثيوس بتخزين البيانات في شكل سلاسل زمنية ؛ فهي تدعم العديد من خيارات التخزين:
- التخزين المحلي للقرص: كل ساعتين ، يتم ضغط البيانات المخزنة في الذاكرة وتخزينها على القرص. بشكل افتراضي ، يتم استخدام دليل ./data في دليل العمل لحفظ الملفات المضغوطة ؛
- مستودع التخزين عن بعد: يدعم بروميثيوس التكامل مع مستودعات الطرف الثالث (على سبيل المثال: كافكا ، بوستجرس ، أمازون S3 ، وما إلى ذلك) من خلال محول بروتوكول التخزين المؤقت.
يتم تحديد السلسلة الزمنية المخزنة بواسطة القياس والبيانات الوصفية في شكل أزواج ذات قيمة مفتاح ، على الرغم من أنه إذا لزم الأمر ، قد لا يتم استخدام اسم المقياس وسوف يتكون المقياس نفسه فقط من البيانات التعريفية. يمكن تعريف السلسلة الزمنية رسميًا على أنها <metric name> {<metadata>}. المفتاح هو <metric name> {<metadata>} - ما نقيسه ، والقيمة هي القيمة الفعلية كرقم مع نوع float64 (يدعم Prometheus هذا النوع فقط). يحتوي وصف المفتاح على بيانات أولية (تسميات) ، موصوفة أيضًا بأزواج قيمة المفتاح: <label name> = "<label label>" ، <label name> = "<label label>" ، ...
عند تخزين المقاييس ، يتم استخدام أنواع البيانات التالية:
- عداد - بحساب المبلغ على مدى فترة من الزمن. يمكن أن يزيد هذا النوع من المقاييس فقط (لا يمكنك استخدام القيم السلبية) أو إعادة تعيين القيمة.
قد يكون ذلك مناسبًا ، على سبيل المثال ، لحساب عدد الطلبات في الدقيقة أو عدد الأخطاء في اليوم ، وعدد حزم الشبكة المرسلة / المستلمة ، إلخ. - مقياس - يخزن القيم التي قد تنقص أو تزيد بمرور الوقت.
المقياس لا يُظهر تطور المقاييس على مدار فترة زمنية. باستخدام المقياس ، يمكنك أن تفقد تغييرات القياس غير المنتظمة بمرور الوقت. - المدرج الإحصائي - يحفظ عدة سلاسل زمنية: المجموع الكلي لجميع القيم المرصودة ؛ عدد الأحداث التي تمت ملاحظتها ؛
يشار إلى العدادات التراكمية (الدلاء) في التسمية كـ le="<upper inclusive bound>"
.
يتم جمع القيم في المناطق ذات الحدود العليا المخصصة (الدلاء). - ملخص - يحفظ عدة سلاسل زمنية: المجموع الكلي لجميع القيم الملاحظة ؛ عدد الأحداث التي تمت ملاحظتها ؛
يشار إلى التدفق iles-quantiles (0 ≤ φ ≤ 1) للأحداث المرصودة - في الملصق على أنه quantile="<φ>"
.
كيف يتم حفظ البيانات؟
توصي بروميثيوس "إعطاء" 2/3 من ذاكرة الوصول العشوائي إلى تطبيق قيد التشغيل.
لتخزين البيانات في الذاكرة ، يستخدم Prometheus ملفات تسمى chunk ؛ كل مقياس له ملف خاص به. جميع الملفات المقطوعة غير قابلة للتغيير ، باستثناء آخر ملف يتم فيه كتابة البيانات. يتم حفظ البيانات الجديدة في قطعة وكل ساعتين يعمل دفق الخلفية على دمج البيانات وكتابتها على القرص. يتكون كل كتلة من ساعتين من دليل يحتوي على ملف واحد أو أكثر من المقاطع التي تحتوي على جميع عينات السلاسل الزمنية لتلك الفترة الزمنية ، بالإضافة إلى ملف بيانات التعريف وملف الفهرس (الذي يفهرس أسماء المقاييس والتسميات للسلاسل الزمنية في الملفات المقطوعة). إذا لم تقم بروميثيوس في غضون ساعة واحدة بكتابة البيانات لقطعها ، فسيتم حفظها على القرص وسيتم إنشاء جزء جديد لكتابة البيانات. الحد الأقصى لفترة الاحتفاظ بالبيانات في Prometheus هو 21 يومًا تقريبًا.
لأن تم إصلاح حجم الذاكرة ، وسيتم تقييد أداء النظام للكتابة والقراءة بهذا الحجم من الذاكرة. يتم تحديد مقدار ذاكرة PTSDB حسب الفترة الزمنية الدنيا ، وفترة التجميع ، وعدد مقاييس الوقت.
لدى بروميثيوس أيضًا آلية WAL لمنع فقد البيانات.
الكتابة إلى الأمام سجل (WAL) تسلسل العمليات المحفوظة على وسيط دائم في شكل ملفات السجل. في حالة حدوث فشل ، يمكن استخدام ملفات WAL لاستعادة قاعدة البيانات إلى حالتها المتسقة من خلال الاستعادة من السجلات.
يتم تخزين ملفات السجل في دليل wal في قطاعات 128 ميجابايت. تحتوي هذه الملفات على بيانات أولية لم يتم ضغطها بعد ، لذا فهي أكبر بكثير من ملفات الأجزاء المعتادة.
ستقوم بروميثيوس بتخزين ما لا يقل عن 3 ملفات سجل ، ولكن يمكن للخوادم التي تتمتع بحركة مرور عالية أن ترى أكثر من ثلاثة ملفات WAL ، حيث إنها تحتاج إلى تخزين ما لا يقل عن ساعتين من البيانات الخام.
نتيجة استخدام WAL هو انخفاض كبير في عدد طلبات الكتابة إلى القرص ، كما يحتاج ملف السجل فقط إلى الكتابة على القرص ، وليس كل جزء من البيانات التي تم تغييرها كنتيجة للعملية. تتم كتابة ملف السجل بالتسلسل ، وبالتالي فإن تكلفة مزامنة السجل أقل بكثير من تكلفة كتابة أجزاء البيانات.
يحفظ Prometheus نقاط التوقف الدورية ، والتي تتم إضافتها افتراضيًا كل ساعتين عن طريق ضغط سجلات الفترة الماضية وحفظها على القرص.
يتم تخزين جميع نقاط التوقف في نفس الدليل مثل checkpoint.ddd ، حيث يكون ddd عددًا متزايدًا بشكل رتيب. لذلك ، عند التعافي من الفشل ، يمكنه استعادة نقاط التوقف من كتالوج نقطة التوقف مع الإشارة إلى الترتيب (.ddd).
من خلال كتابة سجلات WAL ، يمكنك الرجوع إلى أي نقطة تفتيش يتوفر لها سجل البيانات.
ماذا حدث في الممارسة؟
عند الإضافة إلى المشروع (.Net Framework) ، استخدمنا حزمة Prometheus.Client.3.0.2 لجمع المقاييس. لجمع المقاييس ، تمت إضافة الطرق والفصول اللازمة إلى المشروع لتخزين المقاييس حتى يتم استلامها بواسطة Prometheus.
تم تعريف واجهة IMetricsService في الأصل على أنها تحتوي على أساليب المؤقت لقياس مدى عمل الطرق:
public interface IMetricsService { Stopwatch StartTimer(); void StopTimer(Stopwatch timer, string controllerName, string actionName, string methodName = "POST"); }
نضيف فئة MetricsService ، التي تنفذ واجهة IMetricsService وتخزين المقاييس مؤقتًا.
public class MetricsService : IMetricsService { private static Histogram _histogram; static MetricsService() { _histogram = CreateHistogram(); } public Stopwatch StartTimer() { try { var timer = new Stopwatch(); timer.Start(); return timer; } catch (Exception exception) { Logger.Error(exception); } return null; } public void StopTimer(Stopwatch timer, string controllerName, string actionName, string methodName = "POST") { try { if (timer == null) { throw new ArgumentException($"{nameof(timer)} can't be null."); } timer.Stop(); _histogram .WithLabels(controllerName, actionName, methodName) .Observe(timer.ElapsedMilliseconds, DateTimeOffset.UtcNow); } catch (Exception exception) { Logger.Error(exception); } } public static List<string> GetAllLabels() { var metricsList = new List<string>(); try { foreach (var keyValuePair in _histogram.Labelled) { var controllerName = keyValuePair.Key.Labels[0].Value; var actionName = keyValuePair.Key.Labels[1].Value; var methodName = keyValuePair.Key.Labels[2].Value; var requestDurationSum = keyValuePair.Value.Value.Sum; var requestCount = keyValuePair.Value.Value.Count; metricsList.Add($"http_request_duration_widget_sum{{controller={controllerName},action={actionName},method={methodName}}} {requestDurationSum}"); metricsList.Add($"http_request_duration_widget_count{{controller={controllerName},action={actionName},method={methodName}}} {requestCount}"); } _histogram = CreateHistogram(); } catch (Exception exception) { Logger.Error(exception); } return metricsList; } private static Histogram CreateHistogram() { var newMetrics = Metrics .WithCustomRegistry(new CollectorRegistry()) .CreateHistogram(name: "http_request_duration_web_api", help: "Histogram metrics of Web.Api", includeTimestamp: true, labelNames: new[] { "controller", "action", "method" }); var oldValue = _histogram; for (var i = 0; i < 10; i++) { var oldValue = Interlocked.Exchange<Histogram>(ref oldValue, newMetrics); if (oldValue != null) { return oldValue; } } return null; } }
الآن يمكننا استخدام الفصل الخاص بنا لحفظ المقاييس التي نخطط لجمعها في الأساليب Application_BeginRequest و Application_Error و Application_EndRequest. في فئة Global.cs ، نضيف مجموعة من المقاييس إلى الأساليب المذكورة أعلاه.
private IMetricsService _metricsService; protected virtual void Application_BeginRequest(object sender, EventArgs e) { var context = new HttpContextWrapper(HttpContext.Current); var metricServiceTimer = _metricsService.StartTimer(); context.Items.Add("metricsService", _metricsService); context.Items.Add("metricServiceTimer", metricServiceTimer); } protected virtual void Application_EndRequest(object sender, EventArgs e) { WriteMetrics(new HttpContextWrapper(HttpContext.Current)); } protected void Application_Error(object sender, EventArgs e) { WriteMetrics(new HttpContextWrapper(HttpContext.Current)); } private void WriteMetrics(HttpContextBase context) { try { _metricsService = context.Items["metricsService"] as IMetricsService; if (_metricsService != null) { var timer = context.Items["metricServiceTimer"] as Stopwatch; string controllerName = null; string actionName = null; var rd = RouteTable.Routes.GetRouteData(context); if (rd != null) { controllerName = rd.GetRequiredString("controller"); actionName = rd.GetRequiredString("action"); } _metricsService.StopTimer(timer, controllerName, actionName, context.Request.HttpMethod); } } catch (Exception exception) { Logger.Error("Can't write metrics.", exception); } }
أضف وحدة تحكم جديدة ، والتي ستكون نقطة مرجعية لإرسال مقاييس API الخاصة بنا إلى Prometheus:
public class MetricsController : Controller { [HttpGet] public string[] GetAllMetrics() { try { var metrics = MetricsService.GetAllLabels(); return metrics.ToArray(); } catch (Exception exception) { Logger.Error(exception); } return new string[] { }; } }
ستكون الخطوة الأخيرة هي تكوين تهيئة Prometheus لجمع المقاييس في قسم scrape_configs ، وبعد ذلك يمكننا أن نرى المقاييس التي تم جمعها بالفعل في Prometheus أو Grafana UI.
الميزات الرئيسية التي كنا مهتمين بها في بروميثيوس:
نموذج البيانات متعدد الأبعاد: المقاييس والعلامات.
لغة استعلام PromQL مرنة. في نفس مُشغل الاستعلام ، يمكننا استخدام عمليات مثل الضرب والإضافة والتسلسل وما إلى ذلك ؛ يمكن أن يؤديها مع مقاييس متعددة.
يجمع البيانات المستندة إلى HTTP باستخدام طريقة السحب.
متوافق مع طريقة الدفع عبر Pushgateway.
من الممكن جمع المقاييس من التطبيقات الأخرى من خلال المصدرين.
يوفر آلية لمنع فقدان البيانات.
يدعم التمثيلات الرسومية المختلفة للبيانات.