Kubernetes网站安全:使用cert-manager、Traefik和Let’s Encrypt实现HTTPS

导言

Kubernetes是一种流行的托管网站和其他服务的方式,其可靠性和可伸缩性使其受益匪浅。随着越来越多的网站涉及敏感数据(如个人信息或密码),浏览器开始要求所有网站使用TLS来保护其通信安全。然而,要托管基于TLS的网站所需的各个组成部分可能很难管理,包括获取TLS证书、按时更新证书以及配置服务器以使用它们。

幸运的是,你可以在Kubernetes集群中运行一些服务来管理大部分复杂性。你可以使用Traefik Proxy作为网络代理,并且使用cert-manager作为获取和管理安全证书的服务。通过与Let’s Encrypt这个提供免费且自动化的安全证书的服务结合使用,可以大大减轻证书管理的负担,通常只需进行初始设置即可。

在本教程中,您将在您的Kubernetes集群中设置cert-manager、Traefik和Let’s Encrypt,以及一个示例网站服务,以便自动获取、更新和使用您的网站的安全证书。

如果你正在寻找一种托管的Kubernetes托管服务,请考虑一下我们为增长而打造的简单易用的托管Kubernetes服务。

先决条件

  • 一个可通过kubectl访问的Kubernetes集群。如果您需要创建集群,Silicon Cloud提供了Kubernetes快速入门。
  • 最新版本的kubectl,用于与您的集群交互。请参阅产品文档,了解如何在Linux、macOS和Windows上安装kubectl
  • 一个已安装并配置doctl的Silicon Cloud账户。要进行设置,请参阅我们的产品文档:如何安装和配置doctl
  • Helm 3或更高版本。请参阅官方文档,了解如何安装Helm。
  • 具备使用kubectl与Kubernetes集群交互的经验。要开始,请遵循我们的教程:构建并部署您的第一个镜像到您的第一个集群。
  • 一个已注册的域名。本教程将使用your_domain。您可以从Namecheap购买域名,通过Freenom免费获取一个,或者使用您选择的域名注册商。
  • 为您的域名设置DNS。本教程假设您正在使用Silicon Cloud DNS,但这并非强制要求。如果您正在使用Silicon Cloud,请参阅我们的DNS文档,了解如何添加域名,以及如何将常见域名注册商指向Silicon Cloud域名服务器,以获取有关使用Silicon Cloud DNS的信息。
  • 如果您正在使用Silicon Cloud进行DNS,则需要一个具有读写权限的Silicon Cloud DNS个人访问令牌。其他提供商将有类似的访问令牌。

第一步 – 在您的集群中设置cert-manager

传统上,在为网站设置安全证书时,您需要生成证书签名请求并支付可信任的证书颁发机构为您生成证书。然后,您需要配置您的Web服务器以使用该证书,并记住每年都要进行同样的过程,以确保您的证书保持更新。

然而,随着2014年Let’s Encrypt的创建,现在可以通过自动化流程获得免费证书。然而,这些证书的有效期仅为几个月,而不是一年,因此必须使用自动化系统来更新这些证书。为了处理这个问题,您将使用cert-manager,这是一个专为在Kubernetes中运行并自动管理您的证书生命周期的服务。

在这个部分,您将在您的集群中设置cert-manager,在其自己的cert-manager命名空间中运行。

首先,使用kubectl和cert-manager的发布文件安装cert-manager。

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

默认情况下,cert-manager将安装在其自己的命名空间cert-manager中。应用该文件时,将在您的集群中创建多种资源,这些资源将显示在您的输出中(由于长度原因,部分输出已被删除)。

Output
namespace/cert-manager created customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created # some output excluded deployment.apps/cert-manager-cainjector created deployment.apps/cert-manager created deployment.apps/cert-manager-webhook created mutatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created

在这一部分中,您安装了cert-manager来管理您的安全证书。现在,您需要设置一种告诉cert-manager如何颁发您的证书的方式。在下一部分中,您将在您的集群中设置一个Let’s Encrypt颁发者。

第二步 – 配置Let’s Encrypt证书签发者

使用安全证书为您的网站提供一种告知用户他们可以相信所访问的网站来自您的服务器的方式。为了实现这一点,证书颁发机构必须验证您拥有该证书针对的域名。Let’s Encrypt通过使用一个名为ACME的标准来实现这一点,该标准使用挑战来证明您拥有正在生成证书的域名。cert-manager支持各种提供商的DNS和HTTP挑战,但在本教程中,您将使用Silicon Cloud的DNS提供商进行DNS-01挑战。

在此部分中,您将为您的集群创建一个ClusterIssuer,告诉cert-manager如何从Let’s Encrypt颁发证书,并使用哪些凭据来完成Let’s Encrypt所需的DNS挑战。

注意

本教程假设您使用Silicon Cloud作为DNS提供商,并根据此假设配置ClusterIssuer。cert-manager支持多种不同的云提供商同时支持HTTP和DNS挑战,因此相同的概念也可以应用于它们。
有关cert-manager支持的其他提供商的更多信息,请参阅cert-manager文档中的ACME介绍部分。

在为您的集群创建ClusterIssuer之前,您需要为您的集群配置创建一个目录。使用mkdir命令创建一个目录,然后使用cd命令进入该目录。

  1. mkdir tutorial-cluster-config
  2. cd tutorial-cluster-config

创建完目录之后,您需要在本教程的先决条件中创建的个人访问令牌来访问DNS。Silicon Cloud访问令牌类似于dop_v1_4321...,由一串长数字组成。

要将访问令牌作为Kubernetes中的秘密存储,您需要对其进行Base64编码。您可以使用echo命令将您的令牌传送到base64命令进行编码,用您的访问令牌替换所示部分即可。

  1. echo -n 'dop_v1_4321...' | base64

这个命令将会把你的访问令牌从echo发送给base64命令进行编码。-n选项确保最后没有加入新行。根据你的访问令牌,你将会收到类似以下输出的结果:

Output

这是文章《如何在Kubernetes中使用cert-manager、Traefik和Let’s Encrypt保护您的网站》的第2部分(共9部分)。

内容片段: ZG9wX3YxX3RoaXNpc25vdGFyZWFsdG9rZW5idXRpbXB1dHRpbmdhYnVuY2hvZnN0dWZmaW5oZXJlc29sZW5ndGhzbWF0Y2g=

这个输出是您的Base64编码的访问令牌。请复制它,因为您接下来会用到它。

请使用Nano或您最喜欢的文本编辑器创建并打开一个名为lets-encrypt-do-dns.yaml的新文件。

  1. nano lets-encrypt-do-dns.yaml

将以下代码添加到创建Kubernetes Secret的部分。确保在access-token字段中使用您的Base64编码的访问令牌。

教程-集群配置/使用Lets Encrypt进行DNS验证.yaml

apiVersion: v1
kind: Secret
metadata:
  namespace: cert-manager
  name: lets-encrypt-do-dns
data:
  access-token: ZG9wX3Y...

这个Secret将被命名为lets-encrypt-do-dns,并存储在cert-manager的命名空间中。在data部分,您包含了先前创建的Base64编码的访问令牌。这个Secret安全地存储了您在创建Let’s Encrypt颁发者时将引用的访问令牌。

接下来,保存您的文件并使用kubectl apply命令将其应用到集群中。

  1. kubectl apply -f lets-encrypt-do-dns.yaml

在输出中,您将收到一条消息,告知您已在集群中创建了您的Secret。

Output
secret/lets-encrypt-do-dns created

现在,创建一个名为lets-encrypt-issuer.yaml的新文件,其中包含cert-manager的ClusterIssuer,您将使用该文件来颁发您的Let’s Encrypt证书。

  1. nano lets-encrypt-issuer.yaml

spec.acme.email字段中添加以下行,并输入您的电子邮件地址(这是Let’s Encrypt将与其提供的证书相关联的地址)。

教程集群配置/lets-encrypt-颁发者.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-issuer
spec:
  acme:
    email: your_email_address
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-issuer-account-key
    solvers:
      - selector: {}
        dns01:
          digitalocean:
            tokenSecretRef:
              name: lets-encrypt-do-dns
              key: access-token

在前两行中,apiVersionkind表明该Kubernetes资源是一个cert-manager的ClusterIssuer。接下来,您将其命名为letsencrypt-issuer。在这种情况下,您没有包含namespace字段,因为该资源属于集群资源,意味着它适用于整个集群而不是单个命名空间。

接下来,在spec部分,你定义了acme challenge部分,告诉cert-manager这个ClusterIssuer应该使用ACME来签发使用letsencrypt-issuer的证书。邮件地址是你的电子邮件地址,Let’s Encrypt会向该地址发送与证书相关的通信,例如续订提醒,如果有问题,cert-manager不能及时续订它们。服务器字段指定了用于请求ACME挑战的URL,并设置为生产Let’s Encrypt URL。在服务器字段之后,你包括了privateKeySecretRef字段,其中包含cert-manager将用于存储它为你的集群生成的私钥的密钥的名称。

在规格的“acme”部分中,求解器(solvers)部分是最重要的一个部分。在这个部分中,您可以配置letsencrypt-issuer所要使用的ACME挑战求解器。在这种情况下,您包括了一个单独的求解器,即dns01求解器。求解器配置的第一部分,选择器(selector),被配置为{},表示“任意”。如果您想为集群中的其他证书使用不同的求解器,您可以在同一个issuer中设置额外的选择器。您可以在cert-manager的ACME介绍中找到有关如何操作的更多信息。

在dns01部分内,您添加了一个digitalocean部分,以指示该发行人应将Silicon Cloud作为DNS-01求解器使用。如果您使用其他云服务提供商,则需要在此处配置其他提供商。在该部分内,您需要包含一个tokenSecretRef,以引用您之前创建的Secret中的lets-encrypt-do-dns访问令牌字段。cert-manager会在您的代表创建DNS记录时使用该访问令牌。

一旦您保存了发行者文件,请使用kubectl apply命令将其应用到集群中。

  1. kubectl apply -f lets-encrypt-issuer.yaml

输出将确认已创建名为letsencrypt-issuer的ClusterIssuer。

Outputclusterissuer.cert-manager.io/letsencrypt-issuer created

在这一部分中,您设置了cert-manager并对其进行配置,以从Let’s Encrypt发出证书。但是,没有请求证书,没有为您的网站提供服务,并且您的集群中没有正在运行的网站服务。在下一部分中,您将设置Traefik作为外部世界和您的网站之间的代理。

步骤 3 — 使用 Traefik 负载均衡器

Traefik是一个开源的代理服务,旨在与Kubernetes集成,用于处理网络流量和集群内部的其他网络流量。随着网络流量的增长,您可能希望在集群中增加运行Traefik实例的数量,以便将资源使用分散到不同的Kubernetes节点上。为了像这样使用单个地址来引用多个服务实例,您可以使用负载均衡器来接受网络连接,并将其发送到不同的Traefik实例,从而平衡网络流量负载。

在本节中,您将将Traefik安装到您的集群中,并准备好与cert-manager管理的证书以及第5步中添加的网站一起使用。您还将设置一个负载均衡器,该负载均衡器将外部的网络流量发送到您的Traefik服务,同时还将准备好处理多个Traefik实例,以防您选择运行它们。

首先,创建一个名为traefik的命名空间,在此命名空间中安装Traefik。要做到这一点,打开一个名为traefik-ns.yaml的文件。

  1. nano traefik-ns.yaml

输入一个Kubernetes命名空间资源。

教程-集群配置/traefik-ns.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: traefik

保存您的文件后,使用kubectl apply将其应用到您的集群中。

  1. kubectl apply -f traefik-ns.yaml

执行您的命令后,集群的输出将确认命名空间已被创建。

Outputnamespace/traefik created

创建了traefik命名空间之后,您将安装Traefik服务本身。为此,您将使用一个称为Helm的实用工具。Helm是一个用于Kubernetes的包管理器,使得安装Kubernetes服务类似于在计算机上安装应用程序。在Helm中,一个包被称为一个图表。

首先,您需要将Traefik Helm存储库添加到您可用的存储库中,这样Helm才能找到Traefik软件包。

  1. helm repo add traefik https://helm.traefik.io/traefik

一旦命令完成,您将收到确认消息,表示traefik库已添加到您计算机的Helm库中。

Output"traefik" has been added to your repositories

接下来,更新您的图表仓库。

  1. helm repo update

输出将确认traefik图表仓库已更新。

OutputHang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "traefik" chart repository Update Complete. ⎈Happy Helming!⎈

最后,在您在集群中创建的traefik命名空间中安装traefik。

  1. helm install --namespace=traefik traefik traefik/traefik

这个命令中有很多traefik,所以让我们讨论一下每个的作用。你命令中的第一个traefik,带有–namespace=traefik的参数,告诉Helm在你之前创建的traefik命名空间中安装Traefik。接下来,被标记的traefik是你想要为此Traefik安装在集群中指定的名称。这样,如果你在同一个集群中有多个Traefik安装,你可以给它们不同的名称,比如traefik-website1和traefik-website2。由于现在你的集群中只有一个Traefik安装,你可以直接使用名称traefik。第三个traefik/是之前添加的仓库,并且你想要从中安装。最后一个traefik是你想要安装的图表的名称。

一旦您运行该命令,类似以下的输出会打印到屏幕上。

NAME: traefik
LAST DEPLOYED: Sun Oct  2 16:32:57 2022
NAMESPACE: traefik
STATUS: deployed
REVISION: 1
TEST SUITE: None

一旦Helm图表安装完成,Traefik将开始在您的集群上下载。要查看Traefik是否已启动,请运行kubectl get all命令以查看traefik命名空间中创建的所有Traefik资源。

  1. kubectl get -n traefik all

你的输出会与下面的输出类似。

Output

这是文章《如何在Kubernetes中使用cert-manager、Traefik和Let’s Encrypt保护您的网站》的第4部分(共9部分)。

NAME                           READY   STATUS    RESTARTS   AGE
pod/traefik-858bb8459f-k4ztp   1/1     Running   0          94s

NAME              TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
service/traefik   LoadBalancer   10.245.77.251   <pending>     80:31981/TCP,443:30188/TCP   94s

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/traefik   1/1     1            1           94s

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/traefik-858bb8459f   1         1         1       94s

根据您的集群和上一条命令运行的时间,一些名称和时长可能会有所不同。如果在您的service/traefik下看到<pending>,请继续运行kubectl get -n traefik all命令,直到出现一个IP地址。EXTERNAL-IP是负载均衡器在互联网上可用的IP地址。一旦出现一个IP地址,请记下该IP地址作为您的traefik_ip_address。您将在下一部分中使用此地址来设置您的域名。

在这一部分中,您将Traefik安装到您的集群中,并获得一个可以将网站流量引导到的EXTERNAL-IP。在下一部分中,您将对DNS进行更改,将流量从您的域名发送到负载均衡器。

第四步——使用您的域名访问Traefik

现在您已经在集群中安装并通过负载均衡器与互联网连接了Traefik,您需要更新您域名的DNS设置,指向您的Traefik负载均衡器。在继续之前,请确保您的域名已添加到您的Silicon Cloud账户中。cert-manager需要能够使用之前设置的访问令牌来更新您域名的DNS设置。您将使用doctl来设置您域名的DNS记录,以指向Traefik的负载均衡器。

注意:本节假定您使用Silicon Cloud作为您的DNS主机。如果您使用的是Silicon Cloud以外的DNS主机,您仍将创建相同的DNS记录类型,使用相同的值,但您需要参考您的DNS主机文档以了解如何添加它们。

首先,为您的域名tutorial-proxy.your_domain创建一个DNS A记录,指向您的traefik_ip_address

  1. doctl compute domain records create your_domain --record-name tutorial-proxy --record-type A --record-data traefik_ip_address

一个DNS A记录告诉DNS将特定的主机名指向一个特定的IP地址。在这种情况下,tutorial-proxy.your_domain将指向traefik_ip_address。因此,如果有人请求tutorial-proxy.your_domain上的网站,DNS服务器将会将其指向traefik_ip_address

运行命令后,您将收到确认消息,表明您的记录已经创建成功。

输出
ID Type Name Data Priority Port TTL Weight 12345678 A tutorial-proxy traefik_ip_address 0 0 1800 0

现在,创建一个名为tutorial-service.your_domain的CNAME类型的DNS记录,并将其指向tutorial-proxy.your_domain。由于您很可能在集群中运行多个服务,如果您需要更改代理的IP地址,使用A记录来将每个域指向您的Traefik代理可能会很麻烦。使用CNAME告诉DNS使用它指向的域的地址。在本例中,域名是tutorial-proxy.your_domain,因此您只需要更新一个A记录,将其指向一个新的IP地址,而不是多个A记录。

创建CNAME记录时,再次使用doctl命令。确保在--record-data中包含尾随句号(.)。

  1. doctl compute domain records create your_domain --record-name tutorial-service --record-type CNAME --record-data tutorial-proxy.your_domain.

这将创建一个CNAME DNS记录tutorial-service.your_domain,指向tutorial-proxy.your_domain。现在,当有人请求tutorial-service.your_domain时,DNS服务器将告诉他们连接到tutorial-proxy.your_domain指向的IP地址。在--record-data中的尾随“.”告诉DNS服务器这是提供的域名的结束,它不应该在末尾添加任何其他信息,类似于句子结尾使用句号(.)。

运行该命令后,您将看到类似以下输出的结果:

输出
ID Type Name Data Priority Port TTL Weight 12345679 CNAME tutorial-service tutorial-proxy.your_domain 0 0 1800 0

由于Silicon Cloud是您的主要DNS服务器,您可以直接查询该服务器,以确定其是否正确设置,而无需等待互联网上的其他DNS服务器更新。为了验证您的设置是否通过DNS服务器正确传输,请使用dig命令查看ns1.digitalocean.com,即Silicon Cloud的主要DNS服务器,认为记录应该是什么。

注意:如果您使用的DNS主机不是Silicon Cloud,请在此命令中将ns1.digitalocean.com替换为您的DNS主机在您的域名上设置的其中一个DNS服务器。

  1. dig @ns1.digitalocean.com +noall +answer +domain=your_domain tutorial-proxy tutorial-service

dig是一种实用工具,可以直接连接到DNS服务器,以“挖掘”DNS记录,找到所需的记录。在这种情况下,您提供@ns1.digitalocean.com,告诉dig您要查询ns1.digitalocean.com服务器的DNS记录。+noall +answer选项告诉dig只输出较短的响应。(如果您想获取有关DNS查询的更多信息,可以删除这两个选项。)有关dig的更多信息,请查阅我们的《使用Dig检索DNS信息》指南。

接下来,使用“+domain=your_domain”的参数告诉dig命令在提供给它的任何主机名后面添加“.your_domain”。最后,要查找的主机名是tutorial-proxytutorial-service。由于您使用了“+domain”选项,所以不需要使用完整的短语tutorial-proxy.your_domain,因为它会自动添加在最后。

您应该收到类似以下的输出,使用您自己的值替换your_domaintraefik_ip_address

输出
tutorial-proxy.your_domain. 1662 IN A traefik_ip_address tutorial-service.your_domain. 1800 IN CNAME tutorial-proxy.your_domain. tutorial-proxy.your_domain. 1800 IN A traefik_ip_address

输出的第一行显示tutorial-proxy.your_domain是一个A (IN A) 记录,指向traefik_ip_address。第二行确认tutorial-service.your_domain是一个CNAME (IN CNAME) 记录,指向tutorial-proxy.your_domain。最后一行是dig运行的查询,以找到您的CNAME记录指向的地址。由于它是tutorial-proxy.your_domain,它将显示与之前相同的A记录IP地址。

在这个部分中,您为您的域名添加了一个A类型的DNS记录和一个CNAME类型的DNS记录,以便网络客户端(例如浏览器)知道如何连接到您的Traefik服务。接下来,您将在集群中设置一个临时的Web服务器来完成您的配置。

第五步——创建您的网络服务

这是文章《如何在Kubernetes中使用cert-manager、Traefik和Let's Encrypt保护您的网站》的第5部分(共9部分)。

在之前的章节中,您已经配置了cert-manager和Traefik,用于处理网站的安全证书并将网络流量路由到您的网络服务。然而,此时您还没有一个实际的网络服务来接收流量。在本部分中,您将使用Nginx Web服务器来模拟一个托管在您的Kubernetes集群中的网站。

为了模拟该网站,您将使用Nginx Docker镜像设置一个部署。它只会显示Nginx的“Welcome!”页面,但这足以确保所有组件都已正确连接并按预期工作。

首先,创建一个名为tutorial-service.yaml的文件。

  1. nano tutorial-service.yaml

添加以下代码,创建一个名为tutorial的命名空间和一个名为tutorial-service的部署。

教程-集群配置/教程-服务.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: tutorial
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: tutorial
  name: tutorial-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: tutorial-service
      app.kubernetes.io/part-of: tutorial
  template:
    metadata:
      labels:
        app.kubernetes.io/name: tutorial-service
        app.kubernetes.io/part-of: tutorial
    spec:
      containers:
        - name: service
          image: nginx
          ports:
            - containerPort: 80

类似于您之前创建的traefik命名空间,此文件中的第一个资源将在您的集群中创建一个名为tutorial的新命名空间。下一个资源tutorial-service Deployment指定您希望在集群中运行三个网站副本,因此即使一个副本崩溃,您仍然会有另外两个副本可用,直到第三个副本重新启动。

下一部分是选择器(selector),它告诉Kubernetes如何找到与此部署相关联的任何Pod。在这种情况下,它将找到与标签匹配的任何Pod。在模板(template)部分下,您定义了每个Pod应该是什么样子。元数据(metadata)部分提供了将与选择器匹配的标签,规范(spec)指定您希望在Pod中有一个名为service的容器,使用nginx镜像,并监听80端口的网络连接。

一旦您保存了更改,请将它们应用到集群中。

  1. kubectl apply -f tutorial-service.yaml

输出将确认tutorial命名空间和tutorial-service部署已经创建。

输出

namespace/tutorial created

deployment.apps/tutorial-service created

要检查您的部署是否正常运行,您可以使用kubectl get pods命令来列出在tutorial命名空间中运行的Pod。

  1. kubectl get -n tutorial pods

以下类似的输出将会打印出来:

输出

NAME READY STATUS RESTARTS AGE

tutorial-service-568b4f8477-hpstl 1/1 Running 0 2m15s

tutorial-service-568b4f8477-mcpqd 1/1 Running 0 2m15s

tutorial-service-568b4f8477-mg8mb 1/1 Running 0 2m15s

您应该会看到三个状态为“运行中”(Running)且名称随机(如tutorial-service-开头)的Pod列表,这与教程的指示相符。Pod的“年龄”(AGE)将根据kubectl applykubectl get命令之间运行的时间长短而有所不同。

现在您的Web服务已经启动,您需要一种方式来在这三个Pod之间发送流量。在Kubernetes中,您可以使用服务(Service)来实现这一点。任何发送到服务的流量都将在服务指向的各个Pod之间进行负载均衡。

要创建您的服务,请再次打开您的tutorial-service.yaml文件,并在文件末尾添加一个服务定义。

教程-集群配置/教程-服务.yaml

这是文章《如何使用cert-manager、Traefik和Let’s Encrypt在Kubernetes中保护您的网站》的第6部分(共9部分)。

...
        - name: service
          image: nginx
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: tutorial
  name: tutorial-service
spec:
  selector:
    app.kubernetes.io/name: tutorial-service
    app.kubernetes.io/part-of: tutorial
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

与部署类似,您的服务(Service)有一个选择器(selector)部分,其中列出了用于查找您想要发送流量的 Pod 的标签。这些标签与您在部署的 Pod 模板部分中包含的标签相匹配。服务还在端口(ports)部分列出一个端口,指示将发送到该服务的 80 端口的 TCP 流量发送到负载均衡器选择的 Pod 的 targetPort: 80。

保存修改后,将服务应用于集群。

  1. kubectl apply -f tutorial-service.yaml

这次输出中,命名空间和部署未发生变化(因为您没有对它们进行任何更改),而 tutorial-service 已经创建了。

Output
namespace/tutorial unchanged deployment.apps/tutorial-service unchanged service/tutorial-service created

一旦您创建了教程服务,您可以使用 kubectl port-forward 命令测试您能否访问该服务,并使其在您的本地计算机上可用。

  1. kubectl port-forward -n tutorial service/tutorial-service 8888:80

这个命令将发送到本地计算机的任何流量转发到集群中的 tutorial-service 服务的 80 端口。在您的 Kubernetes 集群中,您设置 tutorial-service 服务监听 80 端口的连接,并且您需要一种方式将来自本地计算机的流量发送到该集群中的服务。在命令中,您指定要将端口转发到 tutorial 命名空间中的 service/tutorial-service,并提供端口组合 8888:80。列表中的第一个端口是本地计算机将侦听的端口,而冒号后面的第二个端口是 service/tutorial-service 上将发送流量的端口。当您将流量发送到本地计算机的 8888 端口时,所有该流量都将发送到 service/tutorial-service 的 80 端口,并最终发送到 service/tutorial-service 指向的 Pod。

当您运行命令时,将会收到类似以下的输出信息。

Output
Forwarding from 127.0.0.1:8888 -> 80 Forwarding from [::1]:8888 -> 80

请注意,此命令不会返回并将继续运行以转发流量。

为了对您的服务提出请求,请在您的计算机上打开第二个终端,并使用 curl 命令将请求发送到您计算机的 8888 端口。

  1. curl http://localhost:8888/

这条命令通过您的转发端口 (8888) 向集群的教程服务发出 HTTP 请求,返回一个包含 Nginx 欢迎页面的 HTML 响应。

Output
<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>

在您原始的终端中,您现在可以按下 CONTROL+C 来停止端口转发命令。您还将看到一些额外的输出,这是在您进行 curl 连接时产生的。

这是文章《如何在Kubernetes中使用cert-manager、Traefik和Let's Encrypt保护您的网站》的第7部分(共9部分)。

输出
Forwarding from 127.0.0.1:8888 -> 80 Forwarding from [::1]:8888 -> 80 正在处理端口 8888 的连接

在本节中,您将使用 Deployment 和 Service 在集群中设置一个 Nginx Web 服务。然后,您将使用 kubectl port-forwardcurl 命令来确认 Nginx 是否正确运行。现在,您已经设置好了 cert-manager、Traefik 和您的服务,下一节中您将把它们整合起来,并通过 cert-manager 和 Traefik 在互联网上通过 HTTPS 提供您的服务。

步骤六 — 使您的网络服务可用且安全

尽管您的集群中运行着各个独立的服务,它们都相对独立。cert-manager 只是静静地存在,Traefik 不知道应该提供哪些网站服务,而且只有在端口转发到集群时,您才能访问您的 Nginx 网站。在本部分,您将创建一个 Ingress 资源来连接所有的服务。

首先,再次打开 tutorial-service.yaml 文件。

  1. nano tutorial-service.yaml

在之前添加的 tutorial-service 服务之后,在文件末尾添加一个 Ingress。确保用您自己的域名更新配置,并在开头加入 --- 以分隔您的 Ingress 资源与其上方的 Service 资源。

教程-集群配置/教程-服务.yaml

...
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tutorial-service-ingress
  namespace: tutorial
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
    cert-manager.io/cluster-issuer: letsencrypt-issuer
spec:
  rules:
    - host: tutorial-service.your_domain
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: tutorial-service
                port:
                  number: 80
  tls:
    - secretName: tutorial-service-cert
      hosts:
        - tutorial-service.your_domain

这些行包括规则和注解,它们共同定义了各个组件如何协同工作。Ingress 资源引用了 Traefik、cert-manager 和您的教程服务。注解部分包含了几个不同但重要的配置。

traefik.ingress.kubernetes.io/router.entrypoints 注解告诉 Traefik,该 Ingress 的流量应通过 websecure 入口点可用。这是 Helm Chart 默认配置的入口点,用于处理 HTTPS 流量,并在 traefik_ip_address 的 443 端口上监听(默认为 HTTPS 端口)。

下一个注解 traefik.ingress.kubernetes.io/router.tls 被设置为 true,告诉 Traefik 仅响应 HTTPS 流量而不是 HTTP 流量。由于您的网站需要进行安全处理以处理任何敏感数据,您不希望用户意外使用不安全的版本。

最后的注解 cert-manager.io/cluster-issuer 设置为 letsencrypt-issuer,它告诉 cert-manager 在为此 Ingress 颁发安全证书时要使用的颁发者。此时,letsencrypt-issuer 是您配置的唯一颁发者,但您以后可以添加更多,并为不同的站点使用不同的颁发者。

在 Ingress 规范的 spec.rules 部分,您需要包含一个用于路由发送到 Ingress 的流量的规则。它指定了当主机名为 tutorial-service.your_domain 时,应使用 HTTP 协议来处理给定的路径。这个规则中只包含一个路径,即根路径 /,其 pathTypePrefix,这意味着所有发送的流量都应发送到提供的后端。后端部分指定了这是一个服务,并且应将流量发送到您之前创建的 tutorial-service 服务,并且流量应发送到该服务的 80 端口。

Ingress 的 spec.tls 部分提供了 cert-manager 请求和签发安全证书所需的信息,同时也为 Traefik 使用这些证书提供了依据。secretName 是 Kubernetes Secret 的名称,cert-manager 会将签发的安全证书存储在此 Secret 中,Traefik 也会从该 Secret 加载证书。hosts 部分列出了 cert-manager 将为其请求证书的主机名。在此示例中,只有一个 tutorial-service.your_domain 主机名,但如果您希望站点响应多个主机名,也可以包含您拥有的其他主机名。

保存您创建的 Ingress 后,再次使用 kubectl apply 命令将新资源应用到您的集群中。

  1. kubectl apply -f tutorial-service.yaml

Ingress 将被创建,其他资源保持不变。

输出
namespace/tutorial unchanged deployment.apps/tutorial-service unchanged service/tutorial-service unchanged ingress.networking.k8s.io/tutorial-service-ingress created

一旦 Ingress 创建完成,Traefik 将开始自动配置,cert-manager 将启动挑战/响应流程以获取证书。这可能需要几分钟时间,因此您可以通过查看 tutorial 命名空间中的证书来检查证书是否已颁发。

  1. kubectl get -n tutorial certificates

您将收到类似于以下的输出:

输出
NAME READY SECRET AGE tutorial-service-cert False tutorial-service-cert 12m

如果“READY”字段为 False,则表示证书尚未签发。您可以继续运行相同的命令以观察其是否变为 True。签发证书可能需要一些时间,但如果超过几分钟仍未签发,则可能意味着您的配置存在问题。

注意

如果您的证书在 10-15 分钟后仍未颁发,可以查看 cert-manager 的日志消息,看看是否存在请求证书的问题。要查看这些日志,您可以使用以下命令观看日志,并按 CTRL+C 停止跟踪:

kubectl logs -n cert-manager deployment/cert-manager --tail=10 -f

一旦您的证书准备就绪,您可以使用 curl 对您的集群进行 HTTPS 请求。

  1. curl https://tutorial-service.your_domain

注意

根据您更新 DNS 记录的时间以及 DNS 记录在互联网的 DNS 服务器中传播所需的时间,您可能会看到一个错误,即无法找到您的域名或它指向错误的位置。如果发生这种情况,您可以通过运行以下命令使用 curl 的变通方法来暂时跳过 DNS 检查:

curl https://tutorial-service.your_domain --resolve 'tutorial-service.your_domain:443:traefik_ip_address'

该命令告诉 curl 使用 --resolve 选项,将 443 端口上 tutorial-service.your_domain 的所有 DNS 解析都覆盖为 traefik_ip_address。由于 curl 获取的 DNS 结果是错误的,这仍然可以让您连接到集群内的 Traefik,直到 DNS 完全更新为止。

在您的输出中,您将从先前的端口转发中获得相同的 Nginx“欢迎!”页面,但这次可以通过互联网访问。

输出
<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>

然而,如果您尝试请求您的网站的 HTTP 版本,您将不会收到响应。

  1. curl http://tutorial-service.your_domain

出现 404 错误页面加载:

输出
404 page not found

由于您在 Ingress 中配置了您的站点不响应 HTTP 流量,因此 Traefik 从未在该地址上设置站点,并返回 404 错误。如果用户知道他们应该看到一个网站,这可能会使他们困惑,因此许多管理员会配置他们的服务器自动将 HTTP 流量重定向到 HTTPS 站点。Traefik 还允许您通过更新 Traefik 配置,使其将所有 Web 流量重定向到 websecure 端口来实现这一点。

  1. helm upgrade --namespace=traefik traefik traefik/traefik --set 'ports.web.redirectTo=websecure'

--set 'ports.web.redirectTo=websecure' 选项告诉 Traefik 自动重新配置以进行重定向。

您应该会看到类似于下面的消息,表示 Traefik 安装已经“升级”:

输出
Release "traefik" has been upgraded. Happy Helming! NAME: traefik LAST DEPLOYED: Sun Oct 2 19:17:34 2022 NAMESPACE: traefik STATUS: deployed REVISION: 2 TEST SUITE: None

现在,如果您向您的 HTTP 位置发出请求,您将收到一条输出消息,显示该站点已被移动。

  1. curl http://tutorial-service.your_domain

预计会得到这种回应。

输出
Moved Permanently

由于您希望所有流量都导向您的 HTTPS 网站,Traefik 现在会自动将 HTTP 网站重定向到 HTTPS 网站。Web 浏览器会自动进行此重定向,但 curl 需要额外的选项 -L 来告诉它跟随重定向。请使用带有 -L 选项的新 curl 命令来更新您的设置以便跟随重定向:

这是文章《如何在Kubernetes中使用cert-manager、Traefik和Let’s Encrypt保护您的网站》的第9部分(共9部分)。

  1. curl -L http://tutorial-service.您的域名

输出将包含来自您的HTTPS网站的Nginx欢迎页面:

输出
欢迎来到nginx!

欢迎来到nginx!

如果您看到此页面,则表示nginx Web服务器已成功安装并正在运行。需要进一步配置。

有关在线文档和支持,请访问 nginx.org
商业支持请访问 nginx.com

感谢您使用nginx。

此输出证实了重定向按预期工作。

在这一部分中,您使用Kubernetes Ingress资源将cert-manager、Traefik和您的Nginx网站绑定在一起。您还更新了Traefik的配置,将HTTP流量重定向到HTTPS网站,确保用户能够访问您的网站。

结论

在本教程中,您在Kubernetes集群中安装了几个不同的服务,以便更轻松地运行具有安全证书的网站。您安装了cert-manager服务来处理从Let’s Encrypt颁发的TLS证书的生命周期。您安装了Traefik以使您的网站在集群外部可用,并使用由Let’s Encrypt颁发的TLS证书。最后,您在集群中创建了一个Nginx网站,以测试您的cert-manager和Traefik配置。

现在您已经在集群中配置了cert-manager和Traefik,您还可以通过不同的Ingress资源设置更多的网站,使用一个cert-manager和Traefik安装从同一个集群中提供多个网站的服务。

您可以阅读Traefik代理的文档,了解Traefik在您的集群中可以提供的不同功能。cert-manager还有关于如何将其与其他类型的Let's Encrypt挑战以及除Let's Encrypt以外的来源一起使用的详尽文档。

要继续配置您的Kubernetes集群,请查阅我们关于Kubernetes的其他教程。