基于K3s搭建GitOps环境4-证书管理

一、核心定位

本文作为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
# 1. 验证K3s集群状态
kubectl get nodes

# 2. 验证Traefik运行状态(证书验证依赖Traefik)
kubectl get pods -n kube-system -l app=traefik

# 3. 验证Helm可用性
helm version --short

# 4. 验证域名解析(替换为实际域名)
nslookup traefik.example.io
nslookup harbor.example.io
nslookup gitea.example.io

# 5. 检查cert-manager命名空间
kubectl get ns cert-manager 2>/dev/null || echo "命名空间不存在,将自动创建"

前置条件检查清单:

  • K3s集群运行正常
  • Traefik Pod处于Running状态
  • 域名已正确解析至K3s节点IP
  • Helm工具可用
  • 具备集群管理权限

三、部署cert-manager

3.1 Helm部署(推荐方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
# 添加cert-manager官方Helm仓库
helm repo add jetstack https://charts.jetstack.io --force-update
helm repo update

# 创建cert-manager命名空间
kubectl create namespace cert-manager

# 部署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
# 查看cert-manager组件状态
kubectl get pods -n cert-manager -w

# 查看CRD资源
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
# letsencrypt-staging.yaml
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 # K3s默认使用Traefik

生产环境ClusterIssuer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# letsencrypt-prod.yaml
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
# selfsigned-issuer.yaml
# 参考:https://gitee.com/Chemmy/kube-template/blob/master/devops/cert-manager/selfsigned-cluster-issuer.yaml
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

配置说明:

  1. 第一个ClusterIssuer:用于生成根CA证书
  2. Certificate资源:定义根CA证书的规格,生成后存储在root-secret
  3. 第二个ClusterIssuer:基于生成的根CA证书,用于签发其他子证书

应用配置:

1
2
3
4
5
6
7
8
9
# 应用自签名CA配置
kubectl apply -f selfsigned-issuer.yaml

# 验证CA证书生成
kubectl get certificate selfsigned-ca -n cert-manager
kubectl get secret root-secret -n cert-manager

# 等待CA证书就绪
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
# 导入自定义CA证书
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
# custom-ca-issuer.yaml
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
# 应用ClusterIssuer配置
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
# gitops-certificates.yaml
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 # 使用自签名CA签发机构
kind: ClusterIssuer
duration: 2160h # 90天有效期
renewBefore: 360h # 过期前15天自动续签
commonName: "*.example.io"
dnsNames:
- "*.example.io" # 通配符证书,覆盖所有GitOps组件
- 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
# traefik-certificate.yaml
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

# 查看生成的TLS Secret
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
# 创建测试IngressRoute使用证书
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

# 测试HTTPS访问
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
# 创建带自动签发注解的IngressRoute
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

# 等待证书自动创建(约1-2分钟)
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 证书管理策略

  1. 命名空间隔离:将CA证书、ClusterIssuer等核心资源部署在独立命名空间
  2. 网络策略限制:配置网络策略,限制对cert-manager组件的访问
  3. 监控告警:配置证书过期告警,提前发现证书问题
  4. 备份策略:定期备份关键证书和私钥

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
# gitops组件自动签发示例
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
# 导出自签名CA证书和GitOps组件证书

# 导出根CA证书
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

# 导出GitOps通配符证书
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

# 备份所有证书Secret
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环境提供代码托管能力。