[译]数据包在 Kubernetes 中的一生(3)

2021-07-07 16:13:32 浏览数 (1)

原文:

Life of a Packet in Kubernetes — Part 3

链接:

https://dramasamy.medium.com/life-of-a-packet-in-kubernetes-part-3-dd881476da0f

本章我们会讨论一下 Kubernetes 的 kube-proxy 是如何使用 iptables 控制流量的。注意,kube-proxy iptables 的组合并非完成该任务的唯一选择。

我们会从 Kubernetes 的多种通信模型和实现开始,如果读者已经了解了 Service、ClusterIP 以及 NodePort 的概念,可以直接跳到 kube-proxy/iptables 一节。

Pod 到 Pod

CNI 会配置节点和 Pod 的路由,kube-proxy 不会介入 Pod 到 Pod 之间的通信过程。所有的容器都无需 NAT 就能互相通信;节点和容器之间的通信也是无需 NAT 的。

Pod 的 IP 地址是不固定的(也有办法做成静态 IP,但是缺省配置是不提供这种保障的)。在 Pod 重启时 CNI 会给他分配新的 IP 地址,CNI 不负责维护 IP 地址和 Pod 的映射。Pod 名称在 Deployment 之中也是不固定的。

Deployment 中的 Pod 是无状态的,一个应用可能会有多个 Pod 副本,因此需要一个负载均衡之类的东西来负责对外开放服务,Kubernetes 中的 Service 对象负责完成这个任务。

Pod 到外部

Kubernetes 会使用 SNAT 完成从 Pod 向外发出的访问。SNAT 会将 Pod 的内部 IP:Port 替换为主机的 IP:Port。返回数据包到达节点时,IP:Port 又会换回 Pod。这个过程对于原始 Pod 是透明无感知的。

Pod 到 Service

Cluster IP

Kubernetes 有一个叫做 Service 的对象,是一个通向 Pod 的 4 层负载均衡。Service 对象有很多类型,最基本的类型叫做 ClusterIP,这种类型的 Service 有一个唯一的 VIP 地址,其路由范围仅在集群内部有效。

Kubernetes 集群中,Pod 可能发生移动、重启、升级或者扩缩容,因此向应用 Pod 发送流量是有困难的,另外应用通常有多个副本,我们需要一些方法来进行负载均衡。

Kubernetes 使用 Service 对象来解决这个问题。Service 是一个 API 对象,它用一个虚拟 IP 映射到一组 Pod。另外 Kubernetes 为每个 Service 的名称及其虚拟 IP 建立了 DNS 记录,因此可以轻松地根据名称进行寻址。

虚拟 IP 到 Pod IP 的转换是通过每个节点上的 kube-proxy 完成的。在 Pod 向外发起通信时,这个进程会通过 iptables 或者 IPVS 自动把 VIP 转为 Pod IP,每个连接都有跟踪,所以数据包返回时候,地址还能够被正确地转回原样。IPVS 和 iptables 在 VIP 和 Pod IP 之间承担着负载均衡的角色,IPVS 能够提供更多的负载均衡算法。虚拟 IP 并不存在于网络接口上,而是在 iptable 中:

FrontEnd Deployment:

代码语言:javascript复制
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:

代码语言:javascript复制
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:

代码语言:javascript复制
---
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 或者 DNS 名称来访问 Backend 了。CoreDNS 这样的 DNS 服务器具备 Kubernetes 集群感知的能力,他们会对 Kubernetes API 进行监控,一旦新建了 Service,就会新建对应的 DNS 记录。如果集群中启用的 DNS,所有 Pod 都能够自动的根据 DNS 名称来解析到 Service。

NodePort(外部到 Pod)

在集群内部可以用 DNS 访问 Service。然而 Service 的 IP 是私有的和虚拟的,所以集群外是无法访问的。

试试看从外部访问 frontEnd 的 Pod(此时还没有给 frontEnd 创建 Service):

Pod IP 是私有的,无法路由。

接下来创建一个 NodePort 类型的 Service 把 FrontEnd 服务开放给外部世界。如果把 type 字段设置为 NodePort,Kubernetes 控制面使用 --service-node-port-range 参数为 NodePort 服务分配了一个端口范围。每个节点都会会把这个端口映射给特定的服务。Service 使用 .spec.ports[*].nodePort 字段来指定该端口:

代码语言:javascript复制
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: 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
...

这样就可以在集群外使用任意节点的 nodePort 来访问服务了。还可以给 nodePort 赋值以指定特定开放端口。这种情况下,为了防止端口冲突,需要自行管理端口,并且指定端口也必须在参数中声明的端口范围之内。

ExternalTrafficPolicy

ExternalTrafficPolicy 字段表明所属 Service 对象会把来自外部的流量路由给本节点还是集群范围内的端点。如果赋值为 Local,会保留客户端源 IP 同时避免 NodePort 类型服务的多余一跳,但是有流量分配不均匀的隐患;如果设置为 Cluster,会抹掉客户端的源 IP,并导致到其它节点的一跳,但会获得相对较好的均衡效果。

Cluster

这是 Kubernetes Service 的缺省 ExternalTrafficPolicy。这个选项会把流量平均分配给该 Service 的所有 Pod 上。

这种策略的一个弱点是会存在不必要的节点间网络跳转。例如在一个节点的 NodePort 上接收到流量时,即使本节点上存在可用 Pod,流量还是可能会随机地把流量路由到另外一个节点上的 Pod,造成不必要的跳转。

Cluster 策略下,数据包的流向:

  • 客户端把数据包发送给 node2:31380
  • node2 替换源 IP 地址(SNAT)为自己的 IP 地址;
  • node2 将目标地址替换为 Pod IP;
  • 数据包被路由到 node1 或者 node3,然后到达 Pod;
  • Pod 的响应返回到 node2
  • Pod 的响应返回到客户端。
Local

这种策略中,kube-proxy 只会在存在目标 Pod 的节点上加入 NodePort 的代理规则。API Server 要求只有使用 LoadBalancer 或者 NodePort 类型的 Service 才能够使用这种策略。这是因为 Local 策略只跟外部访问相关。

如果使用了 Local 策略,kube-proxy 只会代理到本地 endpoint 的流量,不会向其它节点转发。如果本地没有相应端点,发送到该节点的流量就会被丢弃,所以数据包中会保留正确的源 IP,可以放心的在数据包处理规则中使用。

代码语言:javascript复制
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
spec:
  type: NodePort
  externalTrafficPolicy: Local
  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 策略下的数据包:

  • 客户端发送数据包到 node1:31380,这个端点上存在目标 Pod;
  • node1 把数据包路由到端点,其中带有正确的源 IP;
  • 因为策略限制,node1 不会把数据包发给 node3
  • 客户端发送数据包给 node2:31380,该节点上不存在目标 Pod;
  • 数据包被丢弃。
LoadBalancer Service 类型中的 Local 策略

如果在 Google GKE 上使用 Local 策略,由于健康检查的原因,会把不运行对应 Pod 的节点从负载均衡池中剔除,所以不会发生丢弃流量的问题。这种模型对于需要处理大量外部入栈流量,需要避免跨节点跳转从而降低延迟的应用非常有帮助。另外因为不需要进行 SNAT,从而让源 IP 得以保存。然而官方文档声明,这种策略存在不够均衡的短板。

Kube-Proxy(iptable)

Kubernetes 中负责 Service 对象的组件就是 kube-proxy。它在每个节点上运行,为 Pod 和 Service 生成复杂的 iptables 规则,完成所有的过滤和 NAT 工作。如果登录到 Kubernetes 节点上,运行 iptables-save,会看到 Kubernetes 或者其它组件生成的规则。最重要的是 KUBE-SERVICEKUBE-SVC-* 以及 KUBE-SEP-*

  • KUBE-SERVICEService 包的入口。 它负责匹配 IP:Port,并把数据包发给对应的 KUBE-SVC-*
  • KUBE-SVC-* 担任负载均衡的角色,会平均分配数据包到 KUBE-SEP-*。 每个 KUBE-SVC-* 都有和 Endpoint 同样数量的 KUBE-SEP-*
  • KUBE-SEP-* 代表的是 ServiceEndPoint,它负责的是 DNAT,会把 Service 的 IP:Port 替换为 Pod 的 IP:Port。

Conntrack 会介入 DNAT 过程,使用状态机来跟踪连接状态。为了记住目标地址的变更,并在回包时候进行恢复,这些状态是必须保存的。iptables 还可以根据 conntrack 状态(ctstate)来决定数据包的目标。下面四个 conntrack 状态尤其重要:

  • NEW: conntrack 对该数据包一无所知,该状态出现在收到 SYN 的时候。
  • ESTABLISHED: conntrack 知道该数据包属于一个已发布连接,该状态出现于握手完成之后。
  • RELATED: 这个数据包不属于任何连接,但是他是隶属于其它连接的,在 FTP 之类的协议里常用。
  • INVALID: 有问题的数据包,conntrack 不知道如何处理。 这种状态是 Kubernetes 问题的常客。

Service 和 Pod 之间的 TCP 连接过程如下:

  • 左侧的客户端 Pod 发送数据包到一个 Service: 2.2.2.10:80
  • 数据包经过客户端节点的 iptables 规则,目标改为 1.1.1.20:80
  • 服务端 Pod 处理数据包,发送一个响应包到 1.1.1.10
  • 数据包回到客户端节点,conntrack 认出这个数据包,把源地址改回 2.2.2.10:80
  • 客户端 Pod 收到响应包。

iptables

在 Linux 操作系统中使用 netfilter 处理防火墙工作。这是一个内核模块,决定是否放行数据包。iptables 是 netfilter 的前端。二者经常被混为一谈。

每条链负责一种特定任务。

  • PREROUTING: 决定数据包刚刚进入网络端口时的对策。 有几种不同的选择,例如修改数据包(NAT),丢弃数据包或者什么都不做使其通过;
  • INPUT: 其中经常包含一些用于防止恶意行为的严格规则,防止系统遭到入侵。 开放或者屏蔽端口的行为就是在这里进行的;
  • FORWARD: 顾名思义,负责数据包的转发。 在将服务器作为路由器的时候,就需要在这里完成任务。
  • OUTPUT: 这里负责所有的网络浏览的行为。 这里可以限制所有数据包的发送。
  • POSTROUTING: 发生在数据包离开服务器之前,数据包最后的可跟踪位置。

FORWARD 仅在 ip_forward 启用时才有效。所以下面的命令在 Kubernetes 中很重要:

代码语言:javascript复制
$ sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
$ cat /proc/sys/net/ipv4/ip_forward
1

上面的变更是暂时性的,要持久化这个变更,需要在 /etc/sysctl.conf 中写入 net.ipv4.ip_forward = 1

接下来会讨论 NAT 表,除此之外还有几个:

  • Filter: 缺省表,这里决定是否允许数据包出入本机,因此可以在这里进行屏蔽等操作;
  • Nat: 是网络地址转换的缩写。 下面会有例子说明;
  • Mangle: 仅对特定包有用。 它的功能是在包出入之前修改包中的内容;
  • RAW: 用于处理原始数据包,主要用在跟踪连接状态,下面有一个放行 SSH 连接的例子。
  • Security: 负责在 Filter 之后保障安全。

Kubernetes 中的 iptables 配置

部署一个 2 副本 Nginx 应用,导出 iptables 规则。

服务类型 NodePort

代码语言:javascript复制
$ kubectl get svc webapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp NodePort 10.103.46.104 <none> 80:31380/TCP 3d13h
$ kubectl get ep webapp 
NAME ENDPOINTS AGE
webapp 10.244.120.102:80,10.244.120.103:80 3d13h

ClusterIP 是一个存在于 iptables 中的虚拟 IP,Kubernetes 会把这个地址存在 CoreDNS 中。

代码语言:javascript复制
$ 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

为了能够进行包过滤和 NAT,Kubernetes 会创建一个 KUBE-SERVICES 链,把所有 PREROUTINGOUTPUT 流量转发给 KUBE-SERVICES

代码语言:javascript复制
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-SERVICES,用于处理 NodePort 类型的 Service。

KUBE-SVC-2IRACUALRELARSND 链会处理针对 ClusterIP 的流量,否则的话就会进入 KUBE-NODEPORTS

代码语言:javascript复制
$ 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 的内容:

代码语言:javascript复制
$ 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

看起来 ClusterIPNodePort 处理过程是一样的,那么看看下面的处理流程:

代码语言:javascript复制
# statistic  mode  random -> Random load-balancing between endpoints.
$ sudo iptables -t nat -L KUBE-SVC-2IRACUALRELARSND | column -t
Chain                      KUBE-SVC-2IRACUALRELARSND  (2   references)                                                                             
target                     prot                       opt  source       destination                                                                
KUBE-SEP-AO6KYGU752IZFEZ4  all                        --   anywhere     anywhere     /*  default/webapp  */  statistic  mode  random  probability  0.50000000000
KUBE-SEP-PJFBSHHDX4VZAOXM  all                        --   anywhere     anywhere     /*  default/webapp  */

$ 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

$ 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-SERVICESKUBE-SVC-XXXKUBE-SEP-XXX
  • NodePort:KUBE-SERVICESKUBE-NODEPORTSKUBE-SVC-XXXKUBE-SEP-XXX

NodePort 服务会有一个 ClusterIP 用于处理内外部通信。

上述规则的可视化表达:

ExtrenalTrafficPolicy: Local

如前文所述,使用 ExtrenalTrafficPolicy: Local 会保留源 IP,并在到达节点上没有 Endpoint 的时候丢弃流量。没有本地 Endpoint 的节点上,iptables 的规则会怎样?

使用 ExtrenalTrafficPolicy: Local 部署 Nginx 服务:

代码语言:javascript复制
$ kubectl get svc webapp -o wide -o jsonpath={.spec.externalTrafficPolicy}
Local

$ 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

检查一下没有本地 Endpoint 的节点上的 iptables 规则:

代码语言:javascript复制
$ 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

代码语言:javascript复制
$ 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 */

这里就会看到,集群级别的流量没什么问题,但是 NodePort 流量会被丢弃。

Headless Service

有的应用并不需要负载均衡和服务 IP。在这种情况下就可以使用 headless Service,只要设置 .spec.clusterIPNone 即可。

可以借助这种服务类型和其他服务发现机制协作,无需和 Kubernetes 绑定。kube-proxy 不对这种没有 IP 的服务提供支持,也就没有什么负载均衡和代理之类的能力了。DNS 的配置要根据 Selector 来确定。

有 Selector

定义了 Selector 的 Headless Service,Endpoint 控制器会创建 Endpoint 记录,并修改 DNS 记录来直接返回 Service 后端的 Pod 地址。

代码语言:javascript复制
$ kubectl get svc webapp-hs
NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
webapp-hs   ClusterIP   None         <none>        80/TCP    24s
$ kubectl get ep webapp-hs
NAME        ENDPOINTS                             AGE
webapp-hs   10.244.120.109:80,10.244.120.110:80   31s

无 Selector

没有定义 Selector 的 Headless Service,也就没有 Endpoint 记录。然而 DNS 系统会尝试配置:

  • ExternalName 类型的服务,会产生 CNAME 记录;
  • 其他类型则是所有 Endpoint 共享服务名称。

如果外部 IP 被路由到集群节点上,Kubernetes Service 可以用 externalIPs 开放出来。通过 externalIP 进入集群的流量,会被路由到 Service Endpoint 上。externalIPs 不是 Kubernetes 管理的,需要集群管理员自行维护。

网络策略

阅读至此,Kubernetes 网络策略的实现方法已经呼之欲出了——是的,就是 iptables。目前是 CNI 而非 kube-proxy 负责实现网络策略。这部分内容本来应该写在第二篇 Calico 的内容里,然而我认为这里写出来可能更合适。

我们创建三个服务:frontend、backend 和 db。

缺省情况下,Pod 没有任何隔离,会接受任何来源的通信。

想要制定规则,禁止 frontend 访问 db:

这里推荐阅读 Guide to Kubernetes Ingress Network Policies 了解网络策略配置方面的更多内容。本节内容关注的是 Kubernetes 中策略的实现方式,而非配置知识。

创建一个策略把 db 和 frontend 隔离开,这样一来 frontend 和 db 之间的流量就会被阻断。

上图中为了简单起见,写的是 Service 而非 Pod,安全策略的控制对象实际上是 Pod。

策略实施之后会产生如下效果,frontend 的 Pod 能访问 backend 但是无法访问 db。backend 的 Pod 可以访问 db。

代码语言:javascript复制
$ 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

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

看看这里用到的网络策略:只允许 ‘allow-db-access 标签设置为 true 的 Pod 访问 db。

Calico 会把 Kubernetes 网络策略翻译成 Calico 格式:

代码语言:javascript复制
$ 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

iptables 的 filter 表在网络策略的实现中起了很重要的作用。Calico 中用到了 ipsec 等高级概念,难于进行反向工程。在这个规则中可以看到,只有来自 backend 的流量才被允许发给 db。

使用 calicoctl 获取 endpoint 详情:

代码语言:javascript复制
$ 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 规则:

代码语言:javascript复制
$ sudo iptables-save | grep cali95aa86cbb2a
:cali-fw-cali95aa86cbb2a - [0:0]
:cali-tw-cali95aa86cbb2a - [0:0]
...
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:pm-LK-c1ra31tRwz" -m mark --mark 0x0/0x20000 -j cali-pi-_tTE-E7yY40ogArNVgKt
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:q_zG8dAujKUIBe0Q" -m comment --comment "Return if policy accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-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
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:X19Z-Pa0qidaNsMH" -j cali-pri-kns.default
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:Ljj0xNidsduxDGUb" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:0z9RRvvZI9Gud0Wv" -j cali-pri-ksa.default.default
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:pNCpK-SOYelSULC1" -m comment --comment "Return if profile accepted" -m mark --mark 0x10000/0x10000 -j RETURN
-A cali-tw-cali95aa86cbb2a -m comment --comment "cali:sMkvrxvxj13WlTMK" -m comment --comment "Drop if no profiles matched" -j DROP
$ sudo iptables-save -t filter | grep cali-pi-_tTE-E7yY40ogArNVgKt
:cali-pi-_tTE-E7yY40ogArNVgKt - [0:0]
-A cali-pi-_tTE-E7yY40ogArNVgKt -m comment --comment "cali:M4Und37HGrw6jUk8" -m set --match-set cali40s:LrVD8vMIGQDyv8Y7sPFB1Ge src -j MARK --set-xmark 0x10000/0x10000
-A cali-pi-_tTE-E7yY40ogArNVgKt -m comment --comment "cali:sEnlfZagUFRSPRoe" -m mark --mark 0x10000/0x10000 -j RETURN

检查一下 ipset,会看到只有来自 backend pod 的 10.88.0.27 才能访问 db。

文章转载自伪架构师。点击这里阅读原文了解更多

0 人点赞