راهنمای مهاجرت از Traefik v2 به v3 و رفع خطاهای رایج
در نسخه v3 استفاده از swarmMode یا pilot در تنظیمات استاتیک باعث توقف سرویس میشود. با رفع خطای incompatible deprecated static option و تنظیم مجدد قوانین، مهاجرت را تکمیل کنید.
تغییرات میان Traefik v2 و v3
مهاجرت از Traefik v2 به v3 عمدتاً شامل تغییر نامهاست؛ مشهورترین مورد، تبدیل middleware با نام ipWhiteList به ipAllowList است. فراتر از این، نسخه v3 سینتکس قوانین router را سختگیرانهتر کرده است (PathPrefix دیگر از قابلیتهای regex پشتیبانی نمیکند، چندین matcher تغییر نام یافته یا حذف شدهاند)، برخی از providerها و گزینهها را بهطور کامل کنار گذاشته و سایر بخشها را بدون تغییر حفظ کرده است: entrypointها، تنظیمات گواهی ACME، گردش کار Docker labels و تمام acme.json شما بدون مشکل منتقل میشوند. نسخه v3 همچنین دارای یک حالت سازگاری (compatibility mode) است که سینتکس قوانین v2 را فعال نگه میدارد؛ بنابراین میتوانید ابتدا فایل باینری را ارتقا دهید و قوانین را بهجای یک شب پرخطر، برای هر سرویس بهصورت جداگانه بازنویسی کنید.
این راهنما فرض را بر استفاده از تنظیمات مبتنی بر label در Docker Compose از راهنمای reverse proxy با Traefik میگذارد. آن صفحه با v3 سازگار است؛ این راهنما برای سروری است که همچنان از تگ traefik:v2 استفاده میکند.
تغییر نامها و حذفها
- گزینه
ipWhiteListاکنون بهipAllowListتغییر نام یافته است؛ این موضوع هم برای میانافزار (middleware) HTTP و هم برای TCP صدق میکند. گزینههای داخلی آن بدون تغییر باقی ماندهاند، بنابراینsourcerangeدقیقاً همان معنای قبلی خود را حفظ میکند. نسخههای فعلی v3، از جمله v3.5، همچنان نام قدیمی را به عنوان یک alias منسوخ میپذیرند و لیست را اعمال میکنند، بنابراین این تغییر نام باعث از کار افتادن سرویس نمیشود. با این حال، آن را تغییر دهید: این alias برای حذف زمانبندی شده است و در آینده بدون اطلاع قبلی از لیست موارد منسوخ حذف خواهد شد. - گزینه
providers.docker.swarmMode=trueحذف شده است. Swarm اکنون ارائهدهنده (provider) اختصاصی خود را دارد که با نامproviders.swarm.endpointپیکربندی میشود. - بخش
pilotبهطور کامل حذف شده است. - گزینه
experimental.http3حذف شده است. پروتکل HTTP/3 اکنون مستقیماً در entrypoint فعال میشود. - گزینه
tls.caOptionalاز بخش ارائهدهندهها و میانافزار forwardAuth حذف شده است. اگر این میانافزار در مقابل یک سرویس Authentik SSO میزبانیشده قرار دارد، حذف خطcaOptionalتمام مراحل مهاجرت برای آن است؛ زیرا آدرس forwardAuth، هدرهای مورد اعتماد و outpost پشت آنها، همگی در v3 به همان شکل سابق عمل میکنند. - ارائهدهنده متریک InfluxDB v1، ارائهدهنده Rancher و ارائهدهنده Marathon حذف شدهاند.
- قابلیت Tracing به OpenTelemetry منتقل شده است. بکاندهای اختصاصی tracing، از جمله یکپارچهسازیهای 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 واقعاً آن را نمیشناسد (یک غلط تایپی یا نامی که حذف شده و جایگزین نشده است) رفتار متفاوتی دارد: روتری که به آن ارجاع میدهد به جای بارگذاری مسیر، با خطا مواجه میشود، داشبورد آن را علامتگذاری میکند و API پیام middleware "offce@docker" does not exist را گزارش میدهد. درخواستها به آن نام دامنه با خطای 404 مواجه میشوند زیرا روتر هرگز فعال نشده است. توجه داشته باشید که ipwhitelist در نسخههای فعلی v3 در این دسته قرار نمیگیرد: این گزینه به عنوان یک alias منسوخ باقی میماند، بنابراین برچسبی که تغییر نام داده نشده است همچنان بدون مشکل کار میکند.
تغییرات در نحو قوانین
قوانین (Rules) جایی هستند که بازنویسی واقعی در آنها رخ میدهد. تغییرات در نسخه v3 عبارتند از:
- استفاده از علامت backtick (
) در اطراف مقادیر داخل matchers الزامی است. نسخه v2 از دابلکوتیشن نیز پشتیبانی میکرد، اما v3 اینطور نیست؛ بنابراین Host("app.example.com") باید به Host(app.example.com`) تغییر یابد. - عبارت
PathPrefixدیگر از عبارتهای باقاعده (regular expressions) یا جایگذاریهای سبک{id}پشتیبانی نمیکند. یک قانون در v2 مانند PathPrefix(/api/{version:v[0-9]+}) باید به یک matcher از نوعPathRegexpتبدیل شود که با نحو عبارتهای باقاعده Go نوشته شده باشد. - هر matcher اکنون فقط یک مقدار میپذیرد. نسخه 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 همان عملکرد را دارد. - دو matcher جدید اضافه شدهاند:
QueryRegexpوClientIPبرای تطبیق آدرس کلاینت در داخل یک قانون.
خبر خوب این است که یک قانون ساده Host(app.example.com) که با backtick نوشته شده باشد، از قبل با نحو v3 سازگار است. اکثر تنظیمات کوچک Compose دقیقاً از همین ساختار استفاده میکنند، به این معنی که اکثر labelها بدون نیاز به ویرایش قوانین، مهاجرت میشوند.
پیش از شروع، برچسبهای خود را بازبینی کنید
شما میتوانید حجم مهاجرت خود را با یک جستجوی ساده بسنجید، زیرا هر تغییر در برچسبهای ناسازگار (breaking label change)، الگویی بر جای میگذارد که با grep قابل شناسایی است:
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.ymlهر نتیجه، یک خط برای ویرایش است. ipwhitelist به ipallowlist تبدیل میشود. HostHeader به Host تبدیل میشود. Headers به Header تبدیل میشود. یک جاینگهدار (placeholder) از نوع {...} در داخل PathPrefix به یک تطبیقدهنده (matcher) از نوع PathRegexp تبدیل میشود. یک ویرگول در داخل Host() به دو تطبیقدهنده Host() تبدیل میشود که توسط || به هم متصل شدهاند. صفر نتیجه به این معناست که برچسبهای شما هماکنون با سینتکس v3 سازگار هستند و مهاجرت به پیکربندی ایستا و تگ image محدود میشود. اگر صفحه پر از نتایج است، زمان مناسبی است که از خود بپرسید آیا این همچنان پروکسی مناسبی برای سرور شماست یا خیر؛ و مقایسه Traefik با Nginx و Caddy هزینه بازنویسی را در برابر آنچه دو گزینه دیگر از شما برای هر برنامه میخواهند، قرار میدهد.
مواردی که بدون تغییر باقی میمانند
نقطه ورودها (Entrypoints) و تغییر مسیر HTTP به HTTPS آنها، حلکنندههای ACME با هر دو نوع چالش، exposedByDefault، برچسبهای مسیریاب و سرویس، loadbalancer.server.port، و همچنین داشبورد، همگی در v3 دقیقاً مانند v2 کار میکنند. گواهیهای شما نیز منتقل میشوند، زیرا v3 همچنان فایل acme.json که توسط v2 نوشته شده است را میخواند. با این حال، پیش از شروع کار از فایل نسخه پشتیبان تهیه کنید، چرا که بازگشت به نسخه قبلی (rollback) در صورت از دست دادن این فایل، مستقیماً منجر به برخورد با محدودیت نرخ (rate limit) صدور گواهی تکراری 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: پیکربندی ایستا را پاکسازی کرده و حالت سازگاری (compatibility mode) را فعال کنید. تمام گزینههایی که در نسخه 3 حذف شدهاند (pilot, swarmMode, tls.caOptional, experimental.http3) را حذف کنید، سپس به نسخه 3 دستور دهید که قوانین را بهصورت پیشفرض با سینتکس نسخه 2 پردازش کند. در فایل traefik.yml:
core:
defaultRuleSyntax: v2یا بهعنوان یک فلگ در لیست command: فایل compose: --core.defaultRuleSyntax=v2. حالت سازگاری فقط سینتکس قوانین را پوشش میدهد. این حالت گزینههای حذفشده را بازنمیگرداند و نام میانافزارها (middlewares) را برای شما تغییر نمیدهد.
گام 3: تغییر نام میانافزارها را آماده کنید. فایلهای compose خود را برای یافتن نامهای قدیمی (grep -rn ipwhitelist docker-compose*.yml) جستجو کنید. هر برچسب ipwhitelist را به ipallowlist تغییر دهید، اما هنوز تغییرات را اعمال نکنید؛ زیرا نام جدید در نسخه 2 وجود ندارد. این ویرایشها باید همزمان با تغییر نسخه در گام بعدی اعمال شوند. (اگر موردی از قلم بیفتد، نسخه 3 همچنان نام قدیمی را بهعنوان یک alias منسوخ میپذیرد و لیستها همچنان اجرا میشوند؛ پس آن را در مرحلهٔ بعدی اصلاح کنید، نه در ساعت 2 بامداد.)
گام 4: تگ ایمیج را تغییر دهید. ایمیج Traefik را روی نسخه فعلی 3، یعنی traefik:v3.5 (در زمان نگارش این متن)، تنظیم کنید و سپس:
docker compose up -d
docker compose logs -f traefikاز آنجا که حالت سازگاری فعال است، قوانین نسخه 2 شما همچنان مطابقت دارند و چون up -d سرویسهایی را که برچسب میانافزارشان را تغییر دادهاید بازسازی کرده است، آن روترها بدون خطا بالا میآیند. یک لاگ سالم نباید شامل خطوط field not found و does not exist باشد.
در مورد بازهٔ زمانی که این مرحله ایجاد میکند، صادق باشید. روتری که به نام یک میانافزار ارجاع میدهد که نسخه 3 آن را نمیشناسد (به دلیل غلط تایپی یا حذف گزینه)، از لحظه شروع به کار Traefik جدید تا زمانی که کانتینر برنامه بازسازی شود، از دسترس خارج است. در یک سرور تکی، این زمان چند ثانیهای است که docker compose up -d برای پردازش لیست نیاز دارد. اگر یک مسیر واقعاً نباید قطع شود، پیش از تغییر نسخه، میانافزار تغییرنامیافته را از برچسب middlewares آن روتر حذف کرده و پس از آن دوباره اضافه کنید. از قبل تصمیم بگیرید که آیا آن مسیر میتواند برای یک دقیقه بدون لیست مجاز IP (IP allow list) کار کند یا خیر.
گام 5: قوانین را سرویس به سرویس مهاجرت دهید. هر بار روی یک برنامه کار کنید: قانون آن را به سینتکس نسخه 3 بازنویسی کنید، فقط همان سرویس را با docker compose up -d app بازسازی کنید و پیش از رفتن به سراغ بعدی، آن را تست کنید. اگر سرویسی دارید که قانون آن را هنوز نمیتوانید بازنویسی کنید، به آن روتر خاص برچسب خروج اضطراری traefik.http.routers.app.ruleSyntax=v2 را بدهید و ادامه دهید.
گام 6: حالت سازگاری را غیرفعال کنید. وقتی تمام قوانین به سینتکس نسخه 3 تبدیل شدند، defaultRuleSyntax و هرگونه برچسب ruleSyntax را حذف کنید، Traefik را ریاستارت کنید و تأیید کنید که وضعیت تمام روترها در داشبورد سبز است. به حالت سازگاری عادت نکنید: Traefik هر دو گزینه را در نسخه 3.4 منسوخ کرده و در نسخه اصلی بعدی حذف میکند؛ بنابراین آنها یک پل هستند، نه مقصد نهایی.
پیش و پس از تغییر: برچسبهای یک سرویس
در اینجا برنامهای را میبینید که تمام تغییرات مهم را بهطور همزمان اعمال کرده است: یک Host چندمقداری، یک جایگیر (placeholder) PathPrefix و یک میانافزار (middleware) 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 چندمقداری خود را به دو تطبیقدهنده (matcher) که با || به هم متصل شدهاند تقسیم کرد و جایگیر را با PathRegexp عوض کرد؛ همچنین برچسب میانافزار، ipwhitelist را با ipallowlist جایگزین نمود. نقطه ورود (entrypoint)، حلکننده گواهی (certificate resolver)، اتصال روتر به میانافزار و پورت سرویس بدون تغییر باقی ماندند.
تست هر سرویس با استفاده از داشبورد
پس از هر تغییر، صفحه HTTP routers در داشبورد را باز کنید. وضعیت هر router باید سبز باشد. routerهایی که دارای نشان خطا هستند، مشکل دقیق خود را نمایش میدهند؛ این مشکل معمولاً به دلیل وجود یک middleware است که با نام جدیدش شناخته نمیشود یا قانونی است که نسخه v3 قادر به تجزیه آن نیست. سپس از خارج از شبکه، تکتک نامهای دامنه را بررسی کنید:
curl -sI https://app.example.com/api/v1/statusیک 200 یا تغییر مسیر (redirect) عادی برنامه شما، به این معناست که مسیریابی و TLS هر دو بهدرستی کار میکنند. دریافت 404 از Traefik به این معناست که router بالا نیامده است؛ به داشبورد بازگردید و خطای آن را بخوانید. در حین کار، docker compose logs -f traefik را در یک ترمینال دوم باز نگه دارید، زیرا هر خطای تجزیه (parsing failure) در لحظه restart شدن container در آنجا ثبت میشود.
صداقت در بازگشت به نسخه قبل (Rollback)
فایل compose نسخه v2، پیکربندی ایستا و نسخه پشتیبان acme.json را تا زمانی که تمام سرویسها روی نسخه v3 مسیریابی شوند و بهطور کامل تست گردند، نگه دارید. بازگشت به نسخه قبل (Rollback) به معنای بازگرداندن commit پیش از مهاجرت و اجرای docker compose up -d است. این کار باید برای کل فایل انجام شود، نه فقط تغییر تگ image؛ زیرا برچسبهای مخصوص v3 در محیط v2 دقیقاً به همان شکلی که برچسبهای v2 در v3 اشتباه بودند، عمل نمیکنند: دستور ipallowlist در v2 وجود ندارد و یک تطبیقدهنده (matcher) از نوع PathRegexp نیز در آنجا تجزیه (parse) نخواهد شد. اگر در طول این فرآیند، فایل acme.json از دست رفته یا آسیب دیده است، پیش از شروع v2 نسخه پشتیبان آن را بازیابی کنید تا فرآیند بازگشت به نسخه قبل، با صدور همزمان پنج گواهی، محدودیت نرخ (rate limit) Let's Encrypt شما را مصرف نکند.
FAQ
آیا باید تمام قوانین روتر را برای Traefik v3 بازنویسی کنم؟
خیر. یک قانون ساده Host(app.example.com) که با backtick نوشته شده باشد، در هر دو نسخه معتبر است و اکثر تنظیمات Compose را پوشش میدهد. بازنویسی تنها زمانی لازم است که قانون از ویژگیهای مختص v2 استفاده کرده باشد: regex یا placeholderها در داخل Path و PathPrefix، چندین نام دامنه در یک Host()، استفاده از کوتیشن بهجای backtick، یا استفاده از matcherهای حذفشده Headers، HeadersRegexp و HostHeader.
چه اتفاقی برای ipWhiteList در Traefik v3 افتاده است؟
نام آن به ipAllowList تغییر یافته است، در حالی که پیکربندی داخلی آن تغییری نکرده است. بنابراین یک label در v2 مانند traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24، به همان خط با نام ipallowlist تبدیل میشود. نسخههای فعلی v3، از جمله v3.5، همچنان نام قدیمی را بهعنوان یک alias منسوخ میپذیرند؛ بنابراین label تغییرنیافته همچنان لیست مجاز را اعمال میکند. این وضعیت را بهعنوان یک فرصت موقت در نظر بگیرید، نه دلیلی برای نادیده گرفتن تغییر نام: این alias برای حذف برنامهریزی شده است و اگر Traefik نام یک middleware را نشناسد، با خطای روتر و کد 404 مواجه خواهید شد. داشبورد این خطا را نمایش میدهد و درخواستها به آن نام دامنه با 404 پاسخ داده میشوند.
آیا Traefik v3 همچنان میتواند سینتکس قوانین v2 را بخواند؟
بله. گزینه core.defaultRuleSyntax: v2 را در پیکربندی استاتیک تنظیم کنید تا در حین مهاجرت، سینتکس v2 بهعنوان پیشفرض باقی بماند. پس از بازگرداندن پیشفرض، برای موارد باقیمانده از label مخصوص هر روتر یعنی ruleSyntax=v2 استفاده کنید. هر دو را موقتی بدانید: Traefik این موارد را در v3.4 منسوخ اعلام کرده و در نسخه اصلی بعدی حذف خواهد کرد.
آیا گواهیهای Let's Encrypt من پس از ارتقا باقی میمانند؟
بله. Traefik v3 همچنان فایل acme.json که توسط v2 نوشته شده است را میخواند، بنابراین گواهیها صرفاً به دلیل تغییر فایل اجرایی (binary) دوباره صادر نمیشوند. با این حال، پیش از شروع کار، از فایل مذکور در جایی امن کپی بگیرید؛ زیرا بازگشت به نسخه قبل (rollback) یا حذف شدن volume که منجر به از دست رفتن acme.json شود، باعث میشود تمام گواهیها بهطور همزمان دوباره صادر شوند. Let's Encrypt تنها اجازه صدور پنج گواهی تکراری در هفته برای یک مجموعه از نامهای دامنه را میدهد.
چرا Traefik v3 پس از ارتقا اجرا نمیشود؟
تقریباً همیشه به این دلیل است که پیکربندی استاتیک هنوز شامل گزینهای است که در v3 حذف شده و Traefik از اجرای برنامه با گزینههای ناشناخته خودداری میکند. برای موارد باقیمانده شناختهشده (pilot، providers.docker.swarmMode، experimental.http3)، لاگ عبارت incompatible deprecated static option found را نمایش داده و عامل خطا را نام میبرد؛ برای هر چیزی که v3 هرگز آن را نشناخته است، مانند tls.caOptional، عبارت field not found را همراه با گره (node) مربوطه اعلام میکند. هر کدام را حذف یا جایگزین کنید و سپس کانتینر را دوباره اجرا کنید.