Nâng cấp Traefik v2 lên v3: những lỗi cần lưu ý
Traefik v3 sẽ báo lỗi nếu bạn vẫn để swarmMode hoặc pilot trong static config. Hãy kiểm tra lại các router rule và middleware ipAllowList khi nâng cấp.
Thay đổi giữa Traefik v2 và v3
Việc nâng cấp từ Traefik v2 lên v3 chủ yếu là thay đổi tên gọi, tiêu biểu là middleware ipWhiteList được đổi thành ipAllowList. Ngoài ra, v3 siết chặt cú pháp router rule (PathPrefix mất các tính năng regex, một số matcher bị đổi tên hoặc bị loại bỏ), loại bỏ hoàn toàn một số provider và option, nhưng vẫn giữ nguyên các tính năng khác: entrypoints, thiết lập chứng chỉ ACME, workflow Docker labels và acme.json của bạn đều được giữ nguyên. v3 cũng đi kèm chế độ tương thích để giữ cho cú pháp rule của v2 vẫn hoạt động, giúp bạn có thể nâng cấp binary trước rồi viết lại rule cho từng service một, thay vì phải làm tất cả trong một đêm đầy rủi ro.
Hướng dẫn này giả định bạn đang dùng thiết lập Docker Compose dựa trên label từ hướng dẫn Traefik reverse proxy. Trang đó dành cho v3; còn trang này dành cho các hệ thống vẫn đang chạy tag traefik:v2.
Các thay đổi về đổi tên và loại bỏ
ipWhiteListhiện đã đổi thànhipAllowListcho cả HTTP và TCP middleware. Các tùy chọn bên trong không đổi, nênsourcerangevẫn giữ nguyên ý nghĩa cũ. Các bản phát hành v3 hiện tại, bao gồm cả v3.5, vẫn chấp nhận tên cũ dưới dạng alias đã bị deprecated và vẫn áp dụng danh sách này, nên việc đổi tên này không làm gián đoạn hệ thống. Tuy nhiên, bạn vẫn nên đổi tên: alias này đã được lên lịch để loại bỏ, và nó sẽ biến mất khỏi danh sách deprecation một cách âm thầm chứ không thông báo lỗi.providers.docker.swarmMode=trueđã bị loại bỏ. Swarm hiện có provider riêng, được cấu hình dưới dạngproviders.swarm.endpoint.- Phần
pilotđã bị loại bỏ hoàn toàn. experimental.http3đã bị loại bỏ. HTTP/3 hiện được bật trực tiếp trên entrypoint.tls.caOptionalđã bị loại bỏ khỏi các provider và khỏi forwardAuth middleware.- Các provider metrics InfluxDB v1, Rancher, và Marathon đã bị loại bỏ.
- Tracing đã chuyển sang OpenTelemetry. Các backend tracing chuyên dụng, bao gồm cả tích hợp Jaeger và Zipkin, đã bị loại bỏ; thay vào đó, v3 sẽ export OTLP (giao thức OpenTelemetry).
- Các tùy chọn
ssl*đã bị deprecated bên trong headers middleware (sslRedirect,sslHost, và các tùy chọn khác) đã bị loại bỏ. Entrypoint redirections và redirectScheme middleware đã thay thế chúng.
Những thay đổi này quan trọng hơn vẻ ngoài của chúng, vì Traefik sẽ từ chối khởi động nếu static configuration chứa một tùy chọn mà nó không biết. Một dòng pilot hoặc swarmMode còn sót lại sẽ khiến container dừng ngay khi boot với thông báo lỗi incompatible deprecated static option found chỉ đích danh tùy chọn đó; một tùy chọn mà Traefik chưa từng biết tới (do gõ sai, hoặc do tls.caOptional) sẽ khiến nó dừng với lỗi field not found. Hãy dọn dẹp static configuration trước khi bạn thay đổi image tag.
Một middleware name mà Traefik thực sự không biết (do gõ sai, hoặc là một tên đã bị loại bỏ thay vì đặt alias) sẽ gây lỗi theo cách khác: router tham chiếu đến nó sẽ load kèm theo lỗi thay vì tạo route, dashboard sẽ đánh dấu nó, và API sẽ báo lỗi middleware "offce@docker" does not exist. Các request gửi đến hostname đó sẽ nhận lỗi 404 vì router không thể khởi động. Lưu ý rằng ipwhitelist KHÔNG thuộc nhóm này trên bản v3 hiện tại: nó vẫn tồn tại dưới dạng alias đã bị deprecated, nên một label chưa được đổi tên vẫn sẽ hoạt động bình thường.
Cú pháp rule có sự thay đổi
Rule là nơi diễn ra quá trình rewrite thực sự. Các thay đổi trong v3:
- Bắt buộc phải dùng dấu backtick bao quanh các giá trị bên trong matcher. v2 chấp nhận cả dấu ngoặc kép; v3 thì không, vì vậy Host("app.example.com") phải chuyển thành Host(
app.example.com). PathPrefixkhông còn hiểu regular expression hoặc các placeholder kiểu{id}. Một rule ở v2 như PathPrefix(/api/{version:v[0-9]+}) phải chuyển thành một matcherPathRegexpviết theo cú pháp regular expression của Go.- Các matcher hiện chỉ nhận một giá trị duy nhất. v2 cho phép Host(
app.example.com,www.example.com); v3 yêu cầu Host(app.example.com) || Host(www.example.com). Các ngoại lệ làHeader,HeaderRegexp,Query, vàQueryRegexp, những matcher này vẫn nhận một tên kèm theo một giá trị. HeadersvàHeadersRegexpđược đổi tên thànhHeadervàHeaderRegexp.HostHeaderbị loại bỏ. Hãy dùngHost, matcher này khớp với cùng một đối tượng trong v3.- Hai matcher mới là:
QueryRegexp, vàClientIPđể khớp địa chỉ client bên trong một rule.
Tin tốt là: một rule Host(app.example.com) viết bình thường với dấu backtick đã là cú pháp v3 hợp lệ. Hầu hết các thiết lập Compose nhỏ đều dùng chính xác cách đó, nghĩa là phần lớn các label sẽ được migrate mà không cần chỉnh sửa rule.
Kiểm tra các label trước khi bắt đầu
Bạn có thể ước tính quy mô di chuyển chỉ bằng một lệnh tìm kiếm, vì mọi thay đổi label gây lỗi đều để lại pattern mà grep có thể tìm thấy:
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.ymlMỗi kết quả trả về tương ứng với một dòng cần chỉnh sửa. ipwhitelist sẽ trở thành ipallowlist. HostHeader sẽ trở thành Host. Headers sẽ trở thành Header. Một placeholder {...} bên trong PathPrefix sẽ trở thành một matcher PathRegexp. Một dấu phẩy bên trong Host() sẽ trở thành hai matcher Host() được nối với nhau bằng ||. Nếu không có kết quả nào, nghĩa là các label của bạn đã đúng cú pháp v3, và việc di chuyển sẽ chỉ bao gồm cấu hình tĩnh cộng với image tag.
Những thành phần không thay đổi
Các entrypoint và cơ chế redirect HTTP-to-HTTPS, các ACME resolver với cả hai loại challenge, exposedByDefault, các router và service labels, loadbalancer.server.port, và dashboard đều hoạt động trong v3 giống như v2. Các certificate của bạn cũng được giữ nguyên, vì v3 vẫn đọc từ acme.json mà v2 đã ghi. Tuy nhiên, bạn vẫn nên backup file này trước khi bắt đầu, vì nếu quá trình rollback làm mất file này, bạn sẽ gặp ngay lỗi rate limit duplicate-certificate của Let's Encrypt:
cp ./letsencrypt/acme.json ./letsencrypt/acme.json.v2-backupLộ trình migration
Bước 1: chốt phiên bản đang dùng. Thay đổi bất kỳ tag traefik:latest hoặc traefik:v2 nào thành phiên bản chính xác bạn đang dùng, ví dụ traefik:v2.11, và commit toàn bộ thư mục compose vào git. Mọi bước sau đó đều có thể hoàn tác bằng lệnh checkout. Nếu bạn chưa quen với việc recreate một service đơn lẻ bằng docker compose up -d <service>, hãy xem hướng dẫn cơ bản về Docker Compose để nắm các thao tác cần thiết cho quá trình migration này.
Bước 2: dọn dẹp cấu hình static và bật chế độ compatibility. Xóa mọi option mà v3 đã loại bỏ (pilot, swarmMode, tls.caOptional, experimental.http3), sau đó yêu cầu v3 xử lý các rule theo cú pháp v2 theo mặc định. Trong traefik.yml:
core:
defaultRuleSyntax: v2Hoặc dùng như một flag trong danh sách compose command:: --core.defaultRuleSyntax=v2. Chế độ compatibility chỉ áp dụng cho cú pháp của rule. Nó không khôi phục các option đã bị xóa, và không tự động đổi tên các middleware cho bạn.
Bước 3: chuẩn bị đổi tên middleware. Tìm kiếm trong các file compose các tên cũ: grep -rn ipwhitelist docker-compose*.yml. Sửa mọi label ipwhitelist thành ipallowlist, nhưng đừng áp dụng thay đổi ngay, vì tên mới không tồn tại trong v2. Các thay đổi này sẽ được áp dụng cùng lúc với bước tiếp theo. (Nếu bạn lỡ sót một cái, v3 hiện tại vẫn chấp nhận tên cũ dưới dạng alias đã bị deprecated, nên danh sách vẫn sẽ hoạt động; hãy sửa nó ở lượt sau thay vì thức đến 2 giờ sáng.)
Bước 4: đổi image tag. Thiết lập Traefik image thành phiên bản v3 hiện tại, traefik:v3.5 tại thời điểm viết bài, sau đó:
docker compose up -d
docker compose logs -f traefikVì chế độ compatibility đang bật, các rule v2 của bạn vẫn sẽ khớp, và vì up -d cũng đã recreate các service mà bạn đã đổi tên middleware label, các router đó sẽ khởi động bình thường. Một log chuẩn sẽ không có dòng field not found và không có dòng does not exist.
Hãy cân nhắc kỹ về khoảng thời gian gián đoạn mà bước này gây ra. Một router tham chiếu đến một tên middleware mà v3 không biết (do viết sai hoặc do option đã bị xóa) sẽ bị downtime từ lúc Traefik mới khởi động cho đến khi container của app được recreate; trên một máy đơn lẻ, việc này mất vài giây docker compose up -d để xử lý xong danh sách. Nếu một route thực sự không thể bị ngắt kết nối, hãy xóa middleware đã đổi tên khỏi label middlewares của router đó trước khi flip, sau đó add lại sau, và hãy quyết định trước xem route đó có thể chạy mà không có IP allow list trong phút tạm thời đó hay không.
Bước 5: migrate rule theo từng service. Thực hiện lần lượt từng app một: viết lại rule của nó sang cú pháp v3, chỉ recreate duy nhất service đó bằng docker compose up -d app, và kiểm tra trước khi tiếp tục. Nếu một service có rule mà bạn chưa thể viết lại ngay, hãy gán cho router đó label thoát hiểm traefik.http.routers.app.ruleSyntax=v2 và tiếp tục.
Bước 6: tắt chế độ compatibility. Khi mọi rule đã sang cú pháp v3, hãy xóa defaultRuleSyntax và bất kỳ label ruleSyntax nào, restart Traefik, và xác nhận mọi router vẫn hiển thị màu xanh trên dashboard. Đừng dùng chế độ compatibility lâu dài: Traefik đã deprecated cả hai option này trong v3.4 và sẽ xóa chúng trong phiên bản major tiếp theo, vì vậy chúng chỉ là cầu nối, không phải là đích đến cuối cùng.
Trước và sau: các label của một service
Đây là một app chứa tất cả các thay đổi quan trọng cùng một lúc: một Host đa giá trị, một placeholder PathPrefix, và một middleware ipWhiteList. Khối 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=8080Và chính service đó khi đã migrate sang 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=8080Hai label đã thay đổi. Rule đã tách Host đa giá trị thành hai matcher nối với nhau bằng ||, thay thế placeholder bằng PathRegexp, và label middleware đã đổi ipwhitelist thành ipallowlist. Entrypoint, certificate resolver, kết nối router-to-middleware, và service port không thay đổi.
Kiểm tra từng service bằng dashboard
Sau mỗi lần flip, hãy mở trang HTTP routers của dashboard. Mọi router đều phải có màu xanh. Nếu một router có badge lỗi, nó sẽ hiển thị chính xác vấn đề, thường là do một middleware không tồn tại dưới tên mới hoặc một rule mà v3 không thể parse. Sau đó, hãy xác nhận từ bên ngoài, thực hiện lần lượt từng hostname một:
curl -sI https://app.example.com/api/v1/statusMột 200 hoặc lệnh redirect bình thường của app có nghĩa là cả routing và TLS đều hoạt động tốt. Một lỗi 404 từ Traefik có nghĩa là router không khởi động được; hãy quay lại dashboard để xem lỗi cụ thể. Hãy giữ docker compose logs -f traefik mở ở một terminal thứ hai trong khi làm việc, vì mọi lỗi parsing sẽ xuất hiện ở đó ngay khi một container restart.
Tính trung thực khi rollback
Hãy giữ lại compose file v2, các cấu hình static và bản backup acme.json cho đến khi mọi service đều đã chạy trên v3 và được kiểm tra thực tế. Rollback nghĩa là thực hiện checkout commit trước khi migration và chạy docker compose up -d. Bạn phải chạy toàn bộ file chứ không chỉ thay đổi image tag, vì các label chỉ dành cho v3 sẽ bị lỗi khi chạy trên v2, tương tự như cách các label của v2 bị lỗi trên v3: ipallowlist không tồn tại trong v2, và một matcher PathRegexp cũng sẽ không parse được ở đó. Nếu acme.json bị mất hoặc lỗi trong quá trình thực hiện, hãy khôi phục bản backup trước khi khởi chạy v2, để việc rollback không làm cạn kiệt Let's Encrypt rate limit do phải cấp lại năm certificate cùng một lúc.
FAQ
Tôi có phải viết lại mọi router rule cho Traefik v3 không?
Không. Một rule Host(app.example.com) viết bằng dấu backtick vẫn hợp lệ ở cả hai phiên bản, và điều này bao quát hầu hết các thiết lập Compose. Bạn chỉ cần viết lại khi rule đó sử dụng các tính năng chỉ có ở v2: regex hoặc placeholder bên trong Path và PathPrefix, nhiều hostname trong cùng một Host(), sử dụng dấu ngoặc kép thay vì backtick, hoặc các matcher Headers, HeadersRegexp, và HostHeader đã bị loại bỏ.
ipWhiteList trong Traefik v3 đã thay đổi thế nào?
Nó đã được đổi tên thành ipAllowList, nhưng cấu hình bên trong không đổi, nên một label v2 như traefik.http.middlewares.office.ipwhitelist.sourcerange=10.0.0.0/24 sẽ trở thành cùng một dòng với ipallowlist bên trong. Các bản phát hành v3 hiện tại, bao gồm cả v3.5, vẫn chấp nhận tên cũ dưới dạng alias đã bị deprecated, nên một label chưa đổi tên vẫn sẽ thực thi allowlist một cách âm thầm. Hãy coi đó là giải pháp tạm thời thay vì lý do để bỏ qua việc đổi tên: alias này đã có lịch trình bị xóa bỏ, và nếu dùng một middleware name mà Traefik không nhận diện được, nó sẽ báo lỗi ngay lập tức với lỗi router và mã 404. Dashboard sẽ hiển thị lỗi, và các request đến hostname đó sẽ trả về 404.
Traefik v3 có còn đọc được syntax rule của v2 không?
Có. Hãy thiết lập core.defaultRuleSyntax: v2 trong static configuration để giữ syntax v2 làm mặc định trong quá trình bạn migrate, và sử dụng label ruleSyntax=v2 cho từng router riêng lẻ sau khi bạn đã chuyển default trở lại. Hãy coi cả hai là tạm thời: Traefik đã deprecated chúng trong v3.4 và sẽ loại bỏ chúng trong phiên bản major tiếp theo.
Các chứng chỉ Let's Encrypt của tôi có còn dùng được sau khi upgrade không?
Có. Traefik v3 vẫn đọc file acme.json mà v2 đã ghi, nên các chứng chỉ không bị cấp lại chỉ vì binary thay đổi. Tuy nhiên, hãy copy file đó ra nơi an toàn trước khi bắt đầu, vì nếu rollback hoặc bị mất volume chứa acme.json sẽ buộc phải cấp lại toàn bộ chứng chỉ cùng một lúc, và Let's Encrypt chỉ cho phép tối đa năm chứng chỉ trùng lặp mỗi tuần cho cùng một tập hợp hostname.
Tại sao Traefik v3 không khởi động được sau khi upgrade?
Hầu hết luôn là do static configuration vẫn còn chứa một option mà v3 đã loại bỏ, và Traefik sẽ từ chối khởi động nếu gặp các option mà nó không nhận diện được. Đối với các thành phần cũ phổ biến (pilot, providers.docker.swarmMode, experimental.http3), log sẽ báo incompatible deprecated static option found và chỉ rõ nguyên nhân; đối với bất kỳ thứ gì v3 chưa từng biết đến, chẳng hạn như tls.caOptional, nó sẽ báo field not found kèm theo node. Hãy xóa hoặc thay thế từng cái một, sau đó khởi động lại container.