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的访问主要分为:
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
[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.6
、10.224.2.7
。
node节点(node ip: 10.0.0.201
)访问service(cluster ip: 10.107.237.254
)。
首先流量会到达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
有两条规则:
0x4000/0x4000
,后面会用到这个标记;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
发现有两条子链:
KUBE-SEP-EA2C3KXU4TFQDFBN
;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
链。
[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)
重新创建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
链:
[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
链:
[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
原文:https://www.cnblogs.com/LMIx/p/14588522.html