إدارة أجهزة MikroTik الجماعية عبر RouterOS API: ملاحظات معمارية من الميدان
معمارية إدارة مئات وآلاف أجهزة MikroTik مركزياً وذاتياً عبر RouterOS API: واجهة librouteros الثنائية (binary API)، جسر asyncio، طابور مهام Redis، دفع إعدادات idempotent، circuit breaker والفروق بين الإصدارين v6/v7. من واقع ما تعلمناه أثناء بناء منصة الأتمتة الخاصة بنا.
معمارية إدارة مئات وآلاف أجهزة MikroTik مركزياً وذاتياً عبر RouterOS API: واجهة librouteros الثنائية (binary API)، جسر asyncio، طابور مهام Redis، دفع إعدادات idempotent، circuit breaker والفروق بين الإصدارين v6/v7. من واقع ما تعلمناه أثناء بناء منصة الأتمتة الخاصة بنا.
İçindekiler▾
- طرق إدارة RouterOS برمجياً: API و SSH و REST
- إرسال أمر إلى مئات الأجهزة في آنٍ واحد: معمارية طابور المهام
- إدخال مكتبة متزامنة (synchronous) إلى عالم async: جسر asyncio.to_thread
- أخبث مزالق الـ API: الفرق بين “print” و “action”
- لماذا لا يوجد /export في الـ API، والاضطرار للنزول إلى SSH
- تغيير جماعي آمن: desired state ← diff ← أمر idempotent ← تحقّق
- الفروق بين RouterOS v6 و v7: حيث يُنفَق أكبر قدر من كود الأتمتة
- بيانات الاعتماد والتدقيق: الأمن على نطاق واسع
- فما مقدار هذا النطاق فعلاً؟
- هل تبنيها بنفسك أم تُسنِد إدارتها؟
الجواب المختصر: يمكنك إدارة عشرات الأجهزة يدوياً واحداً واحداً؛ لكن مع مئات أو آلاف الأجهزة يصبح ذلك مستحيلاً. الطريق الصحيح على نطاق واسع هو بناء أتمتة تعمل عبر RouterOS API ببيانات مُهيكلة، وتعتمد على طابور مهام، وتكون idempotent. يشرح هذا المقال القرارات المعمارية والمزالق التي تعلّمناها في الميدان أثناء تطويرنا منصة إدارة MikroTik المركزية الخاصة بنا لتلبية هذه الحاجة تحديداً: لماذا الـ binary API، ولماذا طابور المهام، وكيف يُنفَّذ دفع الإعدادات بأمان، وما هي الحالات الحدّية الحقيقية في RouterOS التي تُصعّب الأتمتة.
هذا المحتوى ليس ترويجاً لمنتج؛ بل خارطة طريق للمهندسين الذين سيبنون نظاماً مماثلاً، وإجابة للمؤسسات التي تفكّر في إسناد إدارة بنية MikroTik للخارج عن سؤال “ما الذي يقبع تحت هذا العمل”. إذا كنت مبتدئاً في MikroTik، ننصحك أولاً بقراءة دليل “ما هو MikroTik”.
طرق إدارة RouterOS برمجياً: API و SSH و REST
يمكن أتمتة RouterOS من الخارج بثلاث طرق؛ والاختيار على نطاق واسع واضح:
- Binary API (المنفذ 8728 / api-ssl 8729): البروتوكول الثنائي الخاص بـ RouterOS. الأوامر والسجلات المُرجعة مُهيكلة، أي أنك تحصل على البيانات كأزواج حقل-قيمة من قوائم مثل
interfaceوip addressوfirewall filter، دون تحليل نصوص. وهذه هي الطبقة الصحيحة للإدارة الجماعية. - REST API (v7، عبر HTTP/HTTPS): جاء مع RouterOS 7 ويُرجع JSON. عملي للتكاملات البسيطة؛ لكننا فضّلنا الـ binary API لأننا نحتاج لدعم أجهزة v6 أيضاً وللبقاء ضمن تجريد عميل (client) واحد (REST متوفر في v7 فقط).
- SSH: الطريق الأكثر مرونة لكنه الأكثر هشاشة: المخرجات نصّ حرّ، تتغيّر من إصدار لآخر، وتحليلها متعب. نحتفظ بـ SSH فقط للمهام الضيقة التي لا تغطيها الـ API (انظر قسم
/exportأدناه).
قرارنا: الطريقة الأساسية هي الـ binary API، والطريقة الثانوية ضيقة الغرض هي SSH. على جانب Python نُنجز ذلك بمكتبة librouteros؛ عميل ناضج يطبّق البروتوكول بشكل صحيح ويتولّى نيابةً عنك تدفّق تسجيل الدخول (login) الخاص بالـ binary API.
إرسال أمر إلى مئات الأجهزة في آنٍ واحد: معمارية طابور المهام
أشيع خطأ هو رد الفعل القائل “إن كان لديّ ألف جهاز فلأفتح ألف اتصال في آنٍ معاً”. هذا يُركِع خادماً واحداً وكامل مكدّس الشبكة بسرعة؛ كما أن جهازاً بطيئاً واحداً يُعطّل العملية بأكملها في الانتظار. النمط الذي يعمل على نطاق واسع مختلف:
- Fan-out (التفريع): عند إطلاق عملية جماعية (مثل “خذ نسخة احتياطية من كل الأجهزة)، تُنتَج مهمة منفصلة لكل جهاز.
- طابور بأولويات: تُكتب هذه المهام في طابور Redis. تُحفظ أنواع المهام المختلفة بأولويات مختلفة، فعملية reconcile عاجلة تتقدّم على فحص مراقبة (monitoring) روتيني.
- توسّع أفقي للـ worker: يستهلك الطابورَ عددٌ كبير من الـ worker المستقلة بالتوازي. زيادة درجة الموازاة تكون بزيادة عدد نسخ الـ worker (replica) لا بتسريع حلقة عملاقة واحدة. هذا يجعل النظام قابلاً للتوسّع أفقياً بشكل طبيعي على Docker/Kubernetes.
لهذه المعمارية ثلاث ضمانات حاسمة:
- طابور موثوق (ACK/NACK): عند التقاط المهمة تُعلَّم كـ “قيد المعالجة”. وإذا انهار الـ worker، تعود المهمة تلقائياً إلى الطابور بعد مدة محددة، فلا يُتخطّى أي جهاز بصمت.
- قفل موزَّع لكل جهاز: يُؤخَذ لكل جهاز قفل في Redis (
lock:device:<id>). بهذا لا يستطيع worker ـان الكتابةَ إلى إعدادات نفس الـ MikroTik في آنٍ واحد. وإن تعذّر أخذ القفل، تُعاد المحاولة بـ exponential backoff قصير (5 ← 10 ← 20 ← 40 ثانية). - Circuit breaker: محاولة الاتصال مراراً بجهاز مُطفأ أو غير قابل للوصول هدرٌ للوقت وللطابور معاً. بعد عدد معيّن من أخطاء الاتصال المتتالية (3 عندنا) “يُفتح المفتاح” لذلك الجهاز فلا تُعاد المحاولة طوال مدة تهدئة (5 دقائق). وعند عودة الجهاز يُغلق المفتاح تلقائياً.
حدّ صادق: ليست كل مهمة جماعية “fan-out حقيقياً”. فمثلاً يمكن وضع فحص health-check لآلاف الأجهزة كمهمة واحدة في الطابور والدوران داخل الـ worker بالتسلسل؛ وهذا بسيط لكنه تسلسلي. الموازاة الحقيقية تظهر حين تُجزّئ العمل لكل جهاز على حدة. أما أي مهمة تكون fan-out وأيها تكون تسلسلية فقرار تصميمي واعٍ.
هذا النوع من الإدارة المركزية يكتسب معناه مع طبقة المراقبة؛ نحن نربط المخزون (inventory) أيضاً بجانب مراقبة الشبكة عبر Zabbix. وللبِنى متعددة الفروع ومن نوع مزوّدي الخدمة (ISP)، يُعدّ دليل إدارة شبكات ISP قراءة مكمّلة.
إدخال مكتبة متزامنة (synchronous) إلى عالم async: جسر asyncio.to_thread
librouteros مكتبة متزامنة (synchronous): عندما ترسل أمراً تظلّ محجوبة (block) حتى يصل الجواب. أما الـ worker لدينا فمبنيّ على asyncio. مزج هذين بصورتهما الخام يعني أن جهازاً بطيئاً واحداً يقفل حلقة الأحداث (event loop) بأكملها، وبالتالي كل الأجهزة التي يعالجها ذلك الـ worker.
الحلّ هو نقل كل استدعاء I/O مع الجهاز إلى thread:
# نفّذ استدعاء librouteros المتزامن دون حجب حلقة الأحداث
api = await asyncio.to_thread(librouteros.connect, host=ip, username=user,
password=pw, port=8728, timeout=10)
data = await asyncio.to_thread(lambda: tuple(api.path("interface")))
هذا الجسر الذي يبدو صغيراً حاسمٌ على نطاق واسع: الاتصال، والقراءة، والأمر: كل خطوة تتحدّث فيها مع الجهاز داخل to_thread. وإلا فسيبدو النظام “async” لكنه عملياً يسير بسرعة جهاز واحد.
ملاحظة: في لغات مثل Go يُبنى الوصول المتزامن إلى آلاف الأجهزة بشكل أطبع عبر الـ goroutine. لكننا بقينا في Python لأن بقية المنصة (FastAPI، نموذج البيانات، إلمام الفريق) بلغة Python، وعوّضنا ذلك بهذا الجسر. اختيار اللغة ليس صواباً أو خطأً، بل قرار سياقي.
أخبث مزالق الـ API: الفرق بين “print” و “action”
في RouterOS API تُعدّ قراءة قائمة أمراً مختلفاً عن إرسال إجراء (action) إليها، وهذا أكثر ما يوقع المبتدئ في الأتمتة.
- التكرار (iteration) على
api.path("interface")يُشغّل في الخلفية/interface/print، أي يقرأ. - أما الأوامر مثل
rebootوupgradeوbackup saveفلا تُرسَل بهذه الطريقة؛ إذ تحتاج إلى استدعاء منفصل يُشغّل الأمر مباشرةً (مثلapi(cmd="/system/reboot")).
من دون معرفة هذا الفرق تُهدَر ساعات في السؤال “لماذا لا يعمل reboot”. في كودنا هذا التمييز مُعلَّم بتعليق في رأس كل دالة إجراء، كي لا نقع في المزلق ذاته بعد ستة أشهر.
حقيقة متصلة: سقوط الاتصال بعد reboot أمر طبيعي. أثناء إعادة تشغيل الجهاز تنقطع جلسة الـ API بطبيعة الحال؛ ينبغي عدم عدّ هذا الاستثناء خطأً بل ابتلاعه (وتسجيله كـ “أُعيد تشغيل الجهاز”).
لماذا لا يوجد /export في الـ API، والاضطرار للنزول إلى SSH
الطريقة التقليدية للحصول على مقطع تكوين كامل وقابل للقراءة لجهاز MikroTik هي أمر /export. لكن الـ binary API لا يُرجع /export. كان هذا أحد أكثر الجدران صلابةً التي اصطدمنا بها أثناء بناء الأتمتة.
هناك حلّان، ونستخدمهما معاً:
- SSH للحصول على
/exportالحقيقي: عند الحاجة لمقطع تكوين طبق الأصل (للتدقيق أو الأرشفة مثلاً)، نفتح SSH عبر paramiko ونأخذ مخرجات/export. هذا مثال تام على مبدأ “احتفظ بـ SSH للمهام الضيقة فقط”. - “pseudo-export” من الـ API: قراءة القوائم قسماً قسماً وتوليد نصّ شبيه بـ RSC. لا يتطلّب SSH لكنه ليس كاملاً كـ
/export.
الدرس: الـ binary API قويّ لكنه لا يغطّي كل شيء؛ الأتمتة الناضجة يجب أن تستطيع الانتقال بنظافة إلى SSH حيث تنتهي الـ API.
تغيير جماعي آمن: desired state ← diff ← أمر idempotent ← تحقّق
لا داعي لأن يكون التغيير الجماعي للإعدادات مُرعباً؛ المُرعب هو التغيير الأعمى. حلقة الـ reconcile (المصالحة) لدينا تمرّ بالخطوات التالية:
- Desired state: تُولَّد الحالة التي ينبغي أن يكون عليها الجهاز من قالب YAML (NTP، SNMP، أساس firewall، مستخدمو الإدارة، إلخ).
- Actual state: يُقرأ التكوين الحالي من الجهاز عبر الـ API.
- Diff: تُقارَن الحالتان قسماً قسماً؛ ولا يُحسب إلا الفرق (drift).
- نسخة احتياطية مسبقة: قبل تطبيق التغيير تُؤخَذ نسخة احتياطية من تكوين الجهاز، وهي ضمان للرجوع.
- تطبيق idempotent: تُولَّد الأوامر بمنطق “إن وُجد فلا تلمسه، وإلا أضِفه” (
add-if-missing)، و“ابحث وحدّث” (set-by-find). ولو شُغّل الـ reconcile ذاته مرتين لما تغيّرت النتيجة. - تحقّق: يُقرأ الجهاز مجدداً، ويُؤكَّد أن الانحراف صار صفراً.
القيمة العملية للـ idempotency هي التالية: إذا توقّف reconcile في منتصفه بسبب انقطاع شبكة، فلا داعي للذعر: إعادة تشغيل المهمة تُكمل النواقص دون تكرار الخطوات المُطبّقة أصلاً. كما يُعلَّم النجاح الجزئي بوضوح كـ partial؛ وحتى لو انفجر أمرٌ واحد تُواصَل محاولة البقية وتُبلَّغ النتيجة بأمانة.
هذا الانضباط هو الطريق الوحيد المستدام للحفاظ على اتساق قواعد الـ firewall أو تكوين الـ VLAN أو إعدادات الواي فاي المركزي (CAPsMAN) عبر مئات الأجهزة.
الفروق بين RouterOS v6 و v7: حيث يُنفَق أكبر قدر من كود الأتمتة
من غير الممكن إدارة أجهزة RouterOS 6 و 7 بقالب أمر واحد؛ إذ يجب دفن فروق الإصدارات داخل الأتمتة. أكثر ما نصادفه في الميدان:
| الموضوع | RouterOS v6 | RouterOS v7 |
|---|---|---|
| BGP | /routing/bgp/peer |
/routing/bgp/connection |
| NTP | primary-ntp / secondary-ntp (حقلان منفصلان) |
servers= (قائمة بفواصل) |
| تصفية bridge VLAN | محدودة / غير ناضجة | مدعومة بالكامل |
| WireGuard | غير موجود | موجود (جاء مع v7) |
المقاربة العملية: عند الاتصال بالجهاز اقرأ إصدار RouterOS أولاً، حدّد الإصدار الرئيسي (major)، وفرّع توليد الأمر بناءً عليه. لا مفرّ من fallback من نوع “جرّب مسار v7، وإن فشل انزل إلى مسار v6”. هذه طبقة تُضخّم الكود لكنها لا غنى عنها في الميدان. وإذا كنت تتعامل مع الانتقال بين الإصدارات كخدمة، فإننا نتناول انتقال RouterOS من 6 إلى 7 بشكل منفصل في صفحة دعم MikroTik.
بيانات الاعتماد والتدقيق: الأمن على نطاق واسع
نظامٌ يمسك بالوصول إلى مئات الأجهزة، لا جهاز واحد، هدفٌ أثمن بكثير في حال تسرّبه. الخطوط الدنيا التي نلتزم بها:
- حفظ كلمات المرور مشفّرة: كلمات مرور الأجهزة تُخزَّن في قاعدة البيانات لا كنصّ صريح بل كنصّ مشفّر (ciphertext) بتشفير متماثل (Fernet)؛ ويُحفظ المفتاح في متغيّر بيئة ولا يدخل المستودع (repo) أبداً. لا تُفكّ كلمة المرور إلا في اللحظة التي سيتصل فيها الـ worker بجهاز، وفي الذاكرة. (حدّ صادق: هذا ليس HashiCorp Vault / KMS، بل حلّ قائم على البيئة، فالأمن معلّق على مفتاح واحد، وهو مجال قابل للنضج.)
- سجلّ تدقيق (audit log): كل مهمة تُسجَّل مع معلومة “من أطلقها” (
created_by: مستخدم / scheduler / تلقائي). وكل إجراء على جهاز (أُخذت نسخة احتياطية، طُبِّق/فشل reconcile، اكتُشِف reboot) يُكتب في جدول أحداث منفصل بتفصيل مَن-ماذا-متى. - أقل امتياز (least privilege): على جانب التطبيق يوجد وصول قائم على الأدوار (viewer افتراضياً، وعمليات الإدارة تتطلّب admin) وتعدّد مستأجرين (multi-tenancy) قائم على المجموعات. وعلى جانب الجهاز، يُجعَل مجتمع SNMP الذي تضيفه الأتمتة للقراءة فقط (
read-access=yes, write-access=no). (أما تضييق صلاحيات مستخدمي الإدارة على جانب الجهاز فهو مجال نحسّنه باستمرار. وبصراحة، ليس مثال “أقل امتياز” سهلاً دائماً هنا.)
فما مقدار هذا النطاق فعلاً؟
صمّمنا المنصة بهدف 50٬000+ جهاز؛ وبنينا المعمارية (الطابور، الـ worker الأفقي، القفل الموزّع، circuit breaker) لتتحمّل هذا الحجم. والصدق هنا مهم: رقم 50٬000 ليس رقماً مُثبَتاً في الإنتاج الحيّ، بل هدف تصميمي. فكون المعمارية مُقاسة لتتحمّل هذا النطاق شيء، وكونها شُغِّلت فعلياً على ذلك النطاق شيء آخر؛ والثاني لا يُتحقَّق منه إلا بحمل حقيقي.
الخلاصة العملية لك: مع 10-20 جهازاً تكفي الإدارة اليدوية أو سكربتات بسيطة. أما حين تنتقل إلى 100+ جهاز، أو إلى فروع متعددة، أو إلى موقع مزوّد خدمة يدير MikroTik لعملائه، فإن المعمارية أعلاه (API مُهيكلة + طابور مهام + reconcile idempotent + تدقيق) ليست “رفاهية” بل شرط مسبق للاستدامة.
هل تبنيها بنفسك أم تُسنِد إدارتها؟
كل ما في هذا المقال قابل للتطبيق ويمكن بناؤه بأدوات مفتوحة المصدر (Python، librouteros، Redis، PostgreSQL). لكن ينبغي رؤية الصورة الصادقة: فروق v6/v7، والـ idempotency، والطابور الموثوق، والـ circuit breaker، والإدارة الآمنة لبيانات الاعتماد استثمار هندسي جادّ وصيانته مستمرة.
إن كنت ستبنيها مع فريقك فهذا المقال يقدّم لك خارطة طريق واقعية وقائمة مزالق. وإن كنت لا تريد حمل هذا العبء، فيمكنك أخذ إدارة عدد كبير من أجهزة MikroTik مركزياً وبشكل قابل للتدقيق كخدمة من فرق مثلنا؛ فصفحتا البنية التحتية للشبكات ودعم MikroTik تنظران إلى هنا. وفي الحالتين المبدأ المفتاحي واحد: أدِر أجهزتك لا يدوياً، بل بنظام قابل للتكرار وقابل للتحقّق.
Kaynaklar
- librouteros: عميل Python لواجهة RouterOS API — PyPI / librouteros (2026)
- الوثائق الرسمية لواجهة RouterOS API — MikroTik (2026)
- الوثائق الرسمية لـ MikroTik RouterOS — MikroTik (2026)
Sıkça Sorulan Sorular
هل ينبغي أن أستخدم RouterOS API أم SSH؟+
للإدارة الجماعية والبرمجية، تُعدّ واجهة RouterOS الثنائية (binary API على المنفذ 8728/8729) أنسب بكثير من SSH: فهي تُرجع بيانات مُهيكلة (structured)، ولا تحتاج إلى تحليل مخرجات الأوامر كنصوص، وتدعم عمليات 'add/set/find' الـ idempotent مباشرةً. نحتفظ بـ SSH فقط للمهام الضيقة التي لا تغطيها الـ API. وأشهر مثال هو الحصول على مخرجات `/export` التي لا مقابل لها في الـ API.
أي منفذ يستخدمه RouterOS API؟+
الـ API غير المشفّرة على المنفذ 8728، والـ API المؤمَّنة بـ TLS (api-ssl) على المنفذ 8729. في الإدارة المكشوفة على الإنترنت يجب استخدام 8729 (api-ssl) فقط مع تقييد الوصول بمصادر موثوقة؛ أما المنفذ 8728 العادي فمقبول فقط داخل شبكة إدارة آمنة/داخلية. تُفعَّل خدمة الـ API من `/ip service` وتُقيَّد بمرشّح عناوين (address filter).
كيف تُرسَل الأوامر إلى مئات أجهزة MikroTik في آنٍ واحد؟+
المقاربة الصحيحة على نطاق واسع ليست فتح آلاف الاتصالات المتزامنة داخل عملية (process) واحدة؛ بل إنتاج 'مهمة' منفصلة لكل جهاز ووضعها في طابور (نحن نستخدم Redis)، ثم استهلاك عدد كبير من الـ worker لهذا الطابور بالتوازي. بهذا تتوسّع الموازاة أفقياً مع عدد الـ worker، ولا يُجمّد جهازٌ بطيء واحد النظام كله، وبفضل قفل خاص بكل جهاز لا يتصادم تغييران على الجهاز ذاته.
هل التغيير الجماعي للإعدادات آمن؟+
نعم إذا صُمِّم بشكل صحيح. التدفق الذي نطبّقه: توليد الحالة المرغوبة (desired state) من قالب، مقارنتها بالحالة الفعلية (actual state) المقروءة من الجهاز (diff)، أخذ نسخة احتياطية من الإعدادات قبل تطبيق أي تغيير، تطبيق الأوامر بشكل idempotent (إن وُجد فلا تلمسه، وإلا أضِفه)، ثم القراءة مجدداً للتحقق من أن الانحراف (drift) قد صار صفراً. بفضل الـ idempotency يمكن إعادة تشغيل عملية توقّفت في منتصفها بأمان.
هل واجهة RouterOS v6 و v7 متماثلة؟+
لا، هناك فروق مهمة، وهنا يُنفَق أكبر قدر من كود الأتمتة. مثلاً BGP في v7 تحت `/routing/bgp/connection`، وفي v6 تحت `/routing/bgp/peer`؛ وإعداد NTP في v7 يُعطى بقائمة مفصولة بفواصل عبر `servers=` بينما في v6 يُستخدم حقلان منفصلان `primary-ntp`/`secondary-ntp`؛ وتصفية bridge VLAN نضجت مع v7. على الأتمتة أن تقرأ إصدار RouterOS للجهاز وتولّد الأمر بناءً عليه.
Profesyonel Destek mi Lazım?
Bu konuda yardıma ihtiyacın varsa yanındayız. Kurulum, konfigürasyon ve sorun giderme için ulaş.
