Cert-Manager在Kubernetes上:自动化TLS证书管理完整配置指南
Cert-Manager 是一个开源 Kubernetes 控制器,通过直接与 Let’s Encrypt、HashiCorp Vault 和私有 PKI 系统等证书颁发机构集成,完全自动化 TLS 证书的生命周期——从初始颁发到验证和续期。它通过将证书视为原生 Kubernetes 资源,并通过自定义资源定义(CRDs)以声明方式进行管理,从而消除了手动证书工作流程。
对于任何生产级 Kubernetes 集群而言,过期或配置错误的 TLS 证书是导致计划外停机的最常见原因之一。Cert-Manager 通过持续协调证书状态、在到期前自动续期凭证,并将生成的密钥材料存储在 Kubernetes Secrets 中(Ingress 控制器和工作负载可立即使用)来解决这一问题。
为什么 Cert-Manager 是 Kubernetes TLS 自动化的标准
在 Cert-Manager 成为事实上的解决方案之前,团队要么在各个节点上编写 certbot 续期脚本,要么跨命名空间手动轮换证书,要么依赖造成供应商锁定的云提供商特定集成。Cert-Manager 将所有这些方法统一在单一的、Kubernetes 原生的控制平面下。
相比临时方案的主要优势:
- 声明式管理:证书意图以 YAML 清单表达,与应用程序代码一起进行版本控制。
- 自动协调:控制器循环检测期望证书状态与实际证书状态之间的偏差,并在无需人工干预的情况下进行纠正。
- 多颁发者支持:单个集群可以同时使用 Let’s Encrypt 用于面向公众的服务、内部 CA 用于服务网格 mTLS,以及 HashiCorp Vault 用于对密钥敏感的工作负载。
- 审计跟踪:每个
CertificateRequest对象都持久化在 Kubernetes API 中,为您提供完整的颁发历史记录。
在具有 NVMe 存储支持和完整 root 访问权限的 VPS 托管平台上运行 Kubernetes,可为您提供配置自定义 DNS 解析器、管理 ACME HTTP-01 挑战防火墙规则以及不受限制地安装集群级 CRDs 所需的控制权——这些都是生产级 Cert-Manager 部署的先决条件。
核心架构:Cert-Manager 内部工作原理
了解 Cert-Manager 的内部架构可以防止配置错误,并帮助您快速调试颁发失败问题。
证书生命周期状态机
Cert-Manager 通过确定性状态机管理证书。每个 Certificate 资源经历以下状态转换:
- Pending(待处理)——
Certificate对象存在,但尚未完成有效的CertificateRequest。 - Issuing(颁发中)——已创建
CertificateRequest并提交给配置的颁发者。 - Ready(就绪)——有效的、未过期的证书存储在引用的 Kubernetes
Secret中。 - Renewal Pending(续期待处理)——证书处于
renewBefore窗口内(默认:到期前 30 天),正在处理新的CertificateRequest。
控制器监视集群范围内的所有 Certificate 对象并持续协调其状态。如果 Secret 被手动删除,Cert-Manager 会检测到缺失的资源并立即触发重新颁发——这是防止意外中断的关键自愈行为。
核心 CRD 组件
| 资源 | 范围 | 用途 |
|---|---|---|
| — | — | — |
| `Issuer` | 命名空间 | 为单个命名空间定义 CA 配置 |
| `ClusterIssuer` | 集群范围 | 定义可供所有命名空间使用的 CA 配置 |
| `Certificate` | 命名空间 | 声明所需的 TLS 证书及其属性 |
| `CertificateRequest` | 命名空间 | 跟踪单个时间点的证书签名请求 |
| `Order` | 命名空间 | 表示与 CA 的 ACME 订单(例如 Let’s Encrypt) |
| `Challenge` | 命名空间 | 跟踪单个 ACME 挑战(HTTP-01 或 DNS-01) |
Order 和 Challenge 资源由 ACME 颁发者自动创建——您很少直接与它们交互,但当颁发停滞时,检查它们是主要的调试路径。
证书存储和 Secret 格式
颁发后,Cert-Manager 将三个数据键写入目标 Kubernetes Secret:
tls.crt——完整的证书链(叶证书 + 中间证书),PEM 编码。
tls.key——私钥,PEM 编码。
ca.crt——颁发 CA 证书(为内部 CA 填充;ACME 颁发者可能为空)。
Ingress 控制器、服务网格和应用程序 Pod 直接挂载此 Secret。该格式与 NGINX、Traefik、Istio、Linkerd 以及大多数其他 Kubernetes 原生 TLS 消费者兼容。
支持的颁发者及各自的使用场景
ACME(Let’s Encrypt 及其替代方案)
ACME 协议是最常见的颁发路径。Cert-Manager 实现了完整的 RFC 8555 ACME 客户端,支持暂存和生产端点。
HTTP-01 挑战:Cert-Manager 临时在 http://<domain>/.well-known/acme-challenge/<token> 提供令牌服务。这要求域名解析到端口 80 上可公开访问的 IP。它适用于单域名和 SAN 证书,但无法颁发通配符证书。
DNS-01 挑战:Cert-Manager 通过 DNS 提供商 API 创建 _acme-challenge.<domain> TXT 记录。这可在防火墙后工作,支持通配符证书(*.example.com),并且对于任何未直接暴露于互联网的集群都是必需的。支持的 DNS 提供商包括 Cloudflare、Route53、Google Cloud DNS、Azure DNS 以及通过 webhook 解析器支持的许多其他提供商。
HashiCorp Vault
Vault 颁发者与 Vault 的 PKI 密钥引擎集成。它是以下场景的首选:
内部微服务 mTLS,其中证书必须是短期的(小时级别,而非月级别)。
具有严格密钥保管要求的环境,其中私钥绝不能离开 Vault。
与 Vault 租约续期系统绑定的自动证书轮换。
自签名和 CA 颁发者
SelfSigned 颁发者生成由证书自身私钥签名的证书——适用于在集群内引导根 CA。CA 颁发者使用包含 CA 密钥对的 Kubernetes Secret 来签署证书,使其适合内部开发环境和服务网格 PKI。
Venafi
Venafi 集成面向具有集中式证书策略执行、合规审计以及与硬件安全模块(HSMs)集成需求的企业环境。
Cert-Manager 与其他 TLS 自动化方案的对比
方案
通配符支持
Kubernetes 原生
多 CA
自动续期
复杂度
—
—
—
—
—
—
Cert-Manager
是(DNS-01)
是(CRDs)
是
是
中等
手动 Certbot
否(仅 HTTP-01)
否
有限
脚本化
低–中等
云负载均衡器 TLS 终止
视情况而定
否
否
是
低
Istio / 服务网格 CA
仅内部
是
否
是
高
HashiCorp Vault(独立)
是
否
是
手动
高
对于在独立服务器或 VPS 上运行 Kubernetes 的团队,Cert-Manager 是唯一同时具备 Kubernetes 原生、支持所有主要 CA 且无需持续人工干预的方案。
安装 Cert-Manager:生产就绪配置
前提条件
Kubernetes 1.22+(推荐 1.26+ 以获得完整的 CRD 稳定性)
配置了集群管理员权限的 kubectl如果您正在管理自己的域名,强烈建议通过提供 API 的提供商进行域名注册——这可以实现完全自动化的 DNS-01 挑战解析,无需人工干预。
第一步:安装 CRDs 和 Cert-Manager 控制器
推荐的生产安装方法是使用 Helm,并单独管理 CRDs 以避免 Helm 升级冲突:
# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io
helm repo update
# Install Cert-Manager with CRDs included
helm install cert-manager jetstack/cert-manager
--namespace cert-manager
--create-namespace
--version v1.14.5
--set installCRDs=true
--set global.leaderElection.namespace=cert-manager在继续之前,验证所有三个核心 Pod 是否正在运行:
kubectl get pods --namespace cert-manager预期输出:
NAME READY STATUS RESTARTS AGE
cert-manager-xxxxxxxxx-xxxxx 1/1 Running 0 60s
cert-manager-cainjector-xxxxxxxxx-xxxxx 1/1 Running 0 60s
cert-manager-webhook-xxxxxxxxx-xxxxx 1/1 Running 0 60scainjector 将 CA 捆绑数据注入 webhook 配置。webhook 验证和变更 Cert-Manager CRD 对象——如果它未运行,所有针对 Cert-Manager 资源的 kubectl apply 操作都将失败。
第二步:为 Let’s Encrypt 配置 ClusterIssuer
始终从 Let’s Encrypt 暂存环境开始,以避免触及生产速率限制(每个注册域名每周 50 个证书):
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-staging-account-key
solvers:
- http01:
ingress:
class: nginx一旦暂存证书成功颁发,创建生产颁发者:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: nginx应用两个清单:
kubectl apply -f clusterissuer-staging.yaml
kubectl apply -f clusterissuer-prod.yaml验证颁发者就绪状态:
kubectl describe clusterissuer letsencrypt-prodStatus.Conditions 字段必须显示 Ready: True。如果显示 False,则 ACME 账户注册失败——请检查电子邮件地址以及 cert-manager Pod 的网络连接。
第三步:显式请求证书
您可以通过直接创建 Certificate 资源或为 Ingress 资源添加注解来请求证书。显式 Certificate 方法为您提供更多控制:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com-tls
namespace: production
spec:
secretName: example-com-tls-secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: example.com
dnsNames:
- example.com
- www.example.com
duration: 2160h # 90 days (Let's Encrypt default)
renewBefore: 720h # Renew 30 days before expiry应用并监控颁发过程:
kubectl apply -f certificate.yaml
kubectl describe certificate example-com-tls -n production查看 CertificateRequest 和 Order 对象以获取详细状态:
kubectl get certificaterequest -n production
kubectl describe order -n production第四步:Ingress 注解方法(简化版)
对于使用 NGINX Ingress Controller 的团队,Cert-Manager 可以通过注解自动触发——无需单独的 Certificate 清单:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
namespace: production
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
- www.example.com
secretName: example-com-tls-secret
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80Cert-Manager 检测到注解并自动创建相应的 Certificate 资源。这是 GitOps 工作流中最常见的模式。
高级配置模式
使用 DNS-01 的通配符证书(Cloudflare 示例)
通配符证书需要 DNS-01 验证。首先,创建包含您的 Cloudflare API 令牌的 Secret:
kubectl create secret generic cloudflare-api-token
--from-literal=api-token=<YOUR_CLOUDFLARE_API_TOKEN>
--namespace cert-manager使用 DNS-01 解析器配置 ClusterIssuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-prod-dns-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token请求通配符证书:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-example-com
namespace: production
spec:
secretName: wildcard-example-com-tls
issuerRef:
name: letsencrypt-prod-dns
kind: ClusterIssuer
dnsNames:
- "*.example.com"
- example.com关键陷阱:当被 ClusterIssuer 引用时,包含 DNS 提供商凭证的 Secret 必须位于 cert-manager 命名空间中。如果将其放置在其他命名空间,控制器将无法读取它,颁发将静默失败。如果挑战停滞,请使用 kubectl logs -n cert-manager deploy/cert-manager 检查 cert-manager 控制器日志。
使用 CA 颁发者的内部 PKI
对于集群内服务间的 mTLS,由自签名根支持的 CA 颁发者比 ACME 更为合适:
# Step 1: Create a self-signed root CA certificate
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-root
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-root-ca
namespace: cert-manager
spec:
isCA: true
commonName: internal-root-ca
secretName: internal-root-ca-secret
issuerRef:
name: selfsigned-root
kind: ClusterIssuer
---
# Step 2: Create a CA issuer backed by the root certificate
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: internal-ca-issuer
spec:
ca:
secretName: internal-root-ca-secret服务现在可以从 internal-ca-issuer 请求短期证书用于 mTLS,无需任何外部 CA 依赖。
为高安全性环境配置 renewBefore
默认的 renewBefore 为 30 天,适用于 90 天有效期的 Let’s Encrypt 证书。对于短期内部证书(例如 24 小时有效期),将 renewBefore 设置为总时长的一个分数:
spec:
duration: 24h
renewBefore: 8hCert-Manager 将在到期前 8 小时开始续期,在 24 小时证书生命周期内为控制器提供三个续期窗口——这是在集群遭遇短暂 API 服务器不可用时的关键安全边际。
调试常见的 Cert-Manager 故障
证书卡在”Issuing”状态
# Check the Certificate status
kubectl describe certificate <name> -n <namespace>
# Check the associated CertificateRequest
kubectl describe certificaterequest -n <namespace>
# Check the ACME Order
kubectl describe order -n <namespace>
# Check the Challenge
kubectl describe challenge -n <namespace>
# Check controller logs
kubectl logs -n cert-manager deploy/cert-manager --tail=100常见根本原因:
- HTTP-01 失败:Ingress 控制器未正确路由
/.well-known/acme-challenge/流量。验证挑战Ingress对象是否已创建,以及端口 80 是否可从互联网访问。主机上的防火墙必须允许入站 TCP/80。 - DNS-01 失败:DNS 提供商 API 令牌权限不足,或 DNS 传播尚未完成。Let’s Encrypt 的验证服务器可能查询与您本地 DNS 不同的解析器——60–120 秒的传播延迟是正常的。
- 超出速率限制:Let’s Encrypt 对每个域名每小时执行 5 次失败验证尝试的限制。达到此限制后,您必须等待才能重试。始终先使用暂存端点进行测试。
- Webhook 未就绪:如果 cert-manager webhook Pod 未运行,所有 CRD 操作将因连接被拒绝错误而失败。确保 webhook 服务具有有效的端点。
验证已部署的证书
# Check the Secret contents
kubectl get secret example-com-tls-secret -n production -o jsonpath='{.data.tls.crt}' | base64 -d | openssl x509 -noout -text
# Check expiry date specifically
kubectl get secret example-com-tls-secret -n production -o jsonpath='{.data.tls.crt}' | base64 -d | openssl x509 -noout -enddate生产加固检查清单
在生产环境中部署 Cert-Manager 不仅仅需要默认的 Helm 安装。在上线前应用以下加固措施:
- 资源限制:在所有三个 Cert-Manager Pod 上设置 CPU 和内存
requests和limits,以防止共享节点上的资源争用。 - RBAC 审计:Cert-Manager 需要广泛的 RBAC 权限。审查已安装的
ClusterRole对象,并将其限制为您的颁发者类型所需的最低权限。 - 独立命名空间:始终将 Cert-Manager 部署到其自己的
cert-manager命名空间中,与应用程序工作负载隔离。 - 监控:公开 Cert-Manager 的 Prometheus 指标端点(
--metrics-listen-address),并对certmanager_certificate_expiration_timestamp_seconds设置告警,以在续期失败影响服务之前捕获问题。 - 备份 CRD 状态:将
Certificate、ClusterIssuer和Issuer对象纳入您的集群备份策略。在灾难恢复事件后丢失这些清单意味着需要手动重新创建所有证书配置。 - 暂存验证:切勿针对生产 ACME 端点颁发您的第一个证书。速率限制违规可能会封锁您的域名长达一周。
如果您正在运行多个 Kubernetes 集群——例如,将暂存和生产工作负载分开——VPS 控制面板可以简化集群生命周期管理,并为您提供统一界面来配置每个集群运行所需的底层基础设施。
对于需要在 TLS 终止端点后进行 GPU 加速推理或 ML 服务的工作负载,相同的 Cert-Manager 模式同样适用于 GPU 托管基础设施,在这些基础设施上,自动化证书管理对于保护模型 API 端点同样至关重要。
实用决策矩阵:选择合适的颁发者
| 场景 | 推荐颁发者 | 挑战类型 | 通配符 |
|---|---|---|---|
| — | — | — | — |
| 公共 Web 应用,单域名 | `letsencrypt-prod` | HTTP-01 | 否 |
| 公共 Web 应用,多个子域名 | `letsencrypt-prod` | DNS-01 | 是 |
| 气隙 / 私有集群 | `CA` 或 `Vault` | N/A(内部) | 是 |
| 有合规要求的企业 | `Venafi` | N/A | 是 |
| 服务网格 mTLS(短期证书) | `Vault` 或 `CA` | N/A(内部) | 是 |
| 开发 / 本地集群 | `SelfSigned` 或 `CA` | N/A | 是 |
关键要点
- 使用 Helm 配合
installCRDs=true安装 Cert-Manager,并在切换到生产环境之前始终针对暂存 ACME 端点进行验证。 - 对于多命名空间集群使用
ClusterIssuer;仅在需要按团队 CA 隔离时使用命名空间范围的Issuer。 - DNS-01 对于通配符证书以及无法从互联网通过端口 80 直接访问的集群是严格要求的。
renewBefore字段在生产环境中不是可选的——将其设置为证书总duration的至少三分之一。- 当颁发停滞时,调试路径始终是:
Certificate→CertificateRequest→Order→Challenge→ 控制器日志。 - 由
ClusterIssuer引用的 DNS 提供商 API 凭证必须位于cert-manager命名空间中,而不是应用程序命名空间中。 - 在 Prometheus 中监控
certmanager_certificate_expiration_timestamp_seconds并设置告警——静默续期失败是最危险的故障模式。 - 将您的
Certificate和ClusterIssuer清单作为灾难恢复计划的一部分进行备份。
常见问题解答
Cert-Manager 中 Issuer 和 ClusterIssuer 有什么区别?
Issuer 是命名空间范围的,只能在其定义的命名空间内颁发证书。ClusterIssuer 是集群范围的,可以被任何命名空间中的 Certificate 资源引用。对于大多数生产集群,ClusterIssuer 是正确的选择,除非您需要严格的命名空间级 CA 隔离。
为什么我的 Cert-Manager 证书卡在”Issuing”状态?
使用 kubectl describe 检查同一命名空间中的 Order 和 Challenge 对象。最常见的原因是:防火墙阻止端口 80(HTTP-01)、DNS API 凭证不正确或传播延迟(DNS-01)、超出 Let’s Encrypt 速率限制,或 cert-manager webhook Pod 未运行。始终使用 kubectl logs -n cert-manager deploy/cert-manager 检查控制器日志。
Cert-Manager 可以从 Let’s Encrypt 颁发通配符证书吗?
可以,但只能使用 DNS-01 挑战类型。HTTP-01 无法验证通配符域名。您必须在 ClusterIssuer 解析器配置中配置 DNS 提供商集成(Cloudflare、Route53 等),并确保 API 凭证有权限在目标域名上创建 TXT 记录。
Cert-Manager 如何自动处理证书续期?
Cert-Manager 的控制器持续将每个 Certificate 对象的到期时间与当前时间进行比较。当剩余有效期低于 renewBefore 阈值(默认:30 天)时,控制器创建新的 CertificateRequest,完成 ACME 挑战,并原子性地替换目标 Secret 的内容——所有这些都无需重启使用该证书的 Pod。将 Secret 作为卷挂载的 Pod 将在下一次 kubelet 同步周期时看到更新后的证书。
Cert-Manager 是否适合保护内部服务间通信,而不仅仅是公共 HTTPS?
是的。使用 CA 颁发者或 HashiCorp Vault 颁发者,Cert-Manager 可以为微服务之间的内部 mTLS 颁发短期证书。这是服务网格部署中的常见模式,其中每个工作负载都需要唯一的身份证书。duration 和 renewBefore 字段允许证书的有效期短至分钟级别,并实现完全自动化的轮换。
