عندما تضيف تطبيقاً جديداً إلى السيرفر Server فهو يحتاج إلى ثلاثة أمور حتى يصل إليه المستخدم: نطاق Domain يكتبه في المتصفح، وشهادة TLS Certificate تحمي الاتصال، وقاعدة في ال Reverse Proxy توجه الطلبات إلى ال Container الصحيح، والمشكلة الحقيقية في هذه القاعدة الأخيرة، فإذا كتبتها يدوياً في ملف إعداد بعيد عن التطبيق فسوف تنسى تعديلها يوماً ما عندما يتغير اسم ال Container أو منفذه، أو تبقى القاعدة في مكانها بعد أن تحذف التطبيق نفسه بشهور ولا يتذكر أحد لماذا هي موجودة.
ولهذه المهمة أكثر من أداة، فمع Nginx Proxy Manager تدير كل نطاق من واجهة ويب Web UI ويحفظ الإعداد في قاعدة بياناته، أما Traefik Proxy فيقرأ الإعداد من التطبيقات نفسها، وتجد الأدوات الثلاث جنباً إلى جنب في مقارنة Nginx Proxy Manager وTraefik وHAProxy، وهذا الدليل عن Traefik.
وTraefik هو Reverse Proxy مفتوح المصدر Open Source يراقب Docker باستمرار، فعندما يبدأ Container يحمل وسوماً Labels معينة ينشئ له Traefik قاعدة توجيه Router ويطلب له شهادة من Let's Encrypt، وعندما يتوقف ال Container يحذف القاعدة، ويسمى هذا السلوك الاكتشاف التلقائي للخدمات Service Discovery. ولاحظ أنه لا توجد هنا واجهة لتعديل الإعداد، وإنما تكتبه في الوسوم وفي ملفات نصية تحفظها في Git وتراجعها كأي كود Code، لذلك فهو مناسب للفريق الذي ينشر تطبيقاته بملفات Compose ويريد إعداد التوجيه Routing بجانبها، أما إذا كنت تفضل الإعداد بالنقر فابق مع Nginx Proxy Manager.
وسوف نناقش في هذا المقال ما يلي:
- كيف يعمل Traefik، والفرق بين الإعداد الثابت والإعداد الديناميكي.
- تثبيت Traefik بملف Compose مع تحويل HTTP إلى HTTPS وشهادات Let's Encrypt التلقائية وترويسات الأمان.
- نشر تطبيق عبر الوسوم، واستخدام ال Middlewares لتعديل الطلب قبل وصوله إلى التطبيق.
- تأمين لوحة التحكم Dashboard، وحماية ال Docker socket بوسيط Socket Proxy.
- التحقق من النجاح، والنسخ الاحتياطي، والتحديث، وأشهر المشكلات وحلولها.
المتطلبات Requirements
- سيرفر يعمل بنظام Ubuntu 24.04 وعليه Docker Engine وCompose v2، وإذا لم يكن جاهزاً فاتبع دليل تثبيت Docker على Ubuntu، ثم دليل تأمين خادم VPS من أول دخول.
- نطاق تدير سجلات DNS Records الخاصة به، وفيه سجل
Aلكل اسم فرعي Subdomain يشير إلى العنوان العام Public IP للسيرفر203.0.113.10، وسوف نستخدم في هذا الدليلapp.example.comللتطبيق وtraefik.example.comللوحة التحكم، وإذا كانت هذه السجلات جديدة عليك فقد شرحناها في شرح DNS وسجلاته للمبتدئين. - المنفذان Ports
80/tcpو443/tcpمفتوحان من الإنترنت إلى السيرفر، في جدار الحماية Firewall لدى المزود وعلى السيرفر معاً، ولاحظ أن المنفذ 80 ضروري للتحقق من ملكية النطاق عبر HTTP (HTTP-01 Challenge) حتى لو كانت خدماتك كلها عبر HTTPS. - لا يستمع أي برنامج آخر على المنفذين، وتحقق من ذلك بالأمر التالي، فإذا لم يطبع شيئاً فالمنفذان متاحان:
sudo ss -ltnp | grep -E ':(80|443) 'أما عن الموارد Resources فإن Traefik برنامج واحد مكتوب بلغة Go ويكفيه أصغر خادم افتراضي خاص VPS، واستهلاكه يزيد مع عدد الطلبات أكثر مما يزيد مع عدد التطبيقات التي يخدمها.
كيف يعمل Traefik؟ الإعداد الثابت والإعداد الديناميكي
يصل الطلب إلى Traefik على نقطة دخول EntryPoint، أي منفذ يستمع عليه مثل 80 أو 443، فيطابقه Traefik مع قاعدة Router مثل اسم النطاق app.example.com، ثم يمر الطلب على Middleware واحد أو أكثر يعدل فيه، كأن يضيف ترويسات Headers أو يطلب كلمة مرور، وفي النهاية يصل إلى ال Service، أي ال Container الذي يخدم هذا الطلب.
وللإعداد Configuration في Traefik قسمان كما يشرح توثيق الإعداد الرسمي، والجدول التالي يبين الفرق بينهما:
| القسم | ما يحدده | مصدره | متى يطبق |
|---|---|---|---|
| الإعداد الثابت Static Configuration | نقاط الدخول، ومصادر الإعداد Providers، وحساب Let's Encrypt، ولوحة التحكم، والسجلات Logs | الملف traefik.yml أو خيارات سطر الأوامر CLI Flags | عند الإقلاع فقط، وأي تغيير فيه يحتاج إلى إعادة تشغيل Traefik |
| الإعداد الديناميكي Dynamic Configuration | ال Routers وال Services وال Middlewares | وسوم Docker، أو ملفات يقرؤها File Provider | فوراً ودون إعادة تشغيل |
وسوف نكتب الإعداد الثابت في الملف traefik.yml وليس في خيارات سطر الأوامر، والسبب أن الملف أسهل في القراءة والمراجعة من سطر طويل من الخيارات، أما الإعداد الديناميكي فنكتبه في وسوم كل تطبيق، ومعه مجلد صغير لل Middlewares المشتركة بين التطبيقات.
التثبيت Installation
نبدأ بشبكة Docker مشتركة
سوف نضع Traefik وكل تطبيق يخدمه على شبكة Docker Docker Network واحدة اسمها proxy، وبهذا الشكل يصل Traefik إلى كل Container مباشرة، ولا تحتاج التطبيقات إلى نشر أي منفذ على السيرفر. وإذا أردت أن تفهم لماذا نفعل ذلك وما أنواع الشبكات في Docker، ولماذا نضع قاعدة البيانات على شبكة داخلية خاصة بها، فقد شرحنا ذلك بالتفصيل في دليل شبكات Docker وأفضل الممارسات. أنشئ الشبكة مرة واحدة كما يلي:
docker network create proxyأين نحفظ الإعداد والشهادات؟
sudo mkdir -p /opt/traefik/dynamic /opt/traefik/letsencrypt
sudo chown -R $USER: /opt/traefik
cd /opt/traefik
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.jsonيحفظ Traefik في الملف acme.json حساب Let's Encrypt والشهادات ومفاتيحها الخاصة Private Keys، ولهذا يرفض استخدام الملف إذا كانت صلاحياته Permissions أوسع من 600، ويكتب في السجل رسالة مثل permissions 644 for /letsencrypt/acme.json are too open, please use 600.
نكتب الإعداد الثابت في traefik.yml
الآن سوف نكتب الإعداد الثابت في الملف /opt/traefik/traefik.yml كما يلي:
global:
checkNewVersion: false
sendAnonymousUsage: false
log:
level: INFO
accessLog: {}
api:
dashboard: true
ping: {}
entryPoints:
web:
address: ":80"
http:
aliasHeadersStrategy: delete
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
http:
aliasHeadersStrategy: delete
tls:
certResolver: letsencrypt
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
providers:
docker:
exposedByDefault: false
network: proxy
file:
directory: /etc/traefik/dynamic
watch: trueفي الإعداد أعلاه لاحظ التالي:
- تستقبل نقطة الدخول
webالمنفذ 80، وتحول كل طلب HTTP إلى HTTPS تحويلاً دائماً Permanent Redirect. وقد يتساءل البعض: إذا كان كل طلب على المنفذ 80 يتحول إلى HTTPS، فكيف تصل Let's Encrypt إلى ملف التحقق Challenge File عبر HTTP؟ والإجابة أن Traefik يعالج طلبات/.well-known/acme-challenge/بمسار داخلي له أعلى أولوية Priority ممكنة، فيصل طلب التحقق إليه قبل أن يمر على قاعدة التحويل. - تستقبل
websecureالمنفذ 443، ومعنىcertResolver: letsencryptأن كل Router عليها يحصل على شهادة تلقائياً، فلا داعي لتكرار ذلك في وسوم كل تطبيق. - الخيار
aliasHeadersStrategy: deleteيحذف كل ترويسة في اسمها حرف غير الحروف والأرقام والشرطة، مثل الشرطة السفلية أو النقطة فيX_Auth_User، والسبب أن تطبيقات PHP وغيرها تقرأ هذا الاسم على أنهX-Auth-User، وبالتالي يستطيع المهاجم أن ينتحل ترويسة يثق بها التطبيق. وقد أضيف هذا الخيار في الإصدار v3.7.12 بديلاً عن الخيار الأقدمunderscoreHeadersStrategy، ويحذرك Traefik في السجل عند الإقلاع عن كل نقطة دخول تركته فيها على قيمته الافتراضيةkeep. - القسم
certificatesResolversيعرف حساب Let's Encrypt باسمletsencrypt، ويستخدم التحقق عبر HTTP (HTTP-01) على نقطة الدخولweb، ويجدد Traefik كل شهادة تلقائياً قبل انتهائها بثلاثين يوماً، فالشهادة صالحة 90 يوماً ويبدأ التجديد في يومها الستين. - مع
exposedByDefault: falseفي Docker Provider لا ينشر Traefik إلا ال Container الذي يحمل الوسمtraefik.enable=true، والسبب أن القيمة الافتراضيةtrueتنشر كل Container على السيرفر، ومنها قواعد البيانات Databases والأدوات الداخلية، وهذا بكل تأكيد ما لا تريده. - يحدد
network: proxyالشبكة التي يصل منها Traefik إلى ال Containers، ويهمك هذا عندما يكون التطبيق على أكثر من شبكة. - يقرأ File Provider كل ملف في المجلد
/etc/traefik/dynamicويطبق أي تعديل فيه فوراً. - يفعل
pingنقطة الفحص/pingالتي يستخدمها فحص الصحة Healthcheck داخل ال Container، ولا ننشر منفذها على السيرفر.
ترويسات الأمان المشتركة بين كل التطبيقات
في الملف /opt/traefik/dynamic/middlewares.yml سوف نعرف Middleware اسمه security-headers يضيف ترويسات الأمان Security Headers إلى كل استجابة، ثم نربطه بالتطبيقات لاحقاً:
http:
middlewares:
security-headers:
headers:
stsSeconds: 31536000
stsIncludeSubdomains: true
contentTypeNosniff: true
frameDeny: true
referrerPolicy: "strict-origin-when-cross-origin"يطلب stsSeconds من المتصفح Browser ألا يستخدم HTTP مع النطاق مدة سنة، وهذا ما يسمى HSTS، ويمنع contentTypeNosniff المتصفح من تخمين نوع الملف، ويمنع frameDeny عرض الصفحة داخل إطار iframe في موقع آخر، ويقلل referrerPolicy ما يرسله المتصفح من روابط صفحاتك إلى المواقع الأخرى، وتجد شرح كل ترويسة وكيف تضيف CSP دون أن تكسر تطبيقك في دليل ترويسات الأمان بالتفصيل: HSTS وCSP.
stsIncludeSubdomains يشمل كل اسم فرعي تحت النطاق، لذلك ابدأ بقيمة صغيرة مثل 300 ثانية، ثم ارفعها إلى سنة بعد أن تتأكد أن الشهادات تصدر وتتجدد لكل الأسماء، وإذا كان أحد التطبيقات يحتاج إلى العرض داخل إطار فعرف له Middleware آخر دون frameDeny.كيف تولد كلمة مرور لوحة التحكم؟
سوف نحمي لوحة التحكم بمصادقة HTTP الأساسية Basic Authentication عبر BasicAuth Middleware، وهذا ال Middleware يقبل كلمة المرور بصيغة هاش Hash مثل BCrypt، وتولدها الأداة htpasswd كما يلي:
sudo apt install -y apache2-utils
htpasswd -nB adminتطلب الأداة كلمة المرور مرتين ثم تطبع سطراً مثل admin:$2y$05$...، فانسخ السطر كاملاً إلى الملف /opt/traefik/.env بين علامتي تنصيص مفردتين:
TRAEFIK_DASHBOARD_USERS='admin:$2y$05$Jm0mK1x5Q8bW0eQnI3zHVe4mR2sL7kT9pYcA1dF6gH8jN0uV3wXyZ'chmod 600 /opt/traefik/.envولاحظ أن الهاش يحتوي على العلامة $، وهي علامة المتغيرات في Compose، ولكن Compose يقرأ القيمة حرفياً إذا وضعتها بين علامتي تنصيص مفردتين في ملف .env، أما إذا كتبت الهاش داخل compose.yaml مباشرة فضاعف كل علامة $ لتصبح $$.
نجمع كل شيء في ملف Compose
الآن نكتب الملف /opt/traefik/compose.yaml، وفيه الإصدار v3.7.13 مثبتاً برقمه الكامل:
services:
traefik:
image: traefik:v3.7.13
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic:/etc/traefik/dynamic:ro
- ./letsencrypt:/letsencrypt
networks:
- proxy
healthcheck:
test: ["CMD", "traefik", "healthcheck", "--ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
start_interval: 5s
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.middlewares=dashboard-auth,security-headers@file"
- "traefik.http.middlewares.dashboard-auth.basicauth.users=${TRAEFIK_DASHBOARD_USERS}"
networks:
proxy:
external: trueفي الإعداد أعلاه لاحظ التالي:
- يبحث Traefik تلقائياً عن إعداده الثابت في
/etc/traefik/traefik.yml، فلا نحتاج إلى أي خيار فيcommand. - يمنع
no-new-privilegesأي عملية داخل ال Container من رفع صلاحياتها، وتستخدمه أمثلة Traefik الرسمية أيضاً. - إذا كان لل Container فحص صحة فلا يوجه Traefik إليه الطلبات قبل أن تصبح حالته
healthy، وهذا يشمل Traefik نفسه ولوحة التحكم، لذلك أضفناstart_intervalليجري الفحص كل 5 ثوان في البداية، فتظهر اللوحة خلال ثوان وليس بعد نصف دقيقة. - تنشئ الوسوم Router اسمه
dashboardيوجه النطاقtraefik.example.comإلى الخدمة الداخليةapi@internal، ويمر الطلب أولاً على كلمة المرور ثم على ترويسات الأمان، والتفاصيل في قسم لوحة التحكم أدناه.
شغل الخدمة وانتظر حتى تصبح حالتها healthy:
cd /opt/traefik
docker compose up -d
docker compose ps
docker compose logs --tail 30 traefikوالمخرج سوف يكون كما يلي:
NAME IMAGE STATUS
traefik traefik:v3.7.13 Up 20 seconds (healthy)وعند قراءة السجل سوف تجد غالباً تحذيرين بمستوى WRN عند الإقلاع، الأول عن رفض بعض الحروف المرمزة Encoded Characters في مسار الطلب، وهو تنبيه عام يظهر لكل من يستخدم القيم الافتراضية، والثاني يقول إن aliasHeadersStrategy غير مضبوط على نقطة الدخول traefik، وهي نقطة دخول داخلية على المنفذ 8080 ينشئها Traefik ل ping ولوحة التحكم ولا ننشرها على السيرفر، لذلك لا يؤثر عليك هذا التحذير، وإذا أردت إسكاته فعرف نقطة الدخول traefik بنفسك في entryPoints وأضف إليها الخيار نفسه.
acme في traefik.yml السطر caServer: https://acme-staging-v02.api.letsencrypt.org/directory، فيستخدم Traefik بيئة الاختبار Staging في Let's Encrypt، وحدودها Rate Limits أوسع بكثير ولكن شهاداتها غير موثوقة في المتصفح، وبعد نجاح التجربة احذف السطر وأفرغ acme.json بالأمر : > letsencrypt/acme.json، ثم أعد تشغيل Traefik ليطلب شهادات حقيقية.كيف تنشر تطبيقاً عبر الوسوم؟
لنأخذ مثالاً بالتطبيق الصغير whoami، وهو تطبيق يعيد تفاصيل الطلب كما وصلته، فترى الترويسات التي أضافها Traefik، وهذا هو الملف /opt/whoami/compose.yaml، ولاحظ أنه لا ينشر أي منفذ على السيرفر:
services:
whoami:
image: traefik/whoami:v1.12.0
container_name: whoami
restart: unless-stopped
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`app.example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.middlewares=security-headers@file"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
networks:
proxy:
external: truesudo mkdir -p /opt/whoami
sudo chown $USER: /opt/whoami
cd /opt/whoami
docker compose up -dفي الإعداد أعلاه لاحظ التالي:
- يحدد
ruleمتى يطابق الطلب هذا ال Router، وتجد بقية الصيغ مثلPathPrefixفي صفحة القواعد، واكتب اسم النطاق بين علامتي`وليس بين علامتي تنصيص. loadbalancer.server.portهو المنفذ داخل ال Container وليس منفذاً على السيرفر، وإذا أعلن ال Image عن منفذ واحد بالتعليمةEXPOSEفسوف يكتشفه Traefik وحده، ولكن كتابته صراحة تحميك من الخطأ عندما يعلن ال Image عن أكثر من منفذ.- يشير
security-headers@fileإلى ال Middleware الذي عرفناه في الملف، أما ال Middleware المعرف في وسوم Docker فلاحقته@docker، وإذا كان ال Middleware في المصدر نفسه فلك أن تحذف اللاحقة كما فعلنا معdashboard-auth.
بعد ثوان من تشغيل ال Container ينشئ Traefik ال Router ويطلب الشهادة، ولا تحتاج إلى انتظار انتشار DNS Propagation لكي تختبر، وإنما وجه النطاق إلى العنوان المحلي من السيرفر نفسه:
curl -s -D - --resolve app.example.com:443:127.0.0.1 https://app.example.com/والمخرج سوف يكون كما يلي:
HTTP/1.1 200 OK
Referrer-Policy: strict-origin-when-cross-origin
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
...
Hostname: 3f2a9c1b7d4e
Host: app.example.com
X-Forwarded-For: 203.0.113.50
X-Forwarded-Proto: https
X-Real-Ip: 203.0.113.50وإذا لم تصدر الشهادة بعد فسوف يرفض curl الاتصال، والسبب أن Traefik يقدم في هذه الحالة شهادته الافتراضية الموقعة ذاتياً Self-signed باسم TRAEFIK DEFAULT CERT، فأضف الخيار -k للتجربة فقط، وراجع قسم الشهادات في المشكلات الشائعة أدناه.
كيف تعدل الطلب قبل وصوله إلى التطبيق بال Middlewares؟
يقع ال Middleware في الطريق بين ال Router وال Service، ولك أن تربط أكثر من Middleware بال Router الواحد مفصولة بفاصلة، فتنفذ بالترتيب الذي كتبتها به. لنفرض أن لديك أداة داخلية تريد قصرها على شبكة المكتب أو الشبكة الخاصة الافتراضية VPN، فالحل أن تضيف إليها IPAllowList كما يلي:
labels:
- "traefik.enable=true"
- "traefik.http.routers.tools.rule=Host(`tools.example.com`)"
- "traefik.http.routers.tools.entrypoints=websecure"
- "traefik.http.routers.tools.middlewares=office-only,security-headers@file"
- "traefik.http.middlewares.office-only.ipallowlist.sourcerange=203.0.113.0/24,10.8.0.0/24"وأي عنوان خارج هذه النطاقات سوف يتلقى الرمز 403، أما ال Middlewares التي تستخدمها أكثر من خدمة فضعها في مجلد dynamic كما فعلنا مع ترويسات الأمان، حتى تعرفها مرة واحدة وتعدلها في مكان واحد.
كيف تؤمن لوحة التحكم؟
تعرض لوحة تحكم Traefik كل ال Routers وال Services وال Middlewares وحالتها، وهي مفيدة جداً في تتبع الأخطاء، ولكنها تكشف أيضاً كل نطاقاتك وبنية خدماتك لمن يصل إليها.
والحل السريع الذي تجده في كثير من الأمثلة هو الخيار api.insecure، وهذا الحل غير مناسب إطلاقاً لسيرفر على الإنترنت، والسبب أنه يعرض اللوحة وال API على المنفذ 8080 دون أي مصادقة Authentication، فإذا نشرت هذا المنفذ أصبح كل ما في اللوحة متاحاً لأي شخص. لذلك لا نستخدمه، وإنما نضع اللوحة خلف Router له ثلاثة شروط:
- نطاق خاص بها هو
traefik.example.com، عبر HTTPS فقط. - كلمة مرور عبر
dashboard-authالذي يقرأ الهاش من.env. - الخدمة
api@internal، وهي الخدمة الداخلية التي تقدم اللوحة وال API معاً.
افتح https://traefik.example.com/dashboard/ مع الشرطة المائلة في آخره، وأدخل اسم المستخدم وكلمة المرور، وإذا فتحت العنوان الرئيسي للنطاق Root URL فسوف يحولك Traefik إلى /dashboard/ تلقائياً.
وإذا لم ترد أن تصل إلى اللوحة من الإنترنت أصلاً فأمامك خياران، الأول أن تضيف إليها Middleware من نوع IPAllowList كما في القسم السابق، فلا يفتحها إلا من على شبكة الفريق أو ال VPN، والثاني أن تحذف وسومها كلها وتجعل dashboard: false في traefik.yml، وتكتفي بالسجلات وبالأمر docker compose logs.
لماذا يحتاج ال Docker socket إلى حماية خاصة؟
يقرأ Traefik الوسوم من ال API الخاص بـ Docker عبر الملف /var/run/docker.sock، ومن يصل إلى هذا الملف يستطيع إنشاء Container بصلاحيات root على السيرفر كله كما ينبه توثيق أمان Docker، وTraefik هو البرنامج المعرض للإنترنت مباشرة، فإذا استغل مهاجم ثغرة Vulnerability فيه فقد يصل عبر ال socket إلى السيرفر بأكمله.
وقد يبدو أن اللاحقة :ro في ملف Compose تحل المشكلة، ولكن في الواقع فهي غير كذلك، فهي تمنع تعديل الملف نفسه داخل ال Container ولا تمنع إرسال أوامر إلى Docker عبره، وبالتالي فهي خطوة صغيرة وليست حماية كاملة. والحل الأنسب كما يوصي توثيق Traefik هو أن تضع بينهما Socket Proxy مثل docker-socket-proxy، وهو وسيط يمرر طلبات القراءة التي يحتاج إليها Traefik فقط ويرفض غيرها بالرمز 403.
أضف الخدمة التالية إلى /opt/traefik/compose.yaml، ثم احذف سطر docker.sock من خدمة traefik وأضف إليها الشبكة socket:
services:
socket-proxy:
image: tecnativa/docker-socket-proxy:v0.5.0
container_name: socket-proxy
restart: unless-stopped
environment:
CONTAINERS: 1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- socket
traefik:
# ... الإعداد السابق نفسه دون سطر docker.sock
networks:
- proxy
- socket
networks:
proxy:
external: true
socket:
internal: trueبعد ذلك وجه Docker Provider إلى ال Socket Proxy في traefik.yml، وأعد إنشاء الخدمتين بالأمر docker compose up -d:
providers:
docker:
endpoint: "tcp://socket-proxy:2375"
exposedByDefault: false
network: proxyفي الإعداد أعلاه لاحظ التالي:
- يسمح
CONTAINERS: 1بقراءة قائمة ال Containers ووسومها، أما الأحداث Events وPINGوVERSIONفمسموحة افتراضياً، وطلبات التعديلPOSTممنوعة افتراضياً، فلو حاول أحد إيقاف Container عبر ال Socket Proxy لتلقى الرمز403. - يعزل
internal: trueالشبكةsocketعن الخارج، فلا يصل إلى ال Socket Proxy إلا Traefik.
التحقق من النجاح Verification
- يعرض الأمر
docker compose psفي/opt/traefikال Containertraefikبحالةhealthy. - يعيد طلب HTTP تحويلاً دائماً إلى HTTPS:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://app.example.com/301 https://app.example.com/- يعيد الأمر
curl -s -o /dev/null -w '%{http_code}\n' https://app.example.com/الرمز200دون الخيار-k، وهذا يعني أن الشهادة موثوقة. - يعرض الأمر التالي جهة الإصدار Issuer وهي Let's Encrypt، وتاريخ انتهاء بعد نحو 90 يوماً:
echo | openssl s_client -connect app.example.com:443 -servername app.example.com 2>/dev/null | openssl x509 -noout -issuer -dates- تعيد لوحة التحكم الرمز
401دون كلمة المرور والرمز200معها، ويعرض ال API ال Routers النشطة وإصدار Traefik:
curl -s -u admin https://traefik.example.com/api/version
curl -s -u admin https://traefik.example.com/api/http/routersوالمخرج سوف يكون كما يلي:
{"Version":"3.7.13","Codename":"langres",...}- أي نطاق لا يطابق Router يعيد الرسالة
404 page not found، ولا يظهر في/api/http/routersأي Container لم تضف إليهtraefik.enable=true.
النسخ الاحتياطي Backup والاستعادة Restore
لا توجد قاعدة بيانات ل Traefik، فحالته كلها ملفات في /opt/traefik: الإعداد الثابت، ومجلد dynamic، وملف Compose، والملف .env، والملف acme.json، أما توجيه كل تطبيق فمحفوظ في ملف Compose الخاص به، وبالتالي يدخل في النسخة الاحتياطية لذلك التطبيق.
انسخ المجلد دون أن توقف Traefik، والسبب أن الملف acme.json لا يتغير إلا عند إصدار شهادة أو تجديدها:
sudo tar czf /root/traefik-$(date +%F).tar.gz -C /opt traefikوللاستعادة على سيرفر جديد ثبت Docker، وأنشئ الشبكة، وفك النسخة، وتأكد من صلاحيات acme.json، ثم شغل الخدمة:
docker network create proxy
sudo tar xzf /root/traefik-2026-10-02.tar.gz -C /opt
sudo chown -R $USER: /opt/traefik
chmod 600 /opt/traefik/letsencrypt/acme.json /opt/traefik/.env
cd /opt/traefik
docker compose up -dومع وجود acme.json يستخدم Traefik الشهادات الحالية مباشرة ولا يطلب من Let's Encrypt شهادات جديدة لكل النطاقات دفعة واحدة، وهذا يهمك إذا كانت نطاقاتك كثيرة حتى لا تصطدم بحدود الإصدار، وإذا نقلت التطبيقات إلى السيرفر الجديد أيضاً فانقلها مع ملفات Compose الخاصة بها، ثم غير سجلات DNS.
التحديث Upgrade إلى إصدار أحدث
- راجع صفحة الإصدارات، واقرأ قسم الإصدار الجديد في ملاحظات الترحيل Migration ضمن السلسلة 3، والسبب أن الإصدارات التصحيحية تغير أحياناً سلوكاً افتراضياً لأسباب أمنية، كما فعل الإصدار v3.7.13 مع بعض الترويسات وصيغ الطلبات.
- أنشئ نسخة احتياطية كما في القسم السابق.
- غير الوسم Tag في
compose.yamlإلى الإصدار الجديد برقمه الكامل، وليسlatestولاv3، ثم نفذ:
cd /opt/traefik
docker compose pull
docker compose up -d
docker compose logs --tail 30 traefikيتوقف استقبال الطلبات بضع ثوان أثناء إعادة إنشاء ال Container، فاختر وقتاً قليل الحركة، وبعد ذلك افتح اللوحة وتأكد أن كل ال Routers خضراء، وابحث في السجل عن WRN وERR لأن Traefik ينبهك فيه إلى الخيارات المهملة Deprecated، وإذا ظهرت مشكلة فأعد الوسم القديم ونفذ docker compose up -d.
أما الانتقال من السلسلة 2 إلى 3 فهو ترحيل كبير يغير صيغة بعض القواعد والخيارات، ولا يكفي فيه تغيير رقم الإصدار، لذلك اتبع فيه دليل الترحيل من v2 إلى v3 خطوة بخطوة.
مشكلات شائعة وحلولها Troubleshooting
يعيد Traefik الرسالة 404 page not found
معنى هذه الرسالة أن الطلب وصل إلى Traefik ولكن لم يطابقه أي Router، فافتح /api/http/routers أو اللوحة وتحقق مما يلي:
- أن ال Container يحمل
traefik.enable=true، فبسببexposedByDefault: falseيتجاهل Traefik ما عداه. - أن النطاق في
Host()يطابق النطاق الذي تطلبه حرفاً بحرف، وأنه بين علامتي`. - أن ال Router مربوط بنقطة الدخول
websecureوأنك تطلب HTTPS. - أن ال Container في حالة
healthyإذا كان له فحص صحة، فTraefik لا يوجه الطلبات إلى Container حالتهstartingأوunhealthy، وتظهر الحالة فيdocker ps. - أن ال Middleware المذكور في الوسوم معرف فعلاً، فإذا أشرت إلى Middleware غير موجود ظهر ال Router في اللوحة بحالة خطأ ولم يعمل.
لا تصدر الشهادة، أو تظهر TRAEFIK DEFAULT CERT
ابحث في السجل عن سبب الرفض:
docker compose logs traefik | grep -i -A3 "acme"غالباً سوف تجد رسالة Unable to obtain ACME certificate for domains ومعها السبب الذي أرسلته Let's Encrypt، فإذا كان السبب Timeout during connect أو Connection refused فالمنفذ 80 لا يصل إلى Traefik من الإنترنت، وعليك مراجعة جدار الحماية لدى المزود وعلى السيرفر، وإذا كان السبب NXDOMAIN أو عنواناً غير عنوان سيرفرك فسجل DNS لم ينتشر بعد أو يشير إلى مكان خاطئ، وتحقق منه بالأمر dig +short app.example.com.
وتفرض Let's Encrypt حدوداً على عدد الشهادات وعلى المحاولات الفاشلة لكل نطاق، لذلك لا تقم بإعادة تشغيل Traefik مرة بعد مرة على أمل أن ينجح الطلب، والسبب أن كل محاولة فاشلة تقربك من الحد، وإنما أصلح السبب أولاً وجرب على بيئة Staging كما وصفنا في قسم التثبيت، وإذا ظهرت رسالة too many certificates أو too many failed authorizations فلا حل إلا أن تنتظر انتهاء المدة المذكورة في الرسالة.
504 Gateway Timeout أو 502 Bad Gateway
الطلب هنا وصل إلى Traefik وطابق ال Router، ولكن Traefik لم يصل إلى ال Container، وأشيع الأسباب أن ال Container ليس على الشبكة proxy، فيحاول Traefik الوصول إليه عبر شبكة أخرى لا يراها، وينتظر 30 ثانية ثم يعيد 504، وسوف تجد في السجل تحذيراً مثل:
Could not find network named "proxy" for container "/whoami". Maybe you're missing the project's prefix in the label?والحل أن تضيف الشبكة proxy إلى الخدمة في ملف Compose مع external: true كما في مثال whoami، وإذا كانت الخدمة على عدة شبكات فأضف إليها الوسم traefik.docker.network=proxy. أما الرمز 502 فمعناه غالباً أن المنفذ في loadbalancer.server.port خاطئ، أو أن التطبيق يستمع على 127.0.0.1 داخل ال Container وليس على 0.0.0.0.
لا يبدأ Traefik لأن المنفذ مستخدم
إذا ظهرت عند docker compose up -d رسالة مثل Bind for 0.0.0.0:80 failed: port is already allocated فهناك Container آخر ينشر المنفذ 80، وغالباً هو Nginx Proxy Manager أو Container قديم ل Traefik، أما الرسالة address already in use فمعناها أن برنامجاً على السيرفر نفسه يستمع على المنفذ مثل Nginx أو Apache، واعرف صاحب المنفذ بالأمرين التاليين:
sudo ss -ltnp | grep -E ':(80|443) '
docker ps --filter publish=80 --filter publish=443والحل أن تختار Reverse Proxy واحداً للسيرفر، فإذا اخترت Traefik فانقل نطاقات Nginx Proxy Manager إلى وسوم في ملفات Compose، ثم أوقف NPM، ثم شغل Traefik، وإذا كان البرنامج خدمة نظام لا تحتاج إليها فأوقفها وعطلها، مثلاً sudo systemctl disable --now nginx.
يتجاهل Traefik ملف acme.json
إذا ظهرت في السجل رسالة permissions 644 for /letsencrypt/acme.json are too open, please use 600 فلن يحفظ Traefik أي شهادة، وسوف يطلبها من جديد مع كل تشغيل حتى تصطدم بالحدود، والحل أن تنفذ chmod 600 /opt/traefik/letsencrypt/acme.json ثم docker compose restart traefik، وتتكرر المشكلة عادة بعد نسخ الملف بأداة لا تحفظ الصلاحيات.
تطلب اللوحة كلمة المرور ثم ترفضها
السبب في الغالب أن علامات $ في الهاش لم تصل كما هي، فتحقق من القيمة التي وصلت إلى الوسم بالأمر docker inspect traefik --format '{{ index .Config.Labels "traefik.http.middlewares.dashboard-auth.basicauth.users" }}'، وإذا ظهر الهاش ناقصاً فضع القيمة في .env بين علامتي تنصيص مفردتين، أو ضاعف كل $ إذا كتبتها في compose.yaml، ثم نفذ docker compose up -d.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- يقرأ Traefik التوجيه من وسوم كل Container، فيبقى إعداد التطبيق ونطاقه وشهادته في ملف Compose واحد، ويختفي التوجيه مع التطبيق عندما تحذفه.
- اجعل
exposedByDefault: falseحتى لا ينشر Traefik إلا ما تختاره، وفعلaliasHeadersStrategy: deleteعلى نقاط الدخول المعرضة للإنترنت. - لا تستخدم
api.insecure، وضع اللوحة خلف نطاق خاص وكلمة مرور، أو أوقفها إذا لم تحتج إليها. - ال Docker socket يعادل صلاحية root على السيرفر، فضع بينه وبين Traefik وسيط Socket Proxy لا يسمح إلا بالقراءة.
- اترك تجديد الشهادات ل Traefik، وجرب على بيئة Staging أولاً، وانسخ
acme.jsonمشفراً خارج السيرفر.
سجل التحديثات (Changelog)
- أكتوبر 2026: كتابة الدليل.