如何化解 Kubernetes 网络的复杂性?-阿里云开发者社区

开发者社区> 云栖号资讯小哥> 正文

如何化解 Kubernetes 网络的复杂性?

简介: 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 本文将以带有两个 Linux 节点的标准 GKE 集群为例,通过跟踪 HTTP 请求被传送到集群服务的整个过程,深度拆解 Kubernetes 网络的复杂性。
+关注继续查看

云栖号资讯:【点击查看更多行业资讯
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!


根据 CNCF 历年发布的 Kubernetes 生态报告,安全、存储、网络始终是开发者最关注的三大槽点。即便在虚拟网络和请求路由方面有丰富经验,很多开发者在处理 Kubernetes 集群网络时还是很混乱。

本文将以带有两个 Linux 节点的标准 Google Kubernetes Engine(GKE)集群为例,通过跟踪 HTTP 请求被传送到集群服务的整个过程,深度拆解 Kubernetes 网络的复杂性。

请求的旅程

当一个人在浏览网页时,他首先单击一个链接,发生了一些事,之后目标页面就被加载出来。这让人不免好奇,从单击链接到页面加载,中间到底发生了什么?

1

对于这个问题,我们可以这样理解。如下图所示,用户请求通过 Internet 被发送给一个非常大的云提供商,然后再被发送到该云提供商基础架构中托管的 Kubernetes 集群。

2

如果进一步放大 Kubernetes 集群,我们可以看到云提供商正向 Kubernetes Service 资源(SVC)发送请求,然后将请求路由到 Kubernetes ReplicaSet(RS)中的 Pod。

3

为了更直观,我们可以部署 YAML 来创建 Kubernetes Service 和 ReplicaSet:

4

现在我们已经在 hello-world ReplicaSet 下创建了两个 Pod,还创建了一个带有负载均衡器的服务资源 hello-world(如果云提供商和集群网络支持),以及一个在 host:port 中有两个条目的 Kubernetes Endpoint 资源,每个 Pod 对应一个,以 Pod IP 作为主机值和端口 8080。

在 GKE 集群上,我们 kubectl 一下会返回以下内容:

5

集群 IP 网络信息:

  • Node - 10.138.15.0/24
  • Cluster - 10.16.0.0/14
  • Service - 10.19.240.0/20

已知服务在集群 CIDR 中的虚拟 IP 地址(VIP)是 10.19.240.1。现在,我们可以从负载均衡器开始,深入跟踪请求进入 Kubernetes 集群的整个“旅程”。

负载均衡器

Kubernetes 通过本地控制器和 Ingress 控制器提供了很多公开服务的方法,但这里我们还是使用 LoadBalancer 类型的标准 Service 资源。

我们的 hello-world 服务需要 GCP 网络负载均衡器。每个 GKE 集群都有一个云控制器,它在集群和 API 端点之间进行接口,以自动创建集群资源所需的 GCP 服务,包括我们的负载均衡器(不同云提供商的负载均衡器在类型、特性上都有不同)。

通过从不同的角度观察集群,我们可以查看外部负载均衡器的位置:

6

kube-proxy

每个节点都有一个 kube-proxy 容器进程(在 Kubernetes 参考框架中,kube-proxy 容器位于 kube-system 命名空间的 Pod 中),它负责把寻址到集群 Kubernetes 服务对象虚拟 IP 地址的流量转发到相应后端 Pod。kube-proxy 当前支持三种不同的实现方式:

  • User space:即用户空间,服务路由是在用户进程空间的 kube-proxy 中进行的,而不是内核网络堆栈。这是kube-proxy 的最初版本,较为稳定,但是效率不太高;
  • iptables:这种方式采用 Linux 内核级 Netfilter 规则为 Kubernetes Services 配置所有路由,是大多数平台实现 kube-proxy 的默认模式。当对多个后端 Pod 进行负载均衡时,它使用未加权的循环调度;
  • IPVS:IPVS 基于 Netfilter 框架构建,在 Linux 内核中实现了 L4 负载均衡,支持多种负载均衡算法,连接最少,预期延迟最短。它从 Kubernetes v1.11 中开始普遍可用,但需要 Linux 内核加载 IPVS 模块。它也不像 iptables 那样拥有各种 Kubernetes 网络项目的广泛支持。

在我们的 GKE 集群中,kube-proxy 以 iptables 模式运行,所以我们后续主要研究该模式的工作方式。

如果查看创建好的 hello-world 服务,我们可以发现它已经被分配了一个节点端口 30510。节点网络上动态分配的端口允许其中托管的多个 Kubernetes 服务在其端点中使用相同的面向 Internet 的端口。

如果服务已被部署到标准 Amazon EKS 集群,它将由 Elastic Load Balance 提供服务,该服务会将传入的连接发送到相应 Pod 节点上我们服务的节点端口。但是,Google Cloud Platform 网络负载均衡器只会将流量转发到与负载均衡器的传入端口位于同一端口的目标,例如,到负载均衡器上的端口 80 的流量会被发送到目标后端实例上的端口 80。

我们的 hello-world pods 绝对没有在节点的端口 80 上监听。所以如果在节点上运行 netstat,我们可以看到没有进程正在监听该端口。

7

那么,通过负载均衡器的请求是如何成功建立连接的呢?如果 kube-proxy 在用户空间模式下运行,它实际上是将连接代理到后端 Pod。但是,在 iptables 模式下,kube-proxy 配置了 Netfilter 链,因此该连接被节点的内核直接路由到了后端容器的端点。

iptables

在我们的 GKE 集群中,如果登录到其中一个节点并运行 iptables,我们可以看到这些规则。

8

根据规则注释,我们可以获得与来自服务的负载均衡器到 hello-world 服务的传入连接相匹配的过滤器链的名称,并遵循该链的规则(在没有规则注释的情况下,我们仍然可以将规则的源 IP 地址与服务的负载均衡器进行匹配)。

9

我们还可以可视化网络堆栈中用于评估和修改数据包的链和规则,查看我们在集群中创建的服务是怎么把流量定向到副本集成员的。

10

KUBE-FW-33X6KPGSXBPETFQV 链有三个规则,每个规则都添加了另一个链来处理数据包。

  • KUBE-MARK-MASQ 向发送到 hello-world 服务的包(来自集群网络外部)添加一个 Netfilter 标记。带有此标记的数据包将按照 POSTROUTING 规则进行更改,以使用源网络地址转换(SNAT),并将节点 IP 地址作为其源 IP 地址;
  • KUBE-SVC-33X6KPGSXBPETFQV 链适用于所有与 hello-world 服务相关的流量(与源无关),并且对每个服务端点(在本例中为两个 Pod)提供规则。使用哪个端点链是完全随机确定的:
  • KUBE-SEP-ALRUKLHE5DT3R34X:如果需要,KUBE-MARK-MASQ 会再次向数据包中添加一个 Netfilter 标记用以 SNAT;DNAT 规则使用 10.16.0.11:8080 端点作为目标来设置目标 NAT
  • KUBE-SEP-X7DMMHFVFOT4JLHD:如果需要,KUBE-MARK-MASQ 会再次为数据包添加一个 Netfilter 标记用以 SNAT;DNAT 规则使用 10.16.1.8:8080 端点作为目标来设置目标 NAT
  • KUBE-MARK-DROP 向此时尚未启用目标 NAT 的数据包添加 Netfilter 标记。这些数据包将在 KUBE-FIREWALL 链中被丢弃。

需要注意的是,尽管我们的集群有两个节点,每个节点都有一个 hello-world Pod,但这种路由方法并不存在优先级。如果我们将服务规范中的 externalTrafficPolicy 更改为 Local,那么情况就会改变。假设此时存在请求,这个请求不仅会转到接收请求的节点上的 Pod,还会导致没有服务 Pod 的节点拒绝连接。

因此,Local 策略通常需要与 Kubernetes daemon sets 一起使用,后者会在集群中的每个节点上调度一个 Pod。虽然前者能明显降低请求的平均网络延迟,但它也可能导致服务 Pods 之间的负载不均衡。

Pod 网络

本文不会深入介绍 Pod 网络,但是在我们的 GKE 集群中,Pod 网络有自己的 CIDR 块,与节点网络分开。Kubernetes 网络模型要求集群中的所有 Pod 能够直接相互寻址(无视其主机节点)。GKE 群集使用 kubenet CNI,它在每个节点上创建到 Pod 网络的网桥接口,为每个节点提供自己的 Pod IP 地址专用 CIDR 块,以简化分配和路由。Google Compute Engine(GCE)网络可以在 VM 之间路由该 Pod 网络流量。

请求

以下是是我们获取 HTTP 200 响应代码的方式:

11

本文提到了许多改变路由的方法,它们由不同 Kubernetes 平台提供,下面是一个简单的清单:

  • 容器网络接口(CNI)插件:每个云提供商默认使用与其 VM 网络模型兼容的 CNI 实现。本文以默认设置的 GKE 集群为例,但如果是 Amazon EKS,那会很不一样,因为 AWS VPC CNI 把容器直接放在节点的 VPC 网络上;
  • Kubernetes Network Policy:Calico 是实施网络策略最受欢迎的 CNI 插件之一,它在节点上为每个 Pod 创建一个虚拟网络接口,并使用 Netfilter 规则来实施其防火墙规则;
  • 尽管大多数情况下仍然使用 Netfilter,但 kube-proxy IPVS 路由模式通常会把服务路由和 NAT 移出 Netfilter 规则;
  • 外部负载均衡器或其他可以将流量直接发送到服务节点端口的源将匹配 iptables 中的不同链(KUBE-NODEPORTS);
  • Kubernetes Ingress 控制器可以通过多种方式更改边缘服务路由;
  • 诸如 Istio 之类的服务网格可能会绕过 kube-proxy,直接连接服务容器之间的内部路由。

保护服务

Kubernetes 网络需要大量可移动部件,它非常复杂,但如果开发者对集群中发生的事有基本了解,这会有助于开发者更有效地监控、保护它。

第一,对于 Kubernetes 服务资源创建的云负载均衡器,添加防火墙限制的通用方法是不存在的。一些云提供商会支持服务规范中的 loadBalancerSourceRanges 字段,这个字段允许开发者提供可以连接到负载均衡器的 IP CIDR 块白名单。如果云提供商不支持此字段,它就会被忽略,因此开发者需要验证外部负载均衡器的网络配置。

而对于不支持 loadBalancerSourceRanges 字段的云提供商,除非已经在云提供商级别采取措施锁定了负载均衡器和运行它们的云网络,开发者还是应该假定负载均衡器上的服务端点是对全世界开放的。由于各种因素,云提供商负载均衡器产品的默认防火墙设置千差万别,一些云提供商可能还支持对 Service 对象的注释,以配置负载均衡器的安全性。

其次,请注意,我们没有通过在 GKE 集群中启用 Kubernetes 网络策略支持来安装 Calico CNI,因为 Calico 创建了大量其他 iptables 规则,这给可视化跟踪到 Pod 的虚拟路由时增加了额外步骤。尽管如此,我们还是建议开发者在生产集群中实现 NetworkPolicy API 的 CNI,并创建限制 Pod 流量的策略。

第三,启用 HostNetwork 属性创建的 Pod 将共享节点的网络空间。虽然存在一些这样做的例子,但通常情况下,大多数 Pod 不需要在主机网络上,尤其是对于有 root 特权的 Pod,这可能会导致受攻击的容器可以查看网络流量。如果开发者需要在节点网络上公开容器端口,而使用 Kubernetes Service 节点端口无法满足需求,一个稳妥的选择是可以在 PodSpec 中为容器指定 hostPort。

最后,使用主机网络的 Pod 不应使用 NET_ADMIN 功能运行,这将使它们能够读取和修改节点的防火墙规则。

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-05-15
本文作者:翔宇
本文来自:“dockone”,了解相关信息可以关注“dockone”

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10014 0
深度解析容器服务Kubernetes集群容量以及网络规划
#背景 在目前云原生技术被如火如荼的大规模使用的过程中。越来越多的用户都会使用Kubernetes集群去部署其应用。但是在这个过程中,如果由于早期对于容量和网络的规划不当,可能造成实际生产中实践中,不能满足业务的真实需要。如果此时在重新规划就面临着集群重建、应用迁移的诸多事项,这样不仅仅浪费了大量的精力,甚至可能会造成业务有一定的中断。因此,为了使得广大使用者可以更加深入的理解阿里云容器服务Ku
840 0
分享录制的正则表达式入门、高阶以及使用 .NET 实现网络爬虫视频教程
我发布的「正则表达式入门以及高阶教程」,欢迎学习。 课程简介 正则表达式是软件开发必须掌握的一门语言,掌握后才能很好地理解到它的威力; 课程采用概念和实验操作 4/6 分隔,帮助大家理解概念后再使用大量的实例加深对概念的理解; 实例操作是对概念最好的理解,也是学习新语言最有效的办法; 在课程中也穿插着大量软件开发的技巧和大家分享; 应该是把晦涩的正则表达式讲解的最生动的课程; 掌握了正则表达式后,您一定会觉得这是一门最值得掌握的语言。
931 0
.Net网络编程——使用TcpClient与TcpListener建立客户端与服务端连接
              一,.NET中如何实现建立连接                         在网络中,我们可以通过IP地址唯一定位一台主机,而在主机中,我们要确定收到的数据包发给谁,可以通过端口号,端口号的作用简单说就是不至于使你要发给QQ好友的消息数据包被错误发到了你的OC程序上。
995 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13814 0
从美图容器优化实践谈Kubernetes网络方案设计
本文通过介绍美图线上容器化的实践经验,包括线上遇到的实际问题,来探讨 Kubernetes 环境下的网络方案设计。值得正在转型 K8S 的架构师学习和借鉴。 李连荣,美图高级系统研发工程师,曾建立支持千万的长连接服务,从零开始在建立美图的容器化服务,并主导完成美图容器化的网络方案。
1295 0
【.Net MF网络开发板研究-05】Socket编程之客户端
官方示例中也包括了SocketClient,不过其完成的功能一是DNS解析,二是网站数据接收。我们需要大幅度改进一下,让SocketClient程序和PC上的Socket Server进行通信。
427 0
+关注
云栖号资讯小哥
云栖号小编在此 ^o^
1403
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载