Traefik مع Docker: بروكسي عكسي يكتشف حاوياتك وحده

Traefik يقرأ labels حاويات Docker ويبني التوجيه وشهادات Let's Encrypt تلقائياً، فتضيف تطبيقاً جديداً بنطاقه وشهادته بسطور قليلة في docker-compose دون لمس ملف إعداد البروكسي أو إعادة تشغيله.
الجواب المختصر
Traefik بروكسي عكسي مصمم لعالم الحاويات: يتصل بـDocker، ويراقب الحاويات التي تبدأ وتتوقف، ويقرأ من كل حاوية labels تحدد نطاقها ومنفذها وسياساتها، ثم يبني التوجيه وحده خلال ثوانٍ. لا ملف إعداد مركزي تعدله مع كل تطبيق، ولا إعادة تحميل يدوية. تضيف خمسة أسطر labels لخدمة جديدة في docker-compose، وتشغلها، فيصبح لها نطاق وشهادة Let's Encrypt صالحة خلال أقل من دقيقة.
اختر Traefik حين تشغّل عدة تطبيقات في حاويات على خادم واحد أو أكثر، وتتغير هذه التطبيقات باستمرار: بيئات اختبار تُنشأ وتُحذف، وأدوات داخلية مثل n8n وOpen WebUI، وتطبيقات العملاء. أما إن كان لديك موقع أو اثنان ثابتان، أو تحتاج تحكماً دقيقاً جداً في التخزين المؤقت وخدمة الملفات الثابتة، فـNginx يبقى أقوى، وCaddy أبسط إن أردت ملف إعداد قصيراً بلا Docker labels.
الحماية في Traefik تأتي عبر middlewares تربطها بأي مسار: تحديد معدل الطلبات، والمصادقة الأساسية، وترويسات الأمان، وقوائم عناوين IP المسموحة. أما لوحة التحكم المدمجة فمفيدة، لكنها يجب أن تبقى خلف مصادقة وقائمة IP دائماً.
Traefik أم Nginx أم Caddy
| المعيار | Traefik | Nginx | Caddy |
|---|---|---|---|
| اكتشاف الحاويات | تلقائي عبر labels | يدوي أو بأدوات إضافية | عبر إضافة خارجية |
| شهادات HTTPS | تلقائية مدمجة | عبر Certbot | تلقائية مدمجة |
| إعادة التحميل عند التغيير | لا حاجة | reload يدوي | عبر API أو reload |
| خدمة الملفات الثابتة | غير مخصص لها | الأقوى | جيدة |
| استهلاك الذاكرة المعتاد | من 50 إلى 100MB | من 10 إلى 30MB | من 30 إلى 60MB |
| الأنسب له | حاويات كثيرة متغيرة | مواقع ثابتة عالية الحمل | مواقع قليلة بإعداد بسيط |
فرق الذاكرة بين الثلاثة لا يهم على سيرفر بـ4GB أو أكثر. الفرق الحقيقي في الجهد: مع Nginx كل تطبيق جديد يعني ملف server block وشهادة وreload، ومع Traefik يعني labels في نفس ملف التطبيق، فيعيش إعداد التوجيه بجانب الكود الذي يخصه.
الإعداد الأساسي لحاوية Traefik
شغّل Traefik كحاوية مستقلة في ملف compose خاص به، واضبطه بالقيم التالية عبر وسائط سطر الأوامر أو ملف traefik.yml:
- نقطتا دخول: web على المنفذ 80 وwebsecure على 443، مع إعادة توجيه كل HTTP إلى HTTPS على مستوى نقطة الدخول.
- مزود Docker مع exposedByDefault=false، حتى لا تُنشر أي حاوية إلا إن طلبت ذلك صراحة بـtraefik.enable=true. هذا الإعداد وحده يمنع كشف قاعدة بيانات أو لوحة إدارة بالخطأ.
- محلل شهادات اسمه le يستخدم ACME مع تحدي TLS أو HTTP، وبريد إلكتروني للتنبيهات، وملف تخزين acme.json بصلاحيات 600 على وحدة تخزين دائمة، وإلا ستطلب شهادات جديدة مع كل إعادة تشغيل حتى تصطدم بحدود Let's Encrypt.
- شبكة Docker خارجية مشتركة باسم proxy تنضم إليها Traefik وكل حاوية تريد نشرها، بينما تبقى قواعد البيانات على شبكات داخلية خاصة بكل تطبيق.
Traefik يحتاج قراءة Docker socket ليرى الحاويات، وهذا وصول حساس لأن من يملك الـsocket يملك السيرفر عملياً. اربطه بوضع القراءة فقط على الأقل، والأفضل أن تضع أمامه docker-socket-proxy يسمح بطلبات القراءة فقط، فإن اختُرقت حاوية Traefik لا تستطيع إنشاء حاويات جديدة.
ثبّت إصدار الصورة بدقة مثل traefik:v3.1 بدل latest، فالترقيات بين الإصدارات الرئيسية غيرت أسماء بعض الإعدادات، وترقية صامتة في منتصف الليل قد تُسقط كل التطبيقات معاً.
labels لتوجيه نطاقات متعددة
لكل خدمة تريد نشرها، أضف في docker-compose الخاص بها هذه الـlabels، مع تغيير الاسم erp إلى اسم فريد لكل تطبيق:
- traefik.enable=true لتفعيل النشر.
- traefik.http.routers.erp.rule=Host(`erp.example.sa`) لتحديد النطاق، ويمكن جمع أكثر من نطاق بـ||.
- traefik.http.routers.erp.entrypoints=websecure وtraefik.http.routers.erp.tls.certresolver=le للشهادة التلقائية.
- traefik.http.services.erp.loadbalancer.server.port=8080 لإخبار Traefik بالمنفذ الداخلي للتطبيق.
لاحظ أنك لا تنشر منفذ التطبيق على السيرفر إطلاقاً، فلا قسم ports في الخدمة. Traefik يصل إليها عبر شبكة proxy الداخلية، فيبقى المنفذ 8080 غير مرئي من الإنترنت. إن شغّلت عدة نسخ من الخدمة نفسها بـreplicas، يوزع Traefik الطلبات بينها تلقائياً.
middlewares للحماية
الـmiddleware يُعرّف مرة واحدة في labels أي حاوية، ثم يُربط بأي router باسمه، والأفضل تعريف المشترك منها على حاوية Traefik نفسها.
تحديد المعدل
ratelimit.average=100 وratelimit.burst=50 يعنيان 100 طلب في الثانية لكل عنوان IP كمعدل، مع سماح بدفعة 50 إضافية. لصفحات الدخول خفّض الرقم إلى 5 أو 10، فهذا يوقف محاولات تخمين كلمات المرور دون أن يلاحظ المستخدم العادي شيئاً.
المصادقة الأساسية
basicauth.users يقبل اسم مستخدم مع تجزئة كلمة مرور من htpasswd. انتبه لأن التجزئة تحتوي علامة الدولار، وفي docker-compose يجب مضاعفتها $$ وإلا اختفت أجزاء من التجزئة ولن تعمل كلمة المرور. استخدمها للأدوات الداخلية فقط، لا كبديل لنظام دخول التطبيق.
ترويسات الأمان وقوائم IP
middleware من نوع headers يضيف stsSeconds=31536000 لفرض HTTPS سنة كاملة، وframeDeny=true وcontentTypeNosniff=true. أما ipallowlist.sourcerange فيحصر الوصول في عناوين مكتبك أو شبكة VPN، وهو الأنسب للوحات الإدارة.
لوحة التحكم وتأمينها
لوحة Traefik تعرض كل router وservice وmiddleware وحالتها، وهي مفيدة جداً عند تتبع خطأ توجيه. فعّلها بـapi.dashboard=true، ولا تستخدم api.insecure=true أبداً لأنه يفتحها على المنفذ 8080 بلا أي حماية. انشرها بدلاً من ذلك عبر router عادي بنطاق مثل traefik.example.sa، مع service باسم api@internal، وأربط به middleware المصادقة الأساسية وقائمة IP معاً. طبقتان من الحماية لأن اللوحة تكشف خريطة بنيتك كاملة لمن يراها.
تشخيص الأعطال الشائعة
ثلاثة أخطاء تتكرر مع كل من يبدأ مع Traefik. الأول 404 لنطاق جديد: غالباً نسيت traefik.enable=true، أو الحاوية ليست على شبكة proxy، أو اسم الـrouter مكرر في تطبيقين فيطغى أحدهما على الآخر. الثاني 502 Bad Gateway: المنفذ في loadbalancer.server.port لا يطابق المنفذ الذي يستمع عليه التطبيق داخل الحاوية، أو التطبيق يستمع على 127.0.0.1 بدل 0.0.0.0. الثالث شهادة افتراضية باسم TRAEFIK DEFAULT CERT: سجل DNS لا يشير للسيرفر بعد، أو المنفذ 80 مغلق فيفشل التحدي.
فعّل accesslog بصيغة JSON ومستوى السجل INFO في البداية، فكل طلب يظهر مع الـrouter الذي التقطه وزمن الاستجابة ورمز الحالة. للمراقبة المستمرة، فعّل مقاييس Prometheus المدمجة، وراقب نسبة استجابات 5xx لكل service، فإن تجاوزت 1% خلال 5 دقائق فهناك تطبيق يحتاج انتباهك.
مثال كامل: ثلاثة تطبيقات على خادم واحد
سيناريو شائع لشركة متوسطة: نظام داخلي مبني بـLaravel، وأداة أتمتة n8n، وواجهة Open WebUI للمساعد الذكي الداخلي. البنية على خادم واحد:
- Traefik في ملف compose مستقل، على شبكة proxy، يستمع على 80 و443.
- نظام Laravel على النطاق erp.example.sa، منفذ داخلي 8000، مع قاعدة بيانات على شبكة داخلية لا تنضم لـproxy، وmiddleware تحديد معدل 10 طلبات في الثانية على مسار الدخول.
- n8n على النطاق n8n.example.sa، منفذ داخلي 5678، خلف قائمة IP للمكتب فقط، مع استثناء مسار webhook بـrouter ثانٍ بلا قائمة IP حتى تصل الطلبات الخارجية.
- Open WebUI على النطاق ai.example.sa، منفذ داخلي 8080، بنظام الدخول الخاص به مع ترويسات الأمان.
كل تطبيق في مجلده بملف compose خاص به، فتحديث n8n مثلاً لا يلمس الاثنين الآخرين، ولا يحتاج Traefik أي تعديل. حين تضيف تطبيقاً رابعاً لاحقاً، تكتب له ملف compose بالـlabels وتشغّله، وتنتهي العملية.
بالنسبة للموارد: Traefik نحو 80MB، وLaravel مع MySQL نحو 1GB، وn8n من 300 إلى 500MB، وOpen WebUI نحو 500MB إن كان متصلاً بنموذج عبر API. باقة نمو (2 vCPU و4GB RAM) بـ6,900 ريال سنوياً تكفي لهذا المجموع بهامش معقول، وإن أردت تشغيل نماذج محلية عبر Ollama فباقة أعمال (4 vCPU و8GB RAM) بـ11,500 ريال سنوياً هي الحد الأدنى العملي. قوالب n8n وOpen WebUI متاحة مثبتة مسبقاً في استضافة الذكاء الاصطناعي، والموارد كلها في الخادم السحابي.
خطوات البدء
- أنشئ شبكة proxy وشغّل Traefik بـexposedByDefault=false وملف acme.json دائم بصلاحيات 600.
- انقل تطبيقاً واحداً أولاً، واحذف قسم ports منه، وتأكد أن شهادته صدرت خلال دقيقة.
- عرّف middlewares الأمان والمعدل مرة واحدة واربطها بكل router جديد.
- انشر اللوحة خلف مصادقة وقائمة IP، أو اتركها معطلة إن لم تحتجها.


