k8s学习-服务发现以及网络2

网友投稿 256 2022-09-10

k8s学习-服务发现以及网络2

网络策略

Kubernetes 中,Network Policy(网络策略)定义了一组 Pod 是否允许相互通信,或者与网络中的其他端点 endpoint 通信。

NetworkPolicy 对象使用标签选择Pod,并定义规则指定选中的Pod可以执行什么样的网络通信,规则通常由如下三类信息组合而成:

允许访问的其他容器组(容器组不能阻止其访问自己的端口)允许访问的名称空间允许访问的 IP 段(例外:从容器组所在的节点访问容器组,或者从容器组访问其所在的节点都是始终被允许的)

前提条件

Network Policy 由网络插件实现,因此,您使用的网络插件必须能够支持 NetworkPolicy 才可以使用此特性。如果您仅仅是创建了一个 Network Policy 对象,但是您使用的网络插件并不支持此特性,您创建的 Network Policy 对象是不生效的。

Isolated/Non-isolated Pods

默认情况下,Pod 都是非隔离的(non-isolated),可以接受来自任何请求方的网络请求。

如果一个 NetworkPolicy 的标签选择器选中了某个 Pod,则该 Pod 将变成隔离的(isolated),并将拒绝任何不被 NetworkPolicy 许可的网络连接。(名称空间中其他未被 NetworkPolicy 选中的 Pod 将认可接受来自任何请求方的网络请求。)

Network Police 不会相互冲突,而是相互叠加的。如果多个 NetworkPolicy 选中了同一个 Pod,则该 Pod 可以接受这些 NetworkPolicy 当中任何一个 NetworkPolicy 定义的(入口/出口)规则,是所有NetworkPolicy规则的并集,因此,NetworkPolicy 的顺序并不重要,因为不会影响到最终的结果。

为了使两个容器组之间的网络能够连通,源容器组的出方向网络策略和目标容器组的入方向无网络策略必须同时允许该网络连接。只要其中的任何一方拒绝了该连接,该连接都不能创建成功。

对象

一个 NetworkPolicy 的 Example 如下所示:

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: test-network-policy namespace: defaultspec: podSelector: matchLabels: role: db policyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: matchLabels: project: myproject - podSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5978

​基本信息:同其他的 Kubernetes 对象一样,NetworkPolicy​ 需要apiVersion、kind、metadata 字段spec:NetworkPolicy​ 的spec 字段包含了定义网络策略的主要信息:

podSelector:同名称空间中,符合此标签选择器.spec.podSelector​ 的 Pod 都将应用这个NetworkPolicy​。上面的 Example中的 podSelector 选择了role=db​ 的 Pod。如果该字段为空,则将对名称空间中所有的 Pod 应用这个NetworkPolicypolicyTypes:.spec.policyTypes​ 是一个数组类型的字段,该数组中可以包含Ingress​、Egress​ 中的一个,也可能两个都包含。该字段标识了此NetworkPolicy​ 是否应用到 入方向的网络流量、出方向的网络流量、或者两者都有。如果不指定policyTypes​ 字段,该字段默认将始终包含Ingress​,当NetworkPolicy​ 中包含出方向的规则时,Egress 也将被添加到默认值。ingress:ingress​ 是一个数组,代表入方向的白名单规则。每一条规则都将允许与from​ 和ports​ 匹配的入方向的网络流量发生。例子中的ingress 包含了一条规则,允许的入方向网络流量必须符合如下条件:

Pod 的监听端口为6379请求方可以是如下三种来源当中的任意一种:

ipBlock 为172.17.0.0/16​ 网段,但是不包括172.17.1.0/24 网段namespaceSelector 标签选择器,匹配标签为project=myprojectpodSelector 标签选择器,匹配标签为role=frontend

egress:egress​ 是一个数组,代表出方向的白名单规则。每一条规则都将允许与to​ 和ports​ 匹配的出方向的网络流量发生。例子中的egress 允许的出方向网络流量必须符合如下条件:

目标端口为5978目标 ipBlock 为10.0.0.0/24 网段

因此,例子中的 NetworkPolicy 对网络流量做了如下限制:

隔离了default 名称空间中带有role=db 标签的所有 Pod 的入方向网络流量和出方向网络流量Ingress规则(入方向白名单规则):

当请求方是如下三种来源当中的任意一种时,允许访问default 名称空间中所有带role=db 标签的 Pod 的6379 端口:

ipBlock 为172.17.0.0/16 网段,但是不包括172.17.1.0/24 网段namespaceSelector 标签选择器,匹配标签为project=myprojectpodSelector 标签选择器,匹配标签为role=frontend

Egress rules(出方向白名单规则):

当如下条件满足时,允许出方向的网络流量:

目标端口为5978目标 ipBlock 为10.0.0.0/24 网段

to和from选择器的行为

NetworkPolicy 的.spec.ingress.from​ 和 .spec.egress.to 字段中,可以指定 4 种类型的标签选择器:

podSelector 选择与 NetworkPolicy 同名称空间中的 Pod 作为入方向访问控制规则的源或者出方向访问控制规则的目标namespaceSelector 选择某个名称空间(其中所有的Pod)作为入方向访问控制规则的源或者出方向访问控制规则的目标namespaceSelector 和 podSelector 在一个 to​ / from​ 条目中同时包含 namespaceSelector​ 和 podSelector 将选中指定名称空间中的指定 Pod。ipBlock 可选择 IP CIDR 范围作为入方向访问控制规则的源或者出方向访问控制规则的目标。这里应该指定的是集群外部的 IP,因为集群内部 Pod 的 IP 地址是临时分配的,且不可预测。

集群的入方向和出方向网络机制通常需要重写网络报文的 source 或者 destination IP。kubernetes 并未定义应该在处理 NetworkPolicy 之前还是之后再修改 source / destination IP,因此,在不同的云供应商、使用不同的网络插件时,最终的行为都可能不一样。这意味着:

对于入方向的网络流量,某些情况下,你可以基于实际的源 IP 地址过滤流入的报文;在另外一些情况下,NetworkPolicy 所处理的 "source IP" 可能是 LoadBalancer 的 IP 地址,或者其他地址对于出方向的网络流量,基于 ipBlock 的策略可能有效,也可能无效

Network Policies - Default

默认情况下,如果名称空间中没有配置 NetworkPolicy,则该名称空间中,所有Pod的所有入方向流量和所有出方向流量都是被允许的。

默认拒绝所有的入方向

在名称空间中创建下面的 NetworkPolicy,该 NetworkPolicy:

选中所有的 Pod不允许任何入方向的流量

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: default-denyspec: podSelector: {} policyTypes: - Ingress

此 NetworkPolicy 将确保名称空间中所有的入方向流量都被限制,同时,不改变出方向的流量。

默认允许所有的入方向流量

在名称空间中创建下面的 NetworkPolicy,该 NetworkPolicy 允许名称空间中所有 Pod 的所有入方向网络流量

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-allspec: podSelector: {} ingress: - {} policyTypes: - Ingress

默认允许所有出方向流量

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: allow-allspec: podSelector: {} egress: - {} policyTypes: - Egress

默认拒绝所有入方向和出方向的网络流量

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: default-denyspec: podSelector: {} policyTypes: - Ingress - Egress

网络策略-示例

创建一个Deployment并配置Service

#创建一个niginx Deployment用于演示kubectl create deployment nginx --image=nginx##############输出结果##############deployment.apps/nginx created#通过Service暴露kubectl expose deployment nginx --port=80##############输出结果###############service/nginx exposed#查询结果kubectl get svc,pod##############输出结果################NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes 10.100.0.1 443/TCP 46mservice/nginx 10.100.0.16 80/TCP 33sNAME READY STATUS RESTARTS AGEpod/nginx-701339712-e0qfq 1/1 Running 0 35s

从另外一个pod访问service

默认是可以从另外一个Pod访问 nginx Service 的。下面的方法可以执行此测试:

在 default 名称空间中创建 busybox 容器,并执行 wget 命令:

kubectl run --generator=run-pod/v1 busybox --rm -ti --image=busybox -- /bin/shWaiting for pod default/busybox-472357175-y0m47 to be running, status is Pending, pod ready: falseHit enter for command prompt/ # wget --spider --timeout=1 nginxConnecting to nginx (10.100.0.16:80)/ #

限制nginx的访问

apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: access-nginxspec: podSelector: matchLabels: app: nginx ingress: - from: - podSelector: matchLabels: access: "true"#创建NetworkPolicykubectl apply -f created

Kubernetes网络模型

ubernetes 用来在集群上运行分布式系统。分布式系统的本质使得网络组件在 Kubernetes 中是至关重要也不可或缺的。理解 Kubernetes 的网络模型可以帮助你更好的在 Kubernetes 上运行、监控、诊断你的应用程序。

关于 Pod 如何接入网络这件事情,Kubernetes 做出了明确的选择。具体来说,Kubernetes 要求所有的网络插件实现必须满足如下要求:

所有的 Pod 可以与任何其他 Pod 直接通信,无需使用 NAT 映射(network address translation)所有节点可以与所有 Pod 直接通信,无需使用 NAT 映射Pod 内部获取到的 IP 地址与其他 Pod 或节点与其通信时的 IP 地址是同一个

在这些限制条件下,需要解决如下四种完全不同的网络使用场景的问题:

Container-to-Container 的网络Pod-to-Pod 的网络Pod-to-Service 的网络Internet-to-Service 的网络

Container-to-Container的网络

通常,我们认为虚拟机中的网络通信是直接使用以太网设备进行的,如下图所示:

实际情况比这个示意图更加复杂一些。Linux系统中,每一个进程都在一个 network namespace (opens new window)中进行通信,network namespace 提供了一个逻辑上的网络堆栈(包含自己的路由、防火墙规则、网络设备)。换句话说,network namespace 为其中的所有进程提供了一个全新的网络堆栈。

Linux 用户可以使用 ip 命令创建 network namespace。例如,下面的命令创建了一个新的 network namespace 名称为 ns1:

$ ip netns add ns1

当创建 network namespace 时,同时将在 /var/run/netns 下创建一个挂载点(mount point)用于存储该 namespace 的信息。

执行 ls /var/run/netns 命令,或执行 ip 命令,可以查看所有的 network namespace:

$ ls /var/run/netnsns1$ ip netnsns1

在 Kubernetes 中,Pod 是一组 docker 容器的集合,这一组 docker 容器将共享一个 network namespace。Pod 中所有的容器都:

使用该 network namespace 提供的同一个 IP 地址以及同一个端口空间可以通过 localhost 直接与同一个 Pod 中的另一个容器通信

Kubernetes 为每一个 Pod 都创建了一个 network namespace。具体做法是,把一个 Docker 容器当做 “Pod Container” 用来获取 network namespace,在创建 Pod 中新的容器时,都使用 docker run 的 --network:container 功能来加入该 network namespace,参考 docker run reference (opens new window)。如下图所示,每一个 Pod 都包含了多个 docker 容器(ctr*),这些容器都在同一个共享的 network namespace 中:

此外,Pod 中可以定义数据卷,Pod 中的容器都可以共享这些数据卷,并通过挂载点挂载到容器内部不同的路径

Pod-to-Pod的网络

在 Kubernetes 中,每一个 Pod 都有一个真实的 IP 地址,并且每一个 Pod 都可以使用此 IP 地址与 其他 Pod 通信

​从 Pod 的视角来看,Pod 是在其自身所在的 network namespace 与同节点上另外一个 network namespace 进程通信。在Linux上,不同的 network namespace 可以通过 Virtual Ethernet Device (opens new window)​或 veth pair (两块跨多个名称空间的虚拟网卡)进行通信。为连接 pod 的 network namespace,可以将 veth pair 的一段指定到 root network namespace,另一端指定到 Pod 的 network namespace。每一组 veth pair 类似于一条网线,连接两端,并可以使流量通过。节点上有多少个 Pod,就会设置多少组 veth pair。下图展示了 veth pair 连接 Pod 到 root namespace 的情况:

此时,我们的 Pod 都有了自己的 network namespace,从 Pod 的角度来看,他们都有自己的以太网卡以及 IP 地址,并且都连接到了节点的 root network namespace。为了让 Pod 可以互相通过 root network namespace 通信,我们将使用 network bridge(网桥)。

Linux Ethernet bridge 是一个虚拟的 Layer 2 网络设备,可用来连接两个或多个网段(network segment)。网桥的工作原理是,在源于目标之间维护一个转发表(forwarding table),通过检查通过网桥的数据包的目标地址(destination)和该转发表来决定是否将数据包转发到与网桥相连的另一个网段。桥接代码通过网络中具备唯一性的网卡MAC地址来判断是否桥接或丢弃数据。

网桥实现了 ARP (opens new window)协议,以发现链路层与 IP 地址绑定的 MAC 地址。当网桥收到数据帧时,网桥将该数据帧广播到所有连接的设备上(除了发送者以外),对该数据帧做出相应的设备被记录到一个查找表中(lookup table)。后续网桥再收到发向同一个 IP 地址的流量时,将使用查找表(lookup table)来找到对应的 MAC 地址,并转发数据包。

数据包的传递:Pod-to-Pod,同节点

在 network namespace 将每一个 Pod 隔离到各自的网络堆栈的情况下,虚拟以太网设备(virtual Ethernet device)将每一个 namespace 连接到 root namespace,网桥将 namespace 又连接到一起,此时,Pod 可以向同一节点上的另一个 Pod 发送网络报文了。下图演示了同节点上,网络报文从一个Pod传递到另一个Pod的情况。

Pod1 发送一个数据包到其自己的默认以太网设备 eth0。

对 Pod1 来说,eth0 通过虚拟以太网设备(veth0)连接到 root namespace网桥cbr0 中为veth0 配置了一个网段。一旦数据包到达网桥,网桥使用ARP (opens new window)协议解析出其正确的目标网段veth1网桥cbr0 将数据包发送到veth1数据包到达veth1 时,被直接转发到 Pod2 的 network namespace 中的eth0 网络设备。

在整个数据包传递过程中,每一个 Pod 都只和 localhost 上的 eth0 通信,且数包被路由到正确的 Pod 上。与开发人员正常使用网络的习惯没有差异。

Kubernetes 的网络模型规定,在跨节点的情况下 Pod 也必须可以通过 IP 地址访问。也就是说,Pod 的 IP 地址必须始终对集群中其他 Pod 可见;且从 Pod 内部和从 Pod 外部来看,Pod 的IP地址都是相同的。

数据包的传递:Pod-to-Pod,跨节点

。Kubernetes 网络模型要求 Pod 的 IP 在整个网络中都可访问,但是并不指定如何实现这一点。实际上,这是所使用网络插件相关的,但是,仍然有一些模式已经被确立了。

通常,集群中每个节点都被分配了一个 CIDR 网段,指定了该节点上的 Pod 可用的 IP 地址段。一旦发送到该 CIDR 网段的流量到达节点,就由节点负责将流量继续转发给对应的 Pod。下图展示了两个节点之间的数据报文传递过程。

中,目标 Pod(以绿色高亮)与源 Pod(以蓝色高亮)在不同的节点上,数据包传递过程如下:

数据包从 Pod1 的网络设备eth0,该设备通过veth0 连接到 root namespace数据包到达 root namespace 中的网桥cbr0网桥上执行 ARP 将会失败,因为与网桥连接的所有设备中,没有与该数据包匹配的 MAC 地址。一旦 ARP 失败,网桥会将数据包发送到默认路由(root namespace 中的eth0 设备)。此时,数据包离开节点进入网络假设网络可以根据各节点的CIDR网段,将数据包路由到正确的节点数据包进入目标节点的 root namespace(VM2 上的eth0)后,通过网桥路由到正确的虚拟网络设备(veth1)最终,数据包通过veth1 发送到对应 Pod 的eth0,完成了数据包传递的过程

通常来说,每个节点知道如何将数据包分发到运行在该节点上的 Pod。一旦一个数据包到达目标节点,数据包的传递方式与同节点上不同Pod之间数据包传递的方式就是一样的了。

此处,我们直接跳过了如何配置网络,以使得数据包可以从一个节点路由到匹配的节点。这些是与具体的网络插件实现相关的

Container Network Interface(CNI) plugin 提供了一组通用 API 用来连接容器与外部网络。具体到容器化应用开发者来说,只需要了解在整个集群中,可以通过 Pod 的 IP 地址直接访问 Pod;网络插件是如何做到跨节点的数据包传递这件事情对容器化应用来说是透明的。AWS 的 CNI 插件通过利用 AWS 已有的 VPC、IAM、Security Group 等功能提供了一个满足 Kubernetes 网络模型要求的,且安全可管理的网络环境。

Pod-to-Service的网络

我们已经了解了如何在 Pod 的 IP 地址之间传递数据包。然而,Pod 的 IP 地址并非是固定不变的,随着 Pod 的重新调度(例如水平伸缩、应用程序崩溃、节点重启等),Pod 的 IP 地址将会出现又消失。此时,Pod 的客户端无法得知该访问哪一个 IP 地址。Kubernetes 中,Service 的概念用于解决此问题。

一个 Kubernetes Service 管理了一组 Pod 的状态,可以追踪一组 Pod 的 IP 地址的动态变化过程。一个 Service 拥有一个 IP 地址,并且充当了一组 Pod 的 IP 地址的“虚拟 IP 地址”。任何发送到 Service 的 IP 地址的数据包将被负载均衡到该 Service 对应的 Pod 上。在此情况下,Service 关联的 Pod 可以随时间动态变化,客户端只需要知道 Service 的 IP 地址即可(该地址不会发生变化)。

从效果上来说,Kubernetes 自动为 Service 创建和维护了集群内部的分布式负载均衡,可以将发送到 Service IP 地址的数据包分发到 Service 对应的健康的 Pod 上。

netfilter and iptables

Kubernetes 利用 Linux 内建的网络框架 - netfilter 来实现负载均衡。Netfilter 是由 Linux 提供的一个框架,可以通过自定义 handler 的方式来实现多种网络相关的操作。Netfilter 提供了许多用于数据包过滤、网络地址转换、端口转换的功能,通过这些功能,自定义的 handler 可以在网络上转发数据包、禁止数据包发送到敏感的地址,等。

iptables 是一个 user-space 应用程序,可以提供基于决策表的规则系统,以使用 netfilter 操作或转换数据包。在 Kubernetes 中,kube-proxy 控制器监听 apiserver 中的变化,并配置 iptables 规则。当 Service 或 Pod 发生变化时(例如 Service 被分配了 IP 地址,或者新的 Pod 被关联到 Service),kube-proxy 控制器将更新 iptables 规则,以便将发送到 Service 的数据包正确地路由到其后端 Pod 上。iptables 规则将监听所有发向 Service 的虚拟 IP 的数据包,并将这些数据包转发到该Service 对应的一个随机的可用 Pod 的 IP 地址,同时 iptables 规则将修改数据包的目标 IP 地址(从 Service 的 IP 地址修改为选中的 Pod 的 IP 地址)。当 Pod 被创建或者被终止时,iptables 的规则也被对应的修改。换句话说,iptables 承担了从 Service IP 地址到实际 Pod IP 地址的负载均衡的工作。

在返回数据包的路径上,数据包从目标 Pod 发出,此时,iptables 规则又将数据包的 IP 头从 Pod 的 IP 地址替换为 Service 的 IP 地址。从请求的发起方来看,就好像始终只是在和 Service 的 IP 地址通信一样。

IPVS

Kubernetes v1.11 开始,提供了另一个选择用来实现集群内部的负载均衡:IPVS。 IPVS(IP Virtual Server)也是基于 netfilter 构建的,在 Linux 内核中实现了传输层的负载均衡。IPVS 被合并到 LVS(Linux Virtual Server)当中,充当一组服务器的负载均衡器。IPVS 可以转发 TCP / UDP 请求到实际的服务器上,使得一组实际的服务器看起来像是只通过一个单一 IP 地址访问的服务一样。IPVS 的这个特点天然适合与用在 Kubernetes Service 的这个场景下。

在节点上创建一个 dummy IPVS interface将 Service 的 IP 地址绑定到该 dummy IPVS interface为每一个 Service IP 地址创建 IPVS 服务器

数据包的传递:Pod-to-Service

在 Pod 和 Service 之间路由数据包时,数据包的发起和以前一样:

数据包首先通过 Pod 的eth0 网卡发出数据包经过虚拟网卡veth0 到达网桥cbr0网桥上的 APR 协议查找不到该 Service,所以数据包被发送到 root namespace 中的默认路由 -eth0此时,在数据包被eth0 接受之前,数据包将通过 iptables 过滤。iptables 使用其规则(由 kube-proxy 根据 Service、Pod 的变化在节点上创建的 iptables 规则)重写数据包的目标地址(从 Service 的 IP 地址修改为某一个具体 Pod 的 IP 地址)数据包现在的目标地址是 Pod 4,而不是 Service 的虚拟 IP 地址。iptables 使用 Linux 内核的conntrack 工具包来记录具体选择了哪一个 Pod,以便可以将未来的数据包路由到同一个 Pod。简而言之,iptables 直接在节点上完成了集群内负载均衡的功能。数据包后续如何发送到 Pod 上,其路由方式与Pod-to-Pod的网络 中的描述相同。

数据包的传递:Service-to-Pod

接收到此请求的 Pod 将会发送返回数据包,其中标记源 IP 为接收请求 Pod 自己的 IP,目标 IP 为最初发送对应请求的 Pod 的 IP当数据包进入节点后,数据包将经过 iptables 的过滤,此时记录在conntrack 中的信息将被用来修改数据包的源地址(从接收请求的 Pod 的 IP 地址修改为 Service 的 IP 地址)然后,数据包将通过网桥、以及虚拟网卡veth0最终到达 Pod 的网卡eth0

使用DNS

Kubernetes 也可以使用 DNS,以避免将 Service 的 cluster IP 地址硬编码到应用程序当中。Kubernetes DNS 是 Kubernetes 上运行的一个普通的 Service。每一个节点上的 kubelet​ 都使用该 DNS Service 来执行 DNS 名称的解析。集群中每一个 Service(包括 DNS Service 自己)都被分配了一个 DNS 名称。DNS 记录将 DNS 名称解析到 Service 的 ClusterIP 或者 Pod 的 IP 地址。SRV 记录 用来指定 Service 的已命名端口。

DNS Pod 由三个不同的容器组成:

kubedns:观察 Kubernetes master 上 Service 和 Endpoints 的变化,并维护内存中的 DNS 查找表dnsmasq:添加 DNS 缓存,以提高性能sidecar:提供一个健康检查端点,可以检查dnsmasq 和kubedns 的健康状态

DNS Pod 被暴露为 Kubernetes 中的一个 Service,该 Service 及其 ClusterIP 在每一个容器启动时都被传递到容器中(环境变量及 /etc/resolves),因此,每一个容器都可以正确的解析 DNS。DNS 条目最终由 kubedns 解析,kubedns 将 DNS 的所有信息都维护在内存中。etcd 中存储了集群的所有状态,kubedns 在必要的时候将 etcd 中的 key-value 信息转化为 DNS 条目信息,以重建内存中的 DNS 查找表。

CoreDNS 的工作方式与 kubedns 类似,但是通过插件化的架构构建,因而灵活性更强。自 Kubernetes v1.11 开始,CoreDNS 是 Kubernetes 中默认的 DNS 实现。

Internet-to-Service的网络

如何将 Service 暴露到集群外部:

从集群内部访问互联网从互联网访问集群内部

出方向 - 从集群内部访问互联网

Kubernetes 集群在 VPC 内运行,在此处,每一个节点都被分配了一个内网地址(private IP address)可以从 Kubernetes 集群内部访问。为了使访问外部网络,通常会在 VPC 中添加互联网网关(Internet Gateway),以实现如下两个目的:

作为 VPC 路由表中访问外网的目标地址提供网络地址转换(NAT Network Address Translation),将节点的内网地址映射到一个外网地址,以使外网可以访问内网上的节点

在有互联网网关(Internet Gateway)的情况下,虚拟机可以任意访问互联网。但是,存在一个小问题:Pod 有自己的 IP 地址,且该 IP 地址与其所在节点的 IP 地址不一样,并且,互联网网关上的 NAT 地址映射只能够转换节点(虚拟机)的 IP 地址,因为网关不知道每个节点(虚拟机)上运行了哪些 Pod (互联网网关不知道 Pod 的存在)。

数据包的传递:Node-to-Internet

下图中:

数据包从 Pod 的 network namespace 发出通过veth0 到达虚拟机的 root network namespace由于网桥上找不到数据包目标地址对应的网段,数据包将被网桥转发到 root network namespace 的网卡eth0。在数据包到达eth0 之前,iptables 将过滤该数据包。在此处,数据包的源地址是一个 Pod,如果仍然使用此源地址,互联网网关将拒绝此数据包,因为其 NAT 只能识别与节点(虚拟机)相连的 IP 地址。因此,需要 iptables 执行源地址转换(source NAT),这样子,对互联网网关来说,该数据包就是从节点(虚拟机)发出的,而不是从 Pod 发出的数据包从节点(虚拟机)发送到互联网网关互联网网关再次执行源地址转换(source NAT),将数据包的源地址从节点(虚拟机)的内网地址修改为网关的外网地址,最终数据包被发送到互联网

在回路径上,数据包沿着相同的路径反向传递,源地址转换(source NAT)在对应的层级上被逆向执行。

入方向 - 从互联网访问Kubernetes

入方向访问(从互联网访问Kubernetes集群)是一个非常棘手的问题。该问题同样跟具体的网络紧密相关,通常来说,入方向访问在不同的网络堆栈上有两个解决方案:

Service LoadBalancerIngress Controller

Layer 4:LoadBalancer

​当创建 Kubernetes Service 时,可以指定其类型为 ​​​LoadBalancer​​​​。 LoadBalancer 的实现由 ​​​cloud controller (opens new window)​​​提供,cloud controller 可以调用云供应商 IaaS 层的接口,为 Kubernetes Service 创建负载均衡器(如果您自建 Kubernetes 集群,可以使用 NodePort 类型的 Service,并手动创建负载均衡器)。用户可以将请求发送到负载均衡器来访问 Kubernetes 中的 Service。

在 AWS,负载均衡器可以将网络流量分发到其目标服务器组(即 Kubernetes 集群中的所有节点)。一旦数据包到达节点,Service 的 iptables 规则将确保其被转发到 Service 的一个后端 Pod。

数据包的传递:LoadBalancer-to-Service

​Layer 4 的入方向访问具体是如何做到的:

Loadbalancer 类型的 Service 创建后,cloud controller 将为其创建一个负载均衡器负载均衡器只能直接和节点(虚拟机沟通),不知道 Pod 的存在,当数据包从请求方(互联网)到达 LoadBalancer 之后,将被分发到集群的节点上节点上的 iptables 规则将数据包转发到合适的 Pod 上 (同数据包的传递:Service-to-Pod)

从 Pod 到请求方的相应数据包将包含 Pod 的 IP 地址,但是请求方需要的是负载均衡器的 IP 地址。iptables 和 conntrack 被用来重写返回路径上的正确的 IP 地址。

下图描述了一个负载均衡器和三个集群节点:

请求数据包从互联网发送到负载均衡器负载均衡器将数据包随机分发到其中的一个节点(虚拟机),此处,我们假设数据包被分发到了一个没有对应 Pod 的节点(VM2)上在 VM2 节点上,kube-proxy 在节点上安装的 iptables 规则会将该数据包的目标地址判定到对应的 Pod 上(集群内负载均衡将生效)iptables 完成 NAT 映射,并将数据包转发到目标 Pod

Layer 7:Ingress控制器

​Layer 7 网络入方向访问在网络堆栈的 HTTP/HTTPS 协议层面工作,并且依赖于 KUbernetes Service。要实现 Layer 7 网络入方向访问,首先需要将 Service 指定为 NodtePort​​ 类型,此时 Kubernetes master 将会为该 Service 分配一个 ​节点端口​,每一个节点上的 iptables 都会将此端口上的请求转发到 Service 的后端 Pod 上。此时,Service-to-Pod 的路由与 ​数据包的传递:Service-to-Pod​ 的描述相同。

接下来,创建一个 Kubernetes Ingress 对象可以将该 Service 发布到互联网。Ingress 是一个高度抽象的 HTTP 负载均衡器,可以将 HTTP 请求映射到 Kubernetes Service。在不同的 Kubernetes 集群中,Ingress 的具体实现可能是不一样的。与 Layer 4 的网络负载均衡器相似,HTTP 负载均衡器只理解节点的 IP 地址(而不是 Pod 的 IP 地址),因此,也同样利用了集群内部通过 iptables 实现的负载均衡特性。

在 AWS 中,ALB Ingress 控制器使用 Amazon 的 Layer 7 Application Load Balancer实现了 Kubernetes Ingress 的功能。下图展示了 AWS 上 Ingress 控制器的细节,也展示了网络请求是如何从 ALB 路由到 Kubernetes 集群的。

ALB Ingress Controller 创建后,将监听 Kubernetes API 上关于 Ingress 的事件。当发现匹配的 Ingress 对象时,Ingress Controller 开始创建 AWS 资源AWS 使用 Application Load Balancer(ALB)来满足 Ingress 对象的要求,并使用 Target Group 将请求路由到目标节点ALB Ingress Controller 为 Kubernetes Ingress 对象中用到的每一个 Kubernetes Service 创建一个 AWS Target GroupListener 是一个 ALB 进程,由 ALB Ingress Controller 根据 Ingress 的注解(annotations)创建,监听 ALB 上指定的协议和端口,并接收外部的请求ALB Ingress Controller 还根据 Kubernetes Ingress 中的路径定义,创建了 Target Group Rule,确保指定路径上的请求被路由到合适的 Kubernetes Service

数据包的传递:Ingress-to-Service

​Ingress-to-Service 的数据包传递与 LoadBalancer-to-Service 的数据包传递非常相似。核心差别是:

Ingress 能够解析 URL 路径(可基于路径进行路由)Ingress 连接到 Service 的 NodePort

下图展示了 Ingress-to-Service 的数据包传递过程。

创建 Ingress 之后,cloud controller 将会为其创建一个新的 Ingress Load Balancer由于 Load Balancer 并不知道 Pod 的 IP 地址,当路由到达 Ingress Load Balancer 之后,会被转发到集群中的节点上(Service的节点端口)节点上的 iptables 规则将数据包转发到合适的 PodPod 接收到数据包

从 Pod 返回的响应数据包将包含 Pod 的 IP 地址,但是请求客户端需要的是 Ingress Load Balancer 的 IP 地址。iptables 和 ​​conntrack​​ 被用来重写返回路径上的 IP 地址。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:文案君:中国版BBC冰雪奇缘纪录片?农夫山泉又来新片了!
下一篇:k8s学习-服务发现以及网络1
相关文章

 发表评论

暂时没有评论,来抢沙发吧~