2020年02月27日,天气晴
很多博主的 https
证书经常容易忘记更新,虽说证书过期前都会有邮件提醒,但是万一确实忙得没时间去处理,忘记了,就会出现证书过期的情况了。
之前在服务器上自己搭博客服务的时候,用 Let's Encrypt
来自动创建并续签证书,确实省了不少事。
在我的博客部署到 k8s
之后,就一直用的一年一签的免费证书,每年更新一次,也不算特别麻烦,但是总归不够高端,我又怀念起了 Let's Encrypt
。
Let's Encrypt
是个好东西,k8s
也是个好东西,两个好东西怎么结合呢?搜寻了一番确实有方案,经过几天的尝试,终于弄好了。花了几天是因为第一天因为有个粗心导致的问题,导致搞了好久没成功,休息了几天再次尝试,才找到问题。
有关 k8s
的基础知识,这里不做赘述,网上教程很多,这里假设大家对 k8s
都有一定了解。
安装 cert-manager
安装 helm
到本地
$ brew install helm
添加仓库和命名空间
$ kubectl create namespace cert-manager # 创建 cert-manager 命名空间
$ kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true # 标记 cert-manager 命名空间以禁用资源验证
$ kubectl apply --validate=false -f https://raw.githubusercontent.com/jetstack/cert-manager/v0.13.1/deploy/manifests/00-crds.yaml # 安装 CustomResourceDefinition 资源
$ helm repo add jetstack https://charts.jetstack.io # 添加 Jetstack Helm repository
$ helm repo update # 更新本地 Helm chart repository
安装 cert-manager
$ helm install cert-manager --namespace cert-manager --version v0.13.1 jetstack/cert-manager
查看 cert-manager
安装情况
$ kubectl get pods --namespace cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-6cff8dc7b9-8vxws 1/1 Running 0 4d10h
cert-manager-cainjector-795c46858f-txczb 1/1 Running 0 4d10h
cert-manager-webhook-5dfc77cd74-skgsv 1/1 Running 0 4d10h
创建 ClusterIssuer
我们需要创建一个签发机构,cert-manager
提供了Issuer
和 ClusterIssuer
两种类型的签发机构,Issuer
只能用来签发自己所在命名空间下的证书,ClusterIssuer 可以签发任意命名空间下的证书,我这里用 ClusterIssuer
为例,创建 letsencrypt-prod.yaml
文件:
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
labels:
name: letsencrypt-prod
name: letsencrypt-prod # 自定义的签发机构名称,后面会引用
spec:
acme:
email: [email protected] # 你的邮箱,证书快过期的时候会邮件提醒,不过我们可以设置自动续期
solvers:
- http01:
ingress:
class: nginx
privateKeySecretRef:
name: letsencrypt-prod # 指示此签发机构的私钥将要存储到哪个 Secret 对象中
server: https://acme-v02.api.letsencrypt.org/directory # acme 协议的服务端,我们用 Let's Encrypt
应用 yaml
$ kubectl create -f letsencrypt-prod.yaml
查看状态
$ kubectl get clusterissuer
NAME READY AGE
letsencrypt-prod True 51s
手动签发证书
手动签发证书测试,创建 test-monkeyrun-net-cert.yaml
文件
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
name: test-monkeyrun-net-cert
namespace: test
spec:
secretName: tls-test-monkeyrun-net # 证书保存的 secret 名
duration: 2160h # 90d
renewBefore: 360h # 15d
organization:
- jetstack
isCA: false
keySize: 2048
keyAlgorithm: rsa
keyEncoding: pkcs1
dnsNames:
- test.monkeyrun.net
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
group: cert-manager.io
应用 yaml
$ kubectl apply -f test-monkeyrun-net-cert.yaml
检查是否生成证书文件
$ kubectl get certificate -n test
NAME READY SECRET AGE
test-monkeyrun-net-cert True test-monkeyrun-net-tls 99m
将该证书配置到 test.monkeyrun.net
的 ingress
上,测试 https
访问,成功。
自动签发证书
创建 test-nginx.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: test-nginx
namespace: test
spec:
replicas: 1
template:
metadata:
labels:
run: test-nginx
spec:
containers:
- name: test-nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: test-nginx
namespace: test
labels:
app: test-nginx
spec:
ports:
- port: 80
protocol: TCP
name: http
selector:
run: test-nginx
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-nginx
namespace: test
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"
certmanager.k8s.io/cluster-issuer: "letsencrypt-prod"
spec:
rules:
- host: test.monkeyrun.net
http:
paths:
- backend:
serviceName: test-nginx
servicePort: 80
path: /
tls:
- secretName: tls-test-monkeyrun-net
hosts:
- test.monkeyrun.net
删除之前手动创建的 Deployment
、Service
、 Ingress
和 Secret
后, 应用 yaml
来自动创建
$ kubectl apply -f test-nginx.yaml
打开 https://test.monkeyrun.net
测试,成功!
通过 DNS 验证域名
刚才通过 http01 的方式验证域名会有个问题,对于已经部署上线的项目,没办法去验证,所以可以通过 dns 的方式来验证。
经过搜寻,找到了几篇文章,都是利用 kevinniu666 这位仁兄基于 jetstack/cert-manager-webhook-example 改成 alidns
的版本来搞的,不过尝试了下,他这里面 cert-manager
版本太老已经跑不起来了,从 GitHub 的 forks 树里面找到了最新的一个 fork,colprog/cert0manager-webhooks-alidns,尝试了下,也不行,他应该是改了镜像,但是不可用了。重新尝试了下上一代 fork pangzineng/cert-manager-webhook-alidns,可用。
$ git clone https://github.com/pangzineng/cert-manager-webhook-alidns.git
$ cd cert-manager-webhook-alidns
$ helm install cert-manager-webhook-alidns --namespace=cert-manager ./deploy/webhook-alidns
创建 alidns AccessKey Id 和 Secret
$ kubectl -n cert-manager create secret generic alidns-access-key-id --from-literal=accessKeyId='xxxxxxx'
$ kubectl -n cert-manager create secret generic alidns-access-key-secret --from-literal=accessKeySecret='xxxxxxx'
修改我们之前创建的 letsencrypt-prod.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
labels:
name: letsencrypt-prod
name: letsencrypt-prod # 自定义的签发机构名称,后面会引用
spec:
acme:
email: [email protected] # 你的邮箱,证书快过期的时候会邮件提醒,不过我们可以设置自动续期
solvers:
- dns01:
webhook:
config:
accessKeyIdRef:
key: accessKeyId
name: alidns-access-key-id
accessKeySecretRef:
key: accessKeySecret
name: alidns-access-key-secret
regionId: 'cn-hangzhou'
ttl: 600
groupName: certmanager.webhook.alidns
solverName: alidns
privateKeySecretRef:
name: letsencrypt-prod # 指示此签发机构的私钥将要存储到哪个 Secret 对象中
server: https://acme-v02.api.letsencrypt.org/directory # acme 协议的服务端,我们用 Let's Encrypt
应用 yaml
$ kubectl create -f letsencrypt-prod.yaml
查看状态
$ kubectl get clusterissuer
NAME READY AGE
letsencrypt-prod True 51s
重新手动签发证书,验证,成功!
PS:需要注意的是,从 http01 认证修改到 dns01 认证后,有个坑,会一直失败,查看 cert-manager 的 Pod 日志,会发现如下错误:
cert-manager/controller/orders "msg"="Failed to determine the list of Challenge resources needed for the Order" "error"="no configured challenge solvers can be used for this challenge" "resource_kind"="Order" "resource_name"="xxx"
研究了半天都没成功,后来在 GitHub 上找到了这个 Issue,按照 demisx 这位仁兄的建议,把所有和 cert-manager
相关的东西全部删除重新用 dns01 的方式部署一遍就 OK 了。
参考链接