从 Ingress NGINX Controller 迁移到 Traefik:零停机迁移完整实战指南
本文基于 Traefik 官方迁移文档《Migrate from Ingress NGINX Controller to Traefik》整理,面向需要在 Kubernetes 集群中完成入口控制器替换的运维与平台工程师。由于 Kubernetes Ingress NGINX Controller 项目已宣布于 2026 年 3 月退役(此后不再有版本更新、安全补丁和缺陷修复),这篇指南会带你走完迁移的全过程:将 ingress-nginx 的集群级 ConfigMap 全局配置翻译为 Traefik 的对应配置、在 NGINX 旁路安装 Traefik 并利用其 Kubernetes Ingress NGINX Provider 自动翻译 NGINX 注解、通过 DNS 渐进切流验证、处理双控制器共存时的 Ingress status 竞争问题,最后以保留 nginx IngressClass 的方式卸载 NGINX——全程零停机。读完本文,你可以直接照做完成一次生产级迁移。
迁移完成后你将得到什么
完成迁移后,你现有的 Ingress 资源无需任何修改即可由 Traefik 接管。Traefik 的 Kubernetes Ingress NGINX Provider 会自动把 NGINX 注解翻译为 Traefik 路由配置。你的 Ingress 保持原样:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
# 这些 NGINX 注解会被 Traefik 自动翻译
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
spec:
ingressClassName: nginx # ← Traefik 会监听这个 class
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: whoami
port:
number: 80
对应的 Service 与 Deployment 同样无需改动:
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
selector:
app: whoami
ports:
- protocol: TCP
port: 80
targetPort: 80
支持的注解完整清单及各注解在两种控制器下的行为差异,参见 Ingress NGINX 路由配置参考。
版本要求:Kubernetes Ingress NGINX Provider 要求 Traefik v3.6.2 或更高版本。
Legacy Scheme 头兼容:如果你的应用仍依赖 ingress-nginx 的旧版 X-Forwarded-Scheme 或 X-Scheme 头,可在入口点开启 entryPoints.<name>.forwardedHeaders.addXForwardedSchemeHeaders=true。该选项保持 X-Forwarded-Proto 不变,并在入口点层面为所有 Provider 恢复兼容头。这一行为在源码中可以直接印证:入口点静态配置中的 AddXForwardedSchemeHeaders 字段会传入转发头中间件,XForwarded 处理器在启用时读取 X-Forwarded-Proto 的值并写入 X-Forwarded-Scheme 等兼容头。
注解是如何被"自动翻译"的(源码视角)
从源码结构看,这套注解翻译机制的核心是一张注解映射表:IngressConfig 结构体用 annotation 标签把每一个 nginx.ingress.kubernetes.io/* 注解对应到一个具名字段,覆盖认证(Basic/ForwardAuth/客户端证书)、SSL 重定向与 passthrough、正则重写、CORS、会话粘性、限速(limit-rpm / limit-rps / limit-burst-multiplier / limit-connections)、缓冲与超时、Canary 等类别。解析过程由 parseIngressConfig 基于反射完成:布尔值按 strings.EqualFold(val, "true") 解析,整数字段解析失败则忽略,字符串列表按逗号分隔。这也解释了一个实用细节——注解值写错(如布尔字段写 yes)时该注解会被静默丢弃,迁移后遇到"注解不生效"时应优先核对取值格式。
迁移前准备
开始前请确认以下条件:
- 集群中已有运行中的 Ingress NGINX Controller;
- 已配置
kubectl并具备集群访问权限; - 集群支持同时在 80/443 端口运行多个 LoadBalancer 服务;
- 已安装 Helm;
- 具备创建 RBAC 资源的集群管理员权限;
- 已备份关键配置(Ingress 资源、ConfigMap、Secret)。
备份建议:
# 导出所有 Ingress 资源
kubectl get ingress --all-namespaces -o yaml > ingress-backup.yaml
# 导出 NGINX ConfigMaps
kubectl get configmap --all-namespaces -l app.kubernetes.io/name=ingress-nginx -o yaml > nginx-configmaps.yaml
迁移策略总览
迁移通过 Traefik 与 NGINX 并行运行 实现零停机:两个控制器同时服务同一批 Ingress 资源,允许你先验证、再逐步切流,最后移除 NGINX。
现状: DNS → LoadBalancer → NGINX → 你的服务
迁移中: DNS → LoadBalancer → NGINX → 你的服务
→ LoadBalancer → Traefik → 你的服务
最终: DNS → LoadBalancer → Traefik → 你的服务
迁移流程:
- Step 0 — 审查 ingress-nginx ConfigMap,把集群级默认值翻译为 Traefik 配置
- Step 1 — 与 NGINX 并行安装 Traefik
- Step 2 — 验证 Traefik 已能处理流量
- Step 3 — 将流量从 NGINX 逐步切换到 Traefik
- Step 4 — 从 DNS 移除 NGINX、保留 IngressClass 并卸载
Step 0:迁移全局 ConfigMap 设置
在安装 Traefik 之前,先审查当前 ingress-nginx ConfigMap 中的全局默认值。ingress-nginx 中控制器 ConfigMap 是一层集群级配置;而在 Traefik 中,同样的能力被拆分到:
providers.kubernetesIngressNGINX静态配置——ingress-nginx 兼容默认值entryPoints——监听器行为,如 HTTP 到 HTTPS 重定向、PROXY 协议- 动态
tls.options与 HTTP 中间件——TLS 策略、HSTS 及其他头行为 - Traefik 访问日志配置——请求日志
先导出当前 ConfigMap 并检查你自定义过的键:
kubectl get configmap --all-namespaces -l app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/component=controller -o yaml
这个标签选择器可以在任意命名空间、任意 release 名下定位控制器 ConfigMap。
单位换算提醒:多个 ingress-nginx ConfigMap 键使用 NGINX 风格值(如 16k、1m、30s),而 Traefik 对应的 providers.kubernetesIngressNGINX 选项要求:
- 请求体/缓冲类设置使用原始字节数
proxyConnectTimeout、proxyNextUpstreamTimeout使用整数秒proxyRequestBuffering、proxyBuffering使用布尔值
ConfigMap 到 Traefik 的映射表
| ingress-nginx ConfigMap 键 | Traefik 对应项(provider 选项) | 说明 |
|---|---|---|
proxy-connect-timeout |
proxyConnectTimeout |
使用整数秒。 |
proxy-request-buffering |
proxyRequestBuffering |
on/off 转为 true/false。注意 ingress-nginx 默认开启请求缓冲,而 Traefik 默认 false。 |
client-body-buffer-size |
clientBodyBufferSize |
如把 16k 换算为字节。 |
proxy-buffering |
proxyBuffering |
on/off 转为 true/false。 |
proxy-body-size |
proxyBodySize |
如把 1m 换算为字节。 |
proxy-buffer-size |
proxyBufferSize |
如把 8k 换算为字节。 |
proxy-buffers-number |
proxyBuffersNumber |
保持整数值。 |
proxy-next-upstream |
proxyNextUpstream |
空格分隔的重试条件列表,如 error timeout http_502。 |
proxy-next-upstream-timeout |
proxyNextUpstreamTimeout |
使用整数秒。 |
proxy-next-upstream-tries |
proxyNextUpstreamTries |
保持整数值。 |
custom-http-errors |
customHTTPErrors |
若想要全局错误页服务,同时配置 providers.kubernetesIngressNGINX.defaultBackendService。 |
global-allowed-response-headers |
globalAllowedResponseHeaders |
nginx.ingress.kubernetes.io/custom-headers 注解生效的前提。 |
allow-cross-namespace-resources |
allowCrossNamespaceResources |
迁移后的 Ingress 需要引用其他命名空间资源时使用。 |
strict-validate-path-type |
strictValidatePathType |
Traefik v3.7 默认该选项为 true。 |
ssl-redirect / force-ssl-redirect |
nginx.ingress.kubernetes.io/ssl-redirect 与 nginx.ingress.kubernetes.io/force-ssl-redirect 注解,或集群级 entryPoint 重定向 |
注解存在时 Traefik 会翻译;全局默认则在 web 入口点配置 HTTP→HTTPS 重定向,需要显式选择入口点时设置 providers.kubernetesIngressNGINX.httpEntryPoint / httpsEntryPoint。 |
ssl-protocols / ssl-ciphers |
TLS 选项 | 通过入口点 TLS 选项全局应用,或通过 traefik.ingress.kubernetes.io/router.tls.options 按 Ingress 指定。 |
hsts、hsts-max-age、hsts-include-subdomains、hsts-preload |
Headers 中间件 | 使用 stsSeconds、stsIncludeSubdomains、stsPreload、forceSTSHeader;把中间件挂到入口点上即形成集群级默认。 |
use-proxy-protocol |
入口点 proxyProtocol 配置 |
配置在每一个后面接说 PROXY 协议负载均衡器的入口点上。 |
access-log-path |
accessLog.filePath |
静态配置。 |
log-format-upstream |
accessLog.format |
使用 Traefik 内置 common、genericCLF 或 json 格式;自定义 NGINX 日志格式模板没有 1:1 等价物。 |
这些选项在源码中集中定义于 Provider 结构体,默认值则由 SetDefaults 对齐 NGINX 原生默认:连接/读/写超时各 60 秒、proxyBodySize 1MB、clientBodyBufferSize 16KB、proxyBufferSize 8KB、proxyBuffersNumber 4、重试 3 次、strictValidatePathType 为 true。也就是说,如果你的 ConfigMap 没有自定义这些项,Traefik 侧可以不配置直接获得等价行为。
没有直接等价项的 ConfigMap 键
一些键是 NGINX 特有的,迁移时可以丢弃,因为 Traefik 不暴露原始 NGINX 内部机制。常见例子:
- worker 调优类:
worker-processes、worker-cpu-affinity、Lua shared dict 设置 - snippet 类键:
main-snippet、http-snippet、server-snippet、location-snippet、stream-snippet - Traefik 内置访问日志格式之外的自定义 NGINX 日志格式模板
遇到这类键时,应翻译其背后的意图,而不是照抄指令。
参考文档
- Kubernetes Ingress NGINX Provider 安装配置
- Traefik TLS Options
- Traefik Headers 中间件
- Traefik EntryPoints 配置
Step 1:与 NGINX 并行安装 Traefik
先读 status 竞争说明:双控制器服务同一批 Ingress 会竞争 status.loadBalancer.ingress[] 字段。安装前请先看 Step 3 中的 Ingress Status 竞争条件 一节,决定采用哪种缓解措施(在 Traefik 上禁用 publishService,或使用过渡 IngressClass)。
(如果你尚未安装 Ingress NGINX Controller,可用以下命令先装一个:helm upgrade --install ingress-nginx ingress-nginx --repo https://kubernetes.github.io/ingress-nginx --namespace ingress-nginx --create-namespace。)
启用 Kubernetes Ingress NGINX Provider 安装 Traefik,两个控制器将同时服务相同的 Ingress 资源:
# 添加 Traefik Helm 仓库
helm repo add traefik https://traefik.github.io/charts
helm repo update
# 安装 Traefik
helm upgrade --install traefik traefik/traefik \
--namespace traefik --create-namespace \
--set providers.kubernetesIngressNGINX.enabled=true
或者使用 values 文件做更多配置(traefik-values.yaml):
providers:
kubernetesIngressNGINX:
enabled: true
helm upgrade --install traefik traefik/traefik \
--namespace traefik --create-namespace \
--values traefik-values.yaml
验证两个控制器都在运行
# 查看 NGINX Pod
kubectl get pods -n ingress-nginx
# 查看 Traefik Pod
kubectl get pods -n traefik
# 确认两个服务都有 LoadBalancer IP
kubectl get svc -n ingress-nginx ingress-nginx-controller
kubectl get svc -n traefik traefik
此时 NGINX 与 Traefik 都在运行且都能服务同一批 Ingress 资源;DNS 仍指向 NGINX 的 LoadBalancer,所以流量仍只走 NGINX。
Step 2:验证 Traefik 正在处理流量
把 Traefik 加进 DNS 之前,先确认它能正确服务你的 Ingress 资源。
通过 Traefik 的 LoadBalancer IP 测试
取出 Traefik 的 LoadBalancer IP,用 --resolve 在不改 DNS 的情况下测试:
# 获取 LoadBalancer IP
NGINX_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}')
TRAEFIK_IP=$(kubectl get svc -n traefik traefik -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}')
echo -e "Nginx IP: $NGINX_IP\nTraefik IP: $TRAEFIK_IP"
# 两边分别测 HTTP
FQDN=myapp.example.com
# 观察 HTTPS 重定向行为:
curl --connect-to "${FQDN}:80:${NGINX_IP}:80" "http://${FQDN}" -D -
curl --connect-to "${FQDN}:80:${TRAEFIK_IP}:80" "http://${FQDN}" -D - # 注意响应头 X-Forwarded-Server 应为 traefik
# 两边分别测 HTTPS
curl --connect-to "${FQDN}:443:${NGINX_IP}:443" "https://${FQDN}"
curl --connect-to "${FQDN}:443:${TRAEFIK_IP}:443" "https://${FQDN}"
TLS 证书注意:HTTPS 测试要成功,NGINX 和 Traefik 都必须持有有效证书。由于验证阶段 Traefik 不对外暴露,Let's Encrypt HTTP challenge 无法工作。过渡期证书方案:
- 现有
tls.secretName证书 —— 如果你用 cert-manager 等外部工具,spec.tls引用的现有 TLS secret 在两个控制器上都能工作; - Let's Encrypt DNS challenge —— 配置 Traefik 的 ACME DNS challenge,无需公网暴露即可申请证书。
不要用 curl -k(跳过证书校验),它掩盖的 TLS 配置问题会在迁移后变成事故。
验证 Ingress 发现情况
查看 Traefik 日志,确认它已发现你的 Ingress 资源:
kubectl logs -n traefik deployment/traefik | grep -i "ingress"
Step 3:把流量切换到 Traefik
两个控制器都已验证无误后,开始渐进切流。
方案 A:基于 DNS 的迁移
把 Traefik 的 LoadBalancer IP 加入 DNS 记录,与 NGINX 并存,让两个控制器同时收流量。
获取 LoadBalancer 地址:
# NGINX LoadBalancer
echo $(kubectl get svc -n ingress-nginx ingress-nginx-controller -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}')
# Traefik LoadBalancer
echo $(kubectl get svc -n traefik traefik -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}')
渐进式 DNS 迁移五步:
- 把 Traefik 加入 DNS —— 两个 IP 开始轮询接收流量;
- 观察 —— 监控两侧流量模式;
- 从 DNS 移除 NGINX —— 确认无误后删除 NGINX IP 记录;
- 等待 DNS 传播 —— 给缓存过期留时间;
- 卸载 NGINX —— 进入 Step 4。
DNS TTL 提醒:部分 ISP 为省流量会忽略 TTL 值、把记录缓存得比指定时间更长。从 DNS 移除 NGINX 后,保持 NGINX 至少再运行 24–48 小时再卸载,避免旧缓存用户掉流量。
Ingress Status 竞争条件(双控制器共存时)
当两个控制器管理同一批 Ingress(同为 ingressClassName: nginx)时,双方都会把 LoadBalancer 地址写入每个 Ingress 的 status.loadBalancer.ingress[]。每次协调循环里它们互相覆盖对方,日志中没有报错(只是两侧反复出现 Updated ingress status 信息行)。
路由本身不受影响——共存窗口内两个控制器都正常服务流量。但 flapping 的 status 字段会影响所有观察它的一方:
- ExternalDNS(可能在两个 LoadBalancer IP 之间来回改 DNS 记录)
- kube-state-metrics、监控看板与告警规则
- ArgoCD、Flux 等 GitOps 工具(会对每个受影响的 Ingress 报告永久漂移)
- 任何基于 Ingress status 字段做协调的自定义 Operator
从源码看这一行为有据可查:Provider 的 updateIngressStatus 在每次 loadConfiguration 后为名下每个 Ingress 回写状态——PublishService 为空且未配置 publishStatusAddress 时直接返回,LoadBalancer 类型 Service 则把 service.Status.LoadBalancer.Ingress 复制进 status。这既解释了竞争从何而来,也给出了缓解手段的开关位置。
推荐缓解(方案 1):共存期间在 Traefik 上禁用 status 发布
-
以
publishService禁用状态安装 Traefik:# traefik-values.yaml providers: kubernetesIngressNginx: enabled: true publishService: enabled: false # 禁止写 statusTraefik 照常服务 Ingress,只是不再写 status 字段,NGINX 成为唯一写入方。
-
用 port-forward 或独立测试域名 测试 Traefik;
-
ExternalDNS 用户:让 NGINX 发布 Traefik 的服务地址,使 ExternalDNS 把流量指到 Traefik:
# nginx-values.yaml controller: publishService: pathOverride: "traefik/traefik" # 指向 Traefik 的服务 -
验证流量走 Traefik —— 此时仍可回滚:移除
pathOverride即可; -
在 Traefik 上重新启用
publishService并进入 Step 4 卸载 NGINX。
备选缓解(方案 2):过渡 IngressClass
给迁移中的 NGINX 一个不同的 IngressClass(例如 nginx-migration),让两个控制器不同时拥有同一批 Ingress,从而彻底避免 status 竞争——代价是用一个短暂的流量切换步骤替代渐进 DNS 切流。
方案 B:外部负载均衡器 + 权重分流
需要更精细控制时,在两个 Kubernetes LoadBalancer 前面放一个外部负载均衡器(Traefik、Cloudflare、AWS ALB 或专用 LB 均可)。
基础设施前提:此方案假定你已有外部负载均衡器,或愿意在迁移开始前搭建一个。新增外部 LB 是独立的基础设施变更,应与入口控制器迁移分开规划与测试。
设置步骤:
- 建一个指向 NGINX Kubernetes LoadBalancer 的外部 LB;
- DNS 指向外部 LB;
- 把 Traefik Kubernetes LoadBalancer 以低权重(如 10%)加入外部 LB;
- 逐步调高 Traefik 权重、调低 NGINX 权重;
- NGINX 无流量后卸载它。
权重推进示例:
| 阶段 | NGINX 权重 | Traefik 权重 | 持续时间 |
|---|---|---|---|
| 初始 | 100% | 0% | - |
| 启动 | 90% | 10% | 1 小时 |
| 提升 | 50% | 50% | 2 小时 |
| 接近完成 | 10% | 90% | 4 小时 |
| 最终 | 0% | 100% | - |
常见外部 LB 选项:Cloudflare Load Balancing(带健康检查的流量引导)、AWS Global Accelerator(加权路由)、Google Cloud Load Balancing(流量分割)、自建 Traefik / HAProxy / NGINX 等。
LoadBalancer IP 保留
若希望 Traefik 最终使用与 NGINX 相同的 LoadBalancer IP(简化 DNS 管理),可以在迁移后转移 IP。由于 Traefik 已带着自己的 LoadBalancer 在运行,这可以零停机完成:
- Traefik 已带自己的 LoadBalancer IP 运行(Step 1);
- 把 Traefik IP 加入 DNS(流量同时流向 NGINX 和 Traefik);
- 从 DNS 移除 NGINX IP 并等待传播;
- 删除 NGINX 的 LoadBalancer 服务以释放 IP;
- 升级 Traefik 认领释放出来的 IP;
- (可选)新 IP 生效后从 DNS 移除 Traefik 旧 IP。
整个转移过程中流量始终在流向 Traefik。
获取当前 NGINX LoadBalancer IP:
kubectl get svc -n ingress-nginx ingress-nginx-controller -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}'
AWS(NLB + 弹性 IP):AWS Classic LB 不支持静态 IP,应使用带弹性 IP 的 NLB,前提是集群已安装 AWS Load Balancer Controller。
# 预先分配弹性 IP(每个可用区一个)
aws ec2 allocate-address --domain vpc --region <your-region>
# 记下每个 EIP 的 AllocationId(eipalloc-xxx)
traefik-values.yaml 更新:
service:
type: LoadBalancer
loadBalancerClass: service.k8s.aws/nlb # 需要 AWS Load Balancer Controller
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
service.beta.kubernetes.io/aws-load-balancer-eip-allocations: "eipalloc-xxx,eipalloc-yyy"
Azure:支持 LB 静态公网 IP。
# 定位现有公网 IP
az network public-ip list --resource-group <your-resource-group> \
--query "[?ipAddress=='<your-ip>'].name" -o tsv
service:
type: LoadBalancer
annotations:
# 仅当公网 IP 与 AKS 集群不在同一资源组时需要
service.beta.kubernetes.io/azure-load-balancer-resource-group: "<public-ip-resource-group>"
spec:
loadBalancerIP: "<your-existing-ip>"
GCP:通过预留区域静态 IP 实现。
# 列出现有静态 IP
gcloud compute addresses list
# 或预留新的区域静态 IP(必须与 GKE 集群同区域)
gcloud compute addresses create traefik-ip --region <your-cluster-region>
service:
type: LoadBalancer
spec:
loadBalancerIP: "<your-static-ip>"
OVHcloud:OVHcloud 公网 LB 基于 OpenStack Octavia,为 LoadBalancer 服务分配 floating IP,需要集群中安装 OpenStack Cloud Controller Manager(MKS 用户已自带)。保留现有 floating IP 的做法:
# 定位现有公网 IP
NGINX_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller \
-o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}')
echo "NGINX IP: $NGINX_IP"
# 给现有 NGINX LoadBalancer 服务加注解,防止删除时释放 floating IP
kubectl annotate svc my-lb-svc loadbalancer.openstack.org/keep-floatingip=true
keep-floatingip 注解可阻止服务删除/修改时释放 floating IP。随后删除 NGINX LoadBalancer 服务、更新 traefik-values.yaml:
service:
type: LoadBalancer
spec:
loadBalancerIP: "<your-existing-floating-ip>"
其他云:DigitalOcean 支持 floating IP 的 loadBalancerIP;Linode 支持 loadBalancerIP;裸机可结合 MetalLB 使用 IP 地址池。
转移 IP 操作:DNS 已指向 Traefik、values 已配好目标 IP 后:
# 确认 Traefik 已在通过当前 LoadBalancer 接收流量
kubectl get svc -n traefik traefik
# 删除 NGINX LoadBalancer 服务以释放 IP
kubectl delete svc -n ingress-nginx ingress-nginx-controller
# 升级 Traefik 认领已释放的 IP
helm upgrade traefik traefik/traefik \
--namespace traefik \
--values traefik-values.yaml
# 确认 Traefik 拿到了原 NGINX 的 IP
kubectl get svc -n traefik traefik
零停机提示:Helm 升级只会重启 Traefik Pod,不会动 LoadBalancer 服务。Traefik 默认 RollingUpdate 部署策略,新 Pod 先起来旧 Pod 再退出。为更稳妥,建议配置高可用:
# traefik-values.yaml
deployment:
replicas: 2
# 把 Pod 打散到不同节点以容忍节点故障
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: traefik
app.kubernetes.io/instance: traefik
topologyKey: kubernetes.io/hostname
# 确保扰动期间至少一个 Pod 可用
podDisruptionBudget:
enabled: true
minAvailable: 1
多副本打散 + PodDisruptionBudget 可保证升级和节点维护期间至少有一个 Pod 在运行。
Step 4:卸载 Ingress NGINX Controller
NGINX 不再接收流量后,将其移出集群。卸载前必须确保 nginx IngressClass 被保留——Traefik 需要它继续发现你的 Ingress。
保留 IngressClass
Helm 安装的 NGINX:添加 helm.sh/resource-policy: keep 注解让 Helm 保留 IngressClass:
# 添加所需注解
helm upgrade ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx \
--reuse-values \
--set-json 'controller.ingressClassResource.annotations={"helm.sh/resource-policy": "keep"}'
# 确认注解确实生效
kubectl describe ingressclass nginx
--reuse-values 很关键——它保留你现有的全部 NGINX 配置;没有它,Helm 会把所有值重置为默认,可能弄坏你的部署。
注意:kubectl annotate/patch/edit 加注解无效。Helm 在内部保存 release 状态,卸载时检查的是其内部清单中的注解,而不是集群里的实时状态;只有 helm upgrade 才能更新 Helm 的内部状态。
GitOps(ArgoCD、Flux)安装的 NGINX:在仓库中把 nginx IngressClass 定义为独立资源,与 NGINX Helm release 分离:
# ingressclass.yaml
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
spec:
controller: k8s.io/ingress-nginx
手动安装的 NGINX:直接创建独立 IngressClass 资源:
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: nginx
spec:
controller: k8s.io/ingress-nginx
EOF
为什么保留的是这个 spec.controller: k8s.io/ingress-nginx?因为 Traefik Ingress NGINX Provider 的默认监听目标正是它:SetDefaults 把 IngressClass 默认为 nginx、ControllerClass 默认为 k8s.io/ingress-nginx。保留原 IngressClass 对象,Traefik 无需任何额外配置即可继续认领这些 Ingress。
删除 NGINX 准入 Webhook
为避免 NGINX 移除后 Ingress 修改出问题,应删除准入 webhook:
kubectl delete validatingwebhookconfiguration ingress-nginx-admission
kubectl delete mutatingwebhookconfiguration ingress-nginx-admission --ignore-not-found
卸载 NGINX
helm uninstall ingress-nginx -n ingress-nginx
如果你加过 helm.sh/resource-policy: keep 注解,应看到:
These resources were kept due to the resource policy:
[IngressClass] nginx
release "ingress-nginx" uninstalled
验证 IngressClass 存在
kubectl get ingressclass nginx
万一 IngressClass 被误删,用上面"保留 IngressClass"一节的命令重建即可。
清理 NGINX 命名空间
kubectl delete namespace ingress-nginx
迁移完成! 你已成功以零停机方式从 Ingress NGINX Controller 迁移到 Traefik。所有 ingressClassName: nginx 的现有 Ingress 继续工作,只是现在由 Traefik 提供服务。
故障排查
Traefik 自带 Dashboard 可以帮助定位问题,开启方式见 API Dashboard 文档。
Ingress 未被 Traefik 发现:
# 验证 IngressClass 存在
kubectl get ingressclass nginx
# 检查 Traefik provider 配置
kubectl logs -n traefik deployment/traefik | grep -i "nginx\|ingress"
# 确认 Ingress 的 ingressClassName 正确
kubectl get ingress <name> -o yaml | grep ingressClassName
注解行为不符合预期:部分 NGINX 注解在 Traefik 中行为存在差异,参见限制说明。
TLS 证书不工作:现有 TLS 配置在 Traefik 上照常工作——
spec.tls条目保持原样,Traefik 使用所引用的 secret 终结 TLS;- TLS secret 必须与 Ingress 位于同一命名空间;
- NGINX 的
ssl-redirect/force-ssl-redirect注解同样被遵守。
# 验证 TLS secret 与 Ingress 同命名空间
kubectl get secrets -n <namespace>
# 检查 secret 格式
kubectl get secret <tls-secret-name> -n <namespace> -o yaml
LoadBalancer IP 未分配:
# 检查服务状态
kubectl describe svc -n traefik traefik
# 查看事件
kubectl get events -n traefik --sort-by='.lastTimestamp'
后续进阶
深入了解 Traefik:
- Kubernetes Ingress NGINX 安装配置 —— Provider 配置详解
- Kubernetes Ingress NGINX 路由配置 —— 路由规则与注解支持
- HTTP 中间件 —— 超越 NGINX 注解的能力
- TLS 配置 —— 高级 TLS 与证书管理
增强你的部署:
- 启用 指标 与 链路追踪
- 配置访问日志完善可观测性
- 探索 Traefik 中间件 实现高级流量管理
- 把基于 NGINX 注解的配置迁移到 Traefik IngressRoute 或 Kubernetes Gateway API
如果你使用 GitOps 管理入口资源,迁移完成后将动态配置收敛到 Traefik CRD(IngressRoute)是自然的下一步——它提供比 Ingress 注解更精确的路由、中间件链与 TLS 控制,也能顺带摆脱 Ingress status 竞争这一类控制器间协作问题。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00