تغییرات و مشکلات مهاجرت به Traefik v3
در نسخه v3 اگر swarmMode یا pilot در static config باشد با خطای incompatible deprecated static option مواجه میشوید. روش رفع خطا و تغییر نام middlewareها.
تفاوتهای بین Traefik v2 و v3
مهاجرت از Traefik v2 به v3 عمدتاً شامل تغییر نامها است؛ مهمترین آنها تغییر نام middleware از ipWhiteList به ipAllowList است. علاوه بر این، نسخه v3 سینتکس router rule را محدودتر کرده است (PathPrefix قابلیتهای regex خود را از دست داده و چندین matcher تغییر نام داده یا حذف شدهاند). همچنین برخی providerها و optionها کاملاً حذف شدهاند، اما سایر موارد بدون تغییر باقی میمانند: entrypoints، تنظیمات ACME certificate، گردشکار Docker labels و acme.json شما همگی حفظ میشوند. نسخه v3 دارای یک compatibility mode است که سینتکس rules نسخه v2 را پشتیبانی میکند؛ بنابراین میتوانید ابتدا فایل binary را ارتقا دهید و به جای انجام یک تغییر پرخطر در یک شب، قوانین را سرویس به سرویس بازنویسی کنید.
این راهنما فرض را بر این میگذارد که شما از تنظیمات label-based Docker Compose در راهنمای reverse proxy Traefik استفاده میکنید. آن صفحه مخصوص نسخه v3 است؛ این صفحه برای سیستمهایی است که هنوز دارای تگ traefik:v2 هستند.
تغییر نامها و حذفها
ipWhiteListاکنون برای هر دو middleware مربوط به HTTP و TCP بهipAllowListتغییر نام یافته است. گزینههای داخل آن بدون تغییر باقی ماندهاند، بنابراینsourcerangeمعنای دقیق خود را حفظ میکند. نسخههای فعلی v3، از جمله v3.5، همچنان نام قدیمی را به عنوان یک alias منسوخ شده (deprecated) میپذیرند و لیست را اعمال میکنند؛ بنابراین این تغییر نام باعث از کار افتادگی چیزی نمیشود. با این حال، تغییر نام را انجام دهید: حذف این alias برنامهریزی شده است و بدون اطلاع قبلی از لیست منسوخشدهها حذف خواهد شد.providers.docker.swarmMode=trueحذف شده است. Swarm اکنون دارای provider اختصاصی خود است که به صورتproviders.swarm.endpointپیکربندی میشود.- بخش
pilotکاملاً حذف شده است. experimental.http3حذف شده است. قابلیت HTTP/3 مستقیماً در entrypoint فعال میشود.tls.caOptionalاز providerها و از middleware مربوط به forwardAuth حذف شده است.- provider مربوط به InfluxDB v1، provider مربوط به Rancher و provider مربوط به Marathon حذف شدهاند.
- قابلیت Tracing به OpenTelemetry منتقل شده است. backendهای اختصاصی tracing، از جمله یکپارچهسازیهای Jaeger و Zipkin، حذف شدهاند و نسخه v3 به جای آنها، OTLP (پروتکل OpenTelemetry) را export میکند.
- گزینههای منسوخ شده
ssl*در داخل middleware مربوط به headers (شاملsslRedirect،sslHostو بقیه) حذف شدهاند. redirections در entrypoint و middleware مربوط به redirectScheme جایگزین آنها شدهاند.
این حذفها اهمیت بیشتری از ظاهر خود دارند، زیرا اگر پیکربندی استاتیک (static configuration) شامل گزینهای باشد که Traefik نمیشناسد، برنامه از اجرا باز میماند. باقی ماندن یک خط pilot یا swarmMode باعث توقف کانتینر در هنگام بوت با یک پیام incompatible deprecated static option found میشود که نام آن گزینه را ذکر میکند؛ اما اگر گزینهای که Traefik هرگز نشنیده است (یک غلط تایپی یا tls.caOptional) وجود داشته باشد، برنامه با پیام field not found متوقف میشود. قبل از تغییر نسخه image، پیکربندی استاتیک را پاکسازی کنید.
نام یک middleware که Traefik واقعاً آن را نمیشناسد (یک غلط تایپی، یا نامی که به جای alias شدن، حذف شده است) به شکل متفاوتی با خطا مواجه میشود: روتر (router) که به آن ارجاع میدهد، به جای یک مسیر (route)، با خطا بارگذاری میشود، داشبورد آن را علامتگذاری میکند و API پیام middleware "offce@docker" does not exist را گزارش میدهد. درخواستها به آن hostname با خطای 404 مواجه میشوند زیرا روتر هرگز بالا نیامده است. توجه داشته باشید که در نسخه فعلی v3، ipwhitelist در این دسته قرار ندارد: این مورد به عنوان یک alias منسوخ شده باقی مانده است، بنابراین یک label تغییرنامنیافته همچنان بدون مشکل کار میکند.
تغییر در سینتکس قوانین
قوانین جایی هستند که بازنویسی اصلی در آنها رخ میدهد. تغییرات در v3:
- استفاده از Backticks برای مقادیر داخل matcherها الزامی است. نسخه v2 از double quotes نیز پشتیبانی میکرد؛ اما نسخه v3 این کار را انجام نمیدهد، بنابراین Host("app.example.com") باید به Host(
app.example.com) تغییر یابد. PathPrefixدیگر regular expressionها یا placeholderهای سبک{id}را درک نمیکند. یک قانون در v2 مانند PathPrefix(/api/{version:v[0-9]+}) باید به یک matcher از نوعPathRegexpکه با سینتکس Go regular expression نوشته شده است، تبدیل شود.- 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برای تطبیق client address در داخل یک قانون.
خبر خوب: یک قانون ساده Host(app.example.com) که با backtick نوشته شده باشد، از قبل سینتکس معتبر v3 است. اکثر تنظیمات کوچک Compose دقیقاً از همین روش استفاده میکنند، به این معنی که اکثر labelها بدون نیاز به ویرایش قوانین، مهاجرت میکنند.
پیش از شروع، برچسبهای خود را بررسی کنید
شما میتوانید حجم مهاجرت خود را با یک جستجو اندازهگیری کنید؛ زیرا هر تغییر در برچسبهای ساختاری، الگویی ایجاد میکند که دستور grep میتواند آن را پیدا کند:
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.ymlهر مورد یافت شده، یک خط برای ویرایش است. ipwhitelist به ipallowlist تبدیل میشود. HostHeader به Host تبدیل میشود. Headers به Header تبدیل میشود. یک جایگزین {...} در داخل PathPrefix به یک تطبیقدهنده PathRegexp تبدیل میشود. یک کاما در داخل Host() به دو تطبیقدهنده Host() که با || به هم متصل شدهاند، تبدیل میشود. صفر مورد یافت شده به این معناست که برچسبهای شما از قبل دارای سینتکس معتبر v3 هستند و حجم مهاجرت تنها شامل پیکربندی استاتیک به علاوه تگ تصویر خواهد بود.
موارد بدون تغییر
Entrypointها و قابلیت redirect از HTTP به HTTPS، ACME resolverها با هر دو نوع challenge، exposedByDefault، برچسبهای router و service، 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 commit کنید. با این کار، تمام مراحل بعدی با یک دستور checkout قابل بازگشت خواهند بود. اگر بازسازی یک سرویس واحد با استفاده از docker compose up -d <service> برای شما ساده نیست، راهنمای اصول اولیه Docker Compose عملیاتی را که این مهاجرت به آنها متکی است، پوشش میدهد.
مرحله 2: پیکربندی استاتیک را پاکسازی و حالت سازگاری (compatibility mode) را فعال کنید. تمام گزینههای حذف شده در v3 شامل (pilot, swarmMode, tls.caOptional, experimental.http3) را حذف کنید، سپس به v3 دستور دهید که قوانین را به صورت پیشفرض با سینتکس v2 در نظر بگیرد. در traefik.yml:
core:
defaultRuleSyntax: v2یا به عنوان یک flag در لیست compose command:: --core.defaultRuleSyntax=v2. حالت سازگاری فقط سینتکس قوانین را پوشش میدهد. این حالت گزینههای حذف شده را احیا نمیکند و نام middlewares را برای شما تغییر نمیدهد.
مرحله 3: تغییر نام middlewares را آماده کنید. فایلهای compose خود را برای یافتن نامهای قدیمی جستجو کنید: grep -rn ipwhitelist docker-compose*.yml. هر لیبل ipwhitelist را به ipallowlist تغییر دهید، اما هنوز تغییر را اعمال نکنید؛ زیرا نام جدید در v2 وجود ندارد. این ویرایشها همراه با تغییر مرحله بعد اعمال میشوند. (اگر موردی از قلم افتاد، نسخه فعلی v3 همچنان نام قدیمی را به عنوان یک alias منسوخ شده میپذیرد، بنابراین لیست همچنان اعمال میشود؛ به جای اصلاح در ساعت 2 صبح، آن را در مرحله بعد اصلاح کنید.)
مرحله 4: تگ ایمیج را تغییر دهید. ایمیج Traefik را روی نسخه فعلی v3، یعنی traefik:v3.5 در زمان نگارش این متن، تنظیم کنید، سپس:
docker compose up -d
docker compose logs -f traefikاز آنجایی که حالت سازگاری فعال است، قوانین v2 شما همچنان مطابقت دارند و چون up -d نیز سرویسهایی را که لیبلهای middleware آنها را تغییر نام داده بودید، بازسازی کرد، آن روترها بدون مشکل بالا میآیند. یک لاگ سالم نباید شامل خط field not found یا خط does not exist باشد.
درباره بازه زمانی این مرحله با خود صادق باشید. روترهایی که به نام middlewareای اشاره میکنند که v3 واقعاً آن را نمیشناسد (یک غلط تایپی یا یک گزینه حذف شده)، از لحظه شروع Traefik جدید تا زمان بازسازی کانتینر اپلیکیشن آن از دسترس خارج میشوند؛ که در یک سیستم، این زمان معادل چند ثانیه است که docker compose up -d نیاز دارد لیست را بررسی کند. اگر یک مسیر (route) واقعاً نمیتواند حتی برای لحظهای قطع شود، قبل از تغییر، middleware تغییر نام یافته را از لیبل middlewares آن روتر حذف کنید و بعد از تغییر، دوباره آن را اضافه کنید؛ همچنین از قبل تصمیم بگیرید که آیا آن مسیر میتواند برای آن یک دقیقه بدون لیست مجاز IP (IP allow list) خود کار کند یا خیر.
مرحله 5: قوانین را سرویس به سرویس مهاجرت دهید. هر بار فقط روی یک اپلیکیشن کار کنید: قانون آن را به سینتکس v3 بازنویسی کنید، فقط همان سرویس را با استفاده از docker compose up -d app بازسازی کنید و قبل از رفتن به مرحله بعد، آن را تست کنید. اگر یک سرویس قانونی دارد که هنوز نمیتوانید آن را بازنویسی کنید، به آن روتر واحد لیبل escape hatch یعنی traefik.http.routers.app.ruleSyntax=v2 را بدهید و به کار خود ادامه دهید.
مرحله 6: حالت سازگاری را خاموش کنید. وقتی تمام قوانین با سینتکس v3 هستند، defaultRuleSyntax و هر لیبل ruleSyntax را حذف کنید، Traefik را ریاستارت کنید و تایید کنید که تمام روترها در داشبورد همچنان وضعیت سبز دارند. با روشن ماندن حالت سازگاری متوقف نشوید: Traefik هر دو گزینه را در نسخه v3.4 منسوخ کرده است و در نسخه major بعدی آنها را حذف میکند، بنابراین این حالت یک پل است، نه مقصد نهایی.
قبل و بعد: لیبلهای یک سرویس
در اینجا یک اپلیکیشن وجود دارد که تمام تغییرات معروف را همزمان اعمال کرده است: یک 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دو لیبل تغییر کردهاند. قانون (rule)، مقدار چندگانه Host خود را به دو matcher تقسیم کرد که با || به هم متصل شدهاند؛ همچنین جایگزین (placeholder) را با PathRegexp عوض کرد و لیبل middleware نیز ipwhitelist را با ipallowlist جایگزین نمود. entrypoint، certificate resolver، اتصال router-to-middleware و پورت سرویس تغییری نکردند.
هر سرویس را با استفاده از داشبورد تست کنید
پس از هر تغییر (flip)، صفحه HTTP routers در داشبورد را باز کنید. تمام routerها باید سبز باشند. اگر routerی دارای نشانگر خطا (error badge) باشد، علت دقیق مشکل را ذکر میکند؛ این مشکل معمولاً مربوط به middleware ای است که با نام جدید وجود ندارد، یا قانونی است که v3 قادر به تجزیه (parse) آن نیست. سپس، هر یک از hostnameها را به صورت جداگانه از بیرون بررسی کنید:
curl -sI https://app.example.com/api/v1/statusیک 200 یا redirect معمولی اپلیکیشن شما به این معناست که routing و TLS هر دو بدون مشکل کار میکنند. یک 404 از سمت Traefik به این معناست که router بالا نیامده است؛ به داشبورد برگردید و متن خطا را بخوانید. هنگام کار، یک ترمینال دیگر را برای docker compose logs -f traefik باز نگه دارید، زیرا هر خطای تجزیه (parsing failure) بلافاصله پس از ریاستارت شدن یک container در آنجا ثبت میشود.
Rollback honesty
فایل compose نسخه v2، پیکربندی static آن و بکآپ acme.json را تا زمانی که تمام سرویسها روی v3 مسیردهی شده و در شرایط واقعی تست شدهاند، نگه دارید. بازگشت به حالت قبل (Rollback) به معنای checkout کردن commit مربوط به پیش از مهاجرت و اجرای docker compose up -d است. این کار باید روی کل فایل انجام شود، نه فقط روی image tag؛ زیرا برچسبهای (labels) مخصوص v3 در نسخه v2 دقیقاً به همان شکلی که برچسبهای v2 در v3 اشتباه بودند، عمل نمیکنند: ipallowlist در v2 وجود ندارد و یک matcher از نوع PathRegexp نیز در آنجا تجزیه (parse) نمیشود. اگر acme.json در این مسیر از دست رفت یا آسیب دید، پیش از شروع v2 نسخه بکآپ را بازیابی کنید؛ تا فرآیند rollback باعث مصرف شدن نرخ محدودیت (rate limit) Let's Encrypt برای صدور مجدد همزمان 5 گواهینامه نشود.
FAQ
آیا باید تمام قوانین router را برای Traefik v3 بازنویسی کنم؟
خیر. یک قانون ساده Host(app.example.com) که با backticks نوشته شده باشد، در هر دو نسخه معتبر است و اکثر تنظیمات Compose را پوشش میدهد. بازنویسی تنها زمانی لازم است که یک قانون از ویژگیهای مخصوص v2 استفاده کرده باشد: regex یا placeholderها در داخل Path و PathPrefix، چندین hostname در یک Host()، استفاده از quotes به جای backticks، یا 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 منسوخ شده (deprecated) میپذیرند، بنابراین یک label تغییرنیافته همچنان لیست مجاز را اعمال میکند. این وضعیت را به عنوان فرصتی موقت در نظر بگیرید تا دلیلی برای نادیده گرفتن تغییر نام: حذف این alias برنامهریزی شده است و اگر از نام middleware که Traefik نمیشناسد استفاده کنید، با خطای router و کد 404 مواجه خواهید شد. داشبورد خطا را نشان میدهد و درخواستها به آن hostname با 404 پاسخ داده میشوند.
آیا Traefik v3 همچنان میتواند syntax قوانین v2 را بخواند؟
بله. برای حفظ syntax نسخه v2 به عنوان حالت پیشفرض در حین مهاجرت، core.defaultRuleSyntax: v2 را در static configuration تنظیم کنید و پس از بازگرداندن حالت پیشفرض، از label ruleSyntax=v2 برای هر router به صورت جداگانه استفاده کنید. هر دو حالت را موقتی در نظر بگیرید: Traefik آنها را در v3.4 منسوخ کرد و در نسخه major بعدی آنها را حذف خواهد کرد.
آیا گواهینامههای Let's Encrypt من پس از ارتقا باقی میمانند؟
بله. Traefik v3 همچنان فایل acme.json را که توسط v2 نوشته شده است میخواند، بنابراین گواهینامهها صرفاً به دلیل تغییر نسخه binary دوباره صادر نمیشوند. با این حال، قبل از شروع، فایل را در جایی امن کپی کنید؛ زیرا بازگشت به نسخه قبل (rollback) یا حذف volume که منجر به از دست رفتن acme.json شود، باعث میشود تمام گواهینامهها همزمان دوباره صادر شوند و Let's Encrypt تنها اجازه صدور 5 گواهینامه تکراری در هفته را برای یک مجموعه از hostnameها میدهد.
چرا Traefik v3 پس از ارتقا در اجرا شکست میخورد؟
تقریباً همیشه به این دلیل است که static configuration همچنان شامل گزینهای است که در v3 حذف شده است و Traefik از اجرا با گزینههای ناشناخته خودداری میکند. برای موارد شناخته شده باقیمانده (pilot، providers.docker.swarmMode، experimental.http3)، لاگ عبارت incompatible deprecated static option found را نمایش داده و عامل خطا را نام میبرد؛ برای هر موردی که v3 هرگز نشنیده است، مانند tls.caOptional، عبارت field not found را همراه با نام node نمایش میدهد. هر کدام را حذف یا جایگزین کنید، سپس container را دوباره اجرا کنید.