在具有大量域的 kubernetes 应用程序上自动化 letsencrypt

automate letsencrypt on a kubernetes app with lots of domains

我有一个基于域名加载数据的节点应用程序。域配置了像 app.service.com 这样的 CNAME(这是节点应用程序)。

Node 应用程序看到请求域并向 API 发送请求以获取应用程序数据。

例如:domain.com CNAME app.service.com -> 然后节点应用程序向 api 询问 domain.com 数据

问题是为所有域设置 HTTPS(使用 letsencrypt)。我认为 cert-manager 可以提供帮助,但不知道如何在不需要为每个新域手动更改配置文件的情况下自动执行此操作。

或者是否有更好的方法在 Kubernetes 中实现这一点?

支持多个域名和/或子域名的标准方法是使用一个 SSL 证书并实施 SAN(主题备用名称)。额外的域名一起存储在 SAN 中。所有 SSL 证书都支持 SAN,但并非所有证书颁发机构都会颁发多域证书。 Let's Encrypt 确实支持 SAN,因此他们的证书将满足您的目标。

首先,您必须在我们的集群中创建一个 job,它使用一个图像来 运行 一个 shell 脚本。该脚本将启动一个 HTTP 服务,创建证书,并将它们保存到一个预定义的秘密中。你的域名和邮箱是环境变量,所以一定要填写:

apiVersion: batch/v1
kind: Job
metadata:
  name: letsencrypt-job
  labels:
    app: letsencrypt
spec:
  template:
    metadata:
      name: letsencrypt
      labels:
        app: letsencrypt
    spec:
      containers:
      # Bash script that starts an http server and launches certbot
      # Fork of github.com/sjenning/kube-nginx-letsencrypt
      - image: quay.io/hiphipjorge/kube-nginx-letsencrypt:latest
        name: letsencrypt
        imagePullPolicy: Always
        ports:
        - name: letsencrypt
          containerPort: 80
        env:
        - name: DOMAINS
          value: kubernetes-letsencrypt.jorge.fail # Domain you want to use. CHANGE ME!
        - name: EMAIL
          value: jorge@runnable.com # Your email. CHANGE ME!
        - name: SECRET
          value: letsencrypt-certs
      restartPolicy: Never

您有一份工作 运行ning,因此您可以创建一个服务来将流量引导至该工作:

apiVersion: v1
kind: Service
metadata:
  name: letsencrypt
spec:
  selector:
    app: letsencrypt
  ports:
  - protocol: "TCP"
    port: 80

这项工作现在可以 运行,但是在我们的工作真正成功并且我们能够通过 HTTPs 访问我们的服务之前,您仍然需要做三件事。

首先,您需要为作业创建一个秘密,以实际更新和存储我们的证书。由于我们创建秘密时您没有任何证书,因此秘密将开始为空。

apiVersion: v1
kind: Secret
metadata:
  name: letsencrypt-certs
type: Opaque

# Create an empty secret (with no data) in order for the update to work

其次,您必须将机密添加到 Ingress 控制器,以便它获取证书。请记住,是 Ingress 控制器知道我们的主机,这就是为什么需要在此处指定我们的证书。将我们的秘密添加到 Ingress 控制器将如下所示:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "kubernetes-demo-app-ingress-service"
spec:
  tls:
  - hosts:
    - kubernetes-letsencrypt.jorge.fail # Your host. CHANGE ME
    secretName: letsencrypt-certs # Name of the secret
  rules:

最后,您必须通过我们的 Nginx 部署将流量通过主机重定向到作业。为此,您将向我们的 Nginx 配置添加一个新路由和一个上游:这可以通过 Ingress 控制器添加一个 /.well-known/* 条目并将其重定向到 letsencrypt 服务来完成。这更复杂,因为您还必须向作业添加健康路由,因此您只需通过 Nginx 部署重定向流量:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
data:
  default.conf: |

...

    # Add upstream for letsencrypt job
    upstream letsencrypt {
      server letsencrypt:80 max_fails=0 fail_timeout=1s;
    }

    server {
      listen 80;

...

      # Redirect all traffic in /.well-known/ to letsencrypt
      location ^~ /.well-known/acme-challenge/ {
        proxy_pass http://letsencrypt;
      }
    }

应用所有这些更改后,销毁 Nginx Pod,以确保 ConfigMap 在新 Pods 中正确更新:

$ kubectl get pods | grep ngi | awk '{print }' | xargs kubectl delete pods

确保它有效。

为了验证这是否有效,您应该确保作业确实成功了。您可以通过 kubectl 获取作业来完成此操作,您还可以查看 Kubernetes 仪表板。

$ kubectl get job letsencrypt-job
NAME              DESIRED   SUCCESSFUL   AGE
letsencrypt-job   1         1            1d

您还可以检查密钥以确保证书已正确填充。您可以通过 kubectl 或通过仪表板执行此操作:

$ kubectl describe secret letsencrypt-certs

Name:   letsencrypt-certs
Namespace:  default
Labels:   <none>
Annotations:
Type:   Opaque
Data
====
tls.crt:  3493 bytes
tls.key:  1704 bytes

现在您可以看到证书已成功创建,您可以执行整个过程中的最后一步。为了让 Ingress 控制器获取 secret 的变化(从没有数据到拥有证书),您需要更新它以便重新加载它。为此,我们只需添加一个时间戳作为 Ingress 控制器的标签:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "kubernetes-demo-app-ingress-service"
  labels:
    # Timestamp used in order to force reload of the secret
    last_updated: "1494099933"
...

请看:kubernetes-letsencrypt.