cert-manager是一款云原生证书管理系统,能够根据Kubernetes原生Ingress对象的注释(annotation)自动为其签发合适的证书。Traefik的一些功能难以配合原生Ingress对象,而需要使用其定制的IngressRoute才能方便地使用,但我们仍然能够利用cert-manager为其自动签发证书。
本文将在自带Traefik Ingress控制器的k3s环境中,介绍如何利用cert-manager为Traefik IngressRoute对象签发Let’s Encrypt证书。
安装cert-manager
首先假设你已经拥有了一个工作正常的k3s集群,并且有一个指向该集群的域名(这个域名可以指向其中一个节点或者指向一个外部负载均衡器)。稍后Let’s Encrypt需要在外网使用这个域名访问我们集群内Traefik的80端口。
k3s可以通过创建HelmChart资源来安装cert-manager的Helm Chart。首先创建文件cert-manager.yaml
,输入以下内容:
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: cert-manager
namespace: cert-manager
spec:
repo: https://charts.jetstack.io
chart: cert-manager
targetNamespace: cert-manager
version: v1.13.2
valuesContent: |-
installCRDs: true
运行以下命令,创建HelmChart资源来安装cert-manager:
sudo kubectl apply -f cert-manager.yaml
运行以下命令来查看安装进度:
sudo kubectl get jobs -n cert-manager
当正在安装时,结果应显示job.batch/helm-install-cert-manager
这一作业的完成情况为“0/1”:
NAME COMPLETIONS DURATION AGE
job.batch/helm-install-cert-manager 0/1 5s 5s
可多次运行上述命令直到安装完成。安装完成后,结果应显示上述作业完成情况为“1/1”:
NAME COMPLETIONS DURATION AGE
job.batch/helm-install-cert-manager 1/1 29s 3m21s
创建Issuer
cert-manager的Issuer是表示证书发行者的Kubernetes资源类型。我们可以创建一个表示Let’s Encrypt测试环境的Issuer,用来测试我们的系统是否工作正常。首先,新建一个lets-encrypt-staging.yaml
文件,并输入如下内容(请根据实际情况将形如{xxx}的内容更换为所需的值):
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: lets-encrypt-staging
spec:
# cert-manager通过ACME协议与Let's Encrypt交互
acme:
# Let's Encrypt的测试ACME API地址
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Let's Encrypt用来联系你的邮箱
email: {你的邮箱}
# 用于与API通信的用户密钥将会存在这个Secret当中
privateKeySecretRef:
name: lets-encrypt-staging
# 使用HTTP-01方式验证域名
solvers:
- http01:
ingress:
# cert-manager会在Traefik中创建相应的服务来响应HTTP-01挑战
ingressClassName: traefik
运行以下命令创建该Issuer:
sudo kubectl apply -f lets-encrypt-staging.yaml
运行以下命令来确认Issuer创建成功:
sudo kubectl get issuer
输出结果应类似:
NAME READY AGE
lets-encrypt-staging True 9s
第二步:创建Certificate
Certificate是cert-manager中用于定义证书的Kubernetes资源类型(参考:Certificate resource – cert-manager)。创建文件certificate-staging.yaml
,输入以下内容:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: certificate-staging
spec:
secretName: tls-staging
duration: 2160h
renewBefore: 720h
dnsNames:
# Let's Encrypt会为这个域名颁发证书
# 请确保这个域名的80端口能够访问集群内的Traefik
- {你的集群的域名}
issuerRef:
# 证书将会通过这里指定的Issuer签发。
name: lets-encrypt-staging
kind: Issuer
运行以下命令创建该Certificate对象:
sudo kubectl apply -f certificate-staging.yaml
运行以下命令查看刚刚创建的Certificate对象:
sudo kubectl get certificate
当证书签发完成后,输出结果应显示ready的值为True:
NAME READY SECRET AGE
certificate-staging True tls-staging 8s
截至目前为止,我们已经成功使用Let’s Encrypt为我们的域名签发了证书。
第三步:创建Web服务
接下来,让我们创建一个Web服务来使用刚才准备好的证书。新建文件whoami.yaml,输入以下内容:
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
labels:
app.kubernetes.io/name: whoami
spec:
selector:
matchLabels:
app.kubernetes.io/name: whoami
template:
metadata:
labels:
app.kubernetes.io/name: whoami
spec:
containers:
- name: whoami
image: traefik/whoami:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
selector:
app.kubernetes.io/name: whoami
ports:
- port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingress
spec:
routes:
- kind: Rule
match: Host("{你的集群的域名}")
services:
- kind: Service
name: whoami
port: 80
tls:
# 这是上一个步骤中创建的Certificate资源的spec.secretName属性
secretName: tls-staging
domains:
- main: {你的集群的域名}
运行以下命令,启动我们的Web服务:
sudo kubectl apply -f whoami.yaml
稍等片刻,运行以下命令查看Deployment启动情况:
sudo kubectl get deployment
当启动完成后,输出结果的ready一列值应为“1/1”:
NAME READY UP-TO-DATE AVAILABLE AGE
whoami 1/1 1 1 2m46s
此时,使用浏览器访问https://{你的集群的域名},浏览器会提示证书无效。这是正常现象,因为我们目前使用的是测试环境颁发的测试证书。查看证书信息可以看到,证书由“(STAGING) Let’s Encrypt”签发,说明我们的证书已经配置正确,我们稍后会切换到正式环境的证书。
第四步:切换到Let’s Encrypt正式环境
首先为Let’s Encrypt正式环境创建Issuer。新建lets-encrypt.yaml
,输入以下内容:
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: lets-encrypt
spec:
# cert-manager通过ACME协议与Let's Encrypt交互
acme:
# Let's Encrypt的正式ACME API地址
server: https://acme-v02.api.letsencrypt.org/directory
# Let's Encrypt用来联系你的邮箱
email: {你的邮箱}
# 用于与API通信的用户密钥将会存在这个Secret当中
privateKeySecretRef:
name: lets-encrypt
# 使用HTTP-01方式验证域名
solvers:
- http01:
ingress:
# cert-manager会在Traefik中创建相应的服务来响应HTTP-01挑战
ingressClassName: traefik
注意,这个文件中metadata.name
、spec.acme.server
、privateKeySecretRef
的值与一开始的lets-encrypt-staging.yaml
中的不同。类似地,运行以下命令来创建新的Issuer:
sudo kubectl apply -f lets-encrypt.yaml
新建文件certificate.yaml
,输入以下内容:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: certificate
spec:
secretName: tls
duration: 2160h
renewBefore: 720h
dnsNames:
# Let's Encrypt会为这个域名颁发证书
# 请确保这个域名的80端口能够访问集群内的Traefik
- {你的集群的域名}
issuerRef:
# 证书将会通过这里指定的Issuer签发
name: lets-encrypt
kind: Issuer
运行这个命令来创建一个新的的Certificate对象:
sudo kubectl apply -f certificate.yaml
将whoami.yaml
文件中的spec.tls.secretName
从tls-staging
改为tls
,如下所示:
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami
labels:
app.kubernetes.io/name: whoami
spec:
selector:
matchLabels:
app.kubernetes.io/name: whoami
template:
metadata:
labels:
app.kubernetes.io/name: whoami
spec:
containers:
- name: whoami
image: traefik/whoami:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
selector:
app.kubernetes.io/name: whoami
ports:
- port: 80
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingress
spec:
routes:
- kind: Rule
match: Host("{你的集群的域名}")
services:
- kind: Service
name: whoami
port: 80
tls:
# 这是上一个步骤中创建的Certificate资源的spec.secretName属性
secretName: tls
domains:
- main: {你的集群的域名}
运行以下命令让我们的Web服务改用最新的证书:
sudo kubectl apply -f whoami.yaml
稍等片刻,使用浏览器访问https://{你的集群的域名}
,将会看到证书已被浏览器信任。cert-manager将会在证书到期前30天自动续签证书。
清理
要删除用于测试的Let’s Encrypt测试环境相关对象,请运行这些命令(注意,cert-manager自动生成的Secret需要手动删除):
sudo kubectl delete -f lets-encrypt-staging.yaml
sudo kubectl delete -f certificate-staging.yaml
sudo kubectl delete secret/lets-encrypt-staging
sudo kubectl delete secret/tls-staging
要删除用于测试的Web服务,请运行以下命令:
sudo kubectl delete -f whoami.yaml
要删除Issuer、Certificate以及它们创建的Secret,请运行以下命令:
sudo kubectl delete -f lets-encrypt.yaml
sudo kubectl delete -f certificate.yaml
sudo kubectl delete secret/lets-encrypt
sudo kubectl delete secret/tls
题外话
Kubernetes正在使用原生Gateway和HTTPRoute对象替代原本的Ingress对象以及各家创造的变体(包括IngressRoute)。cert-manager的自动证书签发功能已经支持了Gateway对象,但Traefik对Gateway对象的支持仍处于实验性阶段。
留言
有想法?请给我们留言!您的留言不会直接显示在网站内。