专栏首页CNCFKubernetes网络揭秘:一个HTTP请求的旅程

Kubernetes网络揭秘:一个HTTP请求的旅程

客座撰稿人:Karen Bruner,StackRox技术专员。原文可以在这里找到。

https://www.stackrox.com/post/2020/01/kubernetes-networking-demystified/

Kubernetes集群网络可能会让人感到非常困惑,即使对于有使用虚拟网络和请求路由实践经验的工程师来说也是如此。在这篇文章中,我们将介绍Kubernetes网络的复杂性,通过跟踪HTTP请求到运行在基本Kubernetes集群上的服务过程。

我们将使用带有两个Linux节点的标准谷歌Kubernetes引擎(GKE)集群作为示例,并说明在其他平台上细节可能有所不同。

一个HTTP请求的旅程

以浏览网页的人为例。他们点击一个链接,发生了一些事情,页面就会加载。

我们需要把这些问号填一下。

在下一个图中,请求通过Internet发送到一个非常大的云提供商,然后发送到位于云提供商基础设施中的Kubernetes集群。

Kubernetes网络政策指南

当我们放大到Kubernetes集群时,我们看到云提供商负载均衡器向Kubernetes服务(Service)资源发送请求,然后将请求路由到Kubernetes副本集(ReplicaSet)中的pod。

我们可以部署以下YAML来创建Kubernetes服务(Service,svc)和副本集(ReplicaSet,rs):

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: hello-world
  labels:
    app: hello-world
spec:
  selector:
    matchLabels:
      app: hello-world
  replicas: 2
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: hello-world
        image: gcr.io/google-samples/node-hello:1.0
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          protocol: TCP

---
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  selector:
    app: hello-world
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
  type: LoadBalancer
  externalTrafficPolicy: Cluster

这些清单应导致在hello-world ReplicaSet中创建两个Pod,并在云提供商和群集网络支持的情况下,创建带有面向外部的负载平衡器的hello-world服务资源。它还应该创建一个Kubernetes端点(Endpoint)资源,该资源在host:port表示法中有两个条目,每个Pod都有一个,其中Pod IP为主机值和端口8080。

在我们的GKE集群上,使用kubectl查询这些资源类型将返回以下内容:

作为参考,我们的集群具有以下IP网络:

  • 节点 – 10.138.15.0/24
  • 群集 – 10.16.0.0/14
  • 服务 – 10.19.240.0/20

我们的服务在群集CIDR块中具有10.19.240.1的虚拟IP地址(Virtual IP,VIP)。

现在,我们准备好从负载均衡器开始,按照请求进入Kubernetes集群的过程。

负载均衡器

虽然Kubernetes通过原生控制器和通过入口控制器提供了多种暴露服务的方法,但我们将使用LoadBalancer类型的标准Service资源。我们的hello-world服务需要GCP网络负载平衡器。每个GKE集群都有一个云控制器,该云控制器在集群和自动创建集群资源(包括我们的负载均衡器)所需的GCP服务的API端点之间进行连接。 (所有云提供商都提供具有不同选项和特性的不同类别的负载均衡器。)

要查看外部负载均衡器的位置,首先我们需要从另一个角度看待集群。

kube-proxy

每个节点都有一个kube-proxy容器进程。 (在Kubernetes参考框架中,该kube-proxy容器位于kube-system命名空间的pod中。)kube-proxy管理将寻址到群集Kubernetes服务对象的虚拟IP地址(VIP)的流量转发到适当的后端Pod。kube-proxy当前支持三种不同的操作模式:

  • 用户空间(User space):此模式之所以得名,是因为服务路由发生在用户进程空间的kube-proxy中,而不是在内核网络堆栈中。它不常用,因为它运行缓慢且过时。
  • iptables:此模式使用Linux内核级Netfilter规则为Kubernetes Services配置所有路由。在大多数平台上,此模式是kube-proxy的默认模式。在为多个后端容器进行负载平衡时,它使用非加权循环调度。
  • IPVS(IP虚拟服务器):基于Netfilter框架,IPVS在Linux内核中实现第4层负载均衡,支持多种负载均衡算法,包括最少的连接和最短的预期延迟。这种kube-proxy模式通常在Kubernetes 1.11中可用,但是它需要Linux内核加载IPVS模块。各种kubernetes网络项目也没有像iptables模式那样广泛地支持它。

GKE集群中的kube-proxy在iptables模式下运行,因此我们将研究该模式的工作方式。

如果我们查看创建的hello-world服务,我们可以看到已为其分配了30510的节点端口(用于节点IP地址的网络端口)。节点网络上动态分配的端口允许群集中托管的多个Kubernetes服务在其端点中使用相同的面向Internet的端口。如果我们的服务已部署到标准的Amazon Elastic Kubernetes服务(EKS)集群,则将由Elastic Load Balancer提供服务,该服务会将传入的连接发送到具有服务pod的节点端口。但是,Google Cloud Platform(GCP)网络负载均衡器仅将流量转发到与负载均衡器上传入端口位于同一端口上的目标,也即是到负载均衡器上端口80的流量将发送到目标后端上的端口80实例。 hello-world pod绝对不会在节点的端口80上侦听。如果在节点上运行netstat,我们将看到没有进程在该端口上侦听。

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

iptables

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

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

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

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

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

请注意,即使我们的集群有两个节点,每个节点都有一个hello-world pod,但此路由方法并未显示优先选择路由到从云负载平衡器接收请求的节点上的Pod。但是,如果我们将服务规范中的externalTrafficPolicy更改为Local,那将会改变。如果存在请求,请求不仅会转到接收请求的节点上的Pod,而且这意味着没有服务Pod的节点将拒绝连接。因此,通常需要将Local策略与Kubernetes守护程序集一起使用,该守护程序集会在集群中的每个节点上调度一个Pod。尽管指定本地交付显然会减少请求的平均网络延迟,但可能导致服务Pod的负载不均衡。

Pod网络

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

HTTP请求

这就是我们获取HTTP 200响应代码的方式。

路由变量

这篇文章提到了各种Kubernetes平台提供的不同选项可以更改路由的一些方式。这是一个不全面的列表:

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

保护服务

  • 不存在将Kubernetes Service资源创建的用于向云负载均衡器添加防火墙限制的通用方法。一些云提供商会遵守Service规范中的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功能一起运行,这将使它们能够读取和修改节点的防火墙规则。

Kubernetes网络需要大量的移动部件。它非常复杂,但是对集群中发生的事情有基本的了解将有助于您更有效地监视和保护它。

资料来源和进一步阅读

  • https://kubernetes.io/docs/concepts/services-networking/service/
  • https://kubernetes.io/docs/concepts/cluster-administration/networking/
  • https://twitter.com/thockin/status/1191766983735296000
  • https://kubernetes.io/blog/2018/07/09/ipvs-based-in-cluster-load-balancing-deep-dive/
  • https://netfilter.org/documentation/HOWTO/NAT-HOWTO-6.html点击文末<<阅读原文>>进入网页了解更多。

本文分享自微信公众号 - CNCF(lf_cncf),作者:CNCF

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-01-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 开始在Kubernetes运行Python应用程序

    你知道你想要在Kubernetes中运行应用程序,但不知道从哪里开始。或者你刚刚开始,但不知道自己不知道什么。在本博客中,你将了解如何封装应用程序,并使其在Ku...

    CNCF
  • Network Service Mesh发布0.2版本,来Run示例玩玩

    https://github.com/networkservicemesh/networkservicemesh/blob/master/docs/guide-...

    CNCF
  • 操作指南:调试Kubernetes应用程序

    调试Kubernetes应用程序通常是一个痛苦的过程,充满未知和不可预知的副作用。当你的Kubernetes集群没有自我愈合时会发生什么?错误配置的资源限制如何...

    CNCF
  • 数据结构与算法(九)二叉搜索树的删除操作

    •前驱节点:中序遍历时的前一个节点•如果左子树存在,从该节点的左子节点的最右的节点。•如果左子树 == null && 父节点!= null 父节点为父节点遍历...

    老沙
  • Python正则表达式的7个使用典范

    作为一个概念而言,正则表达式对于Python来说并不是独有的。但是,Python中的正则表达式在实际使用过程中还是有一些细小的差别。

    一墨编程学习
  • AQS之Condition

    spilledyear
  • Kubernetes本地集群和Google Kubernetes Engine的区别

    版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

    Jerry Wang
  • NLP(4)——用词向量技术简单分析红楼梦人物关系用n-gramma生成词向量word2vect进行模型训练

    前言:出于种种原因,总是不自觉把爱好和工作相互结合起来,每每感叹于曹雪芹构思的巧妙,语言的精炼,情节的感人……于是蹦出想法,看机器能否读懂“宝黛”之间的爱情。

    DC童生
  • 底层技术大对比!分分钟带你看透区块链和云计算

    区块链大本营
  • 底层技术大PK!分分钟带你看透区块链和云计算

    作者 | 谢文杰、金钰 责编 | 贾维娣 本文转载自 智链ChainNova,已经授权CSDN转载 我们在研究区块链的过程中发现,区块链的发展和云计算有非常多的...

    CSDN技术头条

扫码关注云+社区

领取腾讯云代金券