Ingress + TLS 证书自动化
本页目录
场景
生产环境对外服务必须 HTTPS。手动申请、续期、分发证书太麻烦,尤其多域名、多环境时。
cert-manager + Let's Encrypt 实现:自动申请、自动续期、自动分发到 Ingress。
前置条件
- K8s 1.19+,已安装 Ingress Controller(nginx-ingress / traefik / alb)
- 域名已解析到 Ingress 的公网 IP / LB
- 集群能访问外网(访问 Let's Encrypt API:
acme-v02.api.letsencrypt.org:443) - Helm 3 已安装
配置
1. 安装 cert-manager
# 方式 1:Helm(推荐)
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install cert-manager jetstack/cert-manager \
-n cert-manager --create-namespace \
--version v1.13.0 \
--set installCRDs=true \
--set prometheus.enabled=false
# 验证
kubectl get pods -n cert-manager
kubectl get crd | grep cert-manager
2. 配置 Let's Encrypt ClusterIssuer
# cluster-issuer.yaml
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-key
solvers:
- http01:
ingress:
class: nginx # 对应 IngressClass 名称
---
# staging 环境用(测试用,不受速率限制)
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-key
solvers:
- http01:
ingress:
class: nginx
kubectl apply -f cluster-issuer.yaml
kubectl get clusterissuer
# READY 列应为 True
3. Ingress 自动申请证书
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: prod
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
- api.example.com
secretName: myapp-tls # cert-manager 自动创建
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 80
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080
kubectl apply -f ingress.yaml
# 观察证书申请过程
kubectl get certificate -n prod
kubectl describe certificate myapp-tls -n prod
kubectl describe certificaterequest -n prod
4. 通配符证书(DNS-01 挑战)
适用于:子域名多、不想暴露 HTTP 端口、内网域名。
# 需要 DNS 提供商 API Token(以 Cloudflare 为例)
# cluster-issuer-dns.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: ops@example.com
privateKeySecretRef:
name: letsencrypt-dns-key
solvers:
- dns01:
cloudflare:
email: ops@example.com
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
---
# Secret 存 API Token
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token
namespace: cert-manager
type: Opaque
stringData:
api-token: "your-cloudflare-api-token"
# Ingress 使用通配符证书
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-dns
spec:
tls:
- hosts:
- "*.example.com"
secretName: wildcard-example-com
5. 多环境证书隔离
# staging 环境用 staging issuer(不受速率限制)
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
6. 常用排查
# 证书没生成
kubectl describe certificate myapp-tls -n prod
kubectl describe certificaterequest -n prod
kubectl logs -n cert-manager -l app=cert-manager -c cert-manager
# Ingress 没挂上证书
kubectl get ingress -n prod
kubectl describe ingress myapp -n prod
# cert-manager 日志
kubectl logs -n cert-manager -l app=cert-manager -c cert-manager --tail=100
# 常见报错
# "Failed to get ACME directory" -> 网络不通、DNS 解析失败
# "No solver configured" -> Issuer 没配 solver
# "No ingresses found matching" -> IngressClass 不匹配 / 没加 annotation
# "Rate limited" -> 生产环境频率限制,改用 staging
验证
# 证书状态
kubectl get certificate -A
# READY 为 True 即成功
# 查看证书详情
kubectl get secret myapp-tls -n prod -o yaml
# 解码证书
echo "<base64>" | base64 -d | openssl x509 -text -noout
# 浏览器验证
curl -vI https://app.example.com
openssl s_client -connect app.example.com:443 -servername app.example.com
回滚
# 证书异常,强制重签
kubectl delete certificate myapp-tls -n prod
# cert-manager 会按 Issuer 重新申请
# 紧急回滚到手动证书
kubectl delete secret myapp-tls -n prod
kubectl create secret tls myapp-tls \
--cert=/path/to/cert.pem --key=/path/to/key.pem -n prod
# Ingress 自动挂载新 secret
坑
| 坑 | 现象 | 避坑 |
|---|---|---|
| 证书一直 Pending | 网络不通 / IngressClass 不对 | 先 curl acme 服务器,检查 IngressClass 名称 |
| 证书一直 Pending (DNS-01) | DNS 记录没生效 / API Token 无权限 | 先手动 dig txt 验证,检查 API Token 权限 |
| 证书自动续期失败 | cert-manager 重启丢失 lease / 网络抖动 | 设置 --feature-gates=ExperimentalCertificateSigningRequestControllers=true |
| 多域名证书其中一个失败导致全部失败 | Let's Encrypt 多域名是全或无 | 关键域名单独申请证书 |
| HTTP-01 端口 80 被占用 / 防火墙拦截 | 80 端口没暴露 / 防火墙拦截 | 确保 80 端口直通 Ingress Controller |
| 证书续期后 Ingress 没重新加载 | Ingress Controller 没监听 secret 变更 | nginx-ingress 默认 watch secret,检查 --watch-ingress-without-class |
| staging 证书信任报错 | 浏览器不信任 staging 根证书 | 仅测试用,生产必须用 prod issuer |