398 lines
25 KiB
Markdown
398 lines
25 KiB
Markdown
# 03-02-k3s Traefik ACME
|
||
|
||
> **状态:✅ 验证已完成**(2026-03,K3s 4 节点 ylc61~ylc64,Cloudflare DNS-01、Let’s Encrypt 证书、TLS 矩阵 `test01~test04.jackadam.top`,HTTPS 与 HTTP-only 各 16 目标均 200;Ansible `03-02.yml -e nginx_matrix_tls_enable=true` 已实机跑通。)
|
||
|
||
> 为 Traefik 配置 ACME 自动证书(Let's Encrypt + Cloudflare DNS 验证),并部署 **TLS 矩阵**。
|
||
>
|
||
> **为 02-05 的升级版**:02-05 为 HTTP-only(节点 IP、无域名);本页在其基础上增加 ACME 证书、域名、根路径 `/`,用于有域名时的学习或生产。
|
||
|
||
|
||
## TL;DR
|
||
|
||
- **自动化验收**:`./ansible/bin/verify.sh run 03-02`
|
||
- **关键前置**:按本文「前置条件」准备环境变量/Secret/入口 IP
|
||
- **成功判据**:达到本文「预期」且 playbook 断言通过
|
||
- **排障**:见本文「排障」
|
||
|
||
---
|
||
|
||
## 前置条件
|
||
|
||
- Traefik 已可用(见 `01-02-k3s-工作节点.md`)
|
||
- 域名托管在 Cloudflare
|
||
- 已获取 Cloudflare API Token(最小权限)
|
||
- **调度与标签要求同 02-05 矩阵**(M1~M4 仍是控制/工作 × Ingress/IngressRoute 四类):M1 需控制节点带 `node-role.kubernetes.io/control-plane` 标签且 Deployment 含 toleration,否则 M1 Pod 会 Pending。若未打标,排障思路与 `02-05-nginx-验证矩阵-一键部署.md` 中 M1 Pending 相同;**本页访问域名为 HTTPS/HTTP 根路径,不是 `/demo-mx`**。
|
||
|
||
---
|
||
|
||
## 部署说明(Pod、部署方式、配置与存储)
|
||
|
||
- **Pod / 部署**:ACME 配置通过 `HelmChartConfig` 注入到 **同一个 Traefik Deployment**。**副本数为 chart 默认值 1**(即 `deployment.replicas` 未在 values 里写时默认为 1),所以只有 1 个 Traefik Pod;与 03-01 的 Traefik 是同一套 Deployment,只是 values 里多了 ACME 参数与 env。
|
||
- **配置存在哪里**:`HelmChartConfig` 存在 **etcd**(控制节点);K3s 的 chart 控制器据此更新 Traefik 的部署参数,Traefik 进程从 **Kubernetes API** 读取 Ingress/IngressRoute,无需多 Pod 间同步。
|
||
- **ACME 存储(证书与账户)**:`acme.storage` 指向容器内 **`/data/acme.json`**。未配 hostPath 时,K3s 默认会为 Traefik 挂载卷到 `/data`(如 emptyDir 或默认持久卷),**仅当前这一个 Traefik Pod 可写**,Pod 重建后若卷不持久则需重新申请证书。若在 values 里配置了 **hostPath**(见本页可选配置),则 `/data` 对应宿主机目录,证书写在物理机路径,便于备份与复用;Traefik 仍为 1 个 Pod,不存在多副本间同步 acme.json 的问题。**推荐**:Dashboard + ACME 场景直接用 **同一份** [`traefik-dashboard-acme.yaml`](../ansible/files/03-03/traefik-dashboard-acme.yaml)(已含 **`persistence`(local-path)+ ACME**),见 `03-05-k3s-local-path-pvc.md`。不要 Dashboard 时按该文件头注释删减。
|
||
- **第一次部署随机节点、重启后怎么办**:Traefik 未指定 nodeSelector 时,首次会**随机调度**到某一节点。若使用了 **hostPath**,证书只存在于该节点的磁盘上;**Pod 被调度到其他节点**(重启、驱逐、缩容再扩容)时,新节点上的同名 hostPath 是另一块盘,**证书不会跟着走**,可能需重新申请。若希望重启或节点故障后仍保留证书,可:**① 把 Traefik 固定到某一节点**(在 HelmChartConfig 的 `deployment` 下配 `nodeSelector`,例如 `nodeSelector: { kubernetes.io/hostname: ylc61 }(节点名使用短主机名 ylc61~ylc64,便于配合 Cloudflare CDN)`),使 hostPath 始终落在同一台机;**② 或不用 hostPath**,依赖 K3s 默认持久卷(若为 local-path,则卷仍绑定某节点,Pod 重建到同节点可复用);**③ 或改用 NFS 等共享存储**挂到 `/data`,多节点可读同一证书(需自行在 values 里配 PVC/volume)。
|
||
|
||
---
|
||
|
||
## 创建 Secret
|
||
|
||
```bash
|
||
# 首次创建(不存在时)
|
||
kubectl -n kube-system create secret generic cloudflare-api-token \
|
||
--from-literal=api-token='<YOUR_CLOUDFLARE_API_TOKEN>'
|
||
|
||
# 若需更新 Token,可先删除再重建(不会影响已签发的证书,只影响后续申请/续期)
|
||
kubectl -n kube-system delete secret cloudflare-api-token --ignore-not-found=true
|
||
kubectl -n kube-system create secret generic cloudflare-api-token \
|
||
--from-literal=api-token='<YOUR_NEW_CLOUDFLARE_API_TOKEN>'
|
||
```
|
||
|
||
---
|
||
|
||
## Token 与 Secret 验证(建议强制做)
|
||
|
||
> 目的:确认 Cloudflare API Token 对 `jackadam.top`(你的 zone)是可用且“可查 zone / 可编辑 DNS”的,避免 ACME 只报失败但根因不明。
|
||
|
||
### 1)本机验证 Token 是否能查到 zone
|
||
|
||
在任意联网环境的终端运行(Token 不要手工写死到文档里,下面用占位符):
|
||
|
||
```bash
|
||
TOKEN='<YOUR_CLOUDFLARE_API_TOKEN>'
|
||
|
||
curl -sS -X GET "https://api.cloudflare.com/client/v4/zones?name=jackadam.top" \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" | jq '.success,.errors'
|
||
```
|
||
|
||
期望输出:
|
||
|
||
- `.success` 为 `true`
|
||
- `.errors` 为空数组 `[]`
|
||
|
||
如果 `.success=false`,通常会看到类似 `9109: Invalid access token` 或 `403`,这意味着 Token 依然不满足 Cloudflare API 的权限要求。
|
||
|
||
> 如果你的机器没有 `jq`,可以先把 `| jq '.success,.errors'` 去掉看原始 JSON。
|
||
|
||
### 2)集群内验证 Secret 是否写入了该 Token
|
||
|
||
用“哈希对比”的方式校验(只输出哈希,不会直接泄露 Token 原文):
|
||
|
||
```bash
|
||
# 本机 Token 的 sha256
|
||
TOKEN='<YOUR_CLOUDFLARE_API_TOKEN>'
|
||
printf "%s" "$TOKEN" | sha256sum
|
||
|
||
# 集群 Secret 解码后的 sha256(仅输出哈希)
|
||
kubectl -n kube-system get secret cloudflare-api-token \
|
||
-o jsonpath='{.data.api-token}' | base64 -d | sha256sum
|
||
```
|
||
|
||
若两行 `sha256sum` 结果一致,说明集群里的 Secret 已经写入为你当前测试的 Token。
|
||
|
||
---
|
||
|
||
## 配置 HelmChartConfig
|
||
|
||
> **重要**:同一 chart 只能有一份 `HelmChartConfig`(如 `name: traefik`)。若已按 03-01 部署了 Dashboard,再单独 apply 本文件的配置会**覆盖**掉 03-01,Dashboard 会失效。此时应二选一:**要么**使用 `03-03-k3s-traefik-dashboard-acme.md` 中的合并 YAML(Dashboard + ACME 合并为单一清单),**要么**把本页的 ACME 配置合并进已有 03-01 的 `traefik-dashboard.yaml`,只保留一个 manifest 文件。
|
||
>
|
||
> **文件选择**:K3s 自带的 `traefik.yaml` 会被 K3s 覆盖,**不要修改**。所有自定义配置(ACME、nodeSelector、hostPath 以及其他扩展配置)都应写在 **`traefik-acme.yaml`** 这一份 HelmChartConfig 里,与默认 chart 合并生效。
|
||
|
||
1. 在控制节点创建 `traefik-acme.yaml`,推荐放入 K3s manifests 目录(路径同 03-01)。**完整配置见 `ansible/files/03-02/traefik-acme.yaml`**(与 Ansible 共用),复制后替换 `<YOUR_REAL_EMAIL>` 等占位符即可。若走 **Dashboard + ACME** 且需 **证书落盘 local-path PVC**,直接用 [`traefik-dashboard-acme.yaml`](../ansible/files/03-03/traefik-dashboard-acme.yaml)(已内置 persistence,说明见 `03-05-k3s-local-path-pvc.md`)。**仅 ACME、无 Dashboard** 时仍可用本目录 [`traefik-acme.yaml`](../ansible/files/03-02/traefik-acme.yaml),并自行按 `03-05` 在 Helm values 中增加 `persistence` 块(与 `/data/acme.json` 一致)。
|
||
|
||
> 将 `<YOUR_REAL_EMAIL>` 改为你的邮箱。`/data/acme.json` 为容器内路径;`caserver` 为测试服务器(staging),正式上线前删除该行即切回生产 CA。Traefik 在容器内监听 8000/8443,由 Service 和 svclb 映射到节点 80/443。
|
||
>
|
||
> **Ping / PROXY protocol**:`--ping.entryPoint=websecure` 使 `GET /ping` 在 443 端口(HTTPS)返回 200,适合生产仅暴露 TLS、HAProxy 对 443 做健康探测(`option httpchk GET /ping` + `http-check send meth GET uri /ping ssl`)。Traefik 的 ping 一次只能指定一个 entrypoint;若 HAProxy 用 HTTP 探测 80 端口(内网/测试),可改为 `--ping.entryPoint=web`。`trustedIPs` 需包含 HAProxy 所在 IP;若 HAProxy 在前置路由器(如 192.168.2.1),需在 HAProxy 后端加 `send-proxy-v2`,否则 Traefik 无法解析 PROXY 头。
|
||
>
|
||
> **容器外(节点 80/443)在哪里设置?** 由 K3s 自带的 Traefik chart 的 Service 定义,chart 里默认已配好 80→8000、443→8443,一般无需改。若要改对外端口(例如节点用 8080 代替 80),可在 `valuesContent` 里增加 `ports` 覆盖,例如:`ports.web.expose.exposedPort: 8080`、`ports.websecure.expose.exposedPort: 8443`(与 `ports.web.port` / `ports.websecure.port` 区分:前者是节点/svc 暴露端口,后者是容器内端口)。
|
||
>
|
||
> **节点在 80/443 上的对外暴露在哪里设置?** 由 K3s 自带的 Traefik chart 的 Service 定义,chart 里默认已配好 80→8000、443→8443,一般无需改;若要改对外端口,请参考上文关于 `ports` 的说明。
|
||
|
||
2. 应用配置并等待 Traefik 重载(按实际路径选择其一复制执行):
|
||
|
||
```bash
|
||
# 默认路径
|
||
kubectl apply -f /var/lib/rancher/k3s/server/manifests/traefik-acme.yaml
|
||
kubectl -n kube-system rollout status deploy/traefik
|
||
kubectl -n kube-system logs deploy/traefik --tail=100 | grep -i acme || true
|
||
```
|
||
|
||
```bash
|
||
# 自定义 data-dir(如 /storage)
|
||
kubectl apply -f /storage/server/manifests/traefik-acme.yaml
|
||
kubectl -n kube-system rollout status deploy/traefik
|
||
kubectl -n kube-system logs deploy/traefik --tail=100 | grep -i acme || true
|
||
```
|
||
|
||
3. (可选)检查容器内 `acme.json` 是否生成:
|
||
|
||
```bash
|
||
kubectl -n kube-system exec -it deploy/traefik -- sh -c 'ls -l /data/acme.json || true'
|
||
```
|
||
|
||
> **关于持久化与备份(GitLab 等重状态服务)**:请统一使用持久化存储方案(例如 Longhorn)并制定备份策略。见 `03-07-k3s-longhorn-持久化存储.md`。
|
||
|
||
---
|
||
|
||
## 验证与使用顺序概览
|
||
|
||
> 1)按上节完成 **ACME 配置**(Secret + `traefik-acme.yaml`)。
|
||
> 2)apply **本页下方的完整矩阵 YAML**(M1~M4:TLS 仅 `websecure` + 同域名 **HTTP-only** 仅 `web` 供内网/测试,与 02-05 的 `/demo-mx` 清单不同),Traefik 会自动发现并签发证书。
|
||
> 矩阵的域名为 `test01.jackadam.top`~`test04.jackadam.top`(M1~M4 各一);M2/M4 的 hostname 按实际修改。
|
||
> **可选**:也可使用 Ansible 一键部署,见下方「Ansible 一键部署(TLS 矩阵)」。
|
||
|
||
---
|
||
|
||
## 常见问题:ACME 报 DNS 解析错误的处理
|
||
|
||
> 现象:Traefik 日志中反复出现
|
||
>
|
||
> - `lookup acme-staging-v02.api.letsencrypt.org on 10.43.0.10:53: server misbehaving`
|
||
> - 或 CoreDNS 日志中出现 `dial udp [240e:...]:53: connect: network is unreachable`
|
||
>
|
||
> 原因:宿主机启用了 IPv6 DNS,但 k3s 默认 flannel 网络只提供 IPv4 Pod 网络,CoreDNS 在 Pod 内访问 IPv6 上游 DNS 失败,导致 ACME 无法解析 Let’s Encrypt 域名。
|
||
|
||
**简单可靠的修法(保持集群 IPv4-only,仅修上游 DNS):**
|
||
|
||
1. 编辑 CoreDNS 的 ConfigMap,将上游 DNS 改为明确的 IPv4 地址:
|
||
|
||
```bash
|
||
kubectl -n kube-system edit configmap coredns
|
||
```
|
||
|
||
将 Corefile 中的
|
||
|
||
```txt
|
||
forward . /etc/resolv.conf
|
||
```
|
||
|
||
改为类似(按实际环境选择可用的 IPv4 DNS):
|
||
|
||
```txt
|
||
forward . 114.114.114.114 8.8.8.8
|
||
```
|
||
|
||
2. 重启 CoreDNS:
|
||
|
||
```bash
|
||
kubectl -n kube-system rollout restart deploy/coredns
|
||
kubectl -n kube-system rollout status deploy/coredns
|
||
```
|
||
|
||
3. 在 Traefik Pod 内验证解析是否恢复正常:
|
||
|
||
```bash
|
||
POD=$(kubectl -n kube-system get pod -l app.kubernetes.io/name=traefik -o jsonpath='{.items[0].metadata.name}')
|
||
kubectl -n kube-system exec -it "$POD" -- nslookup acme-staging-v02.api.letsencrypt.org || \
|
||
kubectl -n kube-system exec -it "$POD" -- nslookup acme-v02.api.letsigncrypt.org
|
||
```
|
||
|
||
解析成功后,重新访问 `https://test01.jackadam.top`~`https://test04.jackadam.top`,Traefik 会重新尝试通过 ACME 申请证书,`openssl s_client` 输出中的 `issuer` 将不再是 `TRAEFIK DEFAULT CERT`。
|
||
|
||
> 若需要在 k3s 上完整打通 IPv6 / dual-stack(包括 Pod 级 IPv6 出网),通常需要使用支持 IPv6 的 CNI(如 Calico、Cilium)并重新设计网络,建议参考单独的 Calico/Cilium 双栈实验文档。
|
||
|
||
## TLS 矩阵清单(02-05 升级版)
|
||
|
||
> **唯一真源**:[`ansible/files/03-02/`](../ansible/files/03-02/)(`01-control-ingress.yaml`~`04-worker-ingressroute.yaml`),与 [`ansible/playbooks/verify/03-02.yml`](../ansible/playbooks/verify/03-02.yml) 共用;**本文不再内联整份 YAML**。
|
||
|
||
**相对 02-05 的差异摘要**:基于域名根路径 `/`;TLS 仅绑 `websecure`;含 HTTP-only(仅 `web`)路由;与 02-05 的 `/demo-mx` 为两套资源;M2/M4 节点名与域名请在清单内编辑。
|
||
|
||
**清单目录中每文件**已同时包含 TLS 与 HTTP-only:每个 Mx 块内先为 TLS 路由(仅 `websecure`),紧跟同域名的 HTTP-only 路由(仅 `web`,内网/测试用)。**HTTP 也 200** 指 `http://test01~test04.jackadam.top` 直接返回 200(不跳转 HTTPS、不 404),由上述 4 个 HTTP-only 资源实现:M1/M3 为 Ingress(`traefik.ingress.kubernetes.io/router.entrypoints: web`,无 `spec.tls`、无 certresolver),M2/M4 为 IngressRoute(`entryPoints: [web]`,无 `tls` 段),与 TLS 路由共用同一批 Deployment/Service。**生产仅暴露 TLS 时**可删除这些 HTTP-only 资源。
|
||
|
||
---
|
||
|
||
部署有两种方式,任选其一即可。
|
||
|
||
**方式一:使用仓库 YAML 目录(推荐与文档一致)**
|
||
|
||
1. 在仓库中编辑 [`ansible/files/03-02/`](../ansible/files/03-02/) 内各文件(M2/M4 节点名、域名等)。
|
||
2. 按 k3s 存储方案可将整个目录复制到控制节点 manifests,或直接在仓库根执行 `kubectl apply -f ansible/files/03-02/ -R`(与 `01-01-k3s-控制节点含traefik.md` 存储路径说明一致)。
|
||
|
||
3. 清理示例(路径与 apply 时一致):
|
||
```bash
|
||
kubectl delete -f ansible/files/03-02/ -R --ignore-not-found=true
|
||
```
|
||
或沿用下文按资源名删除。
|
||
或按资源名删除(与路径无关):
|
||
```bash
|
||
kubectl delete deployment,svc -n default nginx-m1 nginx-m2 nginx-m3 nginx-m4 --ignore-not-found=true
|
||
kubectl delete ingress -n default nginx-m1 nginx-m3 nginx-m1-http nginx-m3-http --ignore-not-found=true
|
||
kubectl delete ingressroute -n default nginx-m2 nginx-m4 nginx-m2-http nginx-m4-http --ignore-not-found=true
|
||
kubectl delete configmap -n default nginx-m1-html nginx-m2-html nginx-m3-html nginx-m4-html --ignore-not-found=true
|
||
```
|
||
|
||
**方式二:使用仓库内 manifests + Ansible playbook**
|
||
|
||
- 直接使用仓库中已合并好的 4 个文件(每个 Mx 含 TLS + HTTP-only),在**仓库根目录**执行:
|
||
```bash
|
||
kubectl apply -f ansible/files/03-02/ -R
|
||
```
|
||
需保证当前环境已设置 KUBECONFIG 或 `kubectl` 已指向目标集群(例如在控制节点上或已配置远程 kubeconfig)。
|
||
- 一键部署/清理推荐用 Playbook(会先删 02-05 残留、再 apply、并做就绪与 curl 验证):
|
||
- 在 `ansible/` 目录下:`ansible-playbook -i inventory.ini playbooks/verify/03-02.yml`
|
||
- 在仓库根目录:`ansible-playbook -i ansible/inventory.ini ansible/playbooks/verify/03-02.yml`
|
||
- 清理:同上命令后加 `-e mode=cleanup`。
|
||
|
||
验证 HTTP 与 HTTPS 是否正常(将 `192.168.2.61 … 192.168.2.64` 按实际入口节点 IP 修改):
|
||
|
||
```bash
|
||
# HTTP 验证(4 节点 × 4 域名,应均为 200)
|
||
for ip in 192.168.2.61 192.168.2.62 192.168.2.63 192.168.2.64; do
|
||
echo "=== 节点 $ip 上的 4 个域名 (HTTP) ==="
|
||
for id in 1 2 3 4; do
|
||
curl -sI "http://test0$id.jackadam.top/" --resolve "test0$id.jackadam.top:80:$ip" -o /dev/null -w "test0$id:%{http_code}\n" || echo "test0$id:fail"
|
||
done
|
||
echo ""
|
||
done
|
||
|
||
# HTTPS 验证(4 节点 × 4 域名,应均为 200,证书为 Let's Encrypt)
|
||
for ip in 192.168.2.61 192.168.2.62 192.168.2.63 192.168.2.64; do
|
||
echo "=== 节点 $ip 上的 4 个域名 (HTTPS) ==="
|
||
for id in 1 2 3 4; do
|
||
curl -skI "https://test0$id.jackadam.top/" --resolve "test0$id.jackadam.top:443:$ip" -o /dev/null -w "test0$id:%{http_code}\n" || echo "test0$id:fail"
|
||
done
|
||
echo ""
|
||
done
|
||
```
|
||
|
||
---
|
||
|
||
|
||
若 ACME 与 Cloudflare 配置正确,Traefik 日志中可见证书申请成功;四个路径均返回 200,页面分别显示 **M1**、**M2**、**M3**、**M4**,便于区分是哪个后端。
|
||
|
||
**若 curl 报 “SSL certificate problem: self-signed certificate”**:说明 Traefik 未拿到 Let's Encrypt 证书,在用默认自签证书。按下面逐项排查:
|
||
|
||
1. **确认已部署 TLS 矩阵**(Ingress 需带 `spec.tls`、`host`、注解 `certresolver=cloudflare`):
|
||
```bash
|
||
kubectl get ingress -n default nginx-m1 -o yaml | grep -A5 "tls:\|host:\|certresolver"
|
||
```
|
||
若无 `tls` / `host` / `certresolver`,说明当前是 02-05 的非 TLS Ingress,需执行 `kubectl apply -f ansible/files/03-02/ -R`(或跑 Ansible playbook `03-02.yml -e nginx_matrix_tls_enable=true`)。
|
||
|
||
2. **看 Traefik 是否尝试/成功申请证书**:
|
||
```bash
|
||
kubectl -n kube-system logs deploy/traefik --tail=500 | grep -iE "acme|Let's Encrypt|certificate|error"
|
||
```
|
||
若有 `obtained certificate` 则已签发;若有 `acme: error` 或 `failed to get certificate`,记下错误信息(常见:DNS 挑战超时、Cloudflare API Token 权限不足、域名未在 Cloudflare 托管)。
|
||
|
||
3. **确认 Cloudflare**:test01~test04.jackadam.top 的 zone 在 Cloudflare;Secret `cloudflare-api-token` 存在且 API Token 有 “Zone:DNS:Edit”;若用 staging CA 测试,通过后再删 `caserver` 行切回生产。
|
||
|
||
4. **本地验证时加 `-k`** 可忽略证书校验只看 HTTP 是否 200:`curl -sk -o /dev/null -w "%{http_code}" https://test01.jackadam.top/ --resolve test01.jackadam.top:443:192.168.2.61`。证书正常前可用 `-k` 做功能验证。
|
||
|
||
5. **日志出现 “Router uses a nonexistent certificate resolver certificateResolver=cloudflare”**:表示当前 Traefik **未加载 ACME 配置**,没有名为 `cloudflare` 的证书解析器,因此会用自签证书。需确认本页「配置 HelmChartConfig」中的 `traefik-acme.yaml` 已放到 K3s manifests 目录并已 apply,且 Traefik 已用新配置重载:
|
||
```bash
|
||
kubectl get helmchartconfig -n kube-system traefik -o yaml
|
||
# 若存在且 valuesContent 中含 certificatesresolvers.cloudflare,则重启 Traefik 使配置生效
|
||
kubectl -n kube-system rollout restart deploy/traefik
|
||
kubectl -n kube-system logs deploy/traefik --tail=50 | grep -i cloudflare
|
||
```
|
||
若 HelmChartConfig 不存在或没有 cloudflare 段,请按本页「创建 Secret」与「配置 HelmChartConfig」重新创建并 apply。
|
||
|
||
6. **日志出现 “service not found” / “kubernetes service not found: default/nginx-m2” / “middleware … does not exist”**:说明 Ingress/IngressRoute 已存在,但对应的 **Service 或 Middleware 缺失**(例如只 apply 了部分 TLS 矩阵,或先删后 apply 时 Traefik 在中间时刻读到不完整状态)。需**完整** apply TLS 矩阵,保证 M1~M4 的 Deployment、Service、Middleware、Ingress/IngressRoute 一起就绪:
|
||
```bash
|
||
kubectl apply -f ansible/files/03-02/ -R
|
||
kubectl get svc,middleware -n default | grep -E "nginx-m|stripprefix"
|
||
```
|
||
确认 nginx-m1~m4 的 Service 与 stripprefix-m1~m4 的 Middleware 均存在后,Traefik 会重新同步路由;证书仍需按上一步确保 ACME 配置生效。
|
||
|
||
7. **日志出现 “Unable to obtain ACME certificate” 且错误含 “lookup … server misbehaving” 或 “cannot get ACME client get directory”**:说明 **集群内 DNS(CoreDNS)无法解析 Let's Encrypt 的域名**(如 acme-staging-v02.api.letsencrypt.org),Traefik Pod 连不上 ACME 服务器。排查步骤:
|
||
- 在**节点上**测解析(用节点自身 DNS):`nslookup acme-staging-v02.api.letsencrypt.org` 或 `getent hosts acme-staging-v02.api.letsencrypt.org`。若节点也解析不了,需检查节点出口 DNS 与网络(如 /etc/resolv.conf、防火墙、是否可访问公网)。
|
||
- 在**集群内**测解析:`kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup acme-staging-v02.api.letsencrypt.org`。若失败,多为 CoreDNS 的上游 DNS 不可达或不允许外网查询;可临时将 CoreDNS 的 forward 改为 8.8.8.8 / 1.1.1.1 等可访问外网的 DNS 后重试。
|
||
- 若为内网环境且节点无法直接访问外网,需通过 HTTP 代理或允许集群访问外网 DNS/443 后再申请证书。
|
||
|
||
8. **排查时开启 Traefik DEBUG 日志**:默认 INFO 下 ACME 只打少量摘要。若要看到 DNS 挑战、TXT 查询、传播等待等更细的日志,可在 `traefik-acme.yaml` 的 `additionalArguments` 最前面增加一行 `- "--log.level=DEBUG"`,然后 apply 并重启 Traefik:
|
||
```bash
|
||
# 在 valuesContent 的 additionalArguments 下第一项插入(若尚未存在)
|
||
sed -i '/additionalArguments:/a\ - "--log.level=DEBUG"' /storage/server/manifests/traefik-acme.yaml
|
||
# 若已有 additionalArguments 但无 log.level,可手动在 YAML 里加一行:- "--log.level=DEBUG"
|
||
kubectl -n kube-system apply -f /storage/server/manifests/traefik-acme.yaml
|
||
kubectl -n kube-system rollout restart deploy/traefik
|
||
```
|
||
复现问题后查看详细输出(ACME、DNS、TXT、propagation 等):
|
||
```bash
|
||
kubectl -n kube-system logs deploy/traefik --since=15m --all-containers | grep -iE "acme|dnschallenge|TXT|propagation|challenge|authorization|certificate"
|
||
```
|
||
排查结束后可将 `--log.level=DEBUG` 删掉或改为 `INFO`,避免生产日志过多。
|
||
|
||
---
|
||
|
||
## Ansible 一键部署(TLS 矩阵)
|
||
|
||
可使用 Ansible 自动部署 / 清理 TLS 矩阵(test01~test04.jackadam.top)并做 HTTPS 验证:
|
||
|
||
- **Playbook**:`ansible/playbooks/verify/03-02.yml`
|
||
- **Manifests**:`ansible/files/03-02/`(M1~M4 带 TLS,域名为 test01~test04.jackadam.top;按实际修改 M2/M4 节点名 ylc61/ylc64)
|
||
- **前置**:已按本页完成 ACME 配置,且 test01~test04.jackadam.top 已解析到入口 IP
|
||
|
||
```bash
|
||
# 一键部署 TLS 矩阵
|
||
cd ansible
|
||
ansible-playbook -i inventory.ini playbooks/verify/03-02.yml -e mode=deploy
|
||
|
||
# 一键删除 TLS 矩阵
|
||
cd ansible
|
||
ansible-playbook -i inventory.ini playbooks/verify/03-02.yml -e mode=cleanup
|
||
```
|
||
|
||
Playbook 在 `mode=deploy` 时会:拷贝 TLS manifests 到控制节点 → **若存在不含 TLS 的 nginx 矩阵(02-05),先按资源名删除**(deployments、svc、ingress、ingressroute、configmaps 共 M1~M4)→ `kubectl apply` TLS 矩阵 → 等待 Pod 就绪 → 对**所有 k3s_nodes 节点**做 HTTPS 验证(4 节点 × 4 域名 = 16 个目标,与 02-05 HTTP 矩阵一致,所有节点均为入口点)。`mode=cleanup` 时则按资源名删除 TLS 矩阵相关 Deployment/Service/Ingress/IngressRoute/ConfigMap,并清理 `/tmp/nginx-matrix-tls` 目录,恢复到未部署 TLS 矩阵前的状态。
|
||
|
||
---
|
||
|
||
## 删除部署与文件
|
||
|
||
因同一 chart 只能有一份 HelmChartConfig,后续改做 03-01(Dashboard)或 03-03(Dashboard+ACME 合并)时,建议先删除本部署并删掉 manifest 文件。
|
||
|
||
1. **删除集群内 HelmChartConfig**(Traefik 会按 chart 默认重载,ACME 配置失效):
|
||
|
||
```bash
|
||
# 默认路径
|
||
kubectl delete -f /var/lib/rancher/k3s/server/manifests/traefik-acme.yaml
|
||
kubectl -n kube-system rollout status deploy/traefik
|
||
```
|
||
|
||
```bash
|
||
# 自定义 data-dir(如 /storage)
|
||
kubectl delete -f /storage/server/manifests/traefik-acme.yaml
|
||
kubectl -n kube-system rollout status deploy/traefik
|
||
```
|
||
|
||
2. **删除宿主机上的 manifest 文件**(否则 K3s 重启会再次加载):
|
||
|
||
```bash
|
||
# 默认路径
|
||
sudo rm -f /var/lib/rancher/k3s/server/manifests/traefik-acme.yaml
|
||
```
|
||
|
||
```bash
|
||
# 自定义 data-dir(如 /storage)
|
||
sudo rm -f /storage/server/manifests/traefik-acme.yaml
|
||
```
|
||
|
||
3. **可选**:nginx 矩阵的删除见 `02-05-nginx-验证矩阵-一键部署.md` 删除小节。Cloudflare API Token 的 Secret(`cloudflare-api-token`)若不再使用可删:`kubectl -n kube-system delete secret cloudflare-api-token`。
|
||
|
||
---
|
||
|
||
## 注意事项
|
||
|
||
- 证书一直不签发:优先检查 DNS 解析与 Cloudflare Token 权限
|
||
- 首次签发慢:可等待 1-5 分钟再看日志
|
||
- 仍返回 502:优先回到后端链路排查,不是 ACME 本身问题
|
||
|
||
---
|
||
|
||
## 相关文档
|
||
|
||
- `02-05-nginx-验证矩阵-一键部署.md`(独立 HTTP 矩阵 `/demo-mx`,与本文 TLS 矩阵**非同一套**;调度/标签排障可参考)
|
||
- `01-02-k3s-工作节点.md`
|
||
- `03-04-k3s-cloudflare-tunnel-配置接入.md`
|
||
- `06-01-k3s-networkpolicy-故障排查.md`
|
||
|
||
## 下一步
|
||
|
||
- 返回 00-00-构建总览.md,按导航继续。
|
||
|
||
## 排障
|
||
|
||
- **先看 playbook 输出**:失败时先定位是 deploy/wait/http_check 哪一步。
|
||
- **集群侧总览**:`kubectl get nodes -o wide`、`kubectl -n kube-system get pods -o wide`。
|
||
- **事件与日志**:`kubectl -n <ns> describe ...`、`kubectl -n <ns> logs ... --tail=200`。
|