发布时间:2023-01-06 11:30
默认情况下,kube-proxy使用iptables实现service ip到后端pod的转换,可以参考之前写的这篇文章,分析了iptables规则。也可以使用ipvs来实现,今天搞一下后者,在现有k8s环境上修改下kube-proxy的配置即可。
kube-proxy的作用就是将访问service ip的报文转换成后端pod ip,相当于就是对报文做dnat,所以ipvs的dnat模式正好合适,简单说一下ipvs dnat原理。
如下图,client访问vip,报文到达director后,查找路由发现vip是本地的,走input流程,在input的hook点,ipvs生效,将vip转换成rip,对报文做完dnat后,将报文发给rs。
image.png
rs的响应报文目的ip为cip,源ip为rip,到达director后,查找路由发现不是本地的,走forward流程,在forward的hook点,ipvs生效,将rip转换成vip,对报文做完snat后,发给client。
image.png
这里的director就是配置ipvs的主机,vip也要配置在这个主机上。
可以通过下面的proc文件,查看ipvs的表项
root@master:~# cat /proc/net/ip_vs_conn
Pro FromIP FPrt ToIP TPrt DestIP DPrt State Expires PEName PEData
TCP AC12DB43 EDA8 0A600001 01BB C0A87A14 192B ESTABLISHED 898
TCP 0A600001 C626 0A600001 01BB C0A87A14 192B ESTABLISHED 883
TCP AC12DB44 AD66 0A600001 01BB C0A87A14 192B ESTABLISHED 898
TCP 0A600001 C68E 0A600001 01BB C0A87A14 192B ESTABLISHED 885
//其中关键字段含义如下
FromIP: client ip
FPrt: client port
ToIP:service ip
TPrt:service port
DestIP:real server ip
DPrt:real server port
Expires :表项超时时间
表项超时时间
//查看
root@master:~# ipvsadm -l --timeout
Timeout (tcp tcpfin udp): 900 120 300
//设置
root@master:~# ipvsadm --set 60 120 300
kube-proxy提供如下两个参数
--proxy-mode: 指定使用哪种模式,不指定的话默认使用iptables
--ipvs-scheduler:如果指定使用ipvs模式,此参数指定调度模式,不指定的话默认是round-robin
但是我这个k8s使用的kubeadm搭建的,kube-proxy的参数使用configmap映射,所以只需要修改configmap就行,步骤如下
//编辑configmap
kubectl edit configmap kube-proxy -n kube-system
//修改mode参数,从""改成ipvs
mode: ipvs
//如果想修改调度模式,可修改下面的参数
scheduler: ""
//删除所有的kube-proxy pod,重新启动的pod会使用最新的配置
kubectl get pod -n kube-system
kubectl delete pod -n kube-system
//查看kube-proxy pod的log
kubectl logs -n kube-system kube-proxy-xczpc | grep "Using ipvs Proxier"
ipvs生效后,node上会有如下几个变化
a. 每个node上,都会创建一个dummy类型的虚拟接口kube-ipvs0,并且配置了所有的service ip
6: kube-ipvs0: mtu 1500 qdisc noop state DOWN group default
link/ether 16:cd:66:87:d5:13 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.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0
valid_lft forever preferred_lft forever
root@master:~# ip route show table local dev kube-ipvs0
local 10.96.0.1 proto kernel scope host src 10.96.0.1
local 10.96.0.10 proto kernel scope host src 10.96.0.10
//首先安装 ipvsadm命令
root@master:~# apt install ipvsadm
//查看ipvs配置,其中Forward字段指定了ipvs模式(此例中Masq表示nat模式),
//Scheduler指定了调度模式(此例中 rr 表示round-robin模式)
root@master:~# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.0.1:443 rr
-> 192.168.122.20:6443 Masq 1 4 0
TCP 10.96.0.10:53 rr
-> 172.18.219.67:53 Masq 1 0 0
-> 172.18.219.68:53 Masq 1 0 0
TCP 10.96.0.10:9153 rr
-> 172.18.219.67:9153 Masq 1 0 0
-> 172.18.219.68:9153 Masq 1 0 0
UDP 10.96.0.10:53 rr
-> 172.18.219.67:53 Masq 1 0 0
-> 172.18.219.68:53 Masq 1 0 0
//查看下当前k8s中所有的service,可以和ipvs的配置完全对应起来
root@master:~# kubectl get svc -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 443/TCP 8d
kube-system kube-dns ClusterIP 10.96.0.10 53/UDP,53/TCP,9153/TCP 8d
下面使用一个小例子分析下ipvs在k8s中作用,拓扑图如下
image.png
使用此命令创建对应的pod: kubectl apply -f service.yaml
root@master:~# cat service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: client
spec:
selector:
matchLabels:
app: myapp1
replicas: 1
template:
metadata:
labels:
app: myapp1
spec:
nodeName: master
containers:
- name: nginx
image: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: server
spec:
selector:
matchLabels:
app: myapp
replicas: 2
template:
metadata:
labels:
app: myapp
spec:
nodeName: node1
containers:
- name: nginx
image: nginx
ports:
- containerPort: 2222
---
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 2222
targetPort: 2222
看一下变化
//虚拟网卡上增加了service ip 10.102.54.177
6: kube-ipvs0: mtu 1500 qdisc noop state DOWN group default
link/ether 16:cd:66:87:d5:13 brd ff:ff:ff:ff:ff:ff
...
inet 10.102.54.177/32 brd 10.102.54.177 scope global kube-ipvs0
valid_lft forever preferred_lft forever
//local路由表也增加了service ip 10.102.54.177信息
root@master:~# ip route show table local dev kube-ipvs0
...
local 10.102.54.177 proto kernel scope host src 10.102.54.177
//ipvs配置也增加了service ip 10.102.54.177信息
root@master:~# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
...
TCP 10.102.54.177:2222 rr
-> 172.18.166.134:2222 Masq 1 0 0
-> 172.18.166.135:2222 Masq 1 0 0
client访问service流程如下图所示
image.png
client pod的报文发出后,首先到达node上的calixxx接口,根据目的ip vip查找路由发现是本地的(这就是为什么每个service ip都配置在虚拟接口kube-ipvs0的原因,否则报文不会经过INPUT流程,也就不会被ipvs处理了),走input流程,ipvs在这里对报文做dnat,将目的ip vip换成server pod ip,根据server pod ip重新查找路由,后面的流程和iptables模式的就一样了,封装成ipip报文,发送出去。
响应报文流程如下,就不过多解释了
image.png
总结:ipvs在k8s中的应用,访问service的pod相当于client,每个node都相当于director,service后端pod相当于real server。
https://kubernetes.io/blog/2018/07/09/ipvs-based-in-cluster-load-balancing-deep-dive/
https://stackoverflow.com/questions/56493651/enable-ipvs-mode-in-kube-proxy-on-a-ready-kubernetes-local-cluster
https://blog.csdn.net/qq_36183935/article/details/90734936
也可参考:k8s kube-proxy ipvs - 简书 (jianshu.com)