新搭建的 Kubernetes 集群如何承接外部访问的流量,是刚上手 Kubernetes 时常常会遇到的问题。 在公有云上,官方给出了比较直接的答案,使用 LoadBalancer 类型的 Service,利用公有云提供的负载均衡服务来承接流量,同时在多台服务器之间进行负载均衡。
而在私有环境中,如何正确的将外部流量引入到集群内部,却暂时没有标准的做法。 本文将介绍一种基于 IPVS 来承接流量并实现负载均衡的方法,供大家参考。在阅读本文前建议先了解文中相关基础知识点,推荐阅读下「
浅析从外部访问 Kubernetes 集群中应用的几种方式
」一文。
IPVS
IPVS 是 LVS 项目的一部分,是一款运行在 Linux kernel 当中的 4 层负载均衡器,性能异常优秀。 根据这篇文章的介绍,使用调优后的内核,可以轻松处理每秒 10 万次以上的转发请求。目前在中大型互联网项目中,IPVS 被广泛的使用,用于承接网站入口处的流量。
Kubernetes Service
Service
是 Kubernetes 的基础概念之一,它将一组 Pod 抽象成为一项服务,统一的对外提供服务,在各个 Pod 之间实现负载均衡。 Service 有多种类型,最基本的 ClusterIP 类型解决了集群内部访问服务的需求,NodePort 类型通过 Node 节点的端口暴露服务, 再配合上 LoadBalancer 类型所定义的负载均衡器,实现了流量经过前端负载均衡器分发到各个 Node 节点暴露出的端口, 再通过 IPtables进行一次负载均衡,最终分发到实际的 Pod 上这个过程。
在 Service 的 Spec 中,externalIPs 字段平常鲜有人提到,当把 IP 地址填入这个字段后,Kube-Proxy 会增加对应的 IPtables 规则,当有以对应 IP 为目标的流量发送到 Node节点时,IPtables将进行 NAT,将流量转发到对应的服务上。一般情况下,很少会遇到服务器接受非自身绑定 IP 流量的情况,所以 externalIPs 不常被使用,但配合网络层的其他工具,它可以实现给 Service 绑定外部 IP 的效果。
今天我们将使用 externalIPs 配合 IPVS 的 DR(Direct Routing )模式实现将外部流量引入到集群内部,同时实现负载均衡。
环境搭建
为了演示,我们搭建了 4 台服务器组成的集群。一台服务器运行 IPVS,扮演负载均衡器的作用。一台服务器运行 Kubernetes Master 组件,其他两台服务器作为 Node 加入到 Kubernetes 集群当中。搭建过程这里不详细介绍,大家可以参考相关文档,比如:「和我一步步部署 kubernetes 集群」。
所有服务器在 172.17.8.0/24 这个网段中,服务的 VIP 我们设定为 172.17.8.201。整体架构如下图所示:
接下来让我们来配置 IPVS 和 Kubernetes。
使用 externalIPs 暴露 Kubernetes Service
首先在集群内部运行 2 个 nginx Pod 用作演示。
$ kubectl run nginx --image=nginx --replicas=2
再将它暴露为 Service,同时设定 externalIPs 字段
$ kubectl expose deployment nginx --port 80 --external-ip 172.17.8.201
查看 IPtables 配置,确认对应的 IPtables 规则已经被加入。
$ sudo iptables -t nat -L KUBE-SERVICES -n
Chain KUBE-SERVICES (2 references)
target prot opt source destination
KUBE-SVC-4N57TFCL4MD7ZTDA tcp -- 0.0.0.0/0 10.3.0.156 /* default/nginx: cluster IP */ tcp dpt:80
KUBE-MARK-MASQ tcp -- 0.0.0.0/0 172.17.8.201 /* default/nginx: external IP */ tcp dpt:80
KUBE-SVC-4N57TFCL4MD7ZTDA tcp -- 0.0.0.0/0 172.17.8.201 /* default/nginx: external IP */ tcp dpt:80 PHYSDEV match ! --physdev-is-in ADDRTYPE match src-type !LOCAL
KUBE-SVC-4N57TFCL4MD7ZTDA tcp -- 0.0.0.0/0 172.17.8.201 /* default/nginx: external IP */ tcp dpt:80 ADDRTYPE match dst-type LOCAL
KUBE-SVC-NPX46M4PTMTKRN6Y tcp -- 0.0.0.0/0 10.3.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
KUBE-NODEPORTS all -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
配置 IPVS 实现流量转发
首先在 IPVS 服务器上,打开 ipv4_forward。
$ sudo sysctl -w net.ipv4.ip_forward=1
接下来加载 IPVS 内核模块。
$ sudo modprobe ip_vs
将 VIP 绑定在网卡上。
$ sudo ifconfig eth0:0 172.17.8.201 netmask 255.255.255.0 broadcast 172.17.8.255
再使用 ipvsadm 来配置 IPVS,这里我们直接使用 Docker 镜像,避免和特定发行版绑定。
$ docker run --privileged -it --rm --net host luizbafilho/ipvsadm
/ # ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
/ # ipvsadm -A -t 172.17.8.201:80
/ # ipvsadm -a -t 172.17.8.201:80 -r 172.17.8.11:80 -g
/ # ipvsadm -a -t 172.17.8.201:80 -r 172.17.8.12:80 -g
/ # ipvsadm
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.8.201:http wlc
-> 172.17.8.11:http Route 1 0 0
-> 172.17.8.12:http Route 1 0 0
可以看到,我们成功建立了从 VIP 到后端服务器的转发。
验证转发效果
首先使用 curl 来测试是否能够正常访问 Nginx 服务。
$ curl http://172.17.8.201
Welcome to nginx!
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
接下来在 172.17.8.11 上抓包来确认 IPVS 的工作情况。
$ sudo tcpdump -i any port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
04:09:07.503858 IP 172.17.8.1.51921 > 172.17.8.201.http: Flags [S], seq 2747628840, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 1332071005 ecr 0,sackOK,eol], length 0
04:09:07.504241 IP 10.2.0.1.51921 > 10.2.0.3.http: Flags [S], seq 2747628840, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 1332071005 ecr 0,sackOK,eol], length 0
04:09:07.504498 IP 10.2.0.1.51921 > 10.2.0.3.http: Flags [S], seq 2747628840, win 65535, options [mss 1460,nop,wscale 5,nop,nop,TS val 1332071005 ecr 0,sackOK,eol], length 0
04:09:07.504827 IP 10.2.0.3.http > 10.2.0.1.51921: Flags [S.], seq 3762638044, ack 2747628841, win 28960, options [mss 1460,sackOK,TS val 153786592 ecr 1332071005,nop,wscale 7], length 0
04:09:07.504827 IP 10.2.0.3.http > 172.17.8.1.51921: Flags [S.], seq 3762638044, ack 2747628841, win 28960, options [mss 1460,sackOK,TS val 153786592 ecr 1332071005,nop,wscale 7], length 0
04:09:07.504888 IP 172.17.8.201.http > 172.17.8.1.51921: Flags [S.], seq 3762638044, ack 2747628841, win 28960, options [mss 1460,sackOK,TS val 153786592 ecr 1332071005,nop,wscale 7], length 0
04:09:07.505599 IP 172.17.8.1.51921 > 172.17.8.201.http: Flags [.], ack 1, win 4117, options [nop,nop,TS val 1332071007 ecr 153786592], length 0
可以看到,由客户端 172.17.8.1 发送给 172.17.8.201 的封包,经过 IPVS 的中转发送给了 172.17.8.11 这台服务器,并经过 NAT 后发送给了 10.2.0.3 这个 Pod。返回的封包不经过 IPVS 服务器直接从 172.17.8.11 发送给了 172.17.8.1。 说明 IPVS 的 DR 模式工作正常。重复多次测试可以看到流量分别从 172.17.8.11 和 172.17.8.12 进入,再分发给不同的 Pod,说明负载均衡工作正常。
与传统的 IPVS DR 模式配置不同的是,我们并未在承接流量的服务器上执行绑定 VIP,再关闭 ARP 的操作。 那是因为对 VIP 的处理直接发生在 IPtables上,我们无需在服务器上运行程序来承接流量,IPtables 会将流量转发到对应的 Pod 上。
使用这种方法来承接流量,仅需要配置 externalIPs 为 VIP 即可,无需对服务器做任何特殊的设置,使用起来相当方便。
总结
在本文中演示了使用 IPVS 配合 externalIPs 实现将外部流量导入到 Kubernetes 集群中,并实现负载均衡的方法。 希望可以帮助大家理解 IPVS 和 externalIPs 的工作原理,以便在恰当的场景下合理使用这两项技术解决问题。 实际部署时,还需要考虑后台服务器可用性检查,IPVS 节点主从备份,水平扩展等问题。在这里就不详细介绍了。
在 Kubernetes 中还有许多与 externalIPs 类似的非常用功能,有些甚至是使用 Annotation 来进行配置,将来有机会再进一步分享。
来源:极术
原文:http://t.cn/RX8vzvC
题图:来自谷歌图片搜索
版权:本文版权归原作者所有
今日思想
太想要一样东西,就是失去的开始,得不到,会朝思暮想,得到了,又害怕失去,一把沙抓的越紧,流失的越多,一个人,抱的越用力,他走的越快,当你张开双臂,两手空空,你怀里拥抱的是整个世界,在你什么都不想要的时候,得到的一切都是意外之喜。
——
一禅小和尚