مقدمة
في مجال البنية التحتية الحديثة، برز Prometheus كمعيار فعلي لجمع المقاييس وإطلاق التنبيهات. ومع ذلك، لا تزال العديد من المؤسسات تعتمد على أنظمة "قديمة" — مثل التطبيقات الأحادية، وأجهزة الكمبيوتر المركزية، أو البرمجيات الخاصة — التي تفتقر إلى الدعم الأصلي لتنسيق عرض Prometheus. غالباً ما تتحدث هذه الأنظمة بلغة رسائل SNMP، أو سجلات خاصة، أو استجابات HTTP بسيطة غير متوافقة مع مجموعات المراقبة الحديثة.
غالباً ما يكون محاولة استخراج البيانات من هذه الأنظمة مباشرةً مستحيلة بسبب قيود الأمان أو حدود البروتوكول. تكمن الحل في بناء مصدّرات Prometheus مخصصة. المصدّر هو خدمة خفيفة الوزن تقع بين بنية التحتية القديمة الخاصة بك وبين Prometheus، حيث تقوم بترجمة تنسيقات البيانات القديمة إلى مقاييس معيارية يفهمها Prometheus. سيأخذك هذا الدليل عبر البنية، والتنفيذ، وأفضل الممارسات لإنشاء هذه الجسور.
فهم بنية المصدّر
قبل الغوص في الكود، من الضروري فهم النمط المعماري. يعمل المصدّر المخصص كوسيط (Adapter). وعادةً ما يؤدي ثلاث وظائف أساسية:
1. **الاكتشاف/الاتصال:** يتصل بالنظام القديم باستخدام بروتوكول متاح (على سبيل المثال، JDBC لقواعد البيانات، أو SSH للخوادم، أو واجهات برمجة التطبيقات HTTP).
2. **الترجمة:** يستخرج البيانات الخام ويربطها بأنواع مقاييس Prometheus (Counter، Gauge، Histogram، أو Summary).
3. **العرض:** يعرض نقطة نهاية `/metrics` بتنسيق Prometheus النصي، جاهزة للاستخراج.
يجب أن يكون المصدّر نفسه عديم الحالة (Stateless) فيما يتعلق بالبيانات التي يجمعها، لكنه ذو حالة (Stateful) في اتصاله بالنظام القديم. من أفضل الممارسات إبقاء المصدّر منفصلاً عن التطبيق القديم لتجنب الحمل الإضافي على نظام الإنتاج الحرج.
اختيار اللغة المناسبة
بينما يمكنك بناء المصدّرات بأي لغة، تُعد Go (Golang) المعيار الصناعي لمصدّرات Prometheus. توفر المكتبة الرسمية `prometheus/client_golang` مبادئ أولية قوية ومنخفضة المستوى تضمن أداءً عالياً واستهلاكاً ضئيلاً للموارد. كما يُعد Python خياراً قابلاً للتطبيق للبرامج النصية البسيطة، لكن Go توفر معالجة أفضل للتزامن وتجميعاً لثنائيات ثابتة، مما يسهل النشر.
مثال عملي: بناء مصدّر Go
لنفترض أن لدينا خدمة داخلية قديمة تعرض رقماً واحداً عبر طلب HTTP GET بسيط عند العنوان `http://legacy-system:8080/status`. نريد عرض هذا كقيمة مقاس Gauge في Prometheus.
أولاً، تأكد من وجود التبعيات اللازمة:
```bash
go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promhttp
```
إليك تنفيذ كامل وبسيط لمصدّر مخصص:
package main
import (
"io/ioutil"
"log"
"net/http"
"strings"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
// legacyStatusGauge هو المقاس الذي نريد عرضه
var legacyStatusGauge = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "legacy_system_status_code",
Help: "رمز الحالة الحالي الذي يعيده النظام القديم",
},
)
// Collector يطبق واجهة prometheus.Collector
type LegacyCollector struct{}
// Describe يرسل المجموعة الشاملة لجميع الوصفين الممكنين للمقاييس
func (c *LegacyCollector) Describe(ch chan<- *prometheus.Desc) {
ch <- legacyStatusGauge.Desc()
}
// Collect يتم استدعاؤها بواسطة سجل Prometheus عند جمع المقاييس
func (c *LegacyCollector) Collect(ch chan<- prometheus.Metric) {
// جلب البيانات من النظام القديم
resp, err := http.Get("http://legacy-system:8080/status")
if err != nil {
log.Printf("Error fetching status: %v", err)
// لا ترجع إذا كان النظام متوقفاً؛ اترك Prometheus يتعامل مع أخطاء الاتصال
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading body: %v", err)
return
}
// تحليل استجابة النظام القديم (بافتراض أنها سلسلة رقمية)
statusValue := 0.0
if strings.TrimSpace(string(body)) != "" {
// في السيناريو الحقيقي، استخدم منطق تحليل صحيح
statusValue = 1.0
}
// تعيين قيمة المقاس
legacyStatusGauge.Set(statusValue)
// إرسال المقاس إلى Prometheus
ch <- legacyStatusGauge
}
func main() {
// تسجيل المجمع
prometheus.MustRegister(&LegacyCollector{})
// عرض نقطة نهاية /metrics
http.Handle("/metrics", promhttp.Handler())
log.Println("Starting exporter on :9100")
log.Fatal(http.ListenAndServe(":9100", nil))
}
أفضل الممارسات للإنتاج
عند الانتقال من النموذج الأولي إلى الإنتاج، ضع في اعتبارك التحسينات التالية:
* **معالجة الأخطاء:** لا تدع أخطاء الجمع تسبب توقف المصدّر عن العمل (Panic). إذا كان النظام القديم غير قابل للوصول، يجب أن يستمر المصدّر في العمل ويعرض قيمًا صفرية أو قيمًا معروفة سابقة إذا كان ذلك مناسباً، مما يسمح لـ Prometheus بتوليد تنبيهات بناءً على عدم التجديد (Staleness).
* **المصادقة:** إذا كان نظامك القديم يتطلب مصادقة، تأكد من التعامل مع بيانات الاعتماد بأمان عبر متغيرات البيئة أو أدوات إدارة الأسرار، وليس بشكل ثابت في الكود.
* **تسمية المقاييس:** اتبع اتفاقيات تسمية Prometheus. استخدم الشرطة السفلية، وليس camelCase، وضمن الوحدة في نص المساعدة. تجنب "التسميات" المعلوماتية ذات التفرع العالي (مثل معرفات المستخدمين الفريدة)، لأن ذلك قد يثقل قاعدة بيانات السلاسل الزمنية لـ Prometheus.
* **فحوصات الصحة:** نفذ نقطة نهاية `/health` بجانب `/metrics` للسماح لمُديري الحاويات (مثل Kubernetes) بالتحقق من أن المصدّر يعمل وقادر على الاتصال بالخلفية.
الخاتمة
يُعد بناء مصدّرات Prometheus مخصصة مهارة أساسية لمهندسي DevOps الذين يديرون بيئات هجينة أو تعتمد بشكل كبير على الأنظمة القديمة. من خلال فصل منطق المراقبة عن التطبيق القديم، تحصل على إمكانية المراقبة دون المساس بالاستقرار. بينما يتطلب الإعداد الأولي جهداً، فإن الفوائد طويلة المدى للتنبيه الموحد، والتصور، وربط البيانات عبر كامل البنية التحتية الخاصة بك لا تقدر بثمن. ابدأ بشكل صغير، وطوّر تعريفات مقاييسك، وتأكد من أن مصدّراتك خفيفة الوزن وقوية مثل الأنظمة التي تراقبها.