发布于 2026-01-06 2 阅读
0

如何在 Kubernetes 上配置带有自动生成的 Let's Encrypt 证书的 nginx Ingress

如何在 Kubernetes 上配置带有自动生成的 Let's Encrypt 证书的 nginx Ingress

背景:一开始我搜索了一下,发现有很多教程教人怎么做,但没有一个能直接用,要么是教程过时了,要么是用头盔(其实不需要头盔,而且我想尽可能少用工具),所以我不得不查阅很多资料才搞定。基本上,这篇文章是我整理的网上文章合集,里面的方法对我有用。

这里我省略了很多背景信息和解释,因为这些内容可以在相关的教程中找到。本文仅作为一份简要的操作指南,帮助您顺利完成设置。

此外,我们还将设置端口转发,使任何任意服务(非 http/https 服务,例如数据库)都可以通过与网站相同的 IP/负载均衡器从互联网访问。

我使用的是 DigitalOcean 提供的 Kubernetes 服务,如果您是新手,欢迎使用我的链接注册。您可以以每月 10 美元起的价格搭建一个 Kubernetes 集群。

先决条件

  • 对 Kubernetes 对象/类型的基本理解
  • 一个已准备就绪/已设置好的 Kubernetes 集群,kubectl 已就绪/已设置
  • 需要访问 DNS 提供商,以便设置一些 DNS 条目指向您的集群。

来源

本文所引用资料来源如下(排名不分先后):

创建一些虚拟的“echo”部署

我们需要一些虚拟的 Web 服务来响应 HTTP 请求。请创建以下两个文件:

# echo1.yaml
apiVersion: v1
kind: Service
metadata:
  name: echo1
spec:
  ports:
  - port: 80
    targetPort: 5678
  selector:
    app: echo1
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo1
spec:
  selector:
    matchLabels:
      app: echo1
  replicas: 2
  template:
    metadata:
      labels:
        app: echo1
    spec:
      containers:
      - name: echo1
        image: hashicorp/http-echo
        args:
        - "-text=echo1"
        ports:
        - containerPort: 5678
Enter fullscreen mode Exit fullscreen mode
# echo2.yaml
apiVersion: v1
kind: Service
metadata:
  name: echo2
spec:
  ports:
  - port: 80
    targetPort: 5678
  selector:
    app: echo2
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo2
spec:
  selector:
    matchLabels:
      app: echo2
  replicas: 1
  template:
    metadata:
      labels:
        app: echo2
    spec:
      containers:
      - name: echo2
        image: hashicorp/http-echo
        args:
        - "-text=echo2"
        ports:
        - containerPort: 5678
Enter fullscreen mode Exit fullscreen mode

同时采用这两种部署方式:

$ kubectl apply -f echo1.yaml
$ kubectl apply -f echo2.yaml
Enter fullscreen mode Exit fullscreen mode

这将创建 2 个部署和 2 个服务,监听集群内部端口 80:

$ kubectl get service
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
echo1        ClusterIP   10.245.164.177   <none>        80/TCP    1h
echo2        ClusterIP   10.245.77.216    <none>        80/TCP    1h
Enter fullscreen mode Exit fullscreen mode

配置 Ingress nginx

只需两条命令即可启动并运行 ingress-nginx:

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created
Enter fullscreen mode Exit fullscreen mode
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml

service/ingress-nginx created
Enter fullscreen mode Exit fullscreen mode

此时 nginx-ingress 已配置完成,负载均衡器(注意:云服务提供商会对每个负载均衡器收取费用)将启动。这可能需要几分钟时间才能显示出来。您可以使用以下命令检查状态:

$ kubectl get -n ingress-nginx service
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)                      AGE
ingress-nginx   LoadBalancer   10.245.209.25   155.241.87.123   80:30493/TCP,443:30210/TCP   1h
Enter fullscreen mode Exit fullscreen mode

更新:

目前存在一个关于网络路由的未解决的问题,当 pod 尝试请求证书时,如果尝试对请求的域进行自我检查而超时,则会导致问题。

因此,我们需要修改刚刚创建的“ingress-nginx”服务的“externalTrafficPolicy”。创建一个名为“Service_ingress-nginx.yaml”的文件,并添加以下内容:

# Service_ingress-nginx.yaml
kind: Service
apiVersion: v1
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  # default for externalTrafficPolicy = 'Local', but due to an issue
  # (https://stackoverflow.com/questions/59286126/kubernetes-cluterissuer-challenge-timeouts,
  # https://github.com/danderson/metallb/issues/287)
  # it has to be 'Cluster' for now
  externalTrafficPolicy: Cluster
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
    - name: https
      port: 443
      protocol: TCP
      targetPort: https
Enter fullscreen mode Exit fullscreen mode

当你比较/应用此文件时,区别应该在于 externalTrafficPolicy 现在为“Cluster”。这样做的缺点是可能会丢失请求服务的原始 IP 地址,但对我来说这并不重要,而且解决了证书请求问题。

kubectl diff -f Service_ingress-nginx.yaml # show differences
kubectl apply -f Service_ingress-nginx.yaml # activate changes
Enter fullscreen mode Exit fullscreen mode

请记下您的外部 IP 地址,我们将使用它作为我们向公众开放服务的唯一入口。

现在前往您的 DNS 提供商,创建两条记录(任何类型的 A 记录)指向您的外部 IP 地址。

创建一个名为“echo_ingress.yaml”的文件,并对其进行调整以匹配刚刚创建的DNS条目:

# echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echo-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"    
spec:
  rules:
  - host: echo1.yourdomain.com
    http:
      paths:
      - backend:
          serviceName: echo1
          servicePort: 80
  - host: echo2.yourdomain.com
    http:
      paths:
      - backend:
          serviceName: echo2
          servicePort: 80
Enter fullscreen mode Exit fullscreen mode

通过应用以下方式创建入口:

$ kubectl apply -f echo_ingress.yaml
Enter fullscreen mode Exit fullscreen mode

现在这两个网站应该可以通过http访问了。请尝试在浏览器中访问它们。

证书管理器

下一步是安装 cert-manager,它稍后将使用 Issuers 获取我们的证书。

$ kubectl create namespace cert-manager
$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.12.0/cert-manager.yaml
Enter fullscreen mode Exit fullscreen mode

这将安装 cert-manager。您可以检查是否有正在运行的 pod:

$ kubectl get pods --namespace cert-manager
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-5c47f46f57-n9bb6              1/1     Running   0          31s
cert-manager-cainjector-6659d6844d-6zjh5   1/1     Running   0          31s
cert-manager-webhook-547567b88f-9hj5f      1/1     Running   0          31s
Enter fullscreen mode Exit fullscreen mode

为了检查 cert-manager 是否运行正常,我们现在可以颁发自签名证书进行测试。生成一个名为“test-cert-manager.yaml”的文件:

# test-cert-manager.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager-test
---
apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
  name: test-selfsigned
  namespace: cert-manager-test
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: selfsigned-cert
  namespace: cert-manager-test
spec:
  commonName: example.com
  secretName: selfsigned-cert-tls
  issuerRef:
    name: test-selfsigned
Enter fullscreen mode Exit fullscreen mode

应用此方法并检查输出结果:

$ kubectl apply -f test-cert-manager.yaml
$ kubectl describe certificate -n cert-manager-test
Enter fullscreen mode Exit fullscreen mode

describe 输出中的事件应该显示类似“证书已成功颁发”的内容。

如果可以,请删除测试资源:

$ kubectl delete -f test-cert-manager.yaml
Enter fullscreen mode Exit fullscreen mode

cert-manager 启动并运行后,我们还缺少最后一块拼图;一个 Issuer(或者在我们的例子中,我们将选择一个 ClusterIssuer,这样我们就不需要指定命名空间,它就可以全局工作),用于生成有效的证书。

集群发行者的

我们将创建两个颁发者。第一个(测试环境)用于测试一切是否正常运行。否则,如果出现诸如 DNS 设置错误之类的问题,而我们直接使用生产环境的颁发者,则可能会暂时被 Let's Encrypt 服务器拒绝。

创建以下两个文件并修改您的电子邮件地址:

# staging_issuer.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    # The ACME server URL
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: me@example.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-staging
    # Enable the HTTP-01 challenge provider
    solvers:
    - http01:
        ingress:
          class:  nginx
Enter fullscreen mode Exit fullscreen mode
# prod_issuer.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: me@example.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod
    # Enable the HTTP-01 challenge provider
    solvers:
    - http01:
        ingress:
          class:  nginx
Enter fullscreen mode Exit fullscreen mode

创建发行者:

$ kubectl create -f staging_issuer.yaml
$ kubectl create -f prod_issuer.yaml
Enter fullscreen mode Exit fullscreen mode

扩展之前创建的“echo_ingress.yaml”文件,使其内容如下所示:

# echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echo-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"    
    cert-manager.io/cluster-issuer: "letsencrypt-staging"
spec:
  tls:
  - hosts:
    - echo1.yourdomain.com
    - echo2.yourdomain.com
    secretName: letsencrypt-staging
  rules:
  - host: echo1.yourdomain.com
    http:
      paths:
      - backend:
          serviceName: echo1
          servicePort: 80
  - host: echo2.yourdomain.com
    http:
      paths:
      - backend:
          serviceName: echo2
          servicePort: 80
Enter fullscreen mode Exit fullscreen mode

应用调整:

$ kubectl apply -f echo_ingress.yaml
Enter fullscreen mode Exit fullscreen mode

就我而言,证书颁发花了数分钟时间。请查看以下命令的事件部分:

$ kubectl describe certificate letsencrypt-staging
Enter fullscreen mode Exit fullscreen mode

过一段时间后,应该会显示类似“证书颁发成功”的信息。然后您可以重新加载浏览器中的网站并检查证书。此时,证书应该是由 Let's Encrypt 的虚假机构颁发的。

如果成功,我们就可以进行最后一步,将发行者替换为生产环境的发行者。再次调整 'echo_ingress.yaml' 文件,切换到 'letsencrypt-prod':

# echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echo-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"    
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - echo1.yourdomain.com
    - echo2.yourdomain.com
    secretName: letsencrypt-prod
  rules:
  - host: echo1.yourdomain.com
    http:
      paths:
      - backend:
          serviceName: echo1
          servicePort: 80
  - host: echo2.yourdomain.com
    http:
      paths:
      - backend:
          serviceName: echo2
          servicePort: 80
Enter fullscreen mode Exit fullscreen mode

再次应用这些调整:

$ kubectl apply -f echo_ingress.yaml
Enter fullscreen mode Exit fullscreen mode

现在,可能稍等几分钟,您的网站应该就会获得有效的证书。

$ kubectl describe certificate letsencrypt-prod
Enter fullscreen mode Exit fullscreen mode

恭喜!我们的网站终于都通过了有效的证书。现在最棒的是,它具有可扩展性。从现在开始,添加或删除网站都非常容易。

证书颁发故障排除

如果证书在某个阶段无法颁发,了解哪些组件参与其中会有所帮助,直到证书成功颁发。例如,我遇到的情况是挑战未能通过。因此,只需按照官方证书管理器网站上的指南操作,直到找到根证书即可:

https://cert-manager.io/docs/faq/acme/

额外功能——在集群之外提供其他服务

假设你的集群中运行着一些网站以外的服务(例如数据库),并且你想让它能够从外部访问。

一种方法是直接附加一个“NodePort”服务,但这会有一些限制,例如只能使用 30000 以上的端口,而且如果你的某个节点宕机或被替换,则需要调整访问该服务的 IP 地址。

相反,我们可以直接在现有的负载均衡 nginx-ingress 服务(TCP 或 UDP)中添加任意端口。因此,只需要进行一些小的修改。

以下是一个打开 TCP 端口 9000 并将其转发到在端口 27017 上运行的内部服务的实例。

之前我们只是应用了来自 URL ' https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml ' 和 ' https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml ' 的资源。

它们包含两个已激活但需要调整和重新应用的资源。首先,创建文件“ingress-nginx-tcp-configmap.yaml”:

# ingress-nginx-tcp-configmap.yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: tcp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
data:
  9000: "default/someservice:27017"
Enter fullscreen mode Exit fullscreen mode

第二,创建一个名为“ingress-nginx-service.yaml”的文件:

# ingress-nginx-service.yaml
kind: Service
apiVersion: v1
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  externalTrafficPolicy: Local
  type: LoadBalancer
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
    - name: https
      port: 443
      protocol: TCP
      targetPort: https
    - name: proxied-tcp-9000
      port: 9000
      targetPort: 9000
      protocol: TCP

Enter fullscreen mode Exit fullscreen mode

应用调整。

$ kubectl apply -f ingress-nginx-tcp-configmap.yaml
$ kubectl apply -f ingress-nginx-service.yaml
Enter fullscreen mode Exit fullscreen mode

您的服务不应能从集群外部通过与网站相同的 IP 地址访问。

文章来源:https://dev.to/chrisme/setting-up-nginx-ingress-w-automatically- generated-letsencrypt-certificates-on-kubernetes-4f1k