首页 > 其他 > 详细

kube-proxy 实现原理

时间:2021-03-28 17:26:55      阅读:13      评论:0      收藏:0      [点我收藏+]

Service是k8s中的一个概念,是对一组pod的服务抽象,主要负责将请求分发给对应的pod,完成反向代理和负载均衡(负载均衡一般采用Round Robin算法)。

kube-proxy来具体实现Service。
**
kube-proxy是通过iptables实现一系列的包过滤、转发、nat操作的。

在创建Service对象时,会为其分配一个虚拟IP(VIP),称为Cluster IP,Cluster IP在外部无法访问,主要作用是内部节点和pod访问时用到的。

[root@master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d2h

[root@master ~]# ping 10.96.0.1
PING 10.96.0.1 (10.96.0.1) 56(84) bytes of data.
From 10.0.30.10 icmp_seq=1 Time to live exceeded
From 10.0.30.10 icmp_seq=2 Time to live exceeded
From 10.0.30.10 icmp_seq=3 Time to live exceeded
From 10.0.30.10 icmp_seq=5 Time to live exceeded
From 10.0.30.10 icmp_seq=9 Time to live exceeded
^C
--- 10.96.0.1 ping statistics ---
10 packets transmitted, 0 received, +5 errors, 100% packet loss, time 9011ms

具体来说,对Service的访问主要分为:

  1. 内部从pod到service,进而转发到具体的pod;
  2. 外部从node port到service,进而转发到具体的pod。

测试环境

web服务。

首先定义一个提供web服务的RC,由2个tomcat容器副本组成,每个容器通过containerPort设置提供服务的端口号为8080:

[root@master ~]# vi webapp-RC.yaml
apiVersion: v1
kind: ReplicationController
metadata:
  name: webapp
spec:
  replicas: 2
  template:
    metadata:
      name: webapp
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: tomcat
        ports:
        - containerPort: 8080
[root@master ~]# kubectl create -f webapp-RC.yaml
replicationcontroller "webapp" created

然后创建一个service,绑定这两个pod:

[root@master ~]# vi webapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  ports:
  - port: 8081
    targetPort: 8080
  selector:
    app: webapp
[root@master ~]# kubectl create -f webapp-svc.yaml
service "webapp" created

内部访问service

[root@master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
...
webapp       ClusterIP   10.107.237.254   <none>        8081/TCP   34m
[root@master ~]# kubectl get pod -o wide
NAME                    READY   STATUS    RESTARTS   AGE     IP           NODE    NOMINATED NODE   READINESS GATES
webapp-74bxv            1/1     Running   0          46m     10.244.2.7   node1   <none>           <none>
webapp-ln5nh            1/1     Running   0          46m     10.244.1.6   node2   <none>           <none>

webapp的Cluster IP为10.107.237.254,pod的ip为10.244.1.610.224.2.7

node节点(node ip: 10.0.0.201)访问service(cluster ip: 10.107.237.254)。

OUTPUT链

首先流量会到达OUTPUT链:

[root@master ~]# iptables-save -t nat | grep -- ‘-A OUTPUT‘
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

然后该链跳转到KUBE-SERVICES子链里:

[root@master ~]# iptables-save -t nat | grep -- ‘-A KUBE-SERVICES‘
...
-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.107.237.254/32 -p tcp -m comment --comment "default/webapp cluster IP" -m tcp --dport 8081 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.107.237.254/32 -p tcp -m comment --comment "default/webapp cluster IP" -m tcp --dport 8081 -j KUBE-SVC-2IRACUALRELARSND

有两条规则:

  1. 第一条负责打标记MARK0x4000/0x4000,后面会用到这个标记;
  2. 第二条规则跳到KUBE-SVC-2IRACUALRELARSND子链。

KUBE-SVC-2IRACUALRELARSND子链的规则:

[root@master ~]# iptables-save -t nat | grep -- ‘-A KUBE-SVC-2IRACUALRELARSND‘
-A KUBE-SVC-2IRACUALRELARSND -m comment --comment "default/webapp" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-EA2C3KXU4TFQDFBN
-A KUBE-SVC-2IRACUALRELARSND -m comment --comment "default/webapp" -j KUBE-SEP-ELK6VM6EZTBKIB4X

发现有两条子链:

  • 1/2的概率跳转到子链KUBE-SEP-EA2C3KXU4TFQDFBN
  • 剩下1/2概率跳转到子链KUBE-SEP-ELK6VM6EZTBKIB4X

首先看KUBE-SEP-EA2C3KXU4TFQDFBN子链:

[root@node1 ~]# iptables-save -t nat | grep -- ‘-A KUBE-SEP-EA2C3KXU4TFQDFBN‘
-A KUBE-SEP-EA2C3KXU4TFQDFBN -s 10.244.1.6/32 -m comment --comment "default/webapp" -j KUBE-MARK-MASQ
-A KUBE-SEP-EA2C3KXU4TFQDFBN -p tcp -m comment --comment "default/webapp" -m tcp -j DNAT --to-destination 10.244.1.6:8080

可见这条规则的目的是做了一次DNAT,DNAT目标为其中一个Endpoint,即Pod服务。

而另一个子链KUBE-SEP-ELK6VM6EZTBKIB4X

[root@node1 ~]# iptables-save -t nat | grep -- ‘-A KUBE-SEP-ELK6VM6EZTBKIB4X‘
-A KUBE-SEP-ELK6VM6EZTBKIB4X -s 10.244.2.7/32 -m comment --comment "default/webapp" -j KUBE-MARK-MASQ
-A KUBE-SEP-ELK6VM6EZTBKIB4X -p tcp -m comment --comment "default/webapp" -m tcp -j DNAT --to-destination 10.244.2.7:8080

可见,KUBE-SVC-2IRACUALRELARSND的功能就是按照等概率的原则DNAT到其中的一个endpoint。

即:

10.0.0.201:xxxx(node) --> 10.107.237.254:8081
↓ DNAT
10.0.0.201:xxxx(node) --> 10.244.1.6:8080(例如pod1)

完成DNAT后,接着到POSTROUTING链。

POSTROUTING链

[root@node1 ~]# iptables-save -t nat | grep -- ‘-A POSTROUTING‘
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING

KUBE-POSTROUTING

[root@node1 ~]# iptables-save -t nat | grep -- ‘-A KUBE-POSTROUTING‘
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE

这两条规则只做一件事就是只要标记了0x4000/0x4000的包就一律做MASQUERADE(SNAT),由于10.244.1.2默认是从flannel.1转发出去的,因此会把源IP改为flannel.1的IP10.244.0.0

10.0.0.201:xxxx(node) --> 10.107.237.254:8081
↓ DNAT
10.0.0.201:xxxx(node) --> 10.244.1.6:8080(例如pod1)
↓ SAT
10.244.0.0:xxxx --> 10.244.1.6:8080(例如pod1)

外部访问service

重新创建NodePort类型的service:

apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  ports:
  - port: 8081 # Cluster IP虚拟端口
    targetPort: 8080 # 容器服务端口
    nodePort: 30080 # NodePort端口
  type: NodePort
  selector:
    app: webapp

外部访问node节点的30080端口,转发到Cluster IP的8080端口,进一步转发到容器的8081端口。

[root@master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
webapp       NodePort    10.96.187.75   <none>        8081:30080/TCP   13m

假设10.0.0.1访问10.0.0.201:30080。

PREROUTING链

首先到达PREROUTING链:

[root@node1 ~]# iptables-save -t nat | grep -- ‘-A PREROUTING‘
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

KUBE-SERVICES

[root@node1 ~]# iptables-save -t nat | grep -- ‘-A KUBE-SERVICES‘
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS

PREROUTING的规则非常简单,凡是发给自己的包,则交给子链KUBE-NODEPORTS处理。注意前面省略了判断ClusterIP的部分规则。

[root@node1 ~]# iptables-save -t nat | grep -- ‘-A KUBE-NODEPORTS‘
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/webapp" -m tcp --dport 30080 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/webapp" -m tcp --dport 30080 -j KUBE-SVC-2IRACUALRELARSND

这个规则首先给包打上标记0x4000/0x4000,然后交给子链KUBE-SVC-2IRACUALRELARSND处理,KUBE-SVC-2IRACUALRELARSND就是按照概率均等的原则DNAT到其中一个Endpoint IP,即Pod IP,假设为10.244.1.6。

10.0.0.1:xxxx(node) --> 10.0.0.201:30080
↓ DNAT
10.0.0.201:xxxx(node) --> 10.244.1.6:8080(例如pod1)

此时发现10.244.1.6不是自己的IP,于是经过路由判断目标为10.244.1.6需要从flannel.1发出去。

FORWARD链

接着到了FORWARD链:

[root@node1 ~]# iptables-save -t filter | grep -- ‘-A FORWARD‘
-A FORWARD -m comment --comment "kubernetes forwarding rules" -j KUBE-FORWARD

KUBE-FORWARD

[root@node1 ~]# iptables-save -t filter | grep -- ‘-A KUBE-FORWARD‘
-A KUBE-FORWARD -m conntrack --ctstate INVALID -j DROP
-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT

FORWARD表在这里只是判断下,只允许打了标记0x4000/0x4000的包才允许转发。

最后来到POSTROUTING链,这里和ClusterIP就完全一样了,在KUBE-POSTROUTING中做一次MASQUERADE(SNAT),最后结果:

10.0.0.1:xxxx(node) --> 10.0.0.201:30080
↓ DNAT
10.0.0.201:xxxx(node) --> 10.244.1.6:8080(例如pod1)
↓ SNAT
10.244.0.0:xxxx --> 10.244.1.6:8080(例如pod1)

具体k8s的flannel网络参考:https://www.jianshu.com/p/2f91907b2aba
具体iptables链参考:
技术分享图片

注意:当我们创建pod时,仅仅是创建了pod,要为其创建rc(ReplicationController),他才会有固定的副本,然后为其创建service,集群内部才能访问该pod,使用 NodePort 或者 LoadBalancer 类型的 Service,外部网络也可以访问该pod;每个 service 会创建出来一个虚拟 ip,通过访问 vip:port 就能获取服务的内容(内部访问,因为这是一个vip,外部无法访问的)

参考

https://www.jianshu.com/p/f28534fe507a
https://www.xiexianbin.cn/kubernetes/2016-07-25-kubernetes-proxy/index.html
https://zhuanlan.zhihu.com/p/94418251?from_voters_page=true
http://www.voidcn.com/article/p-fzkcdsqq-bqu.html
https://www.jianshu.com/p/2f91907b2aba

kube-proxy 实现原理

原文:https://www.cnblogs.com/LMIx/p/14588522.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!