写点什么

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

  • 2024-03-06
    北京
  • 本文字数:7643 字

    阅读完需:约 25 分钟

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

原文作者:Kate Osborn of F5

原文链接:如何简化 Kubernetes 出入向流量管理

转载来源:NGINX 开源社区


NGINX 唯一中文官方社区 ,尽在 nginx.org.cn


编者注:本文引用相关资源的外链和视频较多,如欲获得更多参考资源,请点击此处前往 NGINX 中文官网查看该博客文章。


Kubernetes 出入向的流量管理可以通过服务网格和 Ingress Controller 两个功能来实现,这两个功能通常是分开进行配置的,这样 Kubernetes 环境的管理就会变得非常复杂。


这样的配置不仅会增加延迟,同时还会增加配置错误的概率,进而阻碍适当的流量路由,甚至导致出现安全漏洞(例如攻击者获得过多的应用访问权限)和不好的使用体验(例如客户无法访问其授权访问的应用)。另外分开配置也会消耗更多的配置时间,出现错误的时候还需要更多的时间来处理。


NGINX Plus 同时集成了 NGINX Ingress Controller 和 NGINX Service Mesh,也就是将服务网格和 Ingress

Controller 两个功能在一个组件上完成,通过这个集成功能可以同时控制出入向的 mTLS 流量,这样不仅可以避免分开配置引起的问题,也会节省时间。


下面通过几个章节进行说明:

  • 前提条件

  • 使用 NGINX Service Mesh 部署 NGINX Ingress Controller

  • 使用标准 Kubernetes Ingress 资源发布应用

  • 使用 NGINX VirtualServer 资源发布应用

  • 使用 NGINX Ingress Controller 配置安全出向路由


前提条件


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


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


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


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


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


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

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


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

> curl localhost:9080503
复制代码


使用 NGINX Service Mesh 部署 NGINX Ingress Controller


在发布应用的第一阶段,我们需要部署一个 NGINX Ingress Controller 实例。


NGINX 为此提供了 Deployment 和 Daemonset 的 manifest 文件。本演示中使用的是 Deployment manifest,文件为:nginx-plus-ingress.yaml。


它包括了下面的 annotations,配置这个 annotations 的 NGINX Ingress Controller 会同时实现入向流量和出向流量的管理:

nsm.nginx.com/enable-ingress: "true"nsm.nginx.com/enable-egress: "true"查看在 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 的代理地址:

args:    - -spire-agent-address=/run/spire/sockets/agent.sock查看在 GitHub 托管的nginx-plus-ingress.yaml
复制代码


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

volumes:    - hostPath:          path: /run/spire/sockets          type: DirectoryOrCreate         name: spire-agent-socket查看在 GitHub 托管的nginx-plus-ingress.yaml
复制代码


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

args:     - -enable-internal-routes查看在 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-ingressNAME   READY   STATUS    RESTARTS   AGEpod/nginx-ingress-867f954b8f0fzdrm   1/1   Running 0 3d3hNAME 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 资源发布应用


现在,我们使用 bookinfo-ingress.yaml 中定义的标准 Kubernetes Ingress 资源在网格中发布 bookinfo 应用。

apiVersion: networking.k8s.io/v1beta1kind: Ingressmetadata:    name: bookinfo-ingressspec:    ingressClassName: nginx # use only with k8s version >= 1.18.0    tls:    - hosts:    - bookinfo.example.com        secretName: bookinfo-secret    rules:    - host: bookinfo.example.com        http:            paths:            - path: /              backend:                  serviceName: productpage                  servicePort: 9080查看在 GitHub 托管的bookinfo-ingress.yaml
复制代码


在 bookinfo-ingress.yaml 的第 10 行引用了 Kubernetes Secret,这个配置提供了路由规则,该规则将对

bookinfo.example.com 的请求转发给 productpage 服务(该配置在第 11–18 行)。


该 Kubernetes Secret 在 bookinfo-secret.yaml 中进行了定义:

apiVersion: v1kind: Secretmetadata:name: bookinfo-secrettype: kubernetes.io/tlsdata:tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0Ktls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMl查看在 GitHub 托管的bookinfo-ingress.yaml
复制代码


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

> kubectl apply -f bookinfo-secret.yamlsecret/bookinfo-secret unchanged
复制代码


然后我们激活 Ingress 资源:

> kubectl apply -f bookinfo-ingress.yamlingress.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.yaml(下载)中定义的三个 reviews 服务版本的依次请求而发生周期性改变。


接下来,我们开始检查进入集群的流量。首先运行 generate-traffic.sh 脚本,然后通过访问 NGINX Ingress

Controller 发布的外网 IP 地址来请求productpage 服务,再运行 nginx-meshctl top 命令来监控流量:

> nginxmesh-ctl top deploy/productpage-v1Deployment  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 资源发布应用


下面我们来展示另一种发布应用的方法,即使用 NGINX VirtualServer 资源发布应用。它是一种自定义 NGINX

Ingress Controller 资源,支持更复杂的流量处理,例如流量分割和基于内容的路由。


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

> kubectl delete -f bookinfo-ingress.yamlingress.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

apiVersion: k8s.nginx.org/v1kind: VirtualServermetadata:name: bookinfo-vsspec:host: bookinfo.example.comtls:secret: bookinfo-secretupstreams: - name: productservice: productpageport: 9080routes:- path: /matches:- conditions:    - variable: $request_methodvalue: GETaction:pass: productaction:return:code: 405body: "Method not allowed\n"查看在 GitHub 托管的 bookinfo-vs.yaml
复制代码


下面来创建 NGINX VirtualServer 资源:

> kubectl apply -f bookinfo-vs.yamlvirtualserver.kubernetes.nginx.org/bookinfo-vs created
复制代码


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

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


使用 NGINX Ingress Controller 配置安全出向路由


现在我们展示如何通过 NGINX Ingress Controller 处理出向流量。


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

> kubectl get allNAME  READY  STATUS    RESTARTS   AGEpod/bash-6ccb678958-zsgm7   2/2    Running   0   77sNAME    TYPE    CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGEservice/kubernetes   ClusterIP   10.31.240.1   <none>        443/TCP   4d2h...
复制代码


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


  • 在集群外

  • 在另一个集群上

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


在本演示中,我们确定了示例的最终状态。我们在 legacy 命名空间(不受 NGINX Service Mesh 控制,并禁用

NGINX Service Mesh sidecar 自动注入功能)中部署了一个应用,并只有一个 pod 运行。

> kubectl get all --namespaces=legacyNAME    READY  STATUS    RESTARTS   AGEpod/target-5f7bcb96c6-km9lz   1/1    Running   0     27mNAME    TYPE   CLUSTER-IP     EXTERNAL-IP  PORT(S)   AGEservice/target-svc   ClusterIP   10.31.245.213   <none>    80/TCP,443/TCP   27m...
复制代码


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

> kubectl exec -it bash-6ccb678958-zsgm7 -c bash -- curl target-svc.legacycurl: (56) Recv failure: connection reset by peer503command terminated with exit code 56
复制代码


解决方案是 让 bash pod 发送出向流量时,该流量通过 NGINX Ingress Controller 来发送,操作方式是取消

bash.yaml 第 14-15 行的 annotations:

#annotations:#config.nsm.nginx.com/default-egress-allowed: "true" # uncomment to route egress traffic to NGINX Ingress Controller查看在 GitHub 托管的 bash.yaml
复制代码


然后应用新配置:

> kubectl apply -f bash.yamldeployment.apps/bash configured
复制代码


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

> kubectl get podsNAME                    READY  STATUS        RESTARTS   AGEbash-678c8b4579-7sfml   2/2    Running       0          6sbash-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 中的工作负载。

apiVersion: networking.k8s.io/v1beta1kind: Ingressmetadata:name: target-internal-routenamespace: legacyannotations: nsm.nginx.com/internal-route: "true"spec:ingressClassName: nginx # use only with k8s version >= 1.18.0tls: rules: - host: target-svc.legacy https: paths:- path: /backend: serviceName: target-svcservicePort: 80查看在 GitHub 托管的legacy-route.yaml
复制代码


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

> kubectl apply -f legacy-route.yamlingress.networking.k8s.io/target-internal-route created> kubectl describe ingress target-internal-route -n legacy...Events:
复制代码


现在,当我们运行 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-ingressDeployment      Direction  Resource  Success Rate  P99  P90  P50  NumRequestsnginx-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 告诉我们您的使用体验。


NGINX 唯一中文官方社区 ,尽在 nginx.org.cn

更多 NGINX 相关的技术干货、互动问答、系列课程、活动资源: 开源社区官网 | 微信公众号


用户头像

NGINX 唯一中文官方社区 2022-07-04 加入

NGINX 是全世界最流行的 Web 服务器,也可用于反向代理、负载均衡、API 网关等场景的开源软件,为全世界最繁忙的网站和应用提供支持。 微信:#NGINX开源社区

评论

发布
暂无评论
如何简化 Kubernetes 出入向流量管理_Kubernetes_NGINX开源社区_InfoQ写作社区