Миграция Traefik v2 на v3: основные ошибки и изменения
При обновлении Traefik до v3 сервер не запускается из-за параметров swarmMode и pilot в static config. Устраните ошибку incompatible deprecated static option и обновите правила.
Что изменилось между Traefik v2 и v3
Миграция с Traefik v2 на v3 в основном сводится к переименованию параметров. Самое известное изменение — middleware ipWhiteList теперь называется ipAllowList. Помимо этого, в v3 ужесточен синтаксис правил маршрутизации (PathPrefix больше не поддерживает регулярные выражения, некоторые матчеры были переименованы или удалены), полностью исключены некоторые провайдеры и опции. Остальные компоненты продолжают работать: entrypoints, настройка сертификатов ACME, рабочий процесс с Docker labels и ваш acme.json переносятся без изменений. В v3 также предусмотрен режим совместимости, который позволяет использовать синтаксис правил v2. Это дает возможность сначала обновить бинарный файл, а затем переписывать правила для каждого сервиса по очереди, вместо того чтобы выполнять рискованное обновление за один вечер.
В этом руководстве предполагается использование Docker Compose с метками (labels), как описано в руководстве по reverse proxy Traefik. Эта страница уже адаптирована под v3; данное руководство предназначено для серверов, где всё ещё используется тег traefik:v2.
Переименования и удаления
ipWhiteListтеперь называетсяipAllowListкак для HTTP, так и для TCP middleware. Опции внутри остались прежними, поэтомуsourcerangeсохраняет свой точный смысл. Текущие релизы v3, включая v3.5, всё ещё принимают старое имя как устаревший псевдоним и продолжают применять список, поэтому это переименование не приводит к сбоям. Тем не менее, выполните переименование: псевдоним запланирован к удалению и исчезнет из списка устаревших функций без предупреждения.providers.docker.swarmMode=trueудалён. У Swarm появился собственный провайдер, который настраивается какproviders.swarm.endpoint.- Секция
pilotполностью удалена. experimental.http3удалён. HTTP/3 теперь включается непосредственно в entrypoint.tls.caOptionalудалён из провайдеров и из middleware forwardAuth. Если это middleware используется перед самостоятельно развёрнутым Authentik SSO, то удаление строкиcaOptional— это вся необходимая миграция, так как адрес forwardAuth, доверенные заголовки и outpost за ними работают в v3 точно так же.- Провайдер метрик InfluxDB v1, провайдер Rancher и провайдер Marathon удалены.
- Трассировка переведена на OpenTelemetry. Специализированные бэкенды трассировки, включая интеграции с Jaeger и Zipkin, удалены; теперь v3 экспортирует данные через OTLP (протокол OpenTelemetry).
- Устаревшие опции
ssl*внутри middleware headers (sslRedirect,sslHostи остальные) удалены. Их заменили перенаправления на уровне entrypoint и middleware redirectScheme.
Эти удаления важнее, чем кажется, так как Traefik отказывается запускаться, если в его статической конфигурации есть неизвестная опция. Оставшаяся строка pilot или swarmMode остановит контейнер при загрузке с сообщением incompatible deprecated static option found, в котором будет указана эта опция; опция, о которой Traefik вообще ничего не знает (опечатка или tls.caOptional), остановит его с сообщением field not found. Очистите статическую конфигурацию перед тем, как менять тег образа.
Имя middleware, которое Traefik действительно не знает (опечатка или имя, которое было удалено, а не заменено псевдонимом), вызывает другую ошибку: роутер, ссылающийся на него, загружается с ошибкой вместо маршрута, панель управления помечает его, а API сообщает middleware "offce@docker" does not exist. Запросы к этому хосту получают 404, так как роутер не был запущен. Обратите внимание, что ipwhitelist НЕ относится к этой категории в текущей версии v3: он сохраняется как устаревший псевдоним, поэтому непереименованная метка продолжает работать в штатном режиме.
Изменения в синтаксисе правил
Правила — это место, где происходит реальное переписывание запросов. Изменения в версии v3:
- Значения внутри матчеров теперь обязательно заключаются в обратные кавычки. Версия 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), написанное с использованием обратных кавычек, уже является корректным синтаксисом v3. Большинство небольших конфигураций Compose используют именно такой формат, а значит, большинство меток переносятся без правок правил.
Проверка меток перед началом работы
Вы можете оценить масштаб миграции с помощью одного поиска, так как каждое критическое изменение меток оставляет шаблон, который можно найти через grep:
grep -rnE 'ipwhitelist|HostHeader|Headers\(|PathPrefix\(`[^`]*\{|Host\(`[^`]*`,' docker-compose*.ymlКаждое совпадение — это строка, которую нужно отредактировать. ipwhitelist превращается в ipallowlist. HostHeader превращается в Host. Headers превращается в Header. Заполнитель {...} внутри PathPrefix становится сопоставителем PathRegexp. Запятая внутри Host() превращается в два сопоставителя Host(), объединенных через ||. Отсутствие совпадений означает, что ваши метки уже соответствуют синтаксису v3, и миграция сводится к статической конфигурации и тегу образа. Если экран заполнен результатами поиска, самое время задаться вопросом, является ли этот прокси по-прежнему оптимальным выбором для вашего сервера, а сравнение Traefik с Nginx и Caddy поможет сопоставить затраты на переписывание конфигурации с требованиями других решений для каждого приложения.
Что остаётся неизменным
Точки входа (entrypoints) и их перенаправление с HTTP на HTTPS, ACME-резолверы с обоими типами проверок (challenge), exposedByDefault, метки маршрутизатора и сервисов, loadbalancer.server.port, а также панель управления (dashboard) работают в 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Или в качестве флага в списке command: для compose: --core.defaultRuleSyntax=v2. Режим совместимости затрагивает только синтаксис правил. Он не возвращает удаленные параметры и не переименовывает middleware автоматически.
Шаг 3: подготовьте переименование middleware. Выполните поиск старых имен в файлах compose: grep -rn ipwhitelist docker-compose*.yml. Измените каждую метку ipwhitelist на ipallowlist, но пока не применяйте изменения, так как новое имя в v2 отсутствует. Эти правки должны быть применены одновременно с переключением на следующем шаге. (Если что-то пропустите, текущая версия v3 все еще распознает старое имя как устаревший псевдоним, поэтому список будет работать; исправьте это в следующий проход, а не в 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 обрабатывает список. Если маршрут не должен прерываться ни на секунду, удалите переименованный middleware из метки middlewares этого маршрутизатора до переключения и добавьте его обратно после. Заранее решите, может ли этот маршрут прожить без IP allow list в течение минуты перехода.
Шаг 5: мигрируйте правила сервис за сервисом. Работайте с каждым приложением по очереди: перепишите его правило на синтаксис v3, пересоздайте только этот сервис с помощью docker compose up -d app и протестируйте его перед переходом к следующему. Если для какого-то сервиса правило пока нельзя переписать, добавьте этому конкретному маршрутизатору метку-исключение traefik.http.routers.app.ruleSyntax=v2 и продолжайте работу.
Шаг 6: отключите режим совместимости. Когда все правила будут переведены на синтаксис v3, удалите defaultRuleSyntax и любые метки ruleSyntax, перезапустите Traefik и убедитесь, что все маршрутизаторы отображаются зеленым в панели управления. Не задерживайтесь в режиме совместимости: Traefik пометил обе опции как устаревшие в v3.4 и удалит их в следующей мажорной версии, поэтому это временное решение, а не конечный вариант.
До и после: метки одного сервиса
Ниже представлено приложение, в котором применены все основные изменения: многозначный Host, заполнитель 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 на два условия сопоставления, объединенных через ||, заменило заполнитель на PathRegexp, а в метке middleware значение ipwhitelist было заменено на ipallowlist. Точка входа (entrypoint), резолвер сертификатов, связка роутера с middleware и порт сервиса остались без изменений.
Проверка каждого сервиса через панель управления
После каждого изменения открывайте страницу HTTP routers в панели управления. Все маршрутизаторы должны быть помечены зелёным цветом. Маршрутизатор с индикатором ошибки указывает на конкретную проблему: обычно это middleware, которое отсутствует под новым именем, или правило, которое v3 не может распознать. Затем проверьте работу снаружи, по одному доменному имени за раз:
curl -sI https://app.example.com/api/v1/statusКод 200 или стандартный редирект вашего приложения означают, что маршрутизация и TLS работают корректно. Код 404 от Traefik означает, что маршрутизатор не запустился; вернитесь в панель управления и ознакомьтесь с текстом ошибки. Держите docker compose logs -f traefik открытым во втором терминале во время работы, так как любая ошибка парсинга попадает туда в момент перезапуска контейнера.
Надежность отката
Сохраняйте файл compose версии v2, его статическую конфигурацию и резервную копию acme.json до тех пор, пока каждый сервис не начнет маршрутизироваться в v3 и не пройдет проверку в реальных условиях. Откат означает возврат к коммиту до миграции и запуск docker compose up -d. Это должен быть весь файл целиком, а не только тег образа, так как метки, предназначенные только для v3, некорректны в v2 точно так же, как метки v2 были некорректны в v3: ipallowlist не существует в v2, а сопоставитель PathRegexp также не будет там распознан. Если acme.json был потерян или поврежден в процессе, восстановите резервную копию перед запуском v2, чтобы при откате не исчерпать лимит запросов Let's Encrypt на повторный выпуск пяти сертификатов одновременно.
FAQ
Нужно ли переписывать все правила маршрутизации для Traefik v3?
Нет. Обычное правило Host(app.example.com), записанное с использованием обратных кавычек, корректно работает в обеих версиях, что покрывает большинство конфигураций Compose. Переписывание требуется только в том случае, если в правиле использовались функции, доступные только в v2: регулярные выражения или плейсхолдеры внутри Path и PathPrefix, несколько имен хостов внутри одного Host(), обычные кавычки вместо обратных, либо удаленные матчеры 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 для отдельных маршрутов уже после того, как вернете настройки по умолчанию. Рассматривайте оба варианта как временные: Traefik пометил их как устаревшие в v3.4 и удалит в следующей мажорной версии.
Сохранятся ли мои сертификаты Let's Encrypt после обновления?
Да. Traefik v3 продолжает считывать файл acme.json, созданный в v2, поэтому сертификаты не перевыпускаются только из-за смены бинарного файла. В любом случае перед началом работ скопируйте файл в безопасное место, так как откат или удаление тома, в котором хранится 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 с указанием узла. Удалите или замените каждый из них, затем снова запустите контейнер.