K8s网络实战分析之service调用

在上一篇文章K8s网络实战分析之Calico-ipip模式中,我们通过Pod之间进行ping操作,对基于Calico-IPIP模式的K8s网络进行了实战学习与分析。单单进行Pod-Pod的访问只是K8s的基础功能,基于Service的访问才是K8s的网络核心。本文将基于上一篇文章所搭建的K8s网络,与大家一起探讨,Service访问的IP报文在K8s集群内的流动与原理!

实验准备

为了方便操作,所有的pod都是部署的nginx容器,同时统一增添了一个nginx-service,配置如下:(实验中使用的curl等指令,容器内并没有预置,需要大家自行更新安装)。

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30001

网络设备结构如图,yaml配置中replicas=3,共有3个容器,其中Pod3与Pod2皆在node2中,为了简洁表示,暂没有画出。
image.png
service信息如下,在K8s内部可以通过CLUSTER-IP:80进行访问,在外可以通过nodeIP:30001进行访问。

kubectl get svc -o wide
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE   SELECTOR 
nginx-service   NodePort    10.96.193.229   <none>        80:30001/TCP   9d    app=nginx

Service调用

我们知道,Service调用默认是轮询调用,Service会将访问请求随机转发到其中一个pod进行。根据Service的调用过程的不同,本节将分为四个部分进行实战分析,分别是跨node调用同node内调用同Pod内调用集群外调用

跨node调用

从Pod2发出请求 curl nginx-service</font color=grey> ,到接收来自pod1的reply,IP报的流动是怎样的呢?

实战操作

如果看过我的上一篇文章K8s网络实战分析之Calico-ipip模式,大家一定会非常肯定,IP的流动过程如下。那么恭喜您,猜测是正确的!
image.png
想要知道更多的细节,抓包是必不可少的。在各个节点处通过tcpdump进行抓包分析,得到IP报的流动如下:
Pod2->Pod1

Pod2: curl 10.96.193.229Pod2:
IP 10.100.9.206.45764 > 10.96.193.229.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3411647532 ecr 3411645523], length 77: HTTP: GET / HTTP/1.1
node2:
Tunl0
IP 10.100.9.206.45764 > 10.100.15.150.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3411647532 ecr 3411645523], length 77: HTTP: GET / HTTP/1.1Eth0
172.31.127.252 > 172.31.112.2: IP 10.100.9.206.45764 > 10.100.15.150.80: Flags [P.], seq 0:77, ack 1, win 219, options [nop,nop,TS val 3412008415 ecr 3412006406], length 77: HTTP: GET / HTTP/1.1 (ipip-proto-4)
Node1:
Eth0
IP 172.31.127.252 > 172.31.112.2: IP 10.100.9.206.45764 > 10.100.15.150.80: Flags [P.], seq 0:77, ack 1, win 219, options [nop,nop,TS val 3412008415 ecr 3412006406], length 77: HTTP: GET / HTTP/1.1 (ipip-proto-4)
Tunl0
IP 10.100.9.206.45764 > 10.100.15.150.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3411647532 ecr 3411645523], length 77: HTTP: GET / HTTP/1.1

Pod1:
tcpdump -i eth0 -nn
IP 10.100.9.206.45764 > 10.100.15.150.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3411887650 ecr 3411885641], length 77: HTTP: GET / HTTP/1.1

Pod1 reply Pod2

Pod1:
IP 10.100.15.150.80 > 10.100.9.206.53142: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3429334979 ecr 3429336988], length 238: HTTP: HTTP/1.1 200 OK

Node1
Tunl0
IP 10.100.15.150.80 > 10.100.9.206.53288: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3429693343 ecr 3429695353], length 238: HTTP: HTTP/1.1 200 OK
Eth0
IP 172.31.112.2 > 172.31.127.252: IP 10.100.15.150.80 > 10.100.9.206.53142: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3429334979 ecr 3429336988], length 238: HTTP: HTTP/1.1 200 OK (ipip-proto-4)

Node2
Eth0
IP 172.31.112.2 > 172.31.127.252: IP 10.100.15.150.80 > 10.100.9.206.53142: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3429334979 ecr 3429336988], length 238: HTTP: HTTP/1.1 200 OK (ipip-proto-4)
Tunl0
IP 10.100.15.150.80 > 10.100.9.206.53288: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3429693343 ecr 3429695353], length 238: HTTP: HTTP/1.1 200 OK

Pod2
IP 10.96.193.229.80 > 10.100.9.206.53288: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3429693343 ecr 3429695353], length 238: HTTP: HTTP/1.1 200 OK

实战分析

对照数据,我们画出ip报流动示意图
image.png
与上以前文章中的ping 10.100.15.150相比,node节点之间的通信依然是使用Calico的IPIP模式,通过tunl0进行连接。值得注意的点有以下几个:

  1. node2中,由Cali.c2发出的流量,在进入tunl0之前,通过iptables进行了一次DNAT,将原本的目的地址(即nginx-service地址)10.96.193.229转为了Pod1的ip地址10.100.15.150
  2. node1中,由tunl0发往Cali.4e的ip报,由于在1中已经进行了DNAT,其dst host不是service,因此iptables并不会对其作用,而是直接根据route规则,转给了Cali.4e

iptables分析

如果对iptables不是很熟悉,推荐大家看一下朱双印的iptables系列,非常好的介绍iptables的博客。
我们对上节注意点1中的iptables规则进行具体的分析。从Cali.c2发出的流量,进入到Linux网络协议栈后,会被iptables在多个阶段进行拦截过滤重定向等操作,主要作用点如图。
image.png
从PREROUTING链进行跟踪,本次ip报经在iptables中的主要的作用链如下:
image.png
当iptables随机选择了Pod1( KUBE-SEP-RR4LYAAU6NR5VL5B)作为本次调用的endpoint后,该链会进行一个DNAT操作,–to-destination 10.100.15.150:80,将dst host 从原来的service host转为Pod1的host。

同node内调用

从Pod2发出请求 curl nginx-service</font color=grey> ,到接收来自同node内pod3的reply,IP报的流动是怎样的呢?

实战操作

通过抓包,获取到各个节点上的IP流动如下:
Pod2->Pod3

curl 10.96.193.229
Pod1
IP 10.100.9.206.48024 > 10.96.193.229.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3416946428 ecr 3416946428], length 77: HTTP: GET / HTTP/1.1

Pod3
IP 10.100.9.206.48024 > 10.100.9.207.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3416946428 ecr 3416946428], length 77: HTTP: GET / HTTP/1.1

Pod3 reply Pod1

Pod3
IP 10.100.9.207.80 > 10.100.9.206.48024: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3416946428 ecr 3416946428], length 238: HTTP: HTTP/1.1 200 OK

Pod1
IP 10.96.193.229.80 > 10.100.9.206.48024: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3416946428 ecr 3416946428], length 238: HTTP: HTTP/1.1 200 OK

实战分析

对照数据,我们画出ip报流动示意图:
image.pngimage.png
同node内调用,并没有调用tunl0进行IPIP封装,直接由Cali.c2转发到了Cali.fa,中间经过了DNAT转换和ip route路由,与此前并没有啥不同,不再赘述。这里我们多说一句,就是iptables作用点和ip route路由作用点其实不是一前一后的关系,他们都在IP协议栈里。准确来说,在通过PREROUTING后,会进行ip route路由,接着再进行其他的iptables作用。大概的样子如下图,红色为iptables作用点,黄色为route作用点。只有从PREROUNTING进来的ip报,才会进行ip route哦😯
image.png

同Pod内调用

从Pod2发出请求 curl nginx-service</font color=grey> ,到接收来自同pod的reply,IP报的流动是怎样的呢?

可能大家会觉得和上一节同node内调用一样,那就错啦!!!先卖个关子,下面为大家进行展示。

实战操作

通过抓包,获取到各个节点上的IP流动如下:
Pod2->Pod2

发送
IP 10.100.9.206.49586 > 10.96.193.229.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3420700250 ecr 3420700250], length 77: HTTP: GET / HTTP/1.1
收到
172.31.127.252.49586 > 10.100.9.206.80: Flags [P.], seq 1:78, ack 1, win 219, options [nop,nop,TS val 3420700250 ecr 3420700250], length 77: HTTP: GET / HTTP/1.1

Pod2 reply Pod2

回复
IP 10.100.9.206.80 > 172.31.127.252.49586: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3420700250 ecr 3420700250], length 238: HTTP: HTTP/1.1 200 OK
收到回复
10.96.193.229.80 > 10.100.9.206.49586: Flags [P.], seq 1:239, ack 78, win 217, options [nop,nop,TS val 3420700250 ecr 3420700250], length 238: HTTP: HTTP/1.1 200 OK

实战分析

对照数据,我们画出ip报流动示意图,图中红色代表红色代表发出请求的路径,黄色代表服务器的相应路径。
image.pngimage.png
与同node内调用相比,可以发现,发送请求在经过iptables后,不仅仅进行了DNAT,也进行了SNAT,其src host 由Pod2的ip 10.100.9.206转为了node2的主机ip 172.31.127.252。
为什么要经过SNAT其实很好理解:如果没有SNAT,服务器会看到自己给自己发送的请求,那么,它回复谁去呢?怕是要迷茫了吧哈哈。
###iptables分析
让我们进入iptables,去看看有什么不同的地方。
image.png
与之前相比,同Pod内调用多匹配了一条链:
-A KUBE-SEP-5YGHV5ZP5SB5PVUU -s 10.100.9.206/32 -j KUBE-MARK-MASQ
这会对该ip报进行打标操作,然后在KUBE-POSTROUTING中,进行了MASQUERADE操作
-A KUBE-POSTROUTING -m comment --comment “kubernetes service traffic requiring SNAT” -m mark --mark 0x4000/0x4000 -j MASQUERADE
MASQUERADE操作就是一种自动指定src host的SNAT操作。相信大家看到这肯定明白了同Pod调用和同node调用的区别了吧。同Pod会额外进行SNAT的转换哦。

集群外调用

通过直接访问nodeip:30001,我们可以进行集群外调用实战。与此前的分析方法一样,通过抓包,我们直接贴出ip报流向图:
image.png
总结如下:

  1. 不管从哪个node进入,kube-proxy机制仍会随机将请求转发到某一个Pod中,与node无关。
  2. 访问同node中的Pod,会进行DNAT,将service ip转化为podip。
  3. 访问异node中的Pod,除了进行DNAT,也会进行SNAT,从Pod中看到的src ip为转发该ip报的node中的tunl0的ip地址。

iptables分析

总结3可能有点绕,我们将会对其iptables进行详细分析。iptables链路图如下:
image.png
黄色箭头为公共流向,红色箭头为同node中pod调用,绿色箭头为异node中pod调用。可以看到,
-A KUBE-SEP-5YGHV5ZP5SB5PVUU -s 10.100.9.206/32 -j KUBE-MARK-MASQ
由于src host不是10.100.9.206/32,ip报被打上tag,然后在最后的KUBE-POSTEROUTING中,进行了MASQUERADME,也就是自动SNAT,将src host由172.31.112.1转换为了tunl0的地址 10.100.15.128、

service调用总结

通过上面的实战,相信大家对service调用的各种情况都有了直观的认识,总结来说

  1. 所有的service调用都会进行DNAT转换,将service ip转换为将要调用的Pod ip
  2. 对于同Pod内调用,为了防止出现“自己调自己”的情况,需要进行SNAT,将src ip转换为node ip
  3. 对于集群外访问,如果访问的node地址不是Pod所在的地址,那么会进行一次SNAT,将src ip转换为node内的tunl0 ip。

希望对大家有所帮助~

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值