跳转至

Traefik 入口代理部署变更 SOP

交付物目录:traefik_deploy/(相对项目根目录) 执行时间:2026-09-17 02:30–02:38(CST) 执行结果:成功 命名空间注记:本文档首次执行时使用 traefik 命名空间;2026-09-18 已迁移至 server,文中命令与清单均已同步更新,旧命名空间已删除。

1. 背景

  • 集群为新建 Kubernetes v1.36.4(1 个 control-plane + 3 个 worker,Calico CNI),集群内没有任何入口代理组件(无 Ingress Controller、无 IngressClass、无 LoadBalancer 实现)。
  • 旧集群以 Traefik v2.5 承担 HTTP/HTTPS 入口,但:其镜像仓库 harbor.rainchen.com 在新环境已不可达;v2.11 线的安全支持已于 2026-09-07 到期;旧配置使用 traefik.containo.us CRD 组,该组在 v3 中已被移除。
  • 因此需要以当前活跃支持线重建入口代理层。

2. 目标

变更完成后应达到的可验证状态:

  • Helm release traefik 状态 deployed,chart traefik-41.6.0,应用版本 v3.7.13(Traefik 当前唯一处于 Active+Security 支持的 minor 线的最新补丁)。
  • Pod 1/1 Running 且固定运行在 k8s-master-01。
  • 对外提供 HTTP/HTTPS 入口:http://192.168.31.10/ 返回 404(已接管且无匹配路由),HTTPS 使用自签证书正常完成握手。
  • Dashboard 经 https://traefik.rainchen.top/dashboard/ 访问,未认证返回 401,认证后返回 200。
  • 具备 IngressRoute / Ingress / Middleware 路由能力,供业务后续按命名空间自行接入。

3. 前置条件

项目 要求 本次实测
集群权限 具备 cluster-admin 的 kubeconfig kubernetes-admin@kubernetes,API 192.168.31.8:16443
执行机工具 helm / kubectl / openssl / curl helm v3.16.3、kubectl v1.36.4、OpenSSL 3.0.13
chart 来源 执行机可达 chart 仓库 charts.traefik.io、traefik.github.io 不可达;改用 OCI ghcr.io 拉取
镜像来源 集群节点可达镜像仓库 节点可达 docker.io;镜像已预拉:ctr -n k8s.io images pull docker.io/library/traefik:v3.7.13
端口占用 k8s-master-01 的 80/443 空闲 已核实空闲
既有资源 无同名命名空间 / CRD 残留 traefik 命名空间与 CRD 均不存在

前置检查命令:

kubectl get ns traefik                                     # 预期:NotFound
kubectl get crd | grep traefik.io                          # 预期:无输出
timeout 10 ssh root@192.168.31.10 "ss -lntp | grep -E ':(80|443)\b'"   # 预期:无输出

4. 影响范围

  • 主机:仅 k8s-master-01(192.168.31.10),占用其宿主机 80/443/389/636/3306(由 CNI portmap 以 iptables DNAT 实现,ss 不会显示监听,属正常)。
  • 端口追加记录:2026-09-18 为内网 MariaDB 增加 mariadb entryPoint(容器 3306 → 宿主 3306),对应的 IngressRouteTCP 由 ingress_tunnel_deploy 工程下发。本次 helm upgrade 后 release revision 3,因单副本 + hostPort 需"先停旧再起新",公网入口中断约数秒;升级后实测 5 个公网域名(gitlab / prometheus / grafana / traefik / ldapadmin)均正常响应。
  • 集群对象:新增命名空间 traefik、10 个 traefik.io 组 CRD、1 个 Deployment、1 个 Service、1 个 IngressClass(默认类)、2 个 Secret、1 个 Middleware、1 个 Dashboard IngressRoute。

5. 分步操作

对应实现文件:traefik_deploy/values.yaml、traefik_deploy/manifests/10-dashboard-auth.yaml、traefik_deploy/certs/tls-san.cnf 以下代码块以实现体为准,仅供阅读、复核与手工复现;幂等与错误处理由 Helm/kubectl 自身保证。

BASE="/home/rain/Documents/k8s_ins/traefik_deploy"          # 交付物目录

# —— 步骤 1:获取 chart(官方源不可达时走 OCI)——
cd "$BASE/chart"
helm pull oci://ghcr.io/traefik/helm/traefik --version 41.6.0 -d .
tar -xzf traefik-41.6.0.tgz                                 # 解包作为离线参考
helm show values traefik-41.6.0.tgz > "$BASE/docs/chart-41.6.0-default-values.yaml"

# —— 步骤 2:生成自签证书(内部 CA 签发,有效期 3650 天)——
cd "$BASE/certs"
openssl genrsa -out ca.key 4096                             # 内部 CA 私钥
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
  -subj "/C=CN/O=Rainchen Internal/CN=Rainchen Internal CA" -out ca.crt
openssl genrsa -out tls.key 2048                            # 服务器私钥
openssl req -new -key tls.key \
  -subj "/C=CN/O=Rainchen Internal/CN=traefik.rainchen.top" -out tls.csr
openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out tls.crt -days 3650 -sha256 -extfile tls-san.cnf -extensions v3_req
chmod 600 ca.key tls.key                                    # 私钥仅属主可读
rm -f tls.csr ca.srl

# —— 步骤 3:创建命名空间(幂等写法)——
kubectl create namespace server --dry-run=client -o yaml | kubectl apply -f -

# —— 步骤 4:安装 CRD(只装 traefik.io 组,跳过 chart 中 15 个无用的 Hub CRD)——
for f in "$BASE"/chart/traefik/crds/traefik.io_*.yaml; do
  kubectl apply -f "$f"
done

# —— 步骤 5:导入 TLS 自签证书为 Secret ——
kubectl -n server create secret tls traefik-selfsigned-tls \
  --cert="$BASE/certs/tls.crt" --key="$BASE/certs/tls.key" \
  --dry-run=client -o yaml | kubectl apply -f -

# —— 步骤 6:创建 Dashboard BasicAuth 凭据(口令随机生成,不入版本库)——
openssl rand -base64 30 | tr -dc 'A-Za-z0-9' | cut -c1-20   # 生成 20 位口令
openssl passwd -apr1 "$PASS"                                # 生成 htpasswd 摘要
kubectl -n server create secret generic traefik-dashboard-auth \
  --from-literal=users="admin:${HASH}" --dry-run=client -o yaml | kubectl apply -f -
# 口令落盘于 secrets/dashboard-basic-auth.txt(权限 600),请妥善保管

# —— 步骤 7:应用 Dashboard 认证中间件 ——
kubectl apply -f "$BASE/manifests/10-dashboard-auth.yaml"

# —— 步骤 8:部署 Traefik(【高危】占用 k8s-master-01 宿主机 80/443)——
helm upgrade --install traefik "$BASE/chart/traefik-41.6.0.tgz" \
  -n server -f "$BASE/values.yaml" \
  --skip-crds --wait --timeout 5m

关键配置要点(详见 traefik_deploy/values.yaml 注释):

  • 容器内用非特权端口 8000/8443,宿主端口 80/443 由 hostPort 承担;
  • service.spec.type: ClusterIP——集群无 LoadBalancer 实现,保持默认 LoadBalancer 会一直 Pending;
  • updateStrategy.rollingUpdate.maxUnavailable: 1 / maxSurge: 0——单副本 + hostPort 场景下新 Pod 抢不到宿主端口,必须"先停旧再起新";
  • providers.kubernetesCRD.safeNaming: true、ports.*.http.aliasHeadersStrategy: delete、core.strictTLSOptions: true——按官方建议补齐的安全项。

6. 验证

# 1) release 与工作负载
helm list -n server                                        # 预期 STATUS=deployed,CHART=traefik-41.6.0
kubectl -n server get pods -o wide                          # 预期 1/1 Running,NODE=k8s-master-01
kubectl -n server get deploy traefik                        # 预期 READY 1/1

# 2) 入口连通性
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.31.10/          # 预期 404
curl -sk -o /dev/null -w '%{http_code}\n' \
  --resolve traefik.rainchen.top:443:192.168.31.10 \
  https://traefik.rainchen.top/dashboard/                   # 预期 401(未带凭据)
curl -sk -o /dev/null -w '%{http_code}\n' -u admin:<口令> \
  --resolve traefik.rainchen.top:443:192.168.31.10 \
  https://traefik.rainchen.top/dashboard/                   # 预期 200

# 3) TLS 证书
openssl s_client -connect 192.168.31.10:443 -servername traefik.rainchen.top </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -enddate                   # 预期 CN=traefik.rainchen.top

# 4) IngressClass
kubectl get ingressclass                                    # 预期 traefik (default)

# 5) 启动日志(应无 ERROR)
kubectl -n server logs deploy/traefik --tail=30 | grep -iE 'err' || echo "无错误"

本次执行实测结果:release traefik revision 3 deployed;Pod traefik-f45b5b784-zdcbw 1/1 Running(k8s-master-01);HTTP 404 / HTTPS 401 / 认证 200;证书 CN=traefik.rainchen.top 有效期至 2036-09-13;IngressClass traefik (default)。

幂等复验:重复执行步骤 8 的 helm upgrade,Deployment generation 保持为 3 不变、Pod 无重启,即资源零变更。

7. 回滚

本次为新增组件,未覆盖任何既有配置,回滚风险低。备份物:traefik_deploy/values.yaml、traefik_deploy/certs/(CA 与服务端证书)、traefik_deploy/docs/rendered-manifests.yaml(渲染快照)。

# 方案 A:仅移除 Traefik 本体,保留 CRD 与路由资源定义(推荐)
helm uninstall traefik -n server
kubectl -n server get pods                    # 预期:无 Pod,80/443 释放

# 方案 B:连命名空间一并清理(【高危】将删除该命名空间内的 Secret 等全部对象)
kubectl delete namespace server

# 方案 C:继续移除 CRD(【高危】会级联删除集群中所有 IngressRoute/Middleware 等自定义资源)
#   执行前必须先备份:
kubectl get ingressroutes,middlewares,ingressroutetcps,tlsstores -A -o yaml > /tmp/traefik-cr-backup.yaml
kubectl get crd -o name | grep 'traefik\.io' | xargs -r kubectl delete

回滚后验证:curl -s -o /dev/null -w '%{http_code}\n' http://192.168.31.10/ 应返回连接失败或非 404;helm list -n server 应无该 release;kubectl -n server get pods 为空。

8. 风险说明

风险 说明 缓解措施
入口单点 单副本固定在 k8s-master-01,节点宕机即入口中断 后续可改 deployment.kind: DaemonSet + hostNetwork 实现多节点入口;或引入 VIP
更新瞬断 单副本 + hostPort 必须先停旧 Pod,升级期间入口中断数秒 避开业务高峰变更;如需零中断,需先改多副本 + 反亲和 + 非固定端口方案
调度死锁 maxUnavailable: 0 会导致新 Pod 抢不到宿主端口而永久 Pending(本次已实际踩到) 已固定为 maxUnavailable: 1 / maxSurge: 0,勿改回
占用控制面节点端口 80/443 绑定在 control-plane 节点 端口已核实空闲;如需解耦可将 nodeSelector 指向 worker 节点
自签证书不受信 客户端需导入 certs/ca.crt,否则浏览器告警 内部 CA 已生成,可分发 ca.crt 给内网客户端;后续可换正式 CA 或 ACME
凭据泄露 Dashboard BasicAuth 口令落盘于 secrets/dashboard-basic-auth.txt 文件权限 600;口令为随机生成;如泄露执行 kubectl -n server delete secret traefik-dashboard-auth 后重跑步骤 6–7
CRD 生命周期不由 Helm 管理 使用 --skip-crds,升级 Traefik 大版本时需手工同步 CRD 升级前重跑第 5 章步骤 4;删除 CRD 属高危操作,须先备份自定义资源
Helm 3 版本偏旧 本机 helm v3.16.3,Helm 3 定于 2027-02-10 终止安全支持 后续将 helm 升级至 Helm 4(当前稳定线);chart、release 存储格式均兼容,可直接替换二进制
```

来源:/home/rain/Documents/k8s_ins/traefik_deploy/SOP.md | 最后更新:2026-09-18 04:42(由 build/sync.py 同步,请勿在站点侧修改)