cert-manager:Kubernetes 自动 TLS 证书管理
在现代云原生应用中,HTTPS 已经是基本要求而非可选项。手动申请、续期、部署 TLS 证书不仅繁琐,更容易因为证书过期导致服务中断。cert-manager 是 Kubernetes 生态中事实标准的证书管理器,它通过与 Let's Encrypt、Vault、Venafi 等 CA 对接,实现证书的全自动申请与续期,彻底解放运维人员的双手。本文将带你从零开始,在 K3s 集群上部署 cert-manager,配置 Let's Encrypt 颁发机构,并为 Ingress 自动签发受信任的 HTTPS 证书。

服务器配置
本教程假设你已经有一个运行中的 K3s 集群。cert-manager 本身资源消耗较小,但需要为集群节点预留足够的资源。推荐配置如下:
| 组件 | 规格 |
|---|---|
| CPU | 2 核 |
| 内存 | 4 GB |
| 系统盘 | 40 GB SSD |
| 操作系统 | Ubuntu 22.04 LTS |
| 网络 | 公网 IP(HTTP01 challenge 必须) |
如果你还没有合适的云服务器,可考虑 雨云服务器 rainyun 。注册填 2026off 领 5 折,2 核 4GB 机型完全满足 K3s 单节点集群的运行需求。
确认集群就绪:
bash
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# k3s-master Ready control-plane,master 1d v1.29.0+k3s1
安装
添加 Helm 仓库
bash
helm repo add jetstack https://charts.jetstack.io
helm repo update
安装 cert-manager
cert-manager 依赖多个 CRD(Custom Resource Definition),推荐使用 --set installCRDs=true 在安装时一并部署:
bash
kubectl create namespace cert-manager
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.14.0 \
--set installCRDs=true \
--set global.leaderElection.namespace=cert-manager
验证安装
bash
kubectl get pods -n cert-manager
# NAME READY STATUS RESTARTS AGE
# cert-manager-7d9f5b6b8-xk9pv 1/1 Running 0 2m
# cert-manager-cainjector-6b9b4f9b8-lmn2p 1/1 Running 0 2m
# cert-manager-webhook-5b97f97b4-qrs7t 1/1 Running 0 2m
三个 Pod 全部 Running 后即安装成功。
核心概念
cert-manager 通过以下几个 CRD 来管理证书生命周期:
ClusterIssuer / Issuer
Issuer 是命名空间级别的证书颁发机构配置,ClusterIssuer 是集群级别的,可跨命名空间使用。生产环境推荐使用 ClusterIssuer。
支持的颁发机构类型:
- ACME(Let's Encrypt):最常用,支持 HTTP01 和 DNS01 两种 challenge
- CA:使用集群内部的 CA 证书签发
- Vault:对接 HashiCorp Vault
- Venafi:企业级证书管理
Certificate
Certificate 资源描述了你想要的证书规格(域名、有效期、存储的 Secret 名称等),cert-manager 会根据此资源自动向颁发机构申请证书,并将结果存入指定的 Kubernetes Secret。
CertificateRequest
CertificateRequest 是 cert-manager 内部创建的中间资源,代表一次具体的证书申请动作,通常不需要手动创建,由 Certificate 控制器自动管理。
Challenge / Order
ACME 协议中,cert-manager 会创建 Order 和 Challenge 资源来完成域名所有权验证。HTTP01 challenge 会在集群中临时创建一个 Pod 来响应 Let's Encrypt 的验证请求;DNS01 challenge 则通过修改 DNS 记录来验证。
实战示例
1. 配置 ClusterIssuer(HTTP01 Challenge)
HTTP01 是最简单的验证方式,Let's Encrypt 会向 http://<your-domain>/.well-known/acme-challenge/<token> 发起请求,要求集群能从公网访问。
yaml
# cluster-issuer-http01.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# Let's Encrypt 生产环境
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: traefik # K3s 默认使用 Traefik,nginx 集群改为 nginx
bash
kubectl apply -f cluster-issuer-http01.yaml
# 验证状态
kubectl get clusterissuer letsencrypt-prod
# NAME READY AGE
# letsencrypt-prod True 30s
2. 配置 ClusterIssuer(DNS01 Challenge with Cloudflare)
DNS01 适合通配符证书或无法从公网访问的内网集群:
yaml
# cluster-issuer-dns01.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-dns01-account-key
solvers:
- dns01:
cloudflare:
email: your-cloudflare-email@example.com
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token
首先创建 Cloudflare API Token Secret:
bash
# 在 Cloudflare 控制台创建具有 Zone:DNS:Edit 权限的 API Token
kubectl create secret generic cloudflare-api-token-secret \
--namespace cert-manager \
--from-literal=api-token=YOUR_CLOUDFLARE_API_TOKEN
bash
kubectl apply -f cluster-issuer-dns01.yaml
3. 手动创建 Certificate 资源
yaml
# my-app-certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-app-tls
namespace: default
spec:
secretName: my-app-tls-secret # cert-manager 将证书存入此 Secret
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
commonName: app.example.com
dnsNames:
- app.example.com
- www.app.example.com
duration: 2160h # 90 天
renewBefore: 360h # 提前 15 天续期
bash
kubectl apply -f my-app-certificate.yaml
# 观察证书申请过程
kubectl describe certificate my-app-tls -n default
kubectl get certificaterequest -n default
4. 通过 Ingress 注解自动签发证书
这是最常用的方式,只需在 Ingress 上加一个注解,cert-manager 会自动创建 Certificate 资源:
yaml
# my-app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
namespace: default
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod # 指定 ClusterIssuer
# 如果使用命名空间级别的 Issuer:
# cert-manager.io/issuer: letsencrypt-prod
spec:
ingressClassName: traefik
tls:
- hosts:
- app.example.com
secretName: my-app-tls-secret # cert-manager 会自动填充此 Secret
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
bash
kubectl apply -f my-app-ingress.yaml
常用命令
查看证书状态
bash
# 列出所有命名空间的证书
kubectl get certificate -A
# 查看某个证书详情(包含事件和状态)
kubectl describe certificate my-app-tls -n default
# 查看证书实际内容(过期时间等)
kubectl get secret my-app-tls-secret -n default -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates
排查申请失败问题
bash
# 查看 CertificateRequest 状态
kubectl get certificaterequest -n default
kubectl describe certificaterequest my-app-tls-xxxxx -n default
# 查看 ACME Order 状态
kubectl get order -n default
kubectl describe order my-app-tls-xxxxx -n default
# 查看 Challenge 状态
kubectl get challenge -n default
kubectl describe challenge my-app-tls-xxxxx -n default
# 查看 cert-manager 控制器日志
kubectl logs -n cert-manager deploy/cert-manager -f
# 查看 webhook 日志
kubectl logs -n cert-manager deploy/cert-manager-webhook -f
手动触发证书续期
bash
# 方法1:删除 CertificateRequest,cert-manager 会自动重新申请
kubectl delete certificaterequest my-app-tls-xxxxx -n default
# 方法2:使用 cmctl 工具强制续期
cmctl renew my-app-tls -n default
安装 cmctl(cert-manager CLI)
bash
curl -L -o cmctl https://github.com/cert-manager/cmctl/releases/latest/download/cmctl_linux_amd64
chmod +x cmctl
sudo mv cmctl /usr/local/bin/
# 检查 cert-manager 健康状态
cmctl check api
cmctl status certificate my-app-tls -n default
常见故障排查
bash
# Challenge 一直处于 pending 状态:检查 Ingress/DNS 是否可公网访问
kubectl get challenge -n default -o wide
# 证书 READY=False:查看 conditions 字段
kubectl get certificate my-app-tls -n default -o yaml | grep -A 10 conditions
# 速率限制:Let's Encrypt 有每域名每周 5 次的证书申请限制,测试时使用 staging 环境
# staging server: https://acme-staging-v02.api.letsencrypt.org/directory
cert-manager 将繁琐的 TLS 证书运维工作完全自动化,是 Kubernetes 生产集群不可或缺的组件。配合 Let's Encrypt 的免费证书,你可以零成本实现全站 HTTPS,且无需担心证书过期问题。