NGINX Full Version

如何简化 Kubernetes 出入向流量管理

Kubernetes 出入向的流量管理可以通过服务网格Ingress controller 两个功能来实现,这两个功能通常是分开进行配置的,这样 Kubernetes 环境的管理就会变得非常复杂。这样的配置不仅会增加延迟,同时还会增加配置错误的概率,进而阻碍适当的流量路由,甚至导致出现安全漏洞(例如攻击者获得过多的应用访问权限)和不好的使用体验(例如客户无法访问其授权访问的应用)。另外分开配置也会消耗更多的配置时间,出现错误的时候还需要更多的时间来处理。

NGINX Plus 同时集成了 NGINX Ingress ControllerNGINX Service Mesh 也就是将服务网格和 Ingress Controller 两个功能在一个组件上完成,通过这个集成功能可以同时控制出入向的 mTLS 流量,这样不仅可以避免分开配置引起的问题,也会节省时间。下面的演示视频介绍了整个步骤。

下面通过几个章节对视频内容进行说明:

前提条件 (0:18)

在开始演示之前,我们需要具备以下前提条件:

  1. 在 Kubernetes 集群中安装 NGINX Server Mesh 控制平面 并为服务网格设置 mTLS 和 strict 策略

  2. 在 Kubernetes 集群中安装基于 NGINX Plus 的 NGINX Ingress Controller(使用 Deployment ,而不要使用DaemonSet),启用出向 (egress) 功能,并将其作为 LoadBalancer 类型的服务进行发布

    注:本演示不适用于 NGINX 开源版NGINX Ingress Controller。为方便阅读,我们在后文把基于 NGINX Plus 的 NGINX Ingress Controller 简称为“NGINX Ingress Controller”。

  3. 根据我们的部署文档完成三个内容:下载 bookinfo 示例应用,注入 NGINX Service Mesh sidecar,部署该应用。

由于我们在第 1 步中设置了 strict 策略,sidecar 会拒绝来自网格外客户端的 bookinfo 应用请求。为了解决这个问题,我们需要在演示中设置端口转发,具体命令如下:

> kubectl port-forward svc/product-page 9080
Forwarding from 127.0.0.1:9080 -> 9080
Forwarding from [::1]:9080 -> 9080
Handling connection for 9080

当我们试图访问该应用时,会得到状态码 503,因为我们的本地设备不在服务网格中:

> curl localhost:9080
503

使用 NGINX Service Mesh 部署 NGINX Ingress Controller (1:50)

在发布应用的第一阶段,我们需要部署一个 NGINX Ingress Controller 实例。具体部署方式请参考:《为 Kubernetes 部署 NGINX Plus Ingress Controller》

NGINX 为此提供了 Deployment 和 Daemonset 的 manifest 文件。本演示中使用的是 Deployment manifest,文件为:nginx-plus-ingress.yaml。它包括了下面的 annotations,配置这个 annotations 的 NGINX Ingress Controller 会同时实现入向流量和出向流量的管理:

查看在❤ GitHub 托管的nginx-plus-ingress.yaml

该 manifest 直接集成了 NGINX Ingress Controller 和 Spire,Spire 是 NGINX Service Mesh 的证书颁发机构 (CA),因此NGINX Service Mesh sidecar 不需要注入 NGINX Ingress Controller。NGINX Ingress Controller 将直接从 Spire CA 获取证书和秘钥,用于网格中 pod 的 mTLS。该 manifest 指定了 Spire 的代理地址:

查看在❤ GitHub托管的nginx-plus-ingress.yaml

并将 Spire 代理的 UNIX 套接字安装到 NGINX Ingress Controller pod::

查看在❤ GitHub 托管的 nginx-plus-ingress.yaml

关于 manifest,最后需要注意的一点是-enable-internal-routes CLI 参数,该参数用于出向路由服务:

查看在❤ GitHub 托管的 nginx-plus-ingress.yaml

在开始演示之前,我们通过运行kubectl apply -f nginx-plus-ingress.yaml 命令来 安装 NGINX Ingress Controller,然后检查一下 nginx-ingress 命名空间中的部署情况。如下面输出结果中的 READY 列所示,NGINX Ingress Controller pod 中只有一个容器,是因为我们还没有为其注入 NGINX Service Mesh sidecar。

此外,我们还部署了 LoadBalancer 类型的服务,以便将 NGINX Ingress Controller 的外部 IP 地址(此处为 35.233.133.188)发布到集群外部。后续我们将访问位于该 IP 地址的 bookinfo 示例应用。

> kubectl get pods --namespace=nginx-ingress
NAME                                 READY   STATUS    RESTARTS   AGE
pod/nginx-ingress-867f954b8f0fzdrm   1/1     Running   0          3d3h

NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP      ...
service-nginx-ingress   LoadBalancer   10.31.245.207   35.233.133.188   ...

      ... PORT(S)                      AGE
      ... 80:31469/TCP,443:32481/TCP   4d2h
...

使用标准 Kubernetes Ingress 资源发布应用 (3:55)

现在,我们使用 bookinfo-ingress.yaml 中定义的标准 Kubernetes Ingress 资源在网格中发布 bookinfo 应用。具体部署方式请参考:《使用 NGINX Plus Ingress Controller 发布应用》

查看在❤ GitHub 托管的 bookinfo-ingress.yaml

在 bookinfo-ingress.yaml 的第 10 行引用了 Kubernetes Secret,这个配置提供了路由规则,该规则将对 bookinfo.example.com 的请求转发给 productpage 服务(该配置在第11–18 行)。该Kubernetes Secret 在 bookinfo-secret.yaml 中进行了定义:

查看在❤GitHub托管的 bookinfo-secret.yaml

我们运行以下命令,可以加载密钥和证书(本演示中为自签名证书):

> kubectl apply -f bookinfo-secret.yaml
secret/bookinfo-secret unchanged

然后我们激活 Ingress 资源:

> kubectl apply -f bookinfo-ingress.yaml
ingress.networking.k8s.io/bookinfo-ingress deleted

并验证 Ingress Controller 是否添加了 Ingress 资源中定义的路由,输出结果的最后一行的 event 就说明已经确认了这个信息::

> kubectl describe ingress bookinfo-ingress
...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  5s    nginx-ingress-controller  Configuration for ...
        ...default/bookinfo-ingress was added or updated

在本演示中,使用浏览器通过访问 https://bookinfo.example.com/ 来查看 bookinfo 应用(在本机的 /etc/hosts 文件中,已经添加了域名和 IP 的映射,这里的域名为 bookinfo.example.com,IP 为 35.233.133.188)。页面中Book Reviews 部分的信息会根据对 bookinfo.example.com下载)中定义的三个 reviews 服务版本的依次请求而发生周期性改变。

接下来,我们开始检查进入集群的流量。首先运行 generate-traffic.sh 脚本,然后通过访问 NGINX Ingress Controller 发布的外网 IP 地址来请求 productpage 服务,再运行 nginx-meshctl top 命令来监控流量:

> nginxmesh-ctl top deploy/productpage-v1
Deployment       Direction  Resource       Success Rate  P99    P90    P50   ...
productpage-v1  
                 To         details-v1     100.00%       3ms    3ms    2ms   
                 To         reviews-v2     100.00%       99ms   90ms   20ms
                 To         reviews-v3     100.00%       99ms   85ms   18ms
                 To         reviews-v1     100.00%       20ms   17ms   9ms
                 From       nginx-ingress  100.00%       192ms  120ms  38ms

      ... NumRequests 
      ... 14
      ... 5
      ... 5
      ... 12

使用 NGINX VirtualServer 资源发布应用 (6:45)

下面我们来展示另一种发布应用的方法,即使用 NGINX VirtualServer resource 资源发布应用。它是一种自定义 NGINX Ingress Controller 资源,支持更复杂的流量处理,例如流量分割和基于内容的路由。

首先,我们删除标准 Ingress 资源:

> kubectl delete -f bookinfo-ingress.yaml
ingress.networking.k8s.io "bookinfo-ingress" deleted

通过 bookinfo-vs.yaml 文件使用 Secret 配置 MTLS(第 7-8 行),这个 Secret 与 bookinfo-ingress.yaml 中的 Secret相同。在 bookinfo-vs.yaml第 9–12 行的配置将 productpage 服务定义为上游服务,第 13–24 行 的配置将 bookinfo.example.com 的所有 GET 请求发送到上游。对于除 GET以外的 HTTP 方法,它将返回状态代码 405

查看在❤ GitHub 托管的 bookinfo-vs.yaml

下面来创建 NGINX VirtualServer 资源:

> kubectl apply -f bookinfo-vs.yaml
virtualserver.kubernetes.nginx.org/bookinfo-vs created

然后执行与 Ingress 资源相同的步骤,即通过运行 kubectl describe 命令来确认应用已经部署正常,然后通过浏览器访问该应用。还可以通过看该应用是否拒绝 POST 方法来验证应用的正常运行,具体操作如下:

> curl -k -X POST https://bookinfo.example.com/
Method not allowed

使用 NGINX Ingress Controller 配置安全出向路由 (8:44)

现在我们展示如何通过 NGINX Ingress Controller 处理出向流量。“使用 NGINX Plus Ingress Controller 配置安全出向路由”教程中通过不同应用示例介绍了该流程。

我们在 bash.yaml 中定义了一个简单的 bash pod,并将其部署在default的命名空间中,然后在该命名空间中进行查看。 如下方输出结果中的 READY 列所示,我们已向其注入了 NGINX Service Mesh sidecar。

> kubectl get all
NAME                        READY  STATUS    RESTARTS   AGE
pod/bash-6ccb678958-zsgm7   2/2    Running   0          77s

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.31.240.1   <none>        443/TCP   4d2h
...

在一些其他案例中,您可能想要从 pod 中请求 egress service,请求的服务不属于 NGINX Service Mesh提供的对象。这些部署的服务示例位于:

  • 在集群外
  • 在另一个集群上
  • 在同一集群中,但未注入 NGINX Service Mesh sidecar

在本演示中,我们确定了示例的最终状态。我们在 legacy 命名空间(不受 NGINX Service Mesh 控制,并禁用 NGINX Service Mesh sidecar 自动注入功能)中部署了一个应用,并只有一个 pod 运行。

> kubectl get all --namespaces=legacy
NAME                          READY  STATUS    RESTARTS   AGE
pod/target-5f7bcb96c6-km9lz   1/1    Running   0          27m

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/target-svc   ClusterIP   10.31.245.213   <none>        80/TCP,443/TCP   27m
...

由于我们为 NGINX Service Mesh 配置了一个 strict mTLS 策略,因此,我们无法直接从 bash 向目标服务发送请求,原因是他们之间没有互相进行身份验证。如果我们坚持进行访问的话,我们会得到状态代码 503(如下图所示):

> kubectl exec -it bash-6ccb678958-zsgm7 -c bash -- curl target-svc.legacy
curl: (56) Recv failure: connection reset by peer
503command terminated with exit code 56

解决方案是让 bash pod 发送出向流量时,该流量通过 NGINX Ingress Controller 来发送,操作方式是取消 bash.yaml 第 14-15 行的 annotations:

查看在❤ GitHub 托管的 bash.yaml

然后应用新配置:

> kubectl apply -f bash.yaml
deployment.apps/bash configured

并验证新的 bash pod 是否已正常启动:

> kubectl get pods
NAME                    READY  STATUS        RESTARTS   AGE
bash-678c8b4579-7sfml   2/2    Running       0          6s
bash-6ccb678958-zsgm7   2/2    Terminating   0          3m28s

现在,我们运行和前面一样的 kubectl exec 命令,使得 bash pod 向目标服务发送请求,这样会得到状态代码 404,而不是503。这表明 bash 已经成功将请求发送到 NGINX Ingress Controller,但由于没有定义路由,所以不知道将它转发到哪里。

我们在 legacy-route.yaml 中使用以下 Ingress 资源创建所需的路由。第 7 行配置的 internal-route ,表示目标服务不会发布给互联网,而只发布给 NGINX Service Mesh 中的工作负载。

查看在❤ GitHub 托管的 legacy-route.yaml

通过下列命令激活新资源并确认 NGINX Ingress Controller 已添加相应的路由:

> kubectl apply -f legacy-route.yaml
ingress.networking.k8s.io/target-internal-route created
> kubectl describe ingress target-internal-route -n legacy
...
Events:
  Type    Reason          Age   From                      Message
  ----    ------          ----  ----                      -------
  Normal  AddedOrUpdated  6s    nginx-ingress-controller  Configuration for ...
        ...legacy/target-internal-route was added or updated

现在,当我们运行 kubectl exec 命令时,就可以访问目标服务了:

{"req": {"method": "GET"
                "url": "/",
                "host": "target-svc.legacy",
                "remoteAddr": "10.28.2.76:56086"}}

通过 NGINX Ingress Controller 路由出向流量的优势是,您可以准确控制从集群内部访问的外部服务(仅指定义了路由的外部服务)。

最后一项演示内容是如何监控出向流量。通过运行 kubectl exec 命令来发送几个请求,然后运行以下命令:

> nginxmesh-ctl top deploy/nginx-ingress -n nginx-ingress
Deployment      Direction  Resource  Success Rate  P99  P90  P50  NumRequests
nginx-ingress  
                To         target    100.00%       1ms  1ms  1ms  9
                From       bash      100.00%       0ms  0ms  0ms  9

拒绝延迟 —— 集成 NGINX Service Mesh 与 NGINX Ingress Controller

虽然许多服务网格都提供出入向网关选项,但 NGINX 还具有低延迟优势。大多数服务网格都需要向 Ingress Controller 注入 sidecar,因此流量需要额外一跳才能到达目的应用。虽然延迟很小,但这额外一跳会影响用户体验,进而导致客户流失。

NGINX Service Mesh 不会增加这样的延迟,因为它不需要将 sidecar 注入到 NGINX Ingress Controller。通过直接集成 Spire(服务网格的证书授权中心),NGINX Ingress Controller 可以成为 NGINX Service Mesh 的一部分。NGINX Ingress Controller 只从 Spire 代理获取证书和秘钥,并使用它们参与 MTLS 与网状 pod 的证书交换。

Kubernetes NGINX Ingress Controller 有两个版本:NGINX 开源版和 NGINX Plus 版。如要使用本文所述的使用 NGINX Service Mesh 部署 NGINX Ingress Controller,您必须使用 NGINX Plus 版(支持 30 天免费试用)。

NGINX Service Mesh 完全免费,您可立即下载并在 10 分钟内完成部署!有关使用方法,请查看我们的文档并通过 GitHub 告诉我们您的使用体验。