ترحيل Traefik من v2 إلى v3: ما الذي يتعطل؟
لن يبدأ Traefik v3 عند وجود swarmMode أو pilot في الإعداد الثابت. أصلح الخطأ الحرفي incompatible deprecated static option found ثم رحّل القواعد.
ما الذي يتغير بين Traefik v2 وv3
تتعلق عملية الترحيل من Traefik v2 إلى v3 في معظمها بإعادة تسمية العناصر. وأشهر إعادة تسمية هي تغيير middleware باسم ipWhiteList إلى ipAllowList. إضافة إلى ذلك، يفرض v3 قيوداً أكبر على صياغة قواعد router؛ إذ يفقد PathPrefix ميزات التعبيرات النمطية، وتُعاد تسمية بعض matchers أو تُزال. كما يلغي بعض providers والخيارات نهائياً، مع إبقاء كل العناصر الأخرى قيد التشغيل: entrypoints، وإعداد شهادة ACME، وسير العمل المعتمد على Docker labels، وacme.json لديك. يوفّر v3 أيضاً وضع توافق يُبقي صياغة قواعد v2 عاملة. لذلك يمكنك ترقية binary أولاً ثم إعادة كتابة القواعد لخدمة واحدة في كل مرة، بدلاً من تنفيذ ذلك كله في مساء واحد محفوف بالمخاطر.
يفترض هذا الدليل أنك تستخدم إعداد Docker Compose المعتمِد على labels الوارد في دليل reverse proxy باستخدام Traefik. تلك الصفحة متوافقة أصلاً مع v3، أما هذا الدليل فيخص الخادم الذي لا يزال يشغّل tag باسم traefik:v2.
أسماء العناصر التي تغيّرت أو أزيلت
ipWhiteListأصبحipAllowList، وذلك لكل من وسيط HTTP ووسيط TCP. لم تتغير الخيارات الموجودة داخله، لذلك يحتفظsourcerangeبمعناه الدقيق. ما تزال إصدارات v3 الحالية، بما فيها v3.5، تقبل الاسم القديم كاسم مستعار مهجور، وتواصل فرض القائمة. لذلك لا يؤدي هذا التغيير وحده إلى تعطيل أي شيء عند التبديل. غيّر الاسم رغم ذلك: من المقرر إزالة الاسم المستعار، وسيختفي من قائمة العناصر المهجورة بصمت، لا برسالة واضحة.providers.docker.swarmMode=trueأُزيل. أصبح لـSwarm موفّر خاص به، ويُضبط باستخدامproviders.swarm.endpoint.- أُزيل قسم
pilotبالكامل. experimental.http3أُزيل. يُفعّل HTTP/3 مباشرةً على entrypoint.- أُزيل
tls.caOptionalمن الموفّرات ومن وسيط forwardAuth. إذا كان هذا الوسيط يضع خدمة Authentik SSO مستضافة ذاتياً أمام الخدمة، فإن حذف السطرcaOptionalهو عملية الترحيل الكاملة له، لأن عنوان forwardAuth والرؤوس الموثوقة وoutpost خلفها جميعاً تعمل بالطريقة نفسها في v3. - أُزيل موفّر مقاييس InfluxDB v1 وموفّر Rancher وموفّر Marathon.
- نُقلت التتبعات إلى OpenTelemetry. أُزيلت خلفيات التتبّع المخصصة، ومنها تكاملات Jaeger وZipkin، ويُصدّر v3 البيانات باستخدام OTLP، وهو بروتوكول OpenTelemetry.
- أُزيلت خيارات
ssl*المهجورة داخل وسيط headers، ومنهاsslRedirectوsslHostوغيرها. وقد حلّت إعادة التوجيه على entrypoint ووسيط redirectScheme محلها.
هذه الإزالات أهم مما تبدو عليه، لأن Traefik يرفض البدء عندما تحتوي إعداداته الثابتة على خيار لا يعرفه. يؤدي بقاء سطر pilot أو swarmMode إلى إيقاف الحاوية أثناء الإقلاع مع رسالة incompatible deprecated static option found التي تذكر العنصر المتبقي. أما الخيار الذي لم يسمع به Traefik قط، مثل خطأ إملائي أو tls.caOptional، فيوقفه برسالة field not found بدلاً من ذلك. نظّف الإعدادات الثابتة قبل تغيير وسم image.
يفشل اسم وسيط لا يعرفه Traefik فعلاً، مثل خطأ إملائي أو اسم أُزيل بدلاً من أن يصبح اسماً مستعاراً، بطريقة مختلفة: يُحمّل الـrouter الذي يشير إليه مع ظهور خطأ بدلاً من route، وتعلّمه لوحة المعلومات، وتُبلغ API عن middleware "offce@docker" does not exist. تحصل الطلبات الموجهة إلى اسم المضيف هذا على استجابة 404 لأن الـrouter لم يبدأ قط. لاحظ أن ipwhitelist لا يندرج ضمن هذه الفئة في v3 الحالية: فهو يبقى اسماً مستعاراً مهجوراً، لذلك يواصل label غير المُعاد تسميته العمل بصمت.
تتغير صياغة القواعد
القواعد هي المكان الذي يحدث فيه التغيير الفعلي لإعادة الكتابة. وتشمل التغييرات في v3 ما يلي:
- يجب وضع القيم داخل المطابقات بين backticks. كان v2 يقبل أيضاً علامات الاقتباس المزدوجة، لكن v3 لا يقبلها. لذلك يجب تحويل Host("app.example.com") إلى Host(
app.example.com). - لم يعد
PathPrefixيفهم التعبيرات النمطية أو العناصر النائبة بصيغة{id}. يجب تحويل قاعدة v2 مثل PathPrefix(/api/{version:v[0-9]+}) إلى مطابقةPathRegexpمكتوبة بصيغة التعبيرات النمطية في Go. - تقبل المطابقات الآن قيمة واحدة. كان v2 يسمح باستخدام Host(
app.example.com,www.example.com)، بينما يتطلب v3 استخدام Host(app.example.com) || Host(www.example.com). الاستثناءات هيHeaderوHeaderRegexpوQueryوQueryRegexp، إذ لا تزال هذه المطابقات تقبل اسماً وقيمة. - أُعيدت تسمية
HeadersوHeadersRegexpإلىHeaderوHeaderRegexp. - أُزيلت
HostHeader. استخدمHost، فهي تطابق الشيء نفسه في v3. - أُضيفت مطابقتان جديدتان:
QueryRegexp، وClientIPلمطابقة عنوان العميل داخل قاعدة.
الخبر الجيد هو أن قاعدة Host(app.example.com) البسيطة المكتوبة باستخدام backticks صالحة بالفعل في صياغة v3. تستخدم معظم إعدادات Compose الصغيرة هذه الصياغة تحديداً، ما يعني أن معظم labels تنتقل من دون تعديل القواعد.
راجع تسمياتك قبل البدء
يمكنك قياس حجم عملية الترحيل ببحث واحد، لأن كل تغيير متوافق مع الإصدارات السابقة يترك نمطاً يستطيع grep العثور عليه:
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.ymlكل نتيجة هي سطر واحد يحتاج إلى تعديل. تصبح ipwhitelist ipallowlist. وتصبح HostHeader Host. وتصبح Headers Header. يتحول العنصر النائب {...} داخل PathPrefix إلى مُطابِق PathRegexp. وتتحول الفاصلة داخل Host() إلى مُطابِقَين Host() موصولين بواسطة ||. تعني عدم مطابقة أي نتيجة أن تسمياتك تستخدم بالفعل صيغة v3 الصحيحة، وأن عملية الترحيل تقتصر على الإعدادات الثابتة ووسم الصورة. كما أن ظهور عدد كبير من النتائج هو وقت مناسب للتساؤل عما إذا كان هذا لا يزال الـproxy المناسب لهذا الخادم، وتوضح مقارنة Traefik مع Nginx وCaddy تكلفة إعادة الكتابة هذه مقارنة بما يطلبه منك الخياران الآخران لكل تطبيق.
ما الذي يبقى كما هو
تعمل نقاط الدخول وإعادة التوجيه من HTTP إلى HTTPS، ومحللات ACME بنوعي التحدي، وexposedByDefault، وتسميات الموجّه والخدمة، وloadbalancer.server.port، ولوحة المعلومات في v3 كما كانت في v2. وتنتقل شهاداتك أيضاً، لأن v3 يواصل قراءة acme.json الذي كتبه v2. مع ذلك، خذ نسخة احتياطية من الملف قبل البدء، لأن التراجع الذي يفقده سيؤدي مباشرة إلى الوصول إلى حد Let's Encrypt لمعدل الشهادات المكررة:
cp ./letsencrypt/acme.json ./letsencrypt/acme.json.v2-backupمسار الترحيل
الخطوة 1: ثبّت ما تشغّله اليوم. غيّر أي وسم traefik:latest أو traefik:v2 إلى الإصدار الدقيق الذي تستخدمه، مثل traefik:v2.11، ثم ثبّت مجلد compose بالكامل في git. تصبح كل خطوة لاحقة قابلة للعكس باستخدام checkout. إذا لم تكن إعادة إنشاء خدمة واحدة باستخدام docker compose up -d <service> أمراً مألوفاً لديك بعد، فراجع دليل أساسيات Docker Compose الذي يشرح العمليات التي يعتمد عليها هذا الترحيل.
الخطوة 2: نظّف الإعدادات الثابتة وفعّل وضع التوافق. أزل كل خيار أسقطه v3 (pilot وswarmMode وtls.caOptional وexperimental.http3)، ثم أخبر v3 بأن يتعامل مع القواعد بصيغة v2 افتراضياً. في traefik.yml:
core:
defaultRuleSyntax: v2أو مرّر ذلك كخيار في قائمة compose command:: --core.defaultRuleSyntax=v2. يغطي وضع التوافق صيغة القواعد فقط. ولا يعيد الخيارات المحذوفة، ولا يعيد تسمية middlewares نيابةً عنك.
الخطوة 3: حضّر لإعادة تسمية middlewares. ابحث في ملفات compose عن الأسماء القديمة: grep -rn ipwhitelist docker-compose*.yml. عدّل كل وسم ipwhitelist إلى ipallowlist، لكن لا تطبّق التغيير بعد، لأن الاسم الجديد غير موجود في v2. تُطبَّق هذه التعديلات مع تفعيل التغيير في الخطوة التالية. (إذا فاتتك إحدى الحالات، فإن v3 الحالي ما يزال يقبل الاسم القديم كاسم مستعار مهجور، ولذلك تستمر القائمة في فرض القاعدة؛ أصلحها في المراجعة التالية بدلاً من اكتشافها عند الساعة 2am.)
الخطوة 4: غيّر وسم الصورة. اضبط صورة Traefik على إصدار v3 الحالي، وهو traefik:v3.5 وقت كتابة هذا الدليل، ثم:
docker compose up -d
docker compose logs -f traefikبما أن وضع التوافق مفعّل، تستمر قواعد v2 في المطابقة. وبما أن up -d أعاد أيضاً إنشاء الخدمات التي أعدت تسمية وسوم middleware فيها، تبدأ تلك الـrouters دون أخطاء. لا يحتوي السجل السليم على السطر field not found ولا على السطر does not exist.
كن صريحاً مع نفسك بشأن فترة الانقطاع التي تفتحها هذه الخطوة. يتوقف الـrouter الذي يشير إلى اسم middleware لا يعرفه v3 فعلياً، سواء بسبب خطأ إملائي أو خيار محذوف، منذ لحظة بدء Traefik الجديد إلى أن يُعاد إنشاء حاوية التطبيق الخاصة به. في خادم واحد، يستغرق ذلك الثواني القليلة التي يحتاجها docker compose up -d لمعالجة القائمة. إذا كان لا يمكن لمسار معين أن يتوقف ولو للحظة، فأزل middleware المعاد تسميته من وسم middlewares لذلك الـrouter قبل التغيير، ثم أعد إضافته بعده. وحدد مسبقاً ما إذا كان يمكن للمسار العمل من دون قائمة السماح بعناوين IP خلال الدقيقة الفاصلة.
الخطوة 5: رحّل القواعد خدمةً بعد خدمة. اعمل على تطبيق واحد في كل مرة: أعد كتابة قاعدته بصيغة v3، وأعد إنشاء تلك الخدمة فقط باستخدام docker compose up -d app، ثم اختبرها قبل الانتقال إلى الخدمة التالية. إذا احتوت إحدى الخدمات على قاعدة لا يمكنك إعادة كتابتها بعد، فأضف إلى ذلك الـrouter وحده وسم مخرج الطوارئ traefik.http.routers.app.ruleSyntax=v2 وتابع العمل.
الخطوة 6: عطّل وضع التوافق. بعد تحويل كل قاعدة إلى صيغة v3، احذف defaultRuleSyntax وأي وسوم ruleSyntax، ثم أعد تشغيل Traefik وتأكد من أن كل router ما يزال يظهر باللون الأخضر في لوحة المعلومات. لا تترك وضع التوافق مفعّلاً؛ فقد صنّف Traefik الخيارين على أنهما مهجوران في v3.4، وسيزيلهما في الإصدار الرئيسي التالي. إنهما جسر مؤقت، وليسا حلاً نهائياً.
قبل وبعد: تسميات خدمة واحدة
إليك تطبيقاً واحداً يتضمن جميع التغييرات المعروفة دفعة واحدة: Host متعددة القيم، وعنصر نائب PathPrefix، وبرمجية وسيطة ipWhiteList. كتلة v2:
app:
image: app:1.4
restart: unless-stopped
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.app.rule=Host(`app.example.com`,`www.example.com`) && PathPrefix(`/api/{version:v[0-9]+}`)
- traefik.http.routers.app.entrypoints=websecure
- traefik.http.routers.app.tls.certresolver=le
- traefik.http.routers.app.middlewares=office
- traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24
- traefik.http.services.app.loadbalancer.server.port=8080والخدمة نفسها بعد ترحيلها إلى v3:
app:
image: app:1.4
restart: unless-stopped
networks:
- proxy
labels:
- traefik.enable=true
- traefik.http.routers.app.rule=(Host(`app.example.com`) || Host(`www.example.com`)) && PathRegexp(`^/api/v[0-9]+`)
- traefik.http.routers.app.entrypoints=websecure
- traefik.http.routers.app.tls.certresolver=le
- traefik.http.routers.app.middlewares=office
- traefik.http.middlewares.office.ipallowlist.sourcerange=10.0.0.0/24
- traefik.http.services.app.loadbalancer.server.port=8080تغيّرت تسميتان. قسّمت القاعدة Host متعددة القيم إلى مطابقتين موصولتين بـ ||، واستبدلت العنصر النائب بـ PathRegexp، كما استبدلت تسمية البرمجية الوسيطة ipwhitelist بـ ipallowlist. لم تتغير نقطة الدخول، أو محلّل الشهادات، أو الربط بين الموجّه والبرمجية الوسيطة، أو منفذ الخدمة.
اختبر كل خدمة باستخدام لوحة التحكم
بعد كل تغيير، افتح صفحة موجّهات HTTP في لوحة التحكم. يجب أن تظهر جميع الموجّهات باللون الأخضر. يوضح الموجّه الذي يحمل شارة خطأ المشكلة المحددة، وتكون عادةً وسيطاً غير موجود بالاسم الجديد أو قاعدة لا يستطيع v3 تحليلها. ثم تأكد من خارج الخادم، اسماً مضيفاً واحداً في كل مرة:
curl -sI https://app.example.com/api/v1/statusيشير ظهور 200 أو إعادة التوجيه المعتادة لتطبيقك إلى أن التوجيه وTLS استمرا في العمل. ويشير ظهور 404 من Traefik إلى أن الموجّه لم يبدأ؛ عُد إلى لوحة التحكم واقرأ رسالة الخطأ. أبقِ docker compose logs -f traefik مفتوحاً في طرفية ثانية أثناء العمل، لأن كل فشل في التحليل يظهر فيه فور إعادة تشغيل الحاوية.
الشفافية في التراجع
احتفظ بملف Compose الخاص بـv2، وإعداده الثابت، والنسخة الاحتياطية acme.json حتى تعمل كل خدمة عبر v3 وتختبرها فعلياً. يعني التراجع استخراج commit السابق للترحيل وتشغيل docker compose up -d. ويجب أن يشمل ذلك الملف بأكمله، لا وسم الصورة فقط، لأن labels الخاصة بـv3 تكون غير صحيحة تحت v2 بالطريقة نفسها التي كانت بها labels الخاصة بـv2 غير صحيحة تحت v3: لا يوجد ipallowlist في v2، كما أن matcher من نوع PathRegexp لن يُحلَّل هناك. إذا فُقد acme.json أو تعرض للتلف أثناء العملية، فاستعد النسخة الاحتياطية قبل تشغيل v2، حتى لا يستهلك التراجع حدّ معدل Let's Encrypt بإعادة إصدار خمس شهادات دفعة واحدة.
FAQ
هل يجب أن أعيد كتابة كل قاعدة موجّه في Traefik v3؟
لا. قاعدة Host(app.example.com) عادية مكتوبة باستخدام backticks صالحة في كلا الإصدارين، وهذا يغطي معظم إعدادات Compose. لا تحتاج إلى إعادة الكتابة إلا عندما تستخدم القاعدة ميزات خاصة بـv2: تعبيرات regex أو placeholders داخل Path وPathPrefix، أو عدة أسماء مضيفين داخل Host()، أو علامات اقتباس بدلاً من backticks، أو أدوات المطابقة Headers وHeadersRegexp وHostHeader التي أزيلت.
ماذا حدث لـipWhiteList في Traefik v3؟
أُعيدت تسميته إلى ipAllowList، مع بقاء الإعداد الداخلي دون تغيير، لذلك تصبح تسمية v2 مثل traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24 السطر نفسه مع ipallowlist بداخله. ما زالت إصدارات v3 الحالية، بما فيها v3.5، تقبل الاسم القديم كاسم مستعار مهمل، لذلك تواصل التسمية التي لم تُعد تسميتها تطبيق قائمة السماح بهدوء. اعتبر ذلك مهلة مؤقتة، وليس سبباً لتخطي إعادة التسمية: من المقرر إزالة الاسم المستعار، بينما يفشل اسم middleware الذي لا يعرفه Traefik بوضوح، مع ظهور خطأ في الموجّه وعودة استجابة 404. تعرض لوحة المعلومات الخطأ، وتعيد الطلبات المرسلة إلى اسم المضيف هذا استجابة 404.
هل يستطيع Traefik v3 مواصلة قراءة صيغة قواعد v2؟
نعم. اضبط core.defaultRuleSyntax: v2 في الإعداد الثابت للإبقاء على صيغة v2 كصيغة افتراضية أثناء الترحيل، واستخدم تسمية ruleSyntax=v2 الخاصة بالموجّه لكل حالة فردية متبقية بعد إعادة الإعداد الافتراضي إلى صيغة v3. تعامل مع الخيارين على أنهما مؤقتان: أوقف Traefik دعمهما في v3.4، وسيزيلهما في الإصدار الرئيسي التالي.
هل ستبقى شهادات Let's Encrypt بعد الترقية؟
نعم. يواصل Traefik v3 قراءة ملف acme.json الذي أنشأه v2، لذلك لا تُصدر الشهادات من جديد لمجرد تغيّر الملف التنفيذي. انسخ الملف إلى مكان آمن قبل البدء على أي حال، لأن التراجع إلى الإصدار السابق أو حذف volume يؤدي إلى فقدان acme.json، ما يفرض إعادة إصدار كل الشهادات دفعة واحدة، كما تسمح Let's Encrypt بخمس شهادات مكررة فقط أسبوعياً لمجموعة أسماء المضيفين نفسها.
لماذا يفشل Traefik في البدء بعد الترقية؟
يحدث ذلك في الغالب لأن الإعداد الثابت ما زال يتضمن خياراً أزاله v3، ويرفض Traefik البدء عند وجود خيارات لا يتعرف إليها. بالنسبة إلى الخيارات المتبقية المعروفة (pilot وproviders.docker.swarmMode وexperimental.http3)، يذكر السجل incompatible deprecated static option found ويسمي الخيار المسبب للمشكلة؛ أما أي خيار لم يسمع به v3 من قبل، مثل tls.caOptional، فيذكر field not found مع اسم العقدة. احذف كل خيار منها أو استبدله، ثم شغّل الحاوية من جديد.