关于云计算:Kubernetes-中数据包的生命周期-第-3-部分

4次阅读

共计 18500 个字符,预计需要花费 47 分钟才能阅读完成。

本文翻译自:Life of a Packet in Kubernetes — Part 3 [1]
作者:Dinesh Kumar Ramasamy
本文在原文的根底上做了适当的批改,如有疑问请查阅原文。

本文是 Kubernetes 中数据包的生命周期系列文章的第 3 局部。咱们将探讨 Kubernetes 的 kube-proxy 组件如何应用 iptables 来管制流量。理解 kube-proxy 在 Kubernetes 环境中的作用以及它如何应用 iptables 来管制流量十分重要。

本文蕴含以下章节:

  • 1.Pod-to-Pod
  • 2.Pod-to-External
  • 3.Pod-to-Service
  • 4.NodePort(External-to-Pod)
  • 5.External Traffic Policy
  • 6.Kube-Proxy
  • 7.iptables 规定解决流程
  • 8.Headless Service
  • 9.Network Policy

1 Pod-to-Pod

kube-proxy 不会参加 Pod 之间的通信,因为 CNI 会为 Node 和 Pod 配置所需的路由。所有容器都能够在没有 NAT 的状况下与其余容器进行通信。所有节点都能够在没有 NAT 的状况下与容器通信(反之亦然)。

留神:Pod 的 IP 地址不是动态的(动态 IP 有多种配置形式,但默认配置不保障动态 IP 地址)。当 Pod 重启时,CNI 会为 Pod 调配一个新的 IP 地址,因为 CNI 不会保护 Pod 和 IP 地址之间的映射关系。另外,通过 Deployment 形式部署的 Pod 的名字也不是动态的。

实际上,通过 Deployment 部署的 Pod 应该应用负载平衡类型的实体来公布服务,因为应用程序是无状态的,并且通常会有多个 Pod 托管应用程序。负载均衡器类型的实体在 Kubernetes 中称为 Service

2 Pod-to-external

对于从 Pod 到内部地址的流量,Kubernetes 应用 SNAT(源地址转换)。它所做的是将 Pod IP:Port 替换为 主机 IP:Port。当返回的数据包到主机时,主机会批改数据包中的指标 IP 和端口为 Pod IP:Port,并将数据包发回原始的 Pod。整个过程对原始 Pod 是通明的,Pod 并不知道产生了地址转换。

3 Pod-to-Service

Kubernetes 中有一个叫做 Service 的概念,它是 Pod 后面的 L4 负载均衡器。在 Kubernetes 中有几种不同类型的 Service,最根本的类型称为 ClusterIP。这种类型的 Service 有一个惟一的 VIP 地址,只能在集群外部路由。

仅仅通过 Pod IP 将流量发送到特定的应用程序并不容易。Kubernetes 集群的动静个性意味着 Pod 能够被挪动,重启,降级或者扩缩容。此外,一些服务会有很多正本,因而咱们须要一些办法来均衡它们之间的负载。

Kubernetes 通过 Service 解决了这个问题。Service 是一个 API 对象,它将单个虚构 IP(VIP)映射到一组 Pod IP。此外,Kubernetes 还为每个 Service 的 VIP 提供了 DNS 的解析条目,以便能够轻松通过域名来拜访 Service。

集群内 VIP 到 Pod IP 的映射由每个节点上的 kube-proxy 过程协调。kube-proxy 通过设置 iptables 或者 IPVS 规定将目标地址是 Service VIP 的申请转换为 Pod IP。申请建设的连贯会被追踪,因而数据包从 Pod 返回时能够正确地转换为 Service 的 VIP 再响应客户端。IPVS 和 iptables 能够将单个 Service 的 VIP 负载平衡到多个 Pod IP,IPVS 和 iptables 相比有着更好的性能和更灵便的负载平衡算法。


接下来创立 FrontEnd 和 Backend 两个 Deployment,并通过 ClusterIP 类型的 Service 作为负载平衡。

FrontEnd Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

Backend Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: auth
  labels:
    app: auth
spec:
  replicas: 2
  selector:
    matchLabels:
      app: auth
  template:
    metadata:
      labels:
        app: auth
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

Service:

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: frontend
spec:
  ports:
  - port: 80
    protocol: TCP
  type: ClusterIP
  selector:
    app: webapp
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  labels:
    app: backend
spec:
  ports:
  - port: 80
    protocol: TCP
  type: ClusterIP
  selector:
    app: auth

当初 FrontEnd Pod 能够通过 ClusterIP 或者 Kubernetes 增加的 DNS 条目拜访 Backend。一个集群感知的 DNS 服务器,例如 CoreDNS,监督 Kubernetes API 获取新创建的服务,并为每个服务创立一组 DNS 记录。

“一般“服务(除了无头服务)会以 your-svc.your-namespace.svc.cluster.local 这种名字的模式调配一个 DNS 记录。该名称会解析成对应 Service 的 ClusterIP。

4 NodePort(External-to-Pod)

当初咱们能够在集群外部通过 VIP 或者域名拜访 Service。然而,因为 VIP 是虚构和公有的,此时内部申请还无奈达到集群内的 Service

让咱们创立一个 NodePort 类型的服务将 FrontEnd 服务裸露到集群内部。如果将 Service 的 type 字段设置为 NodePort 时,Kubernetes 会为该服务调配一个随机端口。端口的默认范畴是 30000-32767,能够通过 API Server 的 --service-node-port-range 参数进行设置。每个节点会将拜访 nodePort 端口(每个端口号雷同)的申请代理到你的 Service。

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: NodePort # 将 Service 设置为 NodePort 类型
  selector:
    app: webapp
  ports:
      # By default and for convenience, the `targetPort` is set to the same value as the `port` field.
    - port: 80
      targetPort: 80
      # Optional field
      # By default and for convenience, the Kubernetes control plane will allocate a port from a range (default: 30000-32767)
      nodePort: 31380 # 指定端口
...

当初咱们能够在集群内部通过 < 任何一个节点 IP>:<nodePort 端口 > 拜访 FrontEnd Service 了。如果你须要指定特定的端口号,那么能够在 nodePort 字段中进行设置。这意味着你须要本人解决可能的端口抵触,同时你还必须应用一个无效的端口号,当设置的 nodePort 抵触或超过范畴时,API Server 会返回错误信息。

5 External Traffic Policy

ExternalTrafficPolicy 示意该 Service 是否心愿将内部流量路由到节点本地或集群范畴的端点。Local 示意保留客户端的源 IP,并且 NodePort 或者 LoadBalancer 类型的 Service 将不会把流量散发到其余节点上的 Pod,这样能够防止产生额定的网络跳数,与此同时可能会存在流量流传不均衡的危险。Cluster 暗藏了客户端的源 IP,并且可能会将流量散发到其余节点上的 Pod,此时会产生额定的网络跳数,但这样会具备良好的整体负载平衡。

5.1 Cluster Traffic Policy

Cluster Traffic Policy 是 Kubernetes Service 默认 的 ExternalTrafficPolicy 策略。这里假如的是,你总是心愿将流量均衡地路由到 Service 下的所有 Pod。

应用此策略的一个注意事项是,当内部流量拜访 NodePort Service 时,你可能会在节点之间看到不必要的网络跃点。例如,当通过 NodePort 接管内部流量,NodePort Service 可能会(随机)将流量路由到另一台主机上的 Pod,而它原本能够将流量路由到同一主机上的 Pod,从而防止额定的网络跳数。

Cluster Traffic Policy 中数据包的流量如下:

  • 客户端发送数据包到 Node2:3138
  • Node2 将数据包的指标 IP 通过 DNAT(指标地址)转换为 Pod IP。
  • Node2 将数据包的源 IP 通过 SNAT(源地址转换)转换为 Node2 本身的 IP。
  • 数据包路由到 Node1 或者 Node3,而后交给节点上的 Pod。
  • Pod 的响应回到 Node2,源 IP 是 Pod IP,指标 IP 是 Node2 的 IP。
  • Node2 将 Pod 返回数据包的源 IP 批改为 Node2 的 IP,指标 IP 批改为客户端的 IP,而后响应客户端。

5.2 Local Traffic Policy

当应用 Local Traffic Policy 策略时,kube-proxy 将仅为同一节点(本地)上的 Pod 增加代理规定,而不是为 Service 的每个 Pod 增加代理规定。此时 kube-proxy 只会将申请代理到本地端点,而不会将流量转发到其余节点。这样能够保留原始的源 IP 地址。 如果没有本地端点,则抛弃发送到节点的数据包。

如果你尝试在 Serivce 上设置 externalTrafficPolicy: Local,Kubernetes API 将要求你应用 LoadBalancer 或 NodePort 类型的 Service。这是因为 Local 模式的 ExternalTrafficPolicy 策略仅与内部流量相干,因而只实用于这两种类型的 Service。

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: NodePort
  externalTrafficPolicy: Local # 仅转发到本地 Pod,保留客户端源 IP
  selector:
    app: webapp
  ports:
      # By default and for convenience, the `targetPort` is set to the same value as the `port` field.
    - port: 80
      targetPort: 80
      # Optional field
      # By default and for convenience, the Kubernetes control plane will allocate a port from a range (default: 30000-32767)
      nodePort: 31380
...

Local Traffic Policy 中数据包的流量如下:

  • 客户端发送数据包到 Node1:31380,该节点上有 FrontEnd 的 Pod。
  • Node1 将数据包路由到本地的 Pod,保留正确的源 IP。
  • 因为设置了 Local 的转发策略,因而 Node1 不会将数据包路由到 Node3。

  • 客户端发送数据包到 Node2:31380,该节点上没有 FrontEnd 的 Pod。
  • 数据包将会被 Node2 抛弃。

5.3 Local traffic policy in LoadBalancer Service type

当你将私有云的 Kubernetes 服务(例如 Google Kubernetes Engine,GCE)中的 Service 设置为 externalTrafficPolicy: Local 时,如果节点中没有 Service 对应的 Pod,将不会通过负载均衡器(LB)的健康检查而被移出转发列表,在这种状况下不会产生数据包的抛弃。这种模式非常适合有大量的入访流量,同时心愿防止网络上不必要的跃点以缩小提早的应用程序。同时咱们还能够保留真正的客户端 IP,因为咱们不再须要来自代理节点的 SNAT 流量!然而,正如 Kubernetes 文档中提到的,应用 Local traffic policy 策略的最大毛病是应用程序的流量可能不均衡。

6 Kube-Proxy (iptable mode)

Kubernetes 中实现 Service 的组件称为 kube-proxy。它位于每个节点上,编写简单的 iptables 规定以在 Pod 和 Service 之间进行各种过滤和 NAT。如果你到 Kubernetes 节点上执行 iptables-save 命令,你将可能看到 Kubernetes 或者其余程序插入的 iptables 规定。其中最重要的链是  KUBE-SERVICESKUBE-SVC-* 和 KUBE-SEP-*

  • KUBE-SERVICES 链是拜访 Service 的入口,它的作用是匹配指标 IP:Port 并将数据包散发到相应的 KUBE-SVC-* 链。
  • KUBE-SVC-* 链充当负载均衡器(Service),将数据包平均分配到 KUBE-SEP-* 链。每个 KUBE-SVC-* 链中含有与其前面端点数量雷同的 KUBE-SEP-* 链。
  • KUBE-SEP-* 链代表一个服务端点(Pod),它只做 DNAT,将 Service 的 IP:Port 替换为 Pod 的 IP:Port。

对于 DNAT,conntrack 会跟踪连贯状态,因为它须要记住批改的指标地址,以便数据包返回时将其更改回来。iptables 还能够依附 conntrack 状态 (ctstate) 来决定数据包的命运。这 4 个 conntrack 状态尤其重要:

  • NEW:一个连贯的初始状态(例如:TCP 连贯中,一个 SYN 包的到来)。
  • ESTABLISHED:连贯曾经建设实现。
  • RELATED:这是一个关联连贯,是一个主链接的子连贯,例如 FTP 的数据通道的连贯。
  • INVALID:这是一个非凡的状态,用于记录那些没有依照预期行为进行的连贯。

以下是 Pod 和 Service 之间连贯的工作形式,事件的程序如下:

  • 左侧的客户端 Pod 向 Service 2.2.2.10:80 发送数据包。
  • 数据包在客户端节点通过 iptables 规定,目标 IP 被更改为服务端 Pod IP(1.1.1.20:80)。
  • 数据包返回客户端节点,conntrack 辨认到数据包并将源地址重写回 2.2.2.2:80。
  • 客户端 Pod 收到响应数据包。

GIF 动图演示:

7 Iptables 规定解决流程

netfilter 是 Linux 内核中一个对数据包进行管制、批改和过滤的框架。iptables 是配置 netfilter 过滤性能的用户空间工具。

7.1 Chains 链

iptables 中有 5 条链,每条链负责一个特定的工作:

  • PREROUTING:在数据包做路由抉择前利用此链路中的规定,所有的数据包进来时都先由这条链解决。
  • INPUT:通过路由表判断后,目标为本机的数据包利用这条链上的策略。
  • FORWARD:通过路由表判断后,指标地址不为本机,转发数据包时利用这条链上的策略。
  • OUTPUT:由本机产生的往外发送的数据包利用这条链中的策略。
  • POSTROUTING:数据包发送到网卡之前利用这条链中的策略,所有的数据包进去的时候都由这条链解决。

  • 进入的数据包目标是本机:PREROUTING -> INPUT
  • 进入的数据包目标是其余主机:PREROUTING -> FORWARD -> POSTROUTING
  • 本机产生的数据包:OUTPUT -> POSTROUTING

FORWARD 链仅当 Linux 服务器中启用 ip_forward 时才无效,这就是以下命令在设置和调试 Kubernetes 集群时很重要的起因。

node-1# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
node-1# cat /proc/sys/net/ipv4/ip_forward
1

上述命令的批改不是永恒的,要在 Linux 零碎上永恒启用 IP 转发,请编辑 /etc/sysctl.conf 并增加以下内容:

net.ipv4.ip_forward = 1

7.2 Tables 表

iptables 中有 5 种表:

  • Filter:这是默认的表,用于过滤数据包。
  • NAT:网络地址转换(端口映射、地址映射等)。
  • Mangle:次要性能是依据规定批改数据包的一些标记位,以便其余规定或程序能够利用这些标记对数据包进行过滤或策略路由。
  • Raw:设置 RAW 表时个别是为了不再让 iptables 做数据包的链接跟踪解决,以进步性能。
  • Security:用于在数据包上设置外部 SELinux 平安上下文标记。

7.3 Kubernetes 中的 iptables 配置

让咱们在 Kubernetes 中部署一个正本数为 2 的 Nginx 应用程序并查看 iptables 规定。Service 类型:NodePort

master # kubectl get svc webapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp NodePort 10.103.46.104 <none> 80:31380/TCP 3d13h

master # kubectl get ep webapp 
NAME ENDPOINTS AGE
webapp 10.244.120.102:80,10.244.120.103:80 3d13h

ClusterIP 在主机的接口上并不存在,它的虚构 IP 存在于 iptables 规定中,在 CoreDNS 中增加了上面这个 DNS 条目。

master # kubectl exec -i -t dnsutils -- nslookup webapp.default
Server:  10.96.0.10
Address: 10.96.0.10#53
Name: webapp.default.svc.cluster.local
Address: 10.103.46.104

Kubernetes 在 iptables 中创立了 KUBE-SERVICES 链以便对数据包进行过滤和 NAT。它将会把所有 PREROUTING 和 OUTPUT 链的流量重定向到 KUBE-SERVICES 链上。

$ sudo iptables -t nat -L PREROUTING | column -t
Chain            PREROUTING  (policy  ACCEPT)

target           prot        opt      source    destination                                                      

cali-PREROUTING  all         --       anywhere  anywhere     /*        cali:6gwbT8clXdHdC1b1  */                 

KUBE-SERVICES    all         --       anywhere  anywhere     /*        kubernetes             service   portals  */

DOCKER           all         --       anywhere  anywhere     ADDRTYPE  match                  dst-type  LOCAL

在将 KUBE-SERVICES 链用于包过滤和 NAT 后,Kubernetes 能够查看到 Service 的流量并相应地利用 SNAT/DNAT。在 KUBE-SERVICES 链的末端,它将插入另一个自定义链 KUBE-NODEPORTS 来解决 Service 类型是 NodePort 的流量。

如果流量是针对 ClusterIP 的,KUBE-SVC-2IRACUALRELARSND 链会解决流量;否则,下一条链将解决流量,即 KUBE-NODEPORTS

$ sudo iptables -t nat -L KUBE-SERVICES | column -t
Chain                      KUBE-SERVICES  (2   references)                                                                                                                                                                             
target                     prot           opt  source          destination                                                                                                                                                             
KUBE-MARK-MASQ             tcp            --   !10.244.0.0/16  10.103.46.104   /*  default/webapp                   cluster  IP          */     tcp   dpt:www                                                                          
KUBE-SVC-2IRACUALRELARSND  tcp            --   anywhere        10.103.46.104   /*  default/webapp                   cluster  IP          */     tcp   dpt:www                                                                                                                                             
KUBE-NODEPORTS             all            --   anywhere        anywhere        /*  kubernetes                       service  nodeports;  NOTE:  this  must        be  the  last  rule  in  this  chain  */  ADDRTYPE  match  dst-type  LOCAL

让咱们查看一下 KUBE-NODEPORTS 链。

$ sudo iptables -t nat -L KUBE-NODEPORTS | column -t
Chain                      KUBE-NODEPORTS  (1   references)     

target                     prot            opt  source       destination                               

KUBE-MARK-MASQ             tcp             --   anywhere     anywhere     /*  default/webapp  */  tcp  dpt:31380

KUBE-SVC-2IRACUALRELARSND  tcp             --   anywhere     anywhere     /*  default/webapp  */  tcp  dpt:31380

能够看到 KUBE-NODEPORTS 链最终也会将流量送到 KUBE-SVC-2IRACUALRELARSND 链。

# statistic  mode  random -> Random load-balancing between endpoints.
# Service 的流量随机分发给两个 Pod
$ sudo iptables -t nat -L KUBE-SVC-2IRACUALRELARSND | column -t
Chain                      KUBE-SVC-2IRACUALRELARSND  (2   references)                                                                             
target                     prot                       opt  source       destination

# Pod1
KUBE-SEP-AO6KYGU752IZFEZ4  all                        --   anywhere     anywhere     /*  default/webapp  */  statistic  mode  random  probability  0.50000000000
# Pod2
KUBE-SEP-PJFBSHHDX4VZAOXM  all                        --   anywhere     anywhere     /*  default/webapp  */

# 流量散发到 Pod1
$ sudo iptables -t nat -L KUBE-SEP-AO6KYGU752IZFEZ4 | column -t
Chain           KUBE-SEP-AO6KYGU752IZFEZ4  (1   references)                                               
target          prot                       opt  source          destination

KUBE-MARK-MASQ  all                        --   10.244.120.102  anywhere     /*  default/webapp  */

DNAT            tcp                        --   anywhere        anywhere     /*  default/webapp  */  tcp  to:10.244.120.102:80


# 流量散发到 Pod2
$ sudo iptables -t nat -L KUBE-SEP-PJFBSHHDX4VZAOXM | column -t
Chain           KUBE-SEP-PJFBSHHDX4VZAOXM  (1   references)                                               
target          prot                       opt  source          destination                               
KUBE-MARK-MASQ  all                        --   10.244.120.103  anywhere     /*  default/webapp  */       

DNAT            tcp                        --   anywhere        anywhere     /*  default/webapp  */  tcp  to:10.244.120.103:80

# 源地址转换
$ sudo iptables -t nat -L KUBE-MARK-MASQ | column -t
Chain   KUBE-MARK-MASQ  (24  references)                         
target  prot            opt  source       destination            

MARK    all             --   anywhere     anywhere     MARK  or  0x4000

总结一下:
ClusterIP: KUBE-SERVICES → KUBE-SVC-XXX → KUBE-SEP-XXX。
NodePort: KUBE-SERVICES → KUBE-NODEPORTS → KUBE-SVC-XXX → KUBE-SEP-XXX。

留神:NodePort 类型的 Service 将调配一个 ClusterIP 来解决外部和内部流量。

上述规定的示意图如下:


ExtrenalTrafficPolicy: Local
如前所述,应用 externalTrafficPolicy: Local 将保留源 IP 并抛弃没有本地端点的数据包。让咱们来看看没有本地端点的节点中的 iptables 规定。

master # kubectl get nodes
NAME           STATUS   ROLES    AGE    VERSION
minikube       Ready    master   6d1h   v1.19.2
minikube-m02   Ready    <none>   85m    v1.19.2

这次只部署 1 个 Nginx Pod,Service 设置为 externalTrafficPolicy: Local。

master # kubectl get pods nginx-deployment-7759cc5c66-p45tz -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP               NODE       NOMINATED NODE   READINESS GATES
nginx-deployment-7759cc5c66-p45tz   1/1     Running   0          29m   10.244.120.111   minikube   <none>           <none>

查看 externalTrafficPolicy。

master # kubectl get svc webapp -o wide -o jsonpath={.spec.externalTrafficPolicy}
Local

获取 Service。

master # kubectl get svc webapp -o wide
NAME     TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE   SELECTOR
webapp   NodePort   10.111.243.62   <none>        80:30080/TCP   29m   app=webserver

让咱们查看 minikube-m02 节点中的 iptables 规定,应该有一个 DROP 规定来抛弃数据包,因为该节点上没有 Nginx Pod

$ sudo iptables -t nat -L KUBE-NODEPORTS

Chain KUBE-NODEPORTS (1 references)

target prot opt source destination

KUBE-MARK-MASQ tcp — 127.0.0.0/8 anywhere /* default/webapp */ tcp dpt:30080

KUBE-XLB-2IRACUALRELARSND tcp — anywhere anywhere /* default/webapp */ tcp dpt:30080

查看 KUBE-XLB-2IRACUALRELARSND 链。

$ sudo iptables -t nat -L KUBE-XLB-2IRACUALRELARSND
Chain KUBE-XLB-2IRACUALRELARSND (1 references)

target prot opt source destination

KUBE-SVC-2IRACUALRELARSND all — 10.244.0.0/16 anywhere /* Redirect pods trying to reach external loadbalancer VIP to clusterIP */

KUBE-MARK-MASQ all — anywhere anywhere /* masquerade LOCAL traffic for default/webapp LB IP */ ADDRTYPE match src-type LOCAL

KUBE-SVC-2IRACUALRELARSND all — anywhere anywhere /* route LOCAL traffic for default/webapp LB IP to service chain */ ADDRTYPE match src-type LOCAL

# 抛弃没有本地端点的数据包
KUBE-MARK-DROP all — anywhere anywhere /* default/webapp has no local endpoints */

查看 minikube 节点的 iptables,该节点上有 Nginx Pod,因而不会抛弃数据包。

# NodePort 规定
$ sudo iptables -t nat -L KUBE-NODEPORTS

Chain KUBE-NODEPORTS (1 references)

target prot opt source destination

KUBE-MARK-MASQ tcp — 127.0.0.0/8 anywhere /* default/webapp */ tcp dpt:30080

KUBE-XLB-2IRACUALRELARSND tcp — anywhere anywhere /* default/webapp */ tcp dpt:30080


# 不会抛弃数据包,最初没有 DROP 规定
$ sudo iptables -t nat -L KUBE-XLB-2IRACUALRELARSND

Chain KUBE-XLB-2IRACUALRELARSND (1 references)

target prot opt source destination

KUBE-SVC-2IRACUALRELARSND all — 10.244.0.0/16 anywhere /* Redirect pods trying to reach external loadbalancer VIP to clusterIP */

KUBE-MARK-MASQ all — anywhere anywhere /* masquerade LOCAL traffic for default/webapp LB IP */ ADDRTYPE match src-type LOCAL

KUBE-SVC-2IRACUALRELARSND all — anywhere anywhere /* route LOCAL traffic for default/webapp LB IP to service chain */ ADDRTYPE match src-type LOCAL

KUBE-SEP-5T4S2ILYSXWY3R2J all — anywhere anywhere /* Balancing rule 0 for default/webapp */


# Service 规定,将流量散发到 Pod
$ sudo iptables -t nat -L KUBE-SVC-2IRACUALRELARSND

Chain KUBE-SVC-2IRACUALRELARSND (3 references)

target prot opt source destination

KUBE-SEP-5T4S2ILYSXWY3R2J all — anywhere anywhere /* default/webapp */

8 Headless Service

有的利用并不需要负载平衡和服务 IP。在这种状况下就能够应用 Headless Service,只有设置 .spec.clusterIP 为 None 即可。

能够借助这种服务类型和其余服务发现机制合作,无需和 Kubernetes 绑定。Headless Service 并不会调配 Cluster IP,kube-proxy 不会解决它们,而且平台也不会为它们进行负载平衡和路由。另外,DNS 的配置要依据 selector 来确定。

带选择器(selector)的服务

定义了 selector 的 Headless Service,Endpoint 控制器会创立 Endpoints 记录,并批改 DNS 记录,通过域名能够间接拜访 Service 的后端 Pod。

master # kubectl get svc webapp-hs
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
webapp-hs   ClusterIP   None         <none>        80/TCP    24s

master # kubectl get ep webapp-hs
NAME        ENDPOINTS                             AGE
webapp-hs   10.244.120.109:80,10.244.120.110:80   31s

无选择器的服务

没有定义 selector 的 Headless Service,也就没有 Endpoint 记录。然而 DNS 零碎会尝试配置:

  • ExternalName 类型的服务,会产生 CNAME 记录。
  • 所有其余类型的服务,查找与 Service 名称雷同的 Endpoints 的记录。

9 Network Policy

到目前为止,你可能曾经理解了 Kubernetes 中的网络策略是如何实现的。是的,又是 iptables;CNI 负责施行网络策略,而不是 kube-proxy

让咱们创立 3 个服务:FrontEnd, Backend 和 DB。默认状况下,Pod 是非隔离的;他们承受来自任何起源的流量。

然而,应该有一个网络策略将 DB pod 与 FrontEnd pod 隔离,以防止它们之间的任何流量。

咱们能够通过设置 NetworkPolicy 来实现网络策略,如果入访的 Pod 带有 networking/allow-db-access: "true" 标签,则容许拜访 DB。

# 网络策略
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-db-access
spec:
  podSelector:  # 匹配利用策略的 Pod
    matchLabels:
      app: "db"
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector: # 匹配入访 Pod
        matchLabels:
          networking/allow-db-access: "true"
      
# Backend 服务
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  labels:
    app: backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
        networking/allow-db-access: "true"  # 网络策略会容许有该标签的 Pod 拜访 DB 服务
    spec:
      volumes:
      - name: workdir
        emptyDir: {}
      containers:
      - name: nginx
        image: nginx:1.14.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        volumeMounts:
        - name: workdir
          mountPath: /usr/share/nginx/html
      initContainers:
      - name: install
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ['sh', '-c', "echo $HOSTNAME > /work-dir/index.html"]
        volumeMounts:
        - name: workdir
          mountPath: "/work-dir"
...

利用下面的 NetworkPolicy,能够看到 FrontEnd 能够失常拜访 Backend,然而曾经无法访问 DB 了。

master # kubectl exec -it frontend-8b474f47-zdqdv -- /bin/sh
# curl backend
backend-867fd6dff-mjf92

# curl db
curl: (7) Failed to connect to db port 80: Connection timed out

然而 Backend 仍然能够拜访 DB。

master # kubectl exec -it backend-867fd6dff-mjf92 -- /bin/sh
# curl db
db-8d66ff5f7-bp6kf

如果应用的 CNI 是 Calico,Calico 会将 Kubernetes 的网络策略转换为 Calico 的格局。

master # calicoctl get networkPolicy --output yaml
apiVersion: projectcalico.org/v3
items:
- apiVersion: projectcalico.org/v3
  kind: NetworkPolicy
  metadata:
    creationTimestamp: "2020-11-05T05:26:27Z"
    name: knp.default.allow-db-access
    namespace: default
    resourceVersion: /53872
    uid: 1b3eb093-b1a8-4429-a77d-a9a054a6ae90
  spec:
    ingress:
    - action: Allow
      destination: {}
      source:
        selector: projectcalico.org/orchestrator == 'k8s' && networking/allow-db-access
          == 'true'
    order: 1000
    selector: projectcalico.org/orchestrator == 'k8s' && app == 'db'
    types:
    - Ingress
kind: NetworkPolicyList
metadata:
  resourceVersion: 56821/56821

应用 calicoctl 获取工作负载端点的详细信息。

master # calicoctl get workloadEndpoint
WORKLOAD                         NODE       NETWORKS        INTERFACE         
backend-867fd6dff-mjf92          minikube   10.88.0.27/32   cali2b1490aa46a   
db-8d66ff5f7-bp6kf               minikube   10.88.0.26/32   cali95aa86cbb2a   
frontend-8b474f47-zdqdv          minikube   10.88.0.24/32   cali505cfbeac50

cali95aa86cbb2a 是 DB pod 正在应用的 veth 对的主机端。咱们来获取这个接口相干的 iptables 规定。从 iptables 规定中,能够看到只有数据包来着 Backend Pod 时才容许进入 DB Pod。

# Calico iptables 规定链命名
# cali-: 前缀,calico 的规定链 
# tw: 简写,to wordkoad endpoint
# wl: 简写,workload endpoint
# fw: 简写,from workload endpoint 
# from-XXX: XXX 收回的报文 
# to-XXX: 发送到 XXX 的报文 
# pi: 简写,policy inbound
# po: 简写,policy outbound
# pri: 简写,profile inbound
# pro: 简写,profile outbound 


$ sudo iptables-save | grep cali95aa86cbb2a
...
# 先查看 fw: from workload 的规定,再查看 tw: to workload 的规定,上面以 tw 的规定进行阐明
# 第一步:如果 conntrack 表中可能检索到该连贯的状态,能够间接依据状态放行或者抛弃
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:eCrqwxNk3cKw9Eq6" -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:_krp5nzavhAu5avJ" -m conntrack --ctstate INVALID -j DROP

# 第二步:先对数据包进行标记,--set-xmark value[/mask],value 示意要匹配的 mark 的值,mask 代表掩码,如果要齐全匹配能够省略掉掩码
# 如果通过 Network Policy 后 mark 值没有批改,第七步会抛弃该数据包
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:leBL64hpAXM9y4nk" -m comment --comment "Start of policies" -j MARK --set-xmark 0x0/0x20000

# 第三步(重点):进入 cali-pi-_tTE-E7yY40ogArNVgKt 链进行 Network Policy 判断
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:pm-LK-c1ra31tRwz" -m mark --mark 0x0/0x20000 -j cali-pi-_tTE-E7yY40ogArNVgKt

# 第六步:如果 Network Policy 查看通过(查看是否匹配 mark 0x10000/0x10000), 匹配阐明通过,间接 RETURN,不再查看其余的规定
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:q_zG8dAujKUIBe0Q" -m comment --comment "Return if policy accepted" -m mark --mark 0x10000/0x10000 -j RETURN

# 第七步:如果 mark 没有批改,与原先统一,视为没有任何一个 Network Policy 容许该包通过,抛弃数据包
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:FUDVBYh1Yr6tVRgq" -m comment --comment "Drop if no policies passed packet" -m mark --mark 0x0/0x20000 -j DROP
...

# Network Policy 规定判断
$ sudo iptables-save -t filter | grep cali-pi-_tTE-E7yY40ogArNVgKt
...
# 第四步(重点):如果源 IP 匹配 ipsetali40s:LrVD8vMIGQDyv8Y7sPFB1Ge,将数据包增加 mark 0x10000
-A cali-pi-_tTE-E7yY40ogArNVgKt -m comment --comment "cali:M4Und37HGrw6jUk8" -m set --match-set cali40s:LrVD8vMIGQDyv8Y7sPFB1Ge src -j MARK --set-xmark 0x10000/0x10000

# 第五步:查看是否匹配 mark,如果匹配就 RETURN 放行
-A cali-pi-_tTE-E7yY40ogArNVgKt -m comment --comment "cali:sEnlfZagUFRSPRoe" -m mark --mark 0x10000/0x10000 -j RETURN
...

Calico 应用 ipset 进行流量治理,ipset 是 iptables 的扩大,有以下几个长处:

  • 容许在一条规定中依据多个 IP 地址和端口号进行匹配。
  • 容许针对 IP 地址或者端口号动静更新 iptables 规定。
  • ipset 应用 set 汇合作为存储构造,set 在查问时非常高效。

通过查看 ipset,很显著看到只容许从 Backend Pod 的 IP 10.88.0.27 拜访 DB Pod。

[root@minikube /]# ipset list
Name: cali40s:LrVD8vMIGQDyv8Y7sPFB1Ge
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 1048576
Size in memory: 408
References: 3
Number of entries: 1
Members:
10.88.0.27

参考资料

  • [1] 原文: https://dramasamy.medium.com/…
  • [2] iptables 四表五链及规定组成: https://blog.csdn.net/wfs1994…)
  • [3] 应用 iptables、ipset 的全局智能代理: https://blog.chih.me/global-p…
  • [4] networkpolicy 的实际 –felix: https://segmentfault.com/a/11…
  • [5] calico iptables 详解: https://blog.csdn.net/ptmozhu…
  • [6] iptables mark: https://www.kancloud.cn/pshiz…

欢送关注

正文完
 0