保姆级教程-配置cert-manager 自动管理证书

cert-manager是什么?

cert-manager 是一个在 k8s 上管理证书和证书签发的应用, 可以在自动在过期前更新证书。
cert-manager 可以从各种证书颁发机构获取证书,包括:Let’s Encrypt、HashiCorp Vault、Venafi 和私有 PKI。

安装

cert-manager支持helm和kubectl两种方式的安装,任选其中一种即可。

kubectl

执行安装操作

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.3/cert-manager.yaml

验证是否安装成功

kubectl get pods --namespace cert-manager

helm

执行安装操作

helm repo add jetstack https://charts.jetstack.io --force-update
helm repo update
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.14.3 

验证是否安装成功

kubectl get pods --namespace cert-manager

选择证书颁发者

cert-manager 支持以下几种证书颁发者

  • SelfSigned
  • CA
  • Vault
  • Venafi
  • ACME

由于ACME签发的比较好,这里我们选择它

选择证书校验方式

ACME支持 dns01, http01两种形式的验证

dns01

dns01是通过dns服务商提供的api key操作dns添加txt解析记录,然后颁发机构,将向dns系统查询该记录,如果找到匹配项,就可以颁发证书。此方法支持泛域名证书,无需公网站点。

配置示例

1.选择dns服务商

dns服务商的列表

我这里用的是阿里云的,所以我选择了cert-manager-alidns-webhook

2.安装webhook

helm repo add cert-manager-alidns-webhook https://devmachine-fr.github.io/cert-manager-alidns-webhook
helm repo update
helm install cert-manager-alidns-webhook/alidns-webhook

3.创建secerts

登录阿里云打开【ram访问控制->身份管理->用户】

创建用户会得到AccessKey ID和AccessKey Secret

授予这个用户AliyunOSSFullAccess权限,如下图所示

执行下面命令

kubectl create secret generic alidns-secrets --from-literal="access-token=替换为你的AccessKey ID" --from-literal="secret-key=替换成你的AccessKey Secret" -n cert-manager

4.创建ClusterIssuer

创建ClusterIssuer.yaml,内容如下

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    email: xxxx@gmail.com #替换成你的邮箱
    server: https://acme-v02.api.letsencrypt.org/directory #readme给的是测试的地址,这边已经调整为正式地址,不然会报证书不信任
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - dns01:
        webhook:
            config:
              accessTokenSecretRef:
                key: access-token
                name: alidns-secrets
              regionId: cn-beijing
              secretKeySecretRef:
                key: secret-key
                name: alidns-secrets
            groupName: example.com 
            solverName: alidns-solver

执行下面命令

kubectl apply -f ClusterIssuer.yaml

5.签发证书

创建ClusterIssuerCertificate.yaml,内容如下
我的域名是codeme.cn

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: codeme-wildcard #名称随便定义
  namespace: cert-manager
spec:
  secretName: codeme-wildcard
  commonName: codeme.cn #替换成自己的
  dnsNames:
  - codeme.cn  #替换成自己的
  - "*.codeme.cn"  #替换成自己的
  issuerRef:
    name: letsencrypt
    kind: ClusterIssuer

执行命令

kubectl apply -f ClusterIssuerCertificate.yaml

6.验证证书是否创建成功

kubectl get Certificate -A

NAMESPACE                 NAME                                    READY   SECRET                                  AGE
cert-manager              alidns-webhook-1709742037-ca            True    alidns-webhook-1709742037-ca            46h
cert-manager              alidns-webhook-1709742037-webhook-tls   True    alidns-webhook-1709742037-webhook-tls   46h
cert-manager              codeme-wildcard                         True    codeme-wildcard                         32h

以上可以看出 codeme-wildcard 已经创建成功, READY状态也是 True

复制证书到目标命名空间(namespace)

上面的证书是签发在cert-manager空间下的,其他的namespace要想使用证书,必须要把证书复制到自己的namespace下面。

解决方案参考 配置证书复制到其他 namespace下的内容

使用证书

ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: 20M
  name: bee88
  namespace: bee88
spec:
  ingressClassName: nginx
  rules:
    - host: bee88.codeme.cn #替换成自己的
      http:
        paths:
          - backend:
              service:
                name: mk #替换成自己的
                port:
                  number: 80
            path: /
            pathType: ImplementationSpecific
  tls:
    - hosts:
        - bee88.codeme.cn #替换成自己的
      secretName: codeme-wildcard #替换成自己的

执行命令

kubectl apply -f ingress.yaml -n 目标空间

进阶参考

http01

http01方式需要在你的公网站点根目录下放置一个文件, 来验证你的域名所有权,完成验证,然后就可以生成证书了。此方法仅适用于给使用 Ingress 暴露流量的服务颁发证书,不支持泛域名证书。

配置示例

1.创建ClusterIssuer

#ClusterIssuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt #名称随便定义,后面会用到
spec:
  acme:
    email: xxxx@gmail.com #替换成你的邮箱
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: issuer-account-key
    solvers:
    - http01:
        ingress:
          class: nginx

使用证书

ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
      cert-manager.io/cluster-issuer: letsencrypt # 和上面定义的名称一致
    nginx.ingress.kubernetes.io/proxy-body-size: 20M
  name: saas
  namespace: saas
spec:
  ingressClassName: nginx
  rules:
    - host: saas.codeme.cn #替换成自己的
      http:
        paths:
          - backend:
              service:
                name: mk #替换成自己的
                port:
                  number: 80
            path: /
            pathType: ImplementationSpecific
  tls:
    - hosts:
        - saas.codeme.cn #替换成自己的
      secretName: saas.codeme.cn #替换成自己的

执行命令

kubectl apply -f ingress.yaml -n 目标空间

只要加上注解 cert-manager.io/cluster-issuer: letsencrypt ,那cert-manager会帮我们签发证书到secretName中

常见错误

证书不可信


主要是由于 安装的cert-manager-alidns-webhook 项目的readme给出了错误示范例子
需要把ACME的测试地址

https://acme-staging-v02.api.letsencrypt.org/directory

更改为

https://acme-v02.api.letsencrypt.org/directory

签发证书报错

1.报错User not authorized to operate on the specified resource, or this API doesn’t support RAM

阿里云的ram访问控制,创建用户的时候,没有进行AliyunOSSFullAccess授权,进cert-manger空间下pod中的日志可以看到错误信息

kubectl logs  cert-manager-696746686c-62d7f -n cert-manager

解决方法参考Issuing certificate as Secret does not exist

2.证书卡在pending

证书的READY为false

kubectl get Certificate -A

NAMESPACE                 NAME                                    READY   SECRET                                  AGE
cert-manager              alidns-webhook-1709742037-ca            True    alidns-webhook-1709742037-ca            47h
cert-manager              alidns-webhook-1709742037-webhook-tls   True    alidns-webhook-1709742037-webhook-tls   47h
cert-manager              codeme-wildcard                         False    codeme-wildcard                         33h
kubectl get challenge -A
NAMESPACE      NAME                                       STATE     DOMAIN         AGE
cert-manager   icodeme-wildcard-f9k16-382344    pending   xxx.xxx   24s

查看原因是:

Waiting for DNS-01 challenge propagation: DNS record for “xxx.xxx” not yet

解决办法参考DNS01 配置示例下的内容

我本想参考这个方案,第一天做了部署不行,过了第二天发现pending消失了,自动成功了