k8s-kube-proxy
kube-proxy组件介绍
Kubernetes service只是把应用对外提供服务的方式做了抽象,真正的应用跑在Pod中的container里,我们的请求转到kubernetes nodes对应的nodePort上,那么nodePort上的请求是如何进一步转到提供后台服务的Pod的呢? 就是通过kube-proxy实现的:
kube-proxy部署在k8s的每一个Node节点上,是Kubernetes的核心组件,我们创建一个 service 的时候,kube-proxy 会在iptables中追加一些规则,为我们实现路由与负载均衡的功能。在k8s1.8之前,kube-proxy默认使用的是iptables模式,通过各个node节点上的iptables规则来实现service的负载均衡,但是随着service数量的增大,iptables模式由于线性查找匹配、全量更新等特点,其性能会显著下降。从k8s的1.8版本开始,kube-proxy引入了IPVS模式,IPVS模式与iptables同样基于Netfilter,但是采用的hash表,因此当service数量达到一定规模时,hash查表的速度优势就会显现出来,从而提高service的服务性能。
service是一组pod的服务抽象,相当于一组pod的LB,负责将请求分发给对应的pod。service会为这个LB提供一个IP,一般称为cluster IP。kube-proxy的作用主要是负责service的实现,具体来说,就是实现了内部从pod到service和外部的从node port向service的访问。
1、kube-proxy其实就是管理service的访问入口,包括集群内Pod到Service的访问和集群外访问service。
2、kube-proxy管理sevice的Endpoints,该service对外暴露一个Virtual IP,也可以称为是Cluster IP, 集群内通过访问这个Cluster IP:Port就能访问到集群内对应的serivce下的Pod。
kube-proxy的作用
- 实现pod数据包转发、
- 将Service相关规则进行实现(iptables,ipvs)
Service的均衡是通过Kube-proxy实现的
kube-proxy三种工作模式
Userspace方式:
Client Pod要访问Server Pod时,它先将请求发给内核空间中的service iptables规则,由它再将请求转给监听在指定套接字上的kube-proxy的端口,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求转发给内核空间中的service ip,由service iptables将请求转给各个节点中的Server Pod。
这个模式有很大的问题,客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。由于其需要来回在用户空间和内核空间交互通信,因此效率很差。在Kubernetes 1.1版本之前,userspace是默认的代理模型。
iptables方式:
客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折
iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型。
ipvs方式:
Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本开始成为默认设置。客户端
请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。与iptables类似,ipvs基于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项,例如:
- rr:轮询调度
- lc:最小连接数
- dh:目标哈希
- sh:源哈希
- sed:最短期望延迟
- nq:不排队调度
如果某个服务后端pod发生变化,标签选择器适应的pod又多一个,适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息变化,而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理。如图:
注:
以上不论哪种,kube-proxy都通过watch的方式监控着apiserver写入etcd中关于Pod的最新状态信息,它一旦检查到一个Pod资源被删除了或新建了,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。自k8s1.11以后,service默认使用ipvs规则,若ipvs没有被激活,则降级使用iptables规则.
kube-proxy生成的iptables规则分析
1、service的type类型是ClusterIp,iptables规则分析
在k8s创建的service,虽然有ip地址,但是service的ip是虚拟的,不存在物理机上的,是在iptables或者ipvs规则里的。
[root@master1 service]# kubectl get svc -l run=my-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-nginx ClusterIP 10.98.63.235 <none> 80/TCP 75s
[root@master1 service]# kubectl get pods -l run=my-nginx -o wide NAME READY STATUS RESTARTS AGE IP my-nginx-69f769d56f-6fn7b 1/1 Running 0 13m 10.244.121.22 my-nginx-69f769d56f-xzj5l 1/1 Running 0 13m 10.244.121.21
[root@xianchaomaster1 service]# iptables -t nat -L | grep 10.98.63.235 KUBE-MARK-MASQ tcp -- !10.244.0.0/16 10.98.63.235 /* default/my-nginx cluster IP */ tcp dpt:http KUBE-SVC-L65ENXXZWWSAPRCR tcp -- anywhere 10.98.63.235 /* default/my-nginx cluster IP */ tcp dpt:http
- KUBE-MARK-MASQ 做标记
- KUBE-SVC-L65ENXXZWWSAPRCR 接收转发的规则
[root@master1 service]# iptables -t nat -L | grep KUBE-SVC-L65ENXXZWWSAPRCR KUBE-SVC-L65ENXXZWWSAPRCR tcp -- anywhere 10.98.63.235 /* default/my-nginx cluster IP */ tcp dpt:http Chain KUBE-SVC-L65ENXXZWWSAPRCR (1 references)
[root@xianchaomaster1 service]# iptables -t nat -L | grep 10.244.121.22 KUBE-MARK-MASQ all -- 10.244.121.22 anywhere /* default/my-nginx */ DNAT tcp -- anywhere anywhere /* default/my-nginx */ tcp to:10.244.121.22:80
- 查看pod的IP可以看到接收地址
[root@master1 service]# iptables -t nat -L | grep 10.244.121.21 KUBE-MARK-MASQ all -- 10.244.121.21 anywhere /* default/my-nginx */ DNAT tcp -- anywhere anywhere /* default/my-nginx */ tcp to:10.244.121.21:80
#通过上面可以看到之前创建的service,会通过kube-proxy在iptables中生成一个规则,来实现流量路由,有一系列目标为 KUBE-SVC-xxx 链的规则,每条规则都会匹配某个目标 ip 与端口。也就是说访问某个 ip:port 的请求会由 KUBE-SVC-xxx 链来处理。这个目标 IP 其实就是service ip。
2、service的type类型是nodePort,iptables规则分析
[root@master1 service]# kubectl get pods -l run=my-nginx-nodeport NAME READY STATUS RESTARTS AGE my-nginx-nodeport-649c945f85-l2hj6 1/1 Running 0 21m my-nginx-nodeport-649c945f85-zr47r 1/1 Running 0 21m
[root@master1 service]# kubectl get svc -l run=my-nginx-nodeport NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-nginx-nodeport NodePort 10.104.251.190 <none> 80:30380/TCP 22m
[root@master1 service]# iptables -t nat -S | grep 30380 -A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-SVC-J5QV2XWG4FEBPH3Q
[root@xianchaomaster1 service]# iptables -t nat -S | grep KUBE-SVC-J5QV2XWG4FEBPH3Q -N KUBE-SVC-J5QV2XWG4FEBPH3Q -A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp --dport 30380 -j KUBE-SVC-J5QV2XWG4FEBPH3Q -A KUBE-SERVICES -d 10.104.251.190/32 -p tcp -m comment --comment "default/my-nginx-nodeport cluster IP" -m tcp --dport 80 -j KUBE-SVC-J5QV2XWG4FEBPH3Q -A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-XRUO23GXY67LXLQN -A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -j KUBE-SEP-IIBDPNPJZXXASELC
[root@xianchaomaster1 service]# iptables -t nat -S | grep KUBE-SEP-XRUO23GXY67LXLQN -N KUBE-SEP-XRUO23GXY67LXLQN -A KUBE-SEP-XRUO23GXY67LXLQN -s 10.244.102.90/32 -m comment --comment "default/my-nginx-nodeport" -j KUBE-MARK-MASQ -A KUBE-SEP-XRUO23GXY67LXLQN -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp -j DNAT --to-destination 10.244.102.90:80 -A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-XRUO23GXY67LXLQN
[root@xianchaomaster1 service]# iptables -t nat -S | grep KUBE-SEP-IIBDPNPJZXXASELC -N KUBE-SEP-IIBDPNPJZXXASELC -A KUBE-SEP-IIBDPNPJZXXASELC -s 10.244.121.23/32 -m comment --comment "default/my-nginx-nodeport" -j KUBE-MARK-MASQ -A KUBE-SEP-IIBDPNPJZXXASELC -p tcp -m comment --comment "default/my-nginx-nodeport" -m tcp -j DNAT --to-destination 10.244.121.23:80 -A KUBE-SVC-J5QV2XWG4FEBPH3Q -m comment --comment "default/my-nginx-nodeport" -j KUBE-SEP-IIBDPNPJZXXASELC
修改kube-proxy配置文件的方法
[root@master01 ~]# kubectl get cm -n kube-system NAME DATA AGE calico-config 4 132d coredns 1 133d extension-apiserver-authentication 6 133d kube-proxy 2 133d kubeadm-config 2 133d kubelet-config-1.18 1 133d
[root@master01 ~]# kubectl edit cm -n kube-system kube-proxy
ServiceIP为什么无法ping通
因为没有具体的设备进行响应,他只是提供一个包过滤防火墙,也就是四层的功能
查看iptables的转发表
[root@master01 ~]# iptables-save |grep 30202
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/web:" -m tcp --dport 30202 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/web:" -m tcp --dport 30202 -j KUBE-SVC-BIJGBSD4RZCCZX5R
[root@master01 ~]# iptables-save |grep KUBE-SVC-BIJGBSD4RZCCZX5R
:KUBE-SVC-BIJGBSD4RZCCZX5R - [0:0]
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/web:" -m tcp --dport 30202 -j KUBE-SVC-BIJGBSD4RZCCZX5R
-A KUBE-SERVICES -d 10.102.52.147/32 -p tcp -m comment --comment "default/web: cluster IP" -m tcp --dport 80 -j KUBE-SVC-BIJGBSD4RZCCZX5R
-A KUBE-SVC-BIJGBSD4RZCCZX5R -m comment --comment "default/web:" -j KUBE-SEP-2K47U5U2G7Z3MFI7
可以看到为Service创建的转发规则
启用IPvs:
[root@master01 ~]# lsmod|grep ip_vs #开机启动模块 [root@master01 ~]# cat>>/etc/rc.local <<x > modprobe -- ip_vs > modprobe -- ip_vs_rr > modprobe -- ip_vs_wrr > modprobe -- ip_vs_sh > modprobe -- nf_conntrack_ipv4 > x
因为使用的kubeadm部署的,所以修改kube-proxy的配置文件,需要修改configmap
[root@master01 ~]# kubectl get cm -n kube-system NAME DATA AGE calico-config 4 11d coredns 1 11d extension-apiserver-authentication 6 11d kube-proxy 2 11d kubeadm-config 2 11d kubelet-config-1.18 1 11d
进行修改,找到“mode”默认是iptabls
[root@master01 ~]# kubectl edit cm kube-proxy -n kube-system
ipvs: excludeCIDRs: null minSyncPeriod: 0s scheduler: "" strictARP: false syncPeriod: 0s tcpFinTimeout: 0s tcpTimeout: 0s udpTimeout: 0s kind: KubeProxyConfiguration metricsBindAddress: "" mode: "ipvs" nodePortAddresses: null oomScoreAdj: null
修改完成后,还不能直接就使用到ipvs,还需要重新构建一下ipvs
[root@master01 ~]# kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE calico-kube-controllers-59877c7fb4-qncr7 1/1 Running 2 11d calico-node-p79hh 1/1 Running 4 11d calico-node-qh997 1/1 Running 3 11d calico-node-r7mfk 1/1 Running 2 11d coredns-66bff467f8-4sbx4 1/1 Running 3 11d coredns-66bff467f8-8xwlm 1/1 Running 3 11d etcd-master01 1/1 Running 2 11d kube-apiserver-master01 1/1 Running 5 11d kube-controller-manager-master01 1/1 Running 5 11d kube-proxy-dltmj 1/1 Running 2 11d kube-proxy-tdcj2 1/1 Running 4 11d kube-proxy-vn2vv 1/1 Running 3 11d kube-scheduler-master01 1/1 Running 5 11d
把node2节点的kube-proxy修改为ipvs模式
[root@master01 ~]# kubectl get pod -n kube-system -o wide kube-proxy-dltmj 1/1 Running 2 11d 192.168.1.5 master01 <none> <none> kube-proxy-tdcj2 1/1 Running 4 11d 192.168.1.7 node02 <none> <none> kube-proxy-vn2vv 1/1 Running 3 11d 192.168.1.6 node01 <none> <none>
[root@master01 ~]# kubectl delete pod kube-proxy-tdcj2 -n kube-system pod "kube-proxy-tdcj2" deleted
进行验证node proxy是否改为ipvs模式,下载ipvsadm工具
[root@master01 ~]# yum install -y ipvsadm [root@node02 ~]# yum -y install ipvsadm
进行查看
[root@node02 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.0.1:30001 rr
-> 10.244.241.78:8443 Masq 1 0 0
TCP 192.168.1.7:30001 rr
-> 10.244.241.78:8443 Masq 1 0 0
TCP 192.168.122.1:30001 rr
-> 10.244.241.78:8443 Masq 1 0 0
TCP 10.96.0.1:443 rr
-> 192.168.1.5:6443 Masq 1 0 0
TCP 10.96.0.10:53 rr
-> 10.244.241.73:53 Masq 1 0 0
-> 10.244.241.75:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 10.244.241.73:9153 Masq 1 0 0
-> 10.244.241.75:9153 Masq 1 0 0
TCP 10.100.25.41:8000 rr
-> 10.244.241.76:8000 Masq 1 0 0
而master的proxy没有修改为ipvs所以没有的
[root@master01 ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
可以看一下网卡,ipvs生成一个虚拟网卡
9: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN link/ether ca:6c:f5:71:9c:24 brd ff:ff:ff:ff:ff:ff inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.100.25.41/32 brd 10.100.25.41 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.103.95.133/32 brd 10.103.95.133 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.102.52.147/32 brd 10.102.52.147 scope global kube-ipvs0 valid_lft forever preferred_lft forever
在应用比较多的时候,切换ipvs比iptables数据包转发效果更好一些

评论