Cert-Manager + Traefik + 腾讯云 DNS 实现域名自动续期
AI 总结
本文介绍基于cert-manager、Traefik和腾讯云DNSPod实现Let's Encrypt证书自动申请与续期的方案。首先概述架构,包含Kubernetes集群中Traefik、Secret和cert-manager的协作,以及DNS-01验证流程。详细说明证书申请和续期的完整步骤,包括安装cert-manager、配置腾讯云CAM账号并创建Secret、部署webhook、创建ClusterIssuer和Certificate资源。强调自动续期机制(证书到期前30天触发)和故障排查方法。最终实现证书全自动管理,无需人工干预。
背景: 因证书手动续期繁琐、易过期的问题,基于
cert-manager与腾讯云 DNSPod API实现Let's Encrypt证书的全自动申请与续期。
因为我将前端也放到K8S集群里了,所有本次演示K8S集群的自动续期方式。
架构概述
┌─────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Traefik │◄─────│ Secret │ │
│ │ Ingress │ │ (TLS Cert) │ │
│ └──────────────┘ └──────┬───────┘ │
│ │ │
│ ┌─────▼──────┐ │
│ │ cert-manager│ │
│ └─────┬──────┘ │
│ │ │
└───────────────────────────────┼──────────────────────────┘
│ DNS-01 Challenge
▼
┌──────────────┐
│ 腾讯云 DNS │
│ (DNSPod) │
└──────────────┘
工作流程
- cert-manager 监控
Certificate资源 - 自动向 Let's Encrypt 发起证书申请
- 通过腾讯云 DNS API 自动添加 TXT 记录完成 DNS-01 验证
- 证书签发后自动存入 Kubernetes Secret
- Traefik 自动感知 Secret 更新,无需重启 Pod
完整工作流程
证书申请流程(首次部署)
┌─────────────────────────────────────────────────────────────────┐
│ 1. cert-manager 监控到 Certificate 资源(包含域名列表) │
│ 例如:xwutx.cn, www.xwutx.cn │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. cert-manager 向 Let's Encrypt 发起证书申请请求 │
│ 请求签发包含上述域名的证书 │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. Let's Encrypt 返回挑战(Challenge): │
│ "请证明你拥有这些域名" │
│ 验证方式:DNS-01(在 DNS 添加 TXT 记录) │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. cert-manager 调用 cert-manager-webhook-dnspod │
│ 传入域名和需要创建的 TXT 记录内容 │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 5. webhook 使用 CAM 账号(SecretId/SecretKey) │
│ 调用腾讯云 DNSPod API │
│ 【CAM 账号作用:认证身份,允许操作 DNS 记录】 │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 6. DNSPod 创建 TXT 记录: │
│ _acme-challenge.xwutx.cn TXT "随机字符串" │
│ _acme-challenge.www.xwutx.cn TXT "随机字符串" │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 7. 等待 DNS 传播(propagationTimeout: 300 秒) │
│ 每 10 秒检查一次(pollingInterval: 10) │
│ 确保全球 DNS 服务器都能查到该记录 │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 8. Let's Encrypt 查询 DNS 记录 │
│ 确认 TXT 记录存在且内容正确 │
│ → 验证通过,域名所有权确认 │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 9. Let's Encrypt 签发证书 │
│ 返回证书文件(.crt)和私钥(.key) │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 10. cert-manager 将证书存入 Kubernetes Secret │
│ Secret 名称:xwutx-tls │
│ 类型:kubernetes.io/tls │
│ 包含:tls.crt(证书)+ tls.key(私钥) │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 11. webhook 清理:删除临时 TXT 记录 │
│ 保持 DNS 记录整洁 │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 12. Traefik 感知 Secret 更新(Watch 机制) │
│ 自动加载新证书 │
│ 【无需重启 Pod,热更新】 │
└────────────────────────────┬────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 13. 用户访问 https://xwutx.cn │
│ Traefik 使用新证书建立 HTTPS 连接 │
│ 证书有效期:90 天 │
└─────────────────────────────────────────────────────────────────┘
自动续期流程(每 60 天)
证书到期前 30 天(Renewal Time)
↓
cert-manager 检测到需要续期
↓
重复上述步骤 2-13
↓
新证书自动替换旧证书
↓
Traefik 自动加载新证书
↓
全程无感知,无需人工干预
CAM 账号在流程中的作用
┌─────────────────────────────────────────────────────────┐
│ CAM 账号 = 操作腾讯云 DNS 的"钥匙" │
├─────────────────────────────────────────────────────────┤
│ │
│ 为什么需要? │
│ ├─ 域名托管在腾讯云 DNSPod │
│ ├─ Let's Encrypt 要求 DNS-01 验证 │
│ │ (添加 TXT 记录证明域名所有权) │
│ └─ cert-manager 需要权限调用 DNSPod API │
│ 来创建/删除 TXT 记录 │
│ │
│ CAM 账号的具体操作: │
│ ├─ dnspod:CreateRecord → 创建 TXT 记录 │
│ ├─ dnspod:DescribeRecordList → 查询记录是否存在 │
│ └─ dnspod:DeleteRecord → 删除 TXT 记录 │
│ │
│ 权限最小化原则: │
│ └─ 只授予 DNS 记录操作权限,不包含其他 DNSPod 权限 │
│ │
└─────────────────────────────────────────────────────────┘
环境信息
当前环境
| 项目 | 版本/信息 |
|---|---|
| Kubernetes | 1.32 |
| Traefik | 2.11.18 |
| 域名 | xwutx.cn |
| DNS 服务商 | 腾讯云 DNSPod |
| 证书类型 | Let's Encrypt (免费) |
域名清单
| 域名 | 用途 | 所在 Ingress | Secret 名称 |
|---|---|---|---|
| xwutx.cn | 不带www.访问 | ingress-main | xwutx-tls |
| www.xwutx.cn | 带www.访问 | ingress-www | xwutx-tls |
前置准备
1.安装 Helm(如未安装)
# 下载 Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# 验证
helm version
2. 检查 Traefik 版本
kubectl get deployment -n kube-system traefik -o jsonpath='{.spec.template.spec.containers[0].image}'
预期输出:
rancher/mirrored-library-traefik:2.11.18
✅ Traefik 2.11.18 支持证书热更新,无需重启 Pod
安装 cert-manager
1. 添加 Helm 仓库
helm repo add jetstack https://charts.jetstack.io
helm repo update
2. 安装 cert-manager
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true
3. 验证安装
kubectl get pods -n cert-manager
预期输出:
NAME READY STATUS RESTARTS AGE
cert-manager-7d8f4b5b8c-xxxxx 1/1 Running 0 1m
cert-manager-cainjector-6f4d8b5b8c-xxxxx 1/1 Running 0 1m
cert-manager-webhook-5c7f9b5b8c-xxxxx 1/1 Running 0 1m
配置腾讯云 DNS API
1. 创建自定义权限策略
- 登录 腾讯云 CAM 控制台
- 进入 策略 → 新建自定义策略
- 选择 按策略语法创建
- 选择 空白模板
- 填写策略名称(如
cert-manager-dns) - 粘贴以下策略内容:
{
"version": "2.0",
"statement": [
{
"action": [
"dnspod:DescribeRecordList",
"dnspod:CreateRecord",
"dnspod:DeleteRecord",
"dnspod:ModifyRecord"
],
"resource": "*",
"effect": "allow"
}
]
}
- 7.点击 创建策略
2. 创建 CAM 子账号
- 进入 用户 → 用户列表 → 新建用户 → 自定义创建
- 选择 编程访问,设置用户名(如
cert-manager-dns) - 在设置用户权限步骤,搜索并勾选刚创建的自定义策略(如
cert-manager-dns) - 点击 下一步 完成创建
- 保存 SecretId 和 SecretKey
⚠️ SecretKey 只显示一次,务必保存!
3. 创建 Kubernetes Secret
apiVersion: v1
kind: Secret
metadata:
name: tencent-cloud-secret
namespace: cert-manager
type: Opaque
stringData:
secret-id: "你的SecretId"
secret-key: "你的SecretKey"
⚠️ 注意:SecretKey 不要有空格!
4. 验证 Secret
kubectl get secret tencent-cloud-secret -n cert-manager
安装 cert-manager-webhook-dnspod
1. 安装 webhook
kubectl apply -f https://raw.githubusercontent.com/imroc/cert-manager-webhook-dnspod/master/bundle.yaml
这会创建 Deployment、Service、RBAC 权限和 APIService
2. 创建 RBAC 权限(重要!)
bundle.yaml 不包含以下 RBAC 配置,需要手动创建,否则 cert-manager 无法调用 webhook。
创建文件 cert-manager-webhook-rbac.yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cert-manager-webhook-dnspod:flowcontrol
rules:
- apiGroups: ["acme.dnspod.com"]
resources: ["dnspod"]
verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cert-manager-webhook-dnspod:flowcontrol
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cert-manager-webhook-dnspod:flowcontrol
subjects:
- kind: ServiceAccount
name: cert-manager
namespace: cert-manager
应用:
kubectl apply -f cert-manager-webhook-rbac.yaml
作用说明:
- 赋予 cert-manager ServiceAccount 操作
dnspod资源的权限 - 没有此配置,cert-manager 调用 webhook 会报错:
dnspod.acme.dnspod.com is forbidden
3. 验证安装
# 查看 webhook Pod
kubectl get pods -n cert-manager | grep dnspod
# 查看 APIService
kubectl get apiservice | grep dnspod
# 测试权限(应输出 yes)
kubectl auth can-i create dnspod.acme.dnspod.com \
--as=system:serviceaccount:cert-manager:cert-manager
预期输出:
cert-manager-webhook-dnspod-xxxxx 1/1 Running 0 1m
v1alpha1.acme.dnspod.com cert-manager/cert-manager-webhook-dnspod True 1m
yes
创建 ClusterIssuer
创建文件 cluster-issuer.yaml:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# 生产环境地址(测试环境用 https://acme-staging-v02.api.letsencrypt.org/directory)
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com # 替换为你的邮箱
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- dns01:
webhook:
groupName: acme.dnspod.com
solverName: dnspod
config:
secretIdRef:
name: tencent-cloud-secret
key: secret-id
secretKeyRef:
name: tencent-cloud-secret
key: secret-key
# DNS 传播超时配置(重要!)
propagationTimeout: 300 # 5 分钟
pollingInterval: 10 # 每 10 秒检查一次
应用:
kubectl apply -f cluster-issuer.yaml
验证 ClusterIssuer
kubectl get clusterissuer
预期输出:
NAME READY AGE
letsencrypt-prod True 1m
创建 Certificate 资源
1. 备份现有证书(可选但推荐)
# 备份现有证书
kubectl get secret xwutx-tls -n default -o yaml > xwutx-tls-backup.yaml
2. 创建 Certificate
创建文件 certificates.yaml:
# certificates.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: xwutx-cn-tls
namespace: default
spec:
secretName: xwutx-tls # 与现有 Secret 名称一致,会自动覆盖
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- xwutx.cn
- www.xwutx.cn
应用:
kubectl apply -f certificates.yaml
验证部署
1. 查看 Certificate 状态
kubectl get certificate -n default
预期输出:
NAME READY SECRET AGE
xwutx-cn-tls True xwutx-tls 2m
2. 查看证书详情
kubectl describe certificate xwutx-cn-tls -n default
3. 检查 Secret 已更新
kubectl describe secret xwutx-tls -n default
查看证书信息
echo | openssl s_client -connect xwutx.cn:443 2>/dev/null | openssl x509 -noout -dates -issuer
输出:
notBefore=Sep 15 08:57:59 2026 GMT
notAfter=Dec 14 08:57:58 2026 GMT
issuer=C = US, O = Let's Encrypt, CN = YR1
自动续期机制
工作原理
- 监控:cert-manager 持续监控
Certificate资源 - 续期时机:证书剩余有效期 < 30 天时自动续期
- 自动更新:新证书自动更新到 Secret,Traefik 自动感知
续期流程
证书到期前 30 天
↓
cert-manager 自动检测
↓
重新执行 DNS-01 验证流程
↓
签发新证书 → 更新 Secret
↓
Traefik 自动加载新证书
↓
全程无感知,无需人工干预
查看证书有效期
kubectl get certificate xwutx-cn-tls -n default -o jsonpath='{.status.notAfter}'
查看续期日志
kubectl logs -n cert-manager deployment/cert-manager --tail=100
故障排查
1. 证书一直处于 Pending 状态
# 查看证书事件
kubectl describe certificate xwutx-cn-tls -n default
# 查看证书请求
kubectl get certificaterequest -n default
# 查看订单
kubectl get order -n default
# 查看挑战
kubectl get challenge -n default
常见原因:
- DNS 传播延迟(等待 5-10 分钟)
- SecretId/SecretKey 错误
- 域名不在当前腾讯云账号下
- 权限不足(检查自定义策略是否正确)
2. 查看 cert-manager 日志
kubectl logs -n cert-manager deployment/cert-manager --tail=100
3. 查看 webhook 日志
kubectl logs -n cert-manager deployment/cert-manager-webhook-dnspod --tail=100
4. 测试 DNS 验证
# 手动检查 TXT 记录
dig TXT _acme-challenge.xwutx.cn
dig TXT _acme-challenge.www.xwutx.cn
5. 强制重新签发证书
# 删除现有证书,cert-manager 会自动重新申请
kubectl delete secret xwutx-tls -n default
kubectl delete secret con-xwutx-tls -n default
6. 清理卡住的 Challenge
# 强制删除所有 Challenge
for challenge in $(kubectl get challenge -n default -o name 2>/dev/null); do
kubectl patch $challenge -n default --type=merge -p '{"metadata":{"finalizers":[]}}' 2>/dev/null
done
kubectl delete challenge -n default --all --force --grace-period=0 2>/dev/null
评论 (0)