1. 为什么单机 Docker 跑通 Traefik 2.8 后,上 K8s 还是懵
很多人第一次接触 Traefik 2.8,是在一台装了 Docker 的测试机上。照着官方 Quick Start 写个docker-compose.yml,把 80 端口映射出来,再挂一个whoami容器,浏览器一刷就出结果,感觉这东西挺简单。可一旦把场景换成 Kubernetes,面对IngressRoute、Middleware、TraefikService这些 CRD,还有entryPoints、providers、api@internal这些概念,之前的经验好像突然接不上了。
这篇就按「单机 Docker → Kubernetes Ingress」这条线走一遍,把 Traefik 2.8 的部署与配置讲清楚。核心检索词是 traefik2.8 部署与配置,适合两类人:一类是想在单机上快速验证反向代理和自动服务发现的后端同学;另一类是已经在用 K8s,想从 Nginx Ingress 迁到 Traefik,或者想搞明白 IngressRoute 到底比原生 Ingress 强在哪的运维同学。
Traefik 本质上是一个云原生边缘路由器。它和 Nginx 最大的区别在于「动态」:Nginx 改一条路由通常要改配置文件再 reload,而 Traefik 通过 provider 自动监听 Docker、Kubernetes、Consul 等后端的变化,服务一上线,路由规则自动生效。Traefik 2.8 属于 2.x 系列里比较稳定的版本,支持 TCP、UDP、TLS、中间件,也是很多生产集群仍在用的版本。
我试过在同一个集群里同时跑 Nginx Ingress 和 Traefik,两者并不冲突,靠ingressClass区分即可。下面从单机 Docker 开始,再过渡到 K8s 的 Helm 与 YAML 两种部署方式,每一步都给可复制的配置和验证命令。
2. 单机 Docker 部署 Traefik 2.8 与静态配置文件怎么写
单机场景最适合理解 Traefik 的「静态配置」和「动态配置」这两个概念。静态配置是 Traefik 启动时读取的,决定 entryPoints、providers、日志、API 这些全局行为;动态配置是运行时由 provider 提供的路由规则,比如 Docker 容器的 label。
先建目录结构,把静态配置和动态配置分开:
mkdir -p /opt/traefik/{config,dynamic,logs} touch /opt/traefik/config/traefik.yml touch /opt/traefik/dynamic/conf.yml静态配置文件traefik.yml内容如下,注意 YAML 缩进必须用空格:
global: checkNewVersion: false sendAnonymousUsage: false api: insecure: true dashboard: true entryPoints: web: address: ":80" websecure: address: ":443" providers: docker: endpoint: "unix:///var/run/docker.sock" exposedByDefault: false watch: true file: directory: "/etc/traefik/dynamic" watch: true log: level: INFO filePath: "/etc/traefik/logs/traefik.log" accessLog: filePath: "/etc/traefik/logs/access.log"这里几个关键点:exposedByDefault: false表示容器默认不暴露,必须显式打 label 才会被 Traefik 接管,生产环境强烈建议这么设;providers.file让 Traefik 监听动态目录,方便后面用文件方式加路由;api.insecure: true只适合测试,生产要换成带认证的 secure 模式。
对应的docker-compose.yml:
version: "3.8" services: traefik: image: traefik:v2.8 container_name: traefik restart: unless-stopped ports: - "80:80" - "443:443" - "8080:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./config/traefik.yml:/etc/traefik/traefik.yml:ro - ./dynamic:/etc/traefik/dynamic:ro - ./logs:/etc/traefik/logs networks: - traefik-net whoami: image: traefik/whoami:v1.8 container_name: whoami restart: unless-stopped labels: - "traefik.enable=true" - "traefik.http.routers.whoami.rule=Host(`whoami.local`)" - "traefik.http.routers.whoami.entrypoints=web" - "traefik.http.services.whoami.loadbalancer.server.port=80" networks: - traefik-net networks: traefik-net: driver: bridge启动:
cd /opt/traefik docker compose up -d docker compose ps预期看到traefik和whoami两个容器都是 Up 状态。这里whoami的 label 就是动态配置的一种来源,Traefik 通过 Docker provider 读到这些 label,自动生成路由。
3. 可复制的动态路由配置与 K8s IngressRoute 对照
单机 Docker 用 label 很方便,但配置多了以后 label 会很长,可读性差。这时可以用 file provider,把动态配置写成独立文件。在/opt/traefik/dynamic/conf.yml里写:
http: routers: whoami-file: rule: "Host(`whoami-file.local`)" entryPoints: - web service: whoami-file-svc services: whoami-file-svc: loadBalancer: servers: - url: "http://whoami:80"改完不用重启 Traefik,file provider 的watch: true会自动加载。验证:
curl -H "Host: whoami-file.local" http://127.0.0.1预期输出类似:
Hostname: whoami IP: 172.20.0.3 RemoteAddr: 172.20.0.2:xxxxx GET / HTTP/1.1 Host: whoami-file.local看到Hostname和Host就说明路由生效了。这一步是理解 Traefik 动态配置的关键:router 负责匹配规则,service 负责指向后端,中间还可以插 middleware。
现在把同样的逻辑搬到 Kubernetes。K8s 里 Traefik 用 CRD 表达路由,最核心的是IngressRoute。它和单机的 router/service 是一一对应的:
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: whoami-route namespace: default spec: entryPoints: - web routes: - match: Host(`whoami.k8s.local`) kind: Rule services: - name: whoami port: 80对照一下:entryPoints对应单机的 entryPoints,match对应 rule,services对应 loadBalancer。区别在于 K8s 里 service 名字直接引用 K8s Service,Traefik 会自动做服务发现和负载均衡。
如果要在 K8s 里用 Helm 部署 Traefik 2.8,先加 repo:
helm repo add traefik https://helm.traefik.io/traefik helm repo update helm search repo traefik预期能看到traefik/traefik的 chart 版本和 app 版本。创建命名空间并安装:
kubectl create ns traefik helm install traefik traefik/traefik -n traefik kubectl get pod -n traefikHelm 方式适合快速起步,但它的 values.yaml 有几百行,改起来不够直观。如果要做深度定制,比如指定边缘节点、host 网络、自定义 entryPoints,建议用 YAML 方式部署,下面会讲。
4. 验证请求:从 curl 到 Dashboard 的完整链路
部署完必须验证,否则路由没生效都不知道。先看单机 Docker 场景。确认 Traefik 容器日志没有报错:
docker logs traefik --tail 50然后分别验证 label 路由和 file 路由:
curl -H "Host: whoami.local" http://127.0.0.1 curl -H "Host: whoami-file.local" http://127.0.0.1两条命令都应该返回 whoami 的响应。如果第一条通、第二条不通,多半是 file provider 的目录挂载路径写错了,检查traefik.yml里的directory和 compose 里的 volume 是否一致。
Dashboard 访问http://127.0.0.1:8080/dashboard/,能看到 HTTP 路由列表,里面应该有whoami@docker和whoami-file@file两条。
再看 K8s 场景。Helm 安装后,Dashboard 默认不对外暴露,用端口转发:
kubectl port-forward -n traefik deployment/traefik --address 0.0.0.0 9000:9000如果报unable to do port forwarding: socat not found,在节点上装一下:
yum install -y socat然后浏览器访问http://<节点IP>:9000/dashboard/。要验证 IngressRoute,先部署一个 whoami 应用:
kubectl create deployment whoami --image=traefik/whoami:v1.8 kubectl expose deployment whoami --port=80再应用上面的whoami-route,然后:
kubectl get ingressroute curl -H "Host: whoami.k8s.local" http://<节点IP>预期返回 whoami 的 Hostname 和请求头。如果返回 404,先确认 IngressRoute 的 namespace 和 Service 是否一致,再确认 Traefik 的 entryPoints 里有没有web。
用 YAML 方式部署时,Dashboard 可以通过 IngressRoute 暴露:
apiVersion: traefik.containo.us/v1alpha1 kind: IngressRoute metadata: name: dashboard namespace: traefik spec: entryPoints: - web routes: - match: Host(`traefik.test.com`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)) kind: Rule services: - name: api@internal kind: TraefikService注意api@internal是 Traefik 内置服务,不需要额外定义。客户端加 hosts 解析后访问http://traefik.test.com/dashboard/即可。
5. 常见报错排查:401、404、socat not found 与 OAuth
实际部署时踩的坑基本集中在几类报错上,逐个说。
404 路由不生效。最常见的原因是 IngressRoute 的entryPoints和 Traefik 实际监听的入口名对不上。比如 Traefik 配置里入口叫web,你写成了http,就会 404。另一个原因是多控制器场景下没指定ingressClass或labelSelector,导致 IngressRoute 被另一个 Traefik 实例接管了。检查:
kubectl get ingressroute -A kubectl logs -n traefik deployment/traefik | grep -i "ingressroute"401 Unauthorized。如果开了 Dashboard 的 secure 模式但没配认证,或者访问api@internal时带了错误的 basic auth,会返回 401。测试阶段可以先用api.insecure: true,生产环境用 Middleware 加 basicAuth:
apiVersion: traefik.containo.us/v1alpha1 kind: Middleware metadata: name: dashboard-auth namespace: traefik spec: basicAuth: secret: dashboard-auth-secret然后在 IngressRoute 的 routes 里加middlewares: - name: dashboard-auth。
local proxy failed / reading choices。这类报错通常出现在 Traefik 无法连接后端 Service 时。比如 Service 的 endpoints 为空,或者端口写错。检查:
kubectl get endpoints whoami kubectl describe svc whoami如果 endpoints 是<none>,说明 Pod 没就绪或者 selector 不匹配。Traefik 2.8 里可以开allowEmptyServices: true避免直接报错,但根因还是要修 Service。
OAuth / ForwardAuth 相关。如果用 ForwardAuth 中间件做统一认证,报错多半是认证服务地址写错或者返回格式不对。ForwardAuth 要求认证服务返回 2xx 才放行,返回 401/403 会直接拦截。调试时先直接 curl 认证服务:
curl -v http://auth-service.default.svc.cluster.local/verifysocat not found。前面提过,kubectl port-forward依赖节点上的 socat,装一下即可。这个报错和 Traefik 本身无关,但很容易让人误以为部署失败。
TLS 握手失败。如果配了TLSOption强制 TLS 1.2+,老客户端可能连不上。检查:
openssl s_client -connect traefik.test.com:443 -tls1_1预期应该失败,说明强制生效了。如果业务确实需要兼容老版本,把minVersion调低。
6. 从 Helm 到 YAML:生产级 Traefik 2.8 的落地建议
Helm 适合快速验证,但生产环境我更推荐 YAML 自定义部署,原因是可控。用 Helm 装完,values.yaml几百行,改一个 entryPoint 要翻半天;YAML 方式每个资源文件职责清晰,改哪看哪。
YAML 部署的核心步骤:先装 CRD,再建 RBAC,然后 ConfigMap 存静态配置,最后 Deployment + Service。CRD 文件从官方仓库拿:
wget https://raw.githubusercontent.com/traefik/traefik/v2.8/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml kubectl apply -f kubernetes-crd-definition-v1.yml kubectl get crd | grep traefik预期看到ingressroutes、middlewares、traefikservices等 CRD。注意 K8s 1.16 以上用apiextensions.k8s.io/v1,1.22 之后v1beta1完全移除,别用错版本。
静态配置放 ConfigMap:
kind: ConfigMap apiVersion: v1 metadata: name: traefik-config data: traefik.yaml: |- global: checkNewVersion: false sendAnonymousUsage: false api: insecure: true dashboard: true entryPoints: web: address: ":80" websecure: address: ":443" providers: kubernetesCRD: ingressClass: "traefik-v2.8" allowCrossNamespace: true allowEmptyServices: true log: level: INFO accessLog: {}Deployment 里用 host 网络提升入口性能,并调度到打了IngressProxy=true标签的边缘节点:
spec: template: spec: hostNetwork: true nodeSelector: IngressProxy: "true" tolerations: - operator: "Exists" containers: - name: traefik image: traefik:v2.8.7 args: - --configfile=/etc/traefik/config/traefik.yaml volumeMounts: - mountPath: /etc/traefik/config name: config volumes: - name: config configMap: name: traefik-config多控制器场景下,如果集群里跑了多个 Traefik,靠ingressClass或labelSelector区分。用ingressClass时,IngressRoute 要加 annotation:
metadata: annotations: kubernetes.io/ingress.class: traefik-v2.8用labelSelector时,IngressRoute 加 label:
metadata: labels: app: traefik-v2.8两种方式选一种即可,混用容易乱。日志轮换官方没内置,靠 logrotate 发 USR1 信号让 Traefik 重建日志文件,配置写在/etc/logrotate.d/traefik里,再用 crontab 定时触发。
如果你在本地或 CI 里需要频繁调用模型来生成配置片段、排查报错,可以配合 TaoToken 的模型对话能力做辅助,API 地址是 https://taotoken.net/api,接入文档在 https://taotoken.net/doc,API Key 在 https://taotoken.net/api-keys 管理。长期做编码和 Agent 任务的话,Coding Plan 页面 https://taotoken.net/coding-plan 有更细的说明。这些只是工具链上的补充,Traefik 本身的配置还是以官方文档和实际验证为准。
最后给一个实用技巧:Traefik 2.8 的 Dashboard 里有个「HTTP → Routers」页面,能看到每条路由匹配了哪个 rule、指向哪个 service、有没有报错。排查路由问题时先看这里,比翻日志快得多。