一、核心定位
本文作为GitOps环境搭建系列的第四篇,聚焦cert-manager证书管理的部署与配置。在K3s集群环境中,TLS/SSL证书是保障服务通信安全的核心基础设施,无论是公网HTTPS服务还是内网私有服务,都需要规范的证书管理方案。
在GitOps环境中,cert-manager扮演”证书自动化管理中枢”角色,为所有组件(Traefik、Gitea、ArgoCD、Tekton、Harbor等)提供自动化的证书签发、续签和轮换服务。通过cert-manager,可以实现Let’s Encrypt公网证书的自动获取,或自签名证书的内网安全通信,确保整个GitOps环境的HTTPS安全访问。
二、部署前置检查
部署前需验证K3s集群状态及Traefik运行情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| kubectl get nodes
kubectl get pods -n kube-system -l app=traefik
helm version --short
nslookup traefik.example.io nslookup harbor.example.io nslookup gitea.example.io
kubectl get ns cert-manager 2>/dev/null || echo "命名空间不存在,将自动创建"
|
前置条件检查清单:
三、部署cert-manager
3.1 Helm部署(推荐方式)
1 2 3 4 5 6 7 8 9 10 11 12 13
| helm repo add jetstack https://charts.jetstack.io --force-update helm repo update
kubectl create namespace cert-manager
helm install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --version v1.8.0 \ --set installCRDs=true \ --set prometheus.enabled=false
|
3.2 验证部署状态
1 2 3 4 5 6 7 8
| kubectl get pods -n cert-manager -w
kubectl get crd | grep cert-manager
kubectl wait --for=condition=Ready pods -l app.kubernetes.io/instance=cert-manager -n cert-manager --timeout=300s
|
四、配置签发机构(ClusterIssuer)
cert-manager通过ClusterIssuer定义证书签发规则,支持Let’s Encrypt(公网)和自签名(内网)两种模式。
4.1 公网证书:Let’s Encrypt签发
测试环境ClusterIssuer(无速率限制):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-staging spec: acme: email: admin@example.io server: https://acme-staging-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-staging solvers: - http01: ingress: class: traefik
|
生产环境ClusterIssuer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: letsencrypt-prod spec: acme: email: admin@example.io server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: letsencrypt-prod solvers: - http01: ingress: class: traefik
|
Let’s Encrypt速率限制说明:
- 测试环境:无限制,适合验证配置
- 生产环境:每个域名每周50张证书,每个精确域名组合每周5张
- 建议:先在测试环境验证,再切换到生产环境
4.2 内网证书:自签名签发
对于内网GitOps环境,推荐使用自签名CA证书。以下配置创建自签名CA并基于该CA签发子证书:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: selfsigned-cluster-issuer spec: selfSigned: {} --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: selfsigned-ca namespace: cert-manager spec: isCA: true commonName: selfsigned-ca secretName: root-secret privateKey: algorithm: ECDSA size: 256 issuerRef: name: selfsigned-cluster-issuer kind: ClusterIssuer group: cert-manager.io --- apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: selfsigned-cluster-issuer spec: ca: secretName: root-secret
|
配置说明:
- 第一个
ClusterIssuer:用于生成根CA证书
Certificate资源:定义根CA证书的规格,生成后存储在root-secret中
- 第二个
ClusterIssuer:基于生成的根CA证书,用于签发其他子证书
应用配置:
1 2 3 4 5 6 7 8 9
| kubectl apply -f selfsigned-issuer.yaml
kubectl get certificate selfsigned-ca -n cert-manager kubectl get secret root-secret -n cert-manager
kubectl wait --for=condition=Ready certificate selfsigned-ca -n cert-manager --timeout=300s
|
4.3 自定义CA证书导入(可选)
如果已有自定义CA证书,可先导入到Secret中,再创建引用该CA的ClusterIssuer:
1 2 3 4 5
| kubectl create secret tls custom-ca-secret \ --namespace=cert-manager \ --cert=path/to/ca.crt \ --key=path/to/ca.key
|
1 2 3 4 5 6 7 8
| apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: custom-ca-issuer spec: ca: secretName: custom-ca-secret
|
K3s默认CA说明:
K3s会在首个Server节点启动时生成自签名CA证书,有效期为10年,存储在:
/var/lib/rancher/k3s/server/tls/client-ca.crt
/var/lib/rancher/k3s/server/tls/server-ca.crt
如需轮换K3s默认CA证书,可使用:
1
| k3s certificate rotate-ca
|
4.4 应用签发机构配置
1 2 3 4 5 6
| kubectl apply -f letsencrypt-staging.yaml kubectl apply -f selfsigned-issuer.yaml
kubectl get clusterissuer
|
五、创建域名证书
5.1 GitOps组件证书配置
使用自签名CA签发GitOps环境通配符证书:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: gitops-wildcard-cert namespace: cert-manager spec: secretName: gitops-wildcard-tls issuerRef: name: selfsigned-cluster-issuer kind: ClusterIssuer duration: 2160h renewBefore: 360h commonName: "*.example.io" dnsNames: - "*.example.io" - traefik.example.io - harbor.example.io - gitea.example.io - tekton.example.io - argocd.example.io - notary.example.io subject: organizations: - Example Organization organizationalUnits: - DevOps Team localities: - Beijing provinces: - Beijing countries: - CN privateKey: algorithm: RSA size: 2048 rotationPolicy: Always usages: - server auth - client auth
|
5.2 单个组件证书配置(示例:Traefik)
1 2 3 4 5 6 7 8 9 10 11 12 13
| apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: traefik-cert namespace: kube-system spec: secretName: traefik-dashboard-tls issuerRef: name: selfsigned-cluster-issuer kind: ClusterIssuer dnsNames: - traefik.example.io
|
5.3 应用证书配置
1 2 3 4 5 6 7
| kubectl apply -f gitops-certificates.yaml kubectl apply -f traefik-certificate.yaml
kubectl get certificates -A kubectl describe certificate gitops-wildcard-cert -n cert-manager
|
六、验证证书签发
6.1 检查证书状态
1 2 3 4 5 6 7 8 9 10 11
| kubectl get certificates --all-namespaces
kubectl describe certificate gitops-wildcard-cert -n cert-manager
kubectl get secret gitops-wildcard-tls -n cert-manager -o yaml
kubectl get events --field-selector involvedObject.name=gitops-wildcard-cert -n cert-manager
|
6.2 测试证书使用(使用IngressRoute)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| cat > test-ingressroute.yaml << EOF apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: test-tls-ingressroute namespace: default annotations: cert-manager.io/cluster-issuer: selfsigned-cluster-issuer spec: entryPoints: - websecure routes: - match: Host(`test.example.io`) kind: Rule services: - name: whoami port: 80 tls: secretName: test-tls-secret EOF
kubectl apply -f test-ingressroute.yaml
kubectl get certificate test-tls-secret -n default
curl -k https://test.example.io
kubectl delete -f test-ingressroute.yaml
|
6.3 验证自动签发功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| cat > auto-cert-test.yaml << EOF apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: auto-cert-test namespace: default annotations: cert-manager.io/cluster-issuer: selfsigned-cluster-issuer spec: entryPoints: - websecure routes: - match: Host(`auto-test.example.io`) kind: Rule services: - name: whoami port: 80 tls: secretName: auto-test-tls-secret EOF
kubectl apply -f auto-cert-test.yaml
sleep 60
kubectl get certificate auto-test-tls-secret -n default kubectl describe certificate auto-test-tls-secret -n default
kubectl delete -f auto-cert-test.yaml
|
七、生产环境最佳实践
7.1 证书管理策略
- 命名空间隔离:将CA证书、ClusterIssuer等核心资源部署在独立命名空间
- 网络策略限制:配置网络策略,限制对cert-manager组件的访问
- 监控告警:配置证书过期告警,提前发现证书问题
- 备份策略:定期备份关键证书和私钥
7.2 IngressRoute自动签发
在IngressRoute资源中添加注解,实现证书自动签发。cert-manager会自动检测注解并创建对应的Certificate资源:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: gitea-websecure namespace: gitea annotations: cert-manager.io/cluster-issuer: "selfsigned-cluster-issuer" cert-manager.io/renew-before: "360h" cert-manager.io/common-name: "Gitea Git Repository" spec: entryPoints: - websecure routes: - match: Host(`gitea.example.io`) kind: Rule services: - name: gitea-http port: 3000 tls: secretName: gitea-tls-secret
|
支持的高级注解:
cert-manager.io/cluster-issuer: 指定签发机构
cert-manager.io/issuer: 指定命名空间级签发机构
cert-manager.io/common-name: 证书通用名称
cert-manager.io/duration: 证书有效期(默认90天)
cert-manager.io/renew-before: 续签提前时间(默认30天)
cert-manager.io/usages: 证书用途(如server auth, client auth)
cert-manager.io/private-key-algorithm: 私钥算法(如RSA, ECDSA)
通配符证书配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: gitops-wildcard namespace: gitops annotations: cert-manager.io/cluster-issuer: "letsencrypt-prod" cert-manager.io/dns-names: "*.example.io,example.io" spec: entryPoints: - websecure routes: - match: HostRegexp(`{subdomain:[a-z]+}.example.io`) kind: Rule services: - name: gitops-router port: 80 tls: secretName: gitops-wildcard-tls
|
优势:
- 声明式配置:证书需求与路由配置一体化
- 自动管理:cert-manager自动处理证书的签发、续签、轮换
- 无缝集成:与Traefik IngressRoute深度集成
- 多环境支持:通过注解轻松切换测试/生产环境签发机构
7.3 证书导出与备份
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
kubectl get secret root-secret -n cert-manager -o jsonpath='{.data.ca\.crt}' | base64 --decode > ca.crt kubectl get secret root-secret -n cert-manager -o jsonpath='{.data.tls\.crt}' | base64 --decode > root-ca.crt kubectl get secret root-secret -n cert-manager -o jsonpath='{.data.tls\.key}' | base64 --decode > root-ca.key
kubectl get secret gitops-wildcard-tls -n cert-manager -o jsonpath='{.data.tls\.crt}' | base64 --decode > gitops.crt kubectl get secret gitops-wildcard-tls -n cert-manager -o jsonpath='{.data.tls\.key}' | base64 --decode > gitops.key
openssl x509 -in ca.crt -text -noout | head -20 openssl x509 -in gitops.crt -text -noout | head -20
openssl verify -CAfile ca.crt gitops.crt
kubectl get secret root-secret gitops-wildcard-tls -n cert-manager -o yaml > cert-backup-$(date +%Y%m%d).yaml
|
八、常见问题修复
| 问题现象 |
排查方向 |
修复方案 |
| 证书签发失败 |
ClusterIssuer配置 |
检查ClusterIssuer状态:kubectl describe clusterissuer <name> |
| 证书一直Pending |
域名验证失败 |
检查域名解析,确认Traefik可访问,查看cert-manager日志 |
| 证书过期未续签 |
续签配置错误 |
检查renewBefore设置,查看cert-manager控制器日志 |
| Let’s Encrypt速率限制 |
请求过于频繁 |
切换到测试环境,或等待限制解除 |
| 证书Secret不存在 |
签发过程错误 |
查看Certificate资源事件:kubectl describe certificate <name> |
| HTTPS访问证书错误 |
证书域名不匹配 |
检查证书dnsNames是否包含访问域名 |
| 自签名CA证书无效 |
CA证书未就绪 |
等待CA证书生成:kubectl wait --for=condition=Ready certificate selfsigned-ca |
九、配置参考
所有cert-manager配置文件和部署脚本请参考:
https://gitee.com/Chemmy/kube-template/tree/master/devops/cert-manager
该目录包含:
- cert-manager Helm配置
- ClusterIssuer配置模板(包括自签名CA配置)
- 证书配置示例
- 生产环境优化配置
- 监控和备份脚本
总结
本文完成了cert-manager在K3s集群中的标准化部署,实现了GitOps环境证书的自动化管理。通过cert-manager,可以轻松管理Let’s Encrypt公网证书和自签名内网证书,确保所有组件的HTTPS安全访问。
配置完成后,建议为每个GitOps组件配置相应的证书,并在IngressRoute资源中启用TLS自动签发。下一篇文章将部署Gitea代码仓库,为GitOps环境提供代码托管能力。