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