如何在DigitalOcean Kubernetes上使用Cert-Manager设置Nginx Ingress

在本教程中,学习如何在DigitalOcean Kubernetes上使用Cert-Manager设置和保护Nginx Ingress控制器。

介绍

Kubernetes Ingresses允许您灵活地将来自Kubernetes集群外部的流量路由到集群内的服务。 这是使用Ingress Resources完成的,Ingress Resources定义用于将HTTP和HTTPS流量路由到Kubernetes服务的规则,以及Ingress 控制器 ,它通过负载平衡流量并将其路由到适当的后端服务来实现规则。 流行的Ingress控制器包括NginxContourHAProxyTraefik Ingresses为设置多个LoadBalancer服务提供了更高效和灵活的替代方案,每个服务都使用自己的专用Load Balancer。

在本指南中,我们将设置Kubernetes维护的Nginx入口控制器 ,并创建一些入口资源以将流量路由到多个虚拟后端服务。 一旦我们设置了Ingress,我们就会在我们的集群中安装cert-manager来管理和配置TLS证书,以加密到Ingress的HTTP流量。

先决条件

在开始本指南之前,您应该可以使用以下内容:

  • 启用了基于角色的访问控制 (RBAC)的Kubernetes 1.10+集群
  • kubectl命令行工具安装在本地计算机上并配置为连接到您的群集。 您可以在官方文档中阅读有关安装kubectl更多信息。
  • 域名和DNS A记录,您可以指向Ingress使用的DigitalOcean负载均衡器。 如果您使用DigitalOcean管理域的DNS记录,请参阅如何管理DNS记录以了解如何创建A记录。
  • 安装在本地计算机上的Helm软件包管理器和安装在集群上的Tiller,详细信息请参见如何使用Helm软件包管理器在Kubernetes集群上安装软件
  • wget命令行实用程序安装在本地计算机上。 您可以使用操作系统内置的软件包管理器安装wget

设置好这些组件后,您就可以开始使用本指南了。

第1步 - 设置虚拟后端服务

在我们部署Ingress Controller之前,我们将首先创建并推出两个虚拟echo服务,我们将使用Ingress路由外部流量。 echo服务将运行hashicorp/http-echo Web服务器容器,该容器返回包含在启动Web服务器时传入的文本字符串的页面。 要了解有关http-echo更多信息,请参阅其GitHub Repo ,并了解有关Kubernetes服务的更多信息,请参阅官方Kubernetes文档中的服务。

在本地计算机上,使用nano或您喜欢的编辑器创建和编辑名为echo1.yaml的文件:

nano echo1.yaml

粘贴以下服务和部署清单:

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

在此文件中,我们定义了一个名为echo1的服务,它使用app: echo1标签选择器将流量路由到app: echo1 它接受端口80上的TCP流量,并将其路由到端口5678http-echo的默认端口。

然后我们定义一个Deployment,也称为echo1 ,它使用app: echo1 Label Selector管理Pods。 我们指定部署应该有2个Pod副本,并且Pod应该启动一个名为echo1的容器来运行hashicorp/http-echo图像。 我们传入text参数并将其设置为echo1 ,以便http-echo Web服务器返回echo1 最后,我们在Pod容器上打开端口5678

一旦您对虚拟服务和部署清单感到满意,请保存并关闭该文件。

然后,使用带有-f标志的kubectl create创建Kubernetes资源,指定刚刚保存为参数的文件:

kubectl create -f echo1.yaml

您应该看到以下输出:

service/echo1 created
deployment.apps/echo1 created

通过确认它具有ClusterIP(服务所在的内部IP)来验证服务是否正确启动:

kubectl get svc echo1

您应该看到以下输出:

NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
echo1     ClusterIP   10.245.222.129   <none>        80/TCP    60s

这表示echo1服务现在可在内部在端口80上的10.245.222.12910.245.222.129 它会将流量转发到它选择的5678上的containerPort 5678

既然echo1服务已启动并正在运行,请对echo2服务重复此过程。

创建并打开一个名为echo2.yaml的文件:

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

在这里,我们基本上使用与上面相同的服务和部署清单,但命名并重新标记服务和部署echo2 另外,为了提供一些变化,我们只创建了1个Pod副本。 我们确保将text参数设置为echo2以便Web服务器返回文本echo2

保存并关闭该文件,并使用kubectl创建Kubernetes资源:

kubectl create -f echo2.yaml

您应该看到以下输出:

service/echo2 created
deployment.apps/echo2 created

再次确认服务已启动并正在运行:

kubectl get svc

您应该看到带有已分配的ClusterIP的echo1echo2服务:

NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
echo1        ClusterIP   10.245.222.129   <none>        80/TCP    6m6s
echo2        ClusterIP   10.245.128.224   <none>        80/TCP    6m3s
kubernetes   ClusterIP   10.245.0.1       <none>        443/TCP   4d21h

现在我们的虚拟echoWeb服务已经启动并运行,我们可以继续推出Nginx入口控制器。

第2步 - 设置Kubernetes Nginx入口控制器

在这一步中,我们将推出Kubernetes维护的Nginx入口控制器 请注意,有几个 Nginx入口控制器; Kubernetes社区维护本指南中使用的一个,Nginx Inc.维护kubernetes-ingress 本教程中的说明基于官方Kubernetes Nginx Ingress Controller 安装指南中的说明

Nginx Ingress控制器由一个Pod组成,该Pod运行Nginx Web服务器并监视Kubernetes控制平面以获取新的和更新的Ingress Resource对象。 入口资源本质上是后端服务的流量路由规则列表。 例如,Ingress规则可以指定到达路径/web1 HTTP流量应该指向web1后端Web服务器。 使用Ingress Resources,您还可以执行基于主机的路由:例如,将web1.your_domain.com请求路由到后端Kubernetes Service web1

在这种情况下,由于我们正在将Ingress Controller部署到DigitalOcean Kubernetes集群,因此Controller将创建一个LoadBalancer服务,该服务可以旋转DigitalOcean负载均衡器,所有外部流量都将被引导到该负载均衡器。 此负载均衡器将外部流量路由到运行Nginx的Ingress Controller Pod,然后Nginx将流量转发到相应的后端服务。

我们首先创建Nginx Ingress Controller所需的Kubernetes资源。 它们包括包含Controller配置的ConfigMaps,用于授予Controller访问Kubernetes API的基于角色的访问控制(RBAC)角色以及实际的Ingress Controller部署。 要查看这些所需资源的完整列表,请参阅Kubernetes Nginx Ingress Controller的GitHub存储库中的清单

要创建这些必需资源,请使用kubectl apply-f标志指定GitHub上托管的清单文件:

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

我们在这里使用apply而不是create ,以便将来我们可以逐步apply更改应用于Ingress Controller对象,而不是完全覆盖它们。 要了解有关apply更多信息,请参阅官方Kubernetes文档中的管理资源

您应该看到以下输出:

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.extensions/nginx-ingress-controller created

此输出还可作为从mandatory.yaml清单创建的所有Ingress Controller对象的便捷摘要。

接下来,我们将创建Ingress Controller LoadBalancer服务,该服务将创建一个DigitalOcean负载均衡器,它将负载平衡并将HTTP和HTTPS流量路由到上一个命令中部署的Ingress Controller Pod。

要创建LoadBalancer服务, kubectl apply再次kubectl apply包含服务定义的清单文件:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/provider/cloud-generic.yaml

您应该看到以下输出:

service/ingress-nginx created

现在,通过使用kubectl获取服务详细信息,确认已成功创建DigitalOcean Load Balancer:

kubectl get svc --namespace=ingress-nginx

您应该看到一个外部IP地址,对应于DigitalOcean Load Balancer的IP地址:

NAME            TYPE           CLUSTER-IP      EXTERNAL-IP       PORT(S)                      AGE
ingress-nginx   LoadBalancer   10.245.247.67   203.0.113.0   80:32486/TCP,443:32096/TCP   20h

记下Load Balancer的外部IP地址,因为您将在以后的步骤中使用它。

此负载平衡器接收HTTP和HTTPS端口80和443上的流量,并将其转发到Ingress Controller Pod。 然后,Ingress Controller将流量路由到适当的后端服务。

我们现在可以在此外部负载均衡器上指出我们的DNS记录,并创建一些Ingress资源来实现流量路由规则。

第3步 - 创建入口资源

让我们首先创建一个最小的Ingress资源,将指向给定子域的流量路由到相应的后端服务。

在本指南中,我们将使用测试域example.com 您应该使用您拥有的域名替换它。

我们将首先创建一个简单的规则来路由指向echo1的流量 example.comecho1后端服务和指向echo2的流量 example.comecho2后端服务。

首先在您喜欢的编辑器中打开一个名为echo_ingress.yaml的文件:

nano echo_ingress.yaml

粘贴以下入口定义:

echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echo-ingress
spec:
  rules:
  - host: echo1.example.com
    http:
      paths:
      - backend:
          serviceName: echo1
          servicePort: 80
  - host: echo2.example.com
    http:
      paths:
      - backend:
          serviceName: echo2
          servicePort: 80

完成编辑Ingress规则后,保存并关闭文件。

在这里,我们已经指定我们要创建一个名为echo-ingress的入口资源,并根据Host头路由流量。 HTTP请求主机头指定目标服务器的域名。 要了解有关主机请求标头的更多信息,请参阅Mozilla开发人员网络定义页面 主机echo1的请求 example.com将被定向到第1步中设置的echo1后端,并使用主机echo2进行请求 example.com将被定向到echo2后端。

您现在可以使用kubectl创建Ingress:

kubectl apply -f echo_ingress.yaml

您将看到以下输出确认Ingress创建:

ingress.extensions/echo-ingress created

要测试Ingress,请导航到DNS管理服务,并为echo1.example.comecho2.example.com创建A记录,指向DigitalOcean Load Balancer的外部IP。 Load Balancer的外部IP是ingress-nginx服务的外部IP地址,我们在上一步中获取了该地址。 如果您使用DigitalOcean管理域的DNS记录,请参阅如何管理DNS记录以了解如何创建A记录。

一旦创建了必要的echo1.example.comecho2.example.com DNS记录,就可以使用curl命令行实用程序测试您创建的Ingress Controller和Resource。

从本地计算机, curl echo1服务:

curl echo1.example.com

您应该从echo1服务获得以下响应:

echo1

这确认您对echo1.example.com的请求正在通过Nginx入口正确路由到echo1后端服务。

现在,对echo2服务执行相同的测试:

curl echo2.example.com

您应该从echo2服务获得以下响应:

echo2

这确认您对echo2.example.com的请求正在通过Nginx入口正确路由到echo2后端服务。

此时,您已成功设置基本Nginx Ingress以执行基于虚拟主机的路由。 在下一步中,我们将使用Helm安装cert-manager为我们的Ingress配置TLS证书,并启用更安全的HTTPS协议。

第4步 - 安装和配置Cert-Manager

在此步骤中,我们将使用Helm将cert-manager安装到我们的集群中。 cert-manager是一个Kubernetes服务,它提供来自Let's Encrypt和其他证书颁发机构的TLS证书并管理它们的生命周期。 可以通过使用certmanager.k8s.io/issuer注释注释Ingress资源,在Ingress规范中附加tls部分以及配置一个或多个发行者来指定首选证书颁发机构来请求和配置证书。 要了解有关Issuer对象的更多信息,请参阅有关Issuers的官方证书管理器文档。

我们首先使用Helm将installcert-manager安装到我们的集群中:

helm install --name cert-manager --namespace kube-system --version v0.4.1 stable/cert-manager

您应该看到以下输出:

. . .
NOTES:
cert-manager has been deployed successfully!

In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).

More information on the different types of issuers and how to configure them
can be found in our documentation:

https://cert-manager.readthedocs.io/en/latest/reference/issuers.html

For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:

https://cert-manager.readthedocs.io/en/latest/reference/ingress-shim.html

这表明cert-manager安装成功。

在我们开始为Ingress主机颁发证书之前,我们需要创建一个Issuer,它指定可以从中获取签名的x509证书的证书颁发机构。 在本指南中,我们将使用Let的加密证书颁发机构,该机构提供免费的TLS证书,并提供用于测试证书配置的登台服务器和用于推出可验证TLS证书的生产服务器。

让我们创建一个测试颁发者,以确保证书配置机制正常运行。 在您喜欢的文本编辑器中打开名为staging_issuer.yaml的文件:

nano staging_issuer.yaml

粘贴在以下ClusterIssuer清单中:

staging_issuer.yaml
apiVersion: certmanager.k8s.io/v1alpha1
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: your_email_address_here
   # Name of a secret used to store the ACME account private key
   privateKeySecretRef:
     name: letsencrypt-staging
   # Enable the HTTP-01 challenge provider
   http01: {}

这里我们指定我们要创建一个名为letsencrypt-staging的ClusterIssuer对象,并使用Let的加密登台服务器。 我们稍后将使用生产服务器来推出我们的证书,但生产服务器可能会对针对它的请求进行速率限制,因此出于测试目的,最好使用暂存URL。

然后,我们指定一个电子邮件地址来注册证书,并创建一个名为letsencrypt-staging的Kubernetes Secret来存储证书的私钥。 我们还启用了HTTP-01质询机制。 要了解有关这些参数的更多信息,请参阅有关发布者的官方证书管理员文档。

使用kubectl推出ClusterIssuer:

kubectl create -f staging_issuer.yaml

您应该看到以下输出:

clusterissuer.certmanager.k8s.io/letsencrypt-staging created

现在我们已经创建了Let's Encrypt staging Issuer,我们已经准备好修改上面创建的Ingress资源,并为echo1.example.comecho2.example.com路径启用TLS加密。

在你最喜欢的编辑器中再次打开echo_ingress.yaml

nano echo_ingress.yaml

将以下内容添加到Ingress资源清单中:

echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echo-ingress
  annotations:  
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-staging
spec:
  tls:
  - hosts:
    - echo1.example.com
    - echo2.example.com
    secretName: letsencrypt-staging
  rules:
  - host: echo1.example.com
    http:
      paths:
      - backend:
          serviceName: echo1
          servicePort: 80
  - host: echo2.example.com
    http:
      paths:
      - backend:
          serviceName: echo2
          servicePort: 80

这里我们添加一些注释来指定ingress.class ,它确定应该用于实现Ingress规则的Ingress Controller。 此外,我们将cluster-issuer定义为letsencrypt-staging ,即我们刚刚创建的证书颁发者。

最后,我们添加一个tls块来指定我们要获取证书的主机,并指定我们之前创建的私钥。

完成更改后,保存并关闭文件。

我们现在将使用kubectl apply更新现有的Ingress资源:

kubectl apply -f echo_ingress.yaml

您应该看到以下输出:

ingress.extensions/echo-ingress configured

您可以使用kubectl describe来跟踪刚刚应用的Ingress更改的状态:

kubectl describe ingress
Events:
  Type    Reason             Age               From                      Message
  ----    ------             ----              ----                      -------
  Normal  CREATE             14m               nginx-ingress-controller  Ingress default/echo-ingress
  Normal  UPDATE             1m (x2 over 13m)  nginx-ingress-controller  Ingress default/echo-ingress
  Normal  CreateCertificate  1m                cert-manager              Successfully created Certificate "letsencrypt-staging"

成功创建证书后,您可以对其运行其他describe以进一步确认其成功创建:

kubectl describe certificate

您应该在“ Events部分中看到以下输出:

Events:
  Type    Reason          Age   From          Message
  ----    ------          ----  ----          -------
  Normal  CreateOrder     50s   cert-manager  Created new ACME order, attempting validation...
  Normal  DomainVerified  15s   cert-manager  Domain "echo2.example.com" verified with "http-01" validation
  Normal  DomainVerified  3s    cert-manager  Domain "echo1.example.com" verified with "http-01" validation
  Normal  IssueCert       3s    cert-manager  Issuing certificate...
  Normal  CertObtained    1s    cert-manager  Obtained certificate from ACME server
  Normal  CertIssued      1s    cert-manager  Certificate issued successfully

这确认已成功发布TLS证书,并且对于配置的两个域,HTTPS加密现在处于活动状态。

我们现在准备向后端echo服务器发送请求以测试HTTPS是否正常运行。

运行以下wget命令向echo1.example.com发送请求并将响应头打印到STDOUT

wget --save-headers -O- echo1.example.com

您应该看到以下输出:

URL transformed to HTTPS due to an HSTS policy
--2018-12-11 14:38:24--  https://echo1.example.com/
Resolving echo1.example.com (echo1.example.com)... 203.0.113.0
Connecting to echo1.example.com (echo1.example.net)|203.0.113.0|:443... connected.
ERROR: cannot verify echo1.example.com's certificate, issued by ‘CN=Fake LE Intermediate X1’:
  Unable to locally verify the issuer's authority.
To connect to echo1.example.com insecurely, use `--no-check-certificate'.

这表示已成功启用HTTPS,但无法验证证书,因为它是Let's Encrypt登台服务器发出的伪造临时证书。

现在我们已经使用这个临时假证书测试了一切正常,我们可以为两个主机echo1.example.comecho2.example.com推出生产证书。

第5步 - 推出生产发行人

在此步骤中,我们将修改用于提供登台证书的过程,并为我们的Ingress主机生成有效的,可验证的生产证书。

首先,我们将首先创建一个生产证书ClusterIssuer。

在您喜欢的编辑器中打开一个名为prod_issuer.yaml的文件:

nano prod_issuer.yaml

粘贴在以下清单中:

prod_issuer.yaml
apiVersion: certmanager.k8s.io/v1alpha1
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: your_email_address_here
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod
    # Enable the HTTP-01 challenge provider
    http01: {}

请注意不同的ACME服务器URL和letsencrypt-prod密钥名称。

完成编辑后,保存并关闭文件。

现在,使用kubectl推出此Issuer:

kubectl create -f prod_issuer.yaml

您应该看到以下输出:

clusterissuer.certmanager.k8s.io/letsencrypt-prod created

更新echo_ingress.yaml以使用此新颁发者:

nano echo_ingress.yaml

对文件进行以下更改:

echo_ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: echo-ingress
  annotations:  
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
  - hosts:
    - echo1.example.com
    - echo2.example.com
    secretName: letsencrypt-prod
  rules:
  - host: echo1.example.com
    http:
      paths:
      - backend:
          serviceName: echo1
          servicePort: 80
  - host: echo2.example.com
    http:
      paths:
      - backend:
          serviceName: echo2
          servicePort: 80

在这里,我们将ClusterIssuer和密钥更新为letsencrypt-prod

对更改满意后,保存并关闭文件。

使用kubectl apply推出更改:

kubectl apply -f echo_ingress.yaml
ingress.extensions/echo-ingress configured

等待几分钟让Let's Encrypt生产服务器颁发证书。 您可以使用certificate对象上的kubectl describe跟踪其进度:

kubectl describe certificate letsencrypt-prod

看到以下输出后,证书已成功发出:

Events:
  Type    Reason          Age    From          Message
  ----    ------          ----   ----          -------
  Normal  CreateOrder     4m4s   cert-manager  Created new ACME order, attempting validation...
  Normal  DomainVerified  3m30s  cert-manager  Domain "echo2.example.com" verified with "http-01" validation
  Normal  DomainVerified  3m18s  cert-manager  Domain "echo1.example.com" verified with "http-01" validation
  Normal  IssueCert       3m18s  cert-manager  Issuing certificate...
  Normal  CertObtained    3m16s  cert-manager  Obtained certificate from ACME server
  Normal  CertIssued      3m16s  cert-manager  Certificate issued successfully

我们现在将使用curl执行测试以验证HTTPS是否正常工作:

curl echo1.example.com

你应该看到以下内容:

<html>
<head><title>308 Permanent Redirect</title></head>
<body>
<center><h1>308 Permanent Redirect</h1></center>
<hr><center>nginx/1.15.6</center>
</body>
</html>

这表示正在重定向HTTP请求以使用HTTPS。

https://echo1.example.com上运行curl

curl https://echo1.example.com

您现在应该看到以下输出:

echo1

您可以使用verbose -v标志运行上一个命令,以深入了解证书握手并验证证书信息。

此时,您已使用适用于Nginx Ingress的Let's Encrypt证书成功配置了HTTPS。

结论

在本指南中,您将设置Nginx Ingress以对Kubernetes集群内的后端服务进行负载均衡和路由。 您还通过安装cert-manager证书配置程序并为两个主机路径设置Let's Encrypt证书来保护Ingress。

Nginx Ingress控制器有很多替代品。 要了解更多信息,请参阅官方Kubernetes文档中的Ingress控制器


分享按钮