Skip to content

Kubernetes 1.36 使用 Helm 部署 Traefik(HAProxy 四层负载均衡)

本文适用于使用 kubeadm 部署的 Kubernetes v1.36 集群。集群外部继续使用 HAProxy 作为四层负载均衡器,集群内部使用 Traefik 处理域名、路径和 TLS 等七层路由,并通过 PROXY Protocol v2 将真实客户端 IP 传递给业务应用。

文档版本基线:2026-09-04。本文固定使用 Helm v4.2.4、Traefik Helm Chart 41.4.0、Traefik Proxy v3.7.12,避免安装过程中因上游最新版变化而产生不可预期的配置差异。

一、部署架构

1.1 节点规划

角色主机名IP 地址用途
外部负载均衡haproxy172.22.33.99对外监听 80/443,并转发到 Traefik NodePort
控制平面k8s-master01172.22.33.100Kubernetes 控制平面
工作节点k8s-node01172.22.33.101运行 Traefik DaemonSet 和业务 Pod
工作节点k8s-node02172.22.33.102运行 Traefik DaemonSet 和业务 Pod

Traefik 使用 DaemonSet,并通过节点亲和性排除带有 node-role.kubernetes.io/control-plane 标签的节点。以后新增普通 Worker 节点并加入集群后,不需要再手工添加 Traefik 专用标签,DaemonSet 会自动在新 Worker 上创建一个 Traefik Pod。

1.2 端口规划

流量HAProxy 监听端口Traefik NodePortTraefik 容器端口
HTTP80322808000
HTTPS443324438443

1.3 HTTPS 请求链路

text
客户端
  │ HTTPS

172.22.33.99:443 HAProxy
  │ mode tcp + send-proxy-v2

172.22.33.101:32443 / 172.22.33.102:32443
  │ externalTrafficPolicy: Local

Traefik DaemonSet
  │ TLS 终止、域名和路径匹配、生成 X-Forwarded-For

main-app-service:80

Demo Nginx Pod

HAProxy 只做四层转发,不解密 HTTPS。TLS 证书由 Traefik 读取 Kubernetes Secret 后使用。

二、部署前检查

以下操作如果没有特别说明,均在 k8s-master01 上使用 root 用户执行。

2.1 检查 Kubernetes 集群

bash
kubectl get nodes -o wide
kubectl get pods -A

确认三个 Kubernetes 节点均为 Ready,Calico、CoreDNS 和 kube-proxy 正常运行。

2.2 检查 NodePort 范围

本文使用 3228032443,均位于 Kubernetes 默认 NodePort 范围 30000-32767 内。

确认端口没有被其他 Service 占用:

bash
kubectl get svc -A -o wide | grep -E '32280|32443' || true

如果集群中已经运行 ingress-nginx 并占用了这两个 NodePort,应先使用其他端口部署 Traefik 并完成迁移验证,不能让两个 Service 同时占用相同 NodePort。

2.3 检查域名&提供SSL证书

本文使用:

text
Demo 域名:demo.srebro.cn
TLS Secret:srebro.cn-tls

正式 DNS 应将 demo.srebro.cn 解析到 HAProxy 地址:

text
demo.srebro.cn -> 172.22.33.99

如果暂时不修改 DNS,可以在测试客户端的 /etc/hosts 中添加:

text
172.22.33.99 demo.srebro.cn

2.4 检查镜像访问

本文使用 Traefik 官方镜像 docker.io/traefik:v3.7.12

bash
docker pull traefik:v3.7.12
# 或者使用加速镜像
docker pull docker.cnb.cool/srebro/docker-images-chrom/traefik:v3.7.12_amd64

如果生产环境不能直接访问 Docker Hub,可以提前登录到 node 节点上下载好加速地址的镜像。

三、安装 Helm

3.1 Helm 基本概念

  • Chart:一组 Kubernetes YAML 模板组成的软件包,例如 traefik/traefik
  • Release:Chart 安装到集群后形成的一个实例,本文 Release 名称为 traefik
  • values.yaml:覆盖 Chart 默认配置的参数文件,本文用于定义 DaemonSet、NodePort 和 PROXY Protocol。
  • Repository:保存和分发 Chart 的仓库。

3.2 下载 Helm

bash
wget https://get.helm.sh/helm-v4.2.4-linux-amd64.tar.gz

3.3 安装 Helm 二进制

bash
tar  -xf helm-v4.2.4-linux-amd64.tar.gz
install -m 0755 "linux-amd64/helm" /usr/local/bin/helm

helm version

预期能够看到 v4.2.4。Helm 只需要安装在执行部署命令的管理节点上,不需要安装到所有 Kubernetes 节点。

3.4 验证 Helm 能访问集群

bash
kubectl config current-context
helm list -A

如果 Helm 报 kubeconfig 权限问题,先确认当前用户执行 kubectl get nodes 是否正常。不要把管理员 kubeconfig 随意复制给普通用户。

四、准备 Traefik Helm Chart

4.1 添加 Traefik Chart 仓库

bash
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm search repo traefik/traefik --versions | head -20

示例输出:

text
NAME                    CHART VERSION   APP VERSION     DESCRIPTION
traefik/traefik         41.4.0          v3.7.12         A Traefik based Kubernetes ingress controller
traefik/traefik         41.3.0          v3.7.11         A Traefik based Kubernetes ingress controller
traefik/traefik         41.2.0          v3.7.10         A Traefik based Kubernetes ingress controller
traefik/traefik         41.1.1          v3.7.9          A Traefik based Kubernetes ingress controller
traefik/traefik         41.1.0          v3.7.9          A Traefik based Kubernetes ingress controller
traefik/traefik         41.0.2          v3.7.6          A Traefik based Kubernetes ingress controller
traefik/traefik         41.0.1          v3.7.5          A Traefik based Kubernetes ingress controller
traefik/traefik         41.0.0          v3.7.5          A Traefik based Kubernetes ingress controller
traefik/traefik         40.3.0          v3.7.4          A Traefik based Kubernetes ingress controller
traefik/traefik         40.2.0          v3.7.1          A Traefik based Kubernetes ingress controller
traefik/traefik         40.1.0          v3.7.1          A Traefik based Kubernetes ingress controller
traefik/traefik         40.0.1          v3.7.0          A Traefik based Kubernetes ingress controller
traefik/traefik         40.0.0          v3.7.0          A Traefik based Kubernetes ingress controller
traefik/traefik         39.0.9          v3.6.15         A Traefik based Kubernetes ingress controller
traefik/traefik         39.0.8          v3.6.13         A Traefik based Kubernetes ingress controller
traefik/traefik         39.0.7          v3.6.12         A Traefik based Kubernetes ingress controller
traefik/traefik         39.0.6          v3.6.11         A Traefik based Kubernetes ingress controller
traefik/traefik         39.0.5          v3.6.10         A Traefik based Kubernetes ingress controller
traefik/traefik         39.0.4          v3.6.9          A Traefik based Kubernetes ingress controller

确认列表中存在 Chart 41.4.0,对应的 Traefik 版本是 v3.7.12

4.2 创建工作目录

bash
mkdir -p /etc/kubernetes/addons/traefik
cd /etc/kubernetes/addons/traefik

保存一份官方默认配置,便于后续查询参数:

bash
helm show values traefik/traefik \
  --version 41.4.0 > traefik-default-values.yaml

4.3 创建 traefik-values.yaml

bash
cat >/etc/kubernetes/addons/traefik/traefik-values.yaml <<'EOF'
image:
  registry: docker.io
  repository: traefik
  tag: v3.7.12
  pullPolicy: IfNotPresent

# 每个非 control-plane 节点运行一个 Traefik Pod;新增 Worker 后自动运行。
deployment:
  kind: DaemonSet

affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: node-role.kubernetes.io/control-plane
              operator: DoesNotExist

updateStrategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 1
    maxSurge: 0

ingressClass:
  enabled: true
  isDefaultClass: false
  name: traefik

providers:
  kubernetesCRD:
    enabled: true
    allowCrossNamespace: false
    allowExternalNameServices: false
  kubernetesIngress:
    enabled: true
    allowExternalNameServices: false
    publishedService:
      enabled: false
    ingressEndpoint:
      ip: 172.22.33.99
  kubernetesGateway:
    enabled: false

gateway:
  enabled: false

gatewayClass:
  enabled: false

api:
  dashboard: true
  insecure: false

# 不使用 Chart 自动公开 Dashboard,后文单独创建带认证的 IngressRoute。
ingressRoute:
  dashboard:
    enabled: false

service:
  enabled: true
  spec:
    type: NodePort
    externalTrafficPolicy: Local

ports:
  traefik:
    port: 8080
    expose:
      default: false

  web:
    port: 8000
    expose:
      default: true
    exposedPort: 80
    nodePort: 32280
    protocol: TCP
    proxyProtocol:
      # 只信任外部 HAProxy,不使用 insecure=true。
      trustedIPs:
        - 172.22.33.99/32
      insecure: false
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
          permanent: true

  websecure:
    port: 8443
    expose:
      default: true
    exposedPort: 443
    nodePort: 32443
    protocol: TCP
    proxyProtocol:
      # 必须与 HAProxy 的 send-proxy-v2 配合。
      trustedIPs:
        - 172.22.33.99/32
      insecure: false

log:
  level: INFO

accessLog:
  enabled: true
  format: json
  fields:
    defaultMode: keep
    headers:
      defaultMode: drop

resources:
  requests:
    cpu: 100m
    memory: 128Mi
  limits:
    cpu: 1000m
    memory: 512Mi
EOF

关键配置说明:

  • image.registry/repository/tag:Helm 最终拉取官方镜像 docker.io/traefik:v3.7.12
  • deployment.kind: DaemonSet:每个符合调度条件的非控制平面节点运行一个 Traefik Pod。
  • requiredDuringSchedulingIgnoredDuringExecution:强制排除带有 node-role.kubernetes.io/control-plane 标签的节点;以后新增普通 Worker 后,无需修改 Helm values,DaemonSet 会自动在新节点创建 Pod。
  • externalTrafficPolicy: Local:HAProxy 访问某个 Worker 的 NodePort 时,只转发给本机 Traefik Pod,不跨节点转发。
  • proxyProtocol.trustedIPs:Traefik 只接受 172.22.33.99 传入的 PROXY Protocol 地址。
  • ingressEndpoint.ip:让 Ingress 状态显示 HAProxy 地址 172.22.33.99
  • isDefaultClass: false:业务必须显式填写 ingressClassName: traefik,避免误接管其他 Ingress。
  • api.insecure: false:禁止无认证暴露 Traefik API 和 Dashboard。

不要配置以下参数:

yaml
proxyProtocol:
  insecure: true

该参数会信任任意来源提交的 PROXY Protocol 地址,存在伪造客户端 IP 的风险。

五、使用 Helm 部署 Traefik

5.1 渲染并检查 YAML

在真正安装前,先渲染 Chart:

bash
cd /etc/kubernetes/addons/traefik

helm template traefik traefik/traefik \
  --namespace traefik \
  --version 41.4.0 \
  --values traefik-values.yaml \
  --include-crds \
  > traefik-rendered.yaml

检查关键配置:

bash
grep -nE 'kind: DaemonSet|node-role.kubernetes.io/control-plane|nodePort: 32280|nodePort: 32443|externalTrafficPolicy: Local' \
  traefik-rendered.yaml

示例输出:

text
8800:  externalTrafficPolicy: Local
8810:    nodePort: 32280
8815:    nodePort: 32443
8819:kind: DaemonSet
8858:              - key: node-role.kubernetes.io/control-plane

5.2 安装 Traefik

bash
helm upgrade --install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace \
  --version 41.4.0 \
  --values /etc/kubernetes/addons/traefik/traefik-values.yaml \
  --atomic \
  --wait \
  --timeout 10m

参数说明:

  • upgrade --install:不存在时安装,已经存在时升级,便于重复执行。
  • --version 41.4.0:固定 Chart 版本。
  • --values:使用本文准备的配置文件。
  • --atomic:安装失败时自动回滚本次 Release。
  • --wait:等待 Pod 和 Service 就绪后再返回。
  • --timeout 10m:镜像下载较慢时最多等待 10 分钟。

首次安装时 Helm 会安装 Traefik CRD。后续升级 Chart 时,Helm 不会自动升级已经存在的 CRD,升级章节会单独说明处理方式。

5.3 检查 Helm Release

bash
helm list -n traefik
helm status traefik -n traefik
helm get values traefik -n traefik
helm history traefik -n traefik

5.4 检查 Kubernetes 资源

bash
kubectl get crd | grep traefik.io
kubectl get ingressclass
kubectl get daemonset,pod,svc -n traefik -o wide

预期结果:

  • IngressClass 中存在 traefik
  • 当前三节点集群中,DaemonSetDESIRED/CURRENT/READY 均应为 2;以后增加 Worker 后,该数字应等于非控制平面节点数量。
  • 每个非控制平面节点都有一个 Ready Traefik Pod,k8s-master01 上没有 Traefik Pod。
  • Service 显示 80:32280/TCP443:32443/TCP

进一步检查:

bash
kubectl -n traefik describe daemonset traefik
kubectl -n traefik describe service traefik
kubectl -n traefik logs daemonset/traefik --tail=200

六、配置 HAProxy

本章在 HAProxy 服务器 172.22.33.99 上执行。

6.1 备份 HAProxy 配置

bash
cp -a /etc/haproxy/haproxy.cfg \
  /etc/haproxy/haproxy.cfg.$(date +%Y%m%d%H%M%S).bak

6.2 添加 HTTP/HTTPS 转发

编辑 /etc/haproxy/haproxy.cfg,保留原 Kubernetes API Server 6443 配置,增加或替换原 ingress-nginx 的 80/443 配置:

haproxy
global
    log /dev/log local0 warning
    chroot /var/lib/haproxy
    pidfile /var/run/haproxy.pid
    maxconn 4000
    user haproxy
    group haproxy
    daemon
    stats socket /var/lib/haproxy/stats

defaults
    log global
    option tcplog
    option dontlognull
    timeout connect 5s
    timeout client 1h
    timeout server 1h

frontend traefik-http
    bind *:80
    mode tcp
    option tcplog
    default_backend traefik-http

backend traefik-http
    mode tcp
    option tcp-check
    balance roundrobin
    default-server inter 10s downinter 5s rise 2 fall 2
    server k8s-node01 172.22.33.101:32280 check send-proxy-v2
    server k8s-node02 172.22.33.102:32280 check send-proxy-v2

frontend traefik-https
    bind *:443
    mode tcp
    option tcplog
    default_backend traefik-https

backend traefik-https
    mode tcp
    option tcp-check
    balance roundrobin
    default-server inter 10s downinter 5s rise 2 fall 2
    server k8s-node01 172.22.33.101:32443 check send-proxy-v2
    server k8s-node02 172.22.33.102:32443 check send-proxy-v2

注意:

  • 同一个 HAProxy 实例只能有一个配置绑定 *:80,也只能有一个配置绑定 *:443
  • 如果旧 ingress-nginx frontend 仍然绑定这些端口,应修改原配置,不能重复新增相同监听端口。
  • send-proxy-v2 必须保留,否则 Traefik 无法取得真实客户端 IP。
  • 当前 HAProxy 配置的是两个 Worker 节点,不是控制平面节点。以后新增 Worker 时,需要把新节点的 NodePort 地址加入两个 backend。

6.3 检查并重新加载 HAProxy

bash
haproxy -c -f /etc/haproxy/haproxy.cfg

必须看到配置校验成功后才能重新加载:

bash
systemctl reload haproxy
systemctl status haproxy --no-pager
ss -lntp | grep -E ':80 |:443 '

检查 HAProxy 到两个 NodePort 的网络:

bash
nc -vz 172.22.33.101 32280
nc -vz 172.22.33.102 32280
nc -vz 172.22.33.101 32443
nc -vz 172.22.33.102 32443

externalTrafficPolicy: Local 要求 HAProxy 只访问确实运行着 Ready Traefik Pod 的节点。本文使用 DaemonSet 和排除控制平面节点的节点亲和性确保这一点。

6.4 新增 Worker 节点后的操作

新 Worker 加入集群并变为 Ready 后,DaemonSet 会自动在该节点启动 Traefik,不需要修改 traefik-values.yaml。先确认新节点没有控制平面标签,并检查 Traefik Pod:

bash
kubectl get node <新节点名> -L node-role.kubernetes.io/control-plane
kubectl -n traefik get pods -o wide

等待新节点上的 Traefik Pod 变为 Ready 后,在 HAProxy 主机验证 NodePort。将占位符替换为实际 IP:

bash
nc -vz <新节点IP> 32280
nc -vz <新节点IP> 32443

然后分别在 backend traefik-httpbackend traefik-https 中增加新节点。例如新增 k8s-node03,IP 为 172.22.33.103

haproxy
backend traefik-http
    # 保留已有 server 行
    server k8s-node03 172.22.33.103:32280 check send-proxy-v2

backend traefik-https
    # 保留已有 server 行
    server k8s-node03 172.22.33.103:32443 check send-proxy-v2

校验并重新加载 HAProxy:

bash
haproxy -c -f /etc/haproxy/haproxy.cfg
systemctl reload haproxy

注意:DaemonSet 能自动覆盖新增 Worker,但 HAProxy 使用的是静态 server 列表,不会自动发现 Kubernetes 新节点,因此每次扩容都必须同步更新 HAProxy 后端。

七、创建 TLS Secret

回到 k8s-master01 执行。

假设证书文件为:

text
/etc/kubernetes/addons/traefik/cert/srebro.cn.pem
/etc/kubernetes/addons/traefik/cert/srebro.cn.key

创建或更新 default 命名空间下的 TLS Secret:

bash
kubectl -n default create secret tls srebro.cn-tls \
  --cert=/etc/kubernetes/addons/traefik/cert/srebro.cn.pem \
  --key=/etc/kubernetes/addons/traefik/cert/srebro.cn.key \
  --dry-run=client -o yaml | kubectl apply -f -

检查 Secret:

bash
kubectl -n default get secret srebro.cn-tls
kubectl -n default describe secret srebro.cn-tls

私钥属于敏感数据,不要将证书私钥直接写入 Git 仓库或本文档。

八、部署 Demo 网站

8.1 创建 Deployment、Service 和测试页面

Demo Nginx 会显示它识别到的客户端 IP 和 Traefik 传入的请求头,方便验证真实 IP 透传。

bash
cat >/etc/kubernetes/addons/traefik/demo-app.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: main-app-nginx-config
  namespace: default
data:
  default.conf: |
    # 不使用镜像 nginx.conf 中已经定义的 main,避免 log_format 重名。
    log_format traefik_demo '$remote_addr - $host "$request" $status '
                            'xff="$http_x_forwarded_for" xri="$http_x_real_ip"';

    server {
        listen 80;
        server_name _;

        # Traefik Pod 位于 Kubernetes Pod CIDR 中。
        set_real_ip_from 10.244.0.0/16;
        real_ip_header X-Forwarded-For;
        real_ip_recursive on;

        access_log /var/log/nginx/access.log traefik_demo;

        location / {
            default_type text/plain;
            return 200 "Traefik demo OK\nremote_addr=$remote_addr\nx_real_ip=$http_x_real_ip\nx_forwarded_for=$http_x_forwarded_for\nhost=$host\n";
        }
    }
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: main-app
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: main-app
  template:
    metadata:
      labels:
        app: main-app
    spec:
      containers:
        - name: nginx
          #image: nginx:1.30.4
          image: docker.cnb.cool/srebro/docker-images-chrom/nginx:1.30.4_amd64
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 2
            periodSeconds: 5
          resources:
            requests:
              cpu: 20m
              memory: 32Mi
            limits:
              cpu: 200m
              memory: 128Mi
          volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: default.conf
              readOnly: true
      volumes:
        - name: nginx-config
          configMap:
            name: main-app-nginx-config
---
apiVersion: v1
kind: Service
metadata:
  name: main-app-service
  namespace: default
spec:
  selector:
    app: main-app
  ports:
    - name: http
      port: 80
      targetPort: http
      protocol: TCP
EOF

kubectl apply -f /etc/kubernetes/addons/traefik/demo-app.yaml

检查应用:

bash
kubectl -n default get pod -l app=main-app -o wide
kubectl -n default get service main-app-service
kubectl -n default get endpointslice -l kubernetes.io/service-name=main-app-service

8.2 创建 Traefik Ingress

原 ingress-nginx 案例使用:

yaml
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$2

并使用路径 /()(.*)。该规则实际上没有改变请求路径,因此迁移后直接使用标准的 / Prefix 即可,不需要 Middleware。

bash
cat >/etc/kubernetes/addons/traefik/demo-ingress.yaml <<'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: baseline-sre-test-ingress
  namespace: default
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
  ingressClassName: traefik
  tls:
    - hosts:
        - demo.srebro.cn
      secretName: srebro.cn-tls
  rules:
    - host: demo.srebro.cn
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: main-app-service
                port:
                  number: 80
EOF

kubectl apply -f /etc/kubernetes/addons/traefik/demo-ingress.yaml

检查 Ingress:

bash
kubectl -n default get ingress baseline-sre-test-ingress -o wide
kubectl -n default describe ingress baseline-sre-test-ingress

CLASS 应为 traefikADDRESS 应显示 172.22.33.99

九、验证访问和真实客户端 IP

9.1 验证 HTTP 跳转 HTTPS

在 HAProxy 之外的客户端执行:

bash
curl -I --resolve demo.srebro.cn:80:172.22.33.99 \
  http://demo.srebro.cn/

预期返回 301308,Location 指向:

text
https://demo.srebro.cn/

9.2 验证 HTTPS

bash
curl -v --resolve demo.srebro.cn:443:172.22.33.99 \
  https://demo.srebro.cn/

如果测试证书尚未被客户端信任,可以临时增加 -k,但生产验证应使用完整可信证书链:

bash
curl -vk --resolve demo.srebro.cn:443:172.22.33.99 \
  https://demo.srebro.cn/

预期响应类似:

text
Traefik demo OK
remote_addr=192.0.2.50
x_real_ip=192.0.2.50
x_forwarded_for=192.0.2.50
host=demo.srebro.cn

其中 remote_addr 应是测试客户端地址,而不是:

text
172.22.33.99
172.22.33.101
172.22.33.102
10.244.x.x

9.3 检查 Traefik 和业务日志

bash
kubectl -n traefik logs daemonset/traefik --tail=100
kubectl -n default logs deployment/main-app --tail=100

真实 IP 传递链路为:

text
客户端 IP
  -> HAProxy send-proxy-v2
  -> Traefik web/websecure.proxyProtocol.trustedIPs
  -> X-Forwarded-For / X-Real-Ip
  -> Nginx real_ip 模块
  -> $remote_addr

externalTrafficPolicy: Local 负责避免 NodePort 在 Worker 之间二次转发,但它不能代替 PROXY Protocol。因为客户端首先连接 HAProxy,如果没有 send-proxy-v2,Traefik 看到的只能是 HAProxy 地址。

十、安全启用 Traefik Dashboard

Dashboard 用于查看 EntryPoint、Router、Middleware 和 Service 状态,它不是用于点击修改 Kubernetes 配置的管理后台。

本节使用独立域名:

text
traefik.srebro.cn -> 172.22.33.99

10.1 创建 Dashboard TLS Secret

Secret 不能跨命名空间引用,因此需要在 traefik 命名空间再创建一份 TLS Secret:

bash
kubectl -n traefik create secret tls srebro.cn-tls \
  --cert=/etc/kubernetes/addons/traefik/cert/srebro.cn.pem \
  --key=/etc/kubernetes/addons/traefik/cert/srebro.cn.key \
  --dry-run=client -o yaml | kubectl apply -f -

10.2 创建 BasicAuth Secret

安装 htpasswd

bash
dnf install -y httpd-tools
mkdir -p /etc/kubernetes/addons/traefik/secrets
chmod 700 /etc/kubernetes/addons/traefik/secrets

交互式设置管理员密码,避免把明文密码写进 Shell 历史:

bash
htpasswd -cB /etc/kubernetes/addons/traefik/secrets/dashboard-users admin

创建 Secret:

bash
kubectl -n traefik create secret generic traefik-dashboard-auth \
  --from-file=users=/etc/kubernetes/addons/traefik/secrets/dashboard-users \
  --dry-run=client -o yaml | kubectl apply -f -

10.3 创建 Dashboard Middleware 和 IngressRoute

bash
cat >/etc/kubernetes/addons/traefik/dashboard.yaml <<'EOF'
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: traefik-dashboard-auth
  namespace: traefik
spec:
  basicAuth:
    secret: traefik-dashboard-auth
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: traefik-dashboard
  namespace: traefik
spec:
  ingressClassName: traefik
  entryPoints:
    - websecure
  routes:
    - kind: Rule
      match: Host(`traefik.srebro.cn`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
      middlewares:
        - name: traefik-dashboard-auth
      services:
        - name: api@internal
          kind: TraefikService
  tls:
    secretName: srebro.cn-tls
EOF

kubectl apply -f /etc/kubernetes/addons/traefik/dashboard.yaml

检查资源:

bash
kubectl -n traefik get middleware,ingressroute
kubectl -n traefik describe ingressroute traefik-dashboard

访问:

text
https://traefik.srebro.cn/dashboard/

最后的 / 不能省略。不要给 Traefik 8080 管理端口创建公网 NodePort,也不要设置 api.insecure=true

image-20260904163708749

image-20260904163646645

十一、扩展四层 TCP 服务

如果还要通过 Traefik 代理 MySQL、Redis 或其他 TCP 服务,需要同时增加:

  1. Traefik EntryPoint。
  2. Traefik Service 中的固定 NodePort。
  3. IngressRouteTCP
  4. HAProxy TCP frontend/backend。

以 MySQL 为例,可以在 traefik-values.yamlports 下增加:

yaml
  mysql:
    port: 3306
    expose:
      default: true
    exposedPort: 3306
    nodePort: 32306
    protocol: TCP
    proxyProtocol:
      trustedIPs:
        - 172.22.33.99/32
      insecure: false

更新 Traefik:

bash
helm upgrade traefik traefik/traefik \
  --namespace traefik \
  --version 41.4.0 \
  --values /etc/kubernetes/addons/traefik/traefik-values.yaml \
  --atomic --wait --timeout 10m

创建 TCP 路由:

yaml
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
  name: mysql
  namespace: database
spec:
  ingressClassName: traefik
  entryPoints:
    - mysql
  routes:
    - match: HostSNI(`*`)
      services:
        - name: mysql
          port: 3306

HAProxy 对应配置:

haproxy
frontend mysql
    bind *:3306
    mode tcp
    option tcplog
    default_backend traefik-mysql

backend traefik-mysql
    mode tcp
    option tcp-check
    balance roundrobin
    server k8s-node01 172.22.33.101:32306 check send-proxy-v2
    server k8s-node02 172.22.33.102:32306 check send-proxy-v2

对于 HTTP/HTTPS,Traefik 可以通过 X-Forwarded-For 把客户端地址交给应用。纯 TCP 没有 HTTP Header;如果 TCP 后端也必须取得客户端地址,需要通过 ServersTransportTCP 继续向后端发送 PROXY Protocol,并且后端程序本身必须支持 PROXY Protocol。否则后端只能看到 Traefik 的连接地址。

数据库等敏感服务不应默认暴露到公网,应同时使用网络 ACL、HAProxy 源地址限制或 Traefik MiddlewareTCP IPAllowList。

十二、Helm 常用运维命令

12.1 查看当前配置

bash
helm list -n traefik
helm status traefik -n traefik
helm get values traefik -n traefik --all
helm get manifest traefik -n traefik
helm history traefik -n traefik

12.2 修改配置

修改:

text
/etc/kubernetes/addons/traefik/traefik-values.yaml

然后执行:

bash
helm upgrade traefik traefik/traefik \
  --namespace traefik \
  --version 41.4.0 \
  --values /etc/kubernetes/addons/traefik/traefik-values.yaml \
  --atomic --wait --timeout 10m

12.3 回滚

查看历史版本:

bash
helm history traefik -n traefik

回滚到指定修订,例如 Revision 1:

bash
helm rollback traefik 1 -n traefik --wait --timeout 10m

12.4 升级 Chart 和 CRD

先阅读目标版本的发布说明。Traefik Chart 的大版本可能包含不兼容变更,不能只修改版本号后直接升级。

bash
helm repo update
helm search repo traefik/traefik --versions | head -20

假设目标版本为 <NEW_CHART_VERSION>,先升级 CRD:

bash
helm show crds traefik/traefik \
  --version <NEW_CHART_VERSION> | \
  kubectl apply --server-side --force-conflicts -f -

再升级 Release:

bash
helm upgrade traefik traefik/traefik \
  --namespace traefik \
  --version <NEW_CHART_VERSION> \
  --values /etc/kubernetes/addons/traefik/traefik-values.yaml \
  --atomic --wait --timeout 10m

不要使用 --reuse-values 跨 Traefik Chart 大版本升级,因为旧字段可能与新 Chart 默认值冲突。

12.5 卸载说明

以下命令会删除 Traefik Release,执行前先确认业务入口已经迁移:

bash
helm uninstall traefik -n traefik

Helm 卸载通常不会自动删除 CRD;直接删除 CRD 会连带删除对应的 IngressRoute、Middleware 等自定义资源,因此不要在生产集群中随意执行 CRD 删除命令。

十三、常见问题

13.1 HAProxy 到 NodePort 不通

检查:

bash
kubectl -n traefik get pod -o wide
kubectl -n traefik get svc traefik -o yaml
iptables -t nat -L KUBE-NODEPORTS -n | grep -E '32280|32443'

在 HAProxy 节点测试:

bash
nc -vz 172.22.33.101 32443
nc -vz 172.22.33.102 32443

使用 externalTrafficPolicy: Local 时,目标 Worker 必须有本机 Ready Traefik Pod。

十四、参考资料

最近更新