关于 LVS 系列其他文章的传送门

一、负载均衡器¶
为了缓解集群中服务器的单点压力,生产环境中会使用各种各样的负载均衡器。
负载均衡器工作方式¶
市面上比较流行的负载均衡器大体可以分为 两种工作方式
1. 基于通信子网(4层调度器):
数据报文到达内核,由内核根据 3、4 层的信息进行调度。在这里加入一个软件机制,对 3、4 层报文进行调度后,再发出,报文不会进入用户空间。
2. 基于资源子网(7层调度器):
数据报文通过路由、内核最后到用户空间,由工作在用户空间的应用程序,对报文拆包进行分析后,再调度发出。
可以实现负载均衡的产品¶
1. 软负载:
- 4层:LVS,Nginx(stream)伪,HAProxy(modetcp)伪,…
- 7层:Nginx,HAProxy,ATS,Envoy,Traefik,Kong,…
2. 硬负载:
- F5 公司的 --> BigIp
- NetScaler --> Citrix
二、LVS 介绍¶
Linux virtual server¶
本文主要介绍其中一种负载均衡器,那就是 LVS,目前已被收录进 Linux 内核。
LVS 全称为 Linux virtual server,作者是 章文嵩。
程序主要有两部分组成:
- IPVS:内核中框架。
- IPVSadm:用户空间中的工具。
工作方式¶
那么 LVS 是怎么工作的呢?我们先来回顾下 Linux 中 netfilter 的工作模式。
正常情况下:
- 数据报文到达 ①PREROUTING,先判断目的地是否为本机。
- 如果是,由 ②INPUT 进入用户空间发给程序。
- 如果不是本机,则从 ③FORWORD 转发出去。
LVS:
- 数据报文到达 ①PREROUTING,先判断目的地是否为本机。
- 如果不是本机,则从 ③FORWORD 转发出去。
- 如果是,报文进入 ②INPUT 时,LVS 会强行拦截,LVS 就工作在 ②INPUT 上。
- 经过 LVS 一系列的调度计算后,不发给用户空间,而是直接转发给 ⑥POSTROUTING 离开本机。

LVS 中的一些术语¶
-
Client 客户端:
- CIP (Client Ip):客户端 IP。
-
Director 调度器:
- VIP (virtual Ip):调度器公开访问的 IP。
- DIP (Director Ip):调度器中用于和 Realserver 通信的 IP。
-
Realserver 服务器:
- RIP (Realserver Ip):真正运行业务的服务器。
在工作模式中对应的位置:

LVS 的十种调度算法¶
LVS 共有 10 种调度算法,都是内建在内核中的模块。

主要有两类:静态算法 和 动态算法
1. 静态算法¶
主要关注起点公平:仅根据算法本身和请求报文特征进行调度:
-
RR (轮叫调度):
- 原理:通过“轮叫"调度算法将外部请求按顺序轮流分配到 server。
- 不足:均等地对待每一台服务器,而不管服务器上实际的连接数和系统负载。
-
WRR (加权轮叫):
- 根据不同服务器设置的权重来调度访问请求。
- 适用场景:短连接、无状态的服务,如 html、css 等纯静态资源。
-
SH (源地址散列):
- 原理:源 IP 做 hash 后,通过取模方式计算,选出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器。
- 适用场景:在没有使用 session 共享的又需要保存 session 的环境下(如电子商务网站)。
- 不足:多用户会识别为 1 个 IP。
-
DH (目标地址散列):
- 原理:目标 IP 做 hash 后,通过取模方式计算,选出对应的服务器,若该服务器是可用的且未超载,将请求发送到该服务器。
- 适用场景:主要用于缓存服务器的场景。
取模方式详解
2. 动态算法¶
主要关注结果公平:额外考虑后端各 RS 的当前负载状态:
-
LC (最少链接):
- 原理:负载最低则优先级高,如负载一样选第一个。
- 算法:
overhead=activeconn*256+inactive(256的由来:活动连接对资源的消耗,是非活动资源的 256 倍) - 不足:没有考虑权重。
-
WLC (加权最少链接):LVS 默认调度算法
- 原理:具有较高权值的服务器将承受较大比例的活动连接负载。
- 算法:
overhead=(activeconn*256+inactive)/wtight。 - 适用场景:有状态,长连接服务; 服务器性能有差异的情况。
- 不足:当算法的结果一样时,会自上而下分配,这样可能会分配给权重小的,无法平均分配。
-
SED (最短的期望的延迟):
- 原理:基于 WLC 算法的,只是其计算方法不同。
- 算法:
overhead=(activeconn+1)*256/wtight - 不足:某种连接少时,性能高的忙死,性能低没有连接。
-
NQ (最少队列):
- 原理:无需队列,永不排队,如果有连接数为
0的 server,就直接分配。如果没有连接数为0的 server,则使用 SED 算法。
- 原理:无需队列,永不排队,如果有连接数为
-
LBLC (基于局部性的最少链接):
- 原理:根据请求的目标 IP 地址找出该目标 IP 地址最近使用的服务器,若该服务器是可用的且没有超载,将请求发送到该服务器。若服务器不存在,或者该服务器超载且有服务器处于一半的工作负载,则用“最少链接”的原则选出一个可用的服务器,将请求发送到该服务器。
-
LBLCR (带复制的基于局部性最少链接):
- 原理:它与 LBLC 算法的不同之处是它要维护从一个目标 IP 地址到一组服务器的映射,而 LBLC 算法维护从一个目标 IP 地址到一台服务器的映射。该算法根据请求的目标 IP 地址找出该目标 IP 地址对应的服务器组,按“最小连接"原则从服务器组中选出一台服务器,若服务器没有超载,将请求发送到该服务器。若服务器超载,则按“最小连接"原则从这个集群中选出一台服务器,将该服务器加入到服务器组中,将请求发送到该服务器。同时,当该服务器组有一段时间没有被修改,将最忙的服务器从服务器组中删除,以降低复制的程度。
LVS 的四种调度模式¶
1. NAT¶
多目标 IP 的 DNAT,通过将请求报文中的目标地址和目标端口修改为某个挑出的 RS 的 RIP 和 PORT 实现转发。
工作方式:
- (1). Client 发请求报文给 Director,此时报文的
目标 IP是 VIP。 - (2). Director 根据算法选出 Realserver,把报文的
目标 IP改为 RIP。 - (3). Realserver 收到报文后处理请求,然后把响应报文发回给 Client 的 CIP,但是 Realserver 并没有 CIP 的路由,所以 Realserver 的网关需要指向 DIP。
- (4). Director 收到响应报文后,根据连接追踪机制,把
源 IP从 RIP 改为 VIP 后,发给 Client。 - (5). Client 收到响应报文,此时
源 IP是 VIP,目标 IP是 CIP。

要求:
- RS 的网关必须指向 DIP。
- 因为 Realserver 的网关要指向 DIP,所以 RIP 和 DIP 必须使用同一个 IP 网络,也就是同一个 VLAN,不能跨网段。
- Director 必须是 Linux 系统,但 RS 可以是任意系统。
优点:
- 支持端口映射。
- 节省公有 IP 地址。
- 使用 nat 另外一个好处就是后端的主机相对比较安全。
缺点:
- 请求和响应报文都要经过 Director 转发,极高负载时,Director 可能成为系统瓶颈。
2. DR¶
上面说过,因为 NAT 的调度模式,来回都需要经过 Director,在负载极高时,Director 就会成为瓶颈。而 DR 刚好解决了这个问题。
DR 的调度模式是通过在请求报文中,重新封装一个二层网络的 MAC 首部 再转发。源 MAC 是 DIP 所在的接口的 MAC,目标 MAC 是某挑选出的 Realserver 的 RIP 所在接口的 MAC 地址,而 源 IP/PORT,以及 目标 IP/PORT 均保持不变。
这样 Realserver 收到请求后交给程序处理,到此和 NAT 报文路线是一样的,但是程序处理完后,Realserver 不用再发回 Director,而是自己直接发回给 Client,甚至走的路由和来时都可以不一样,只要能到达 Client 就可以,这样大大缓解了高负载下 Director 的压力。
就像这样子:NAT 和 DR 调度路线的对比

但是这样会带来一个新的问题,Client 请求报文的 目标 IP 是 VIP,所以响应报文的 源 IP 也应该是 VIP,但是由于 Realserver 处理完请求没有发回 Director,而是直接发给 Client,所以 Client 收到的报文的 源 IP 却是 RIP。这下麻烦了。
给我答案的人并不是我当初问的人,我不信可他。
所以为了解决这个问题,DR 调度模式有个先提条件:那就是 VIP 必须也同时配置在所有 Realserver 的网络接口上,这样发回 Client 的报文 源 IP 才能和来时的一样 VIP。
那么在同一个网络中,就有 3 相同的 VIP,这下不就 IP 冲突了嘛,所以怎样才能防止 IP 冲突呢?

首先分析 IP 冲突会发生在何时:
- ARP 通告:服务器加入一个网络时,会通过广播发送 ARP 通告,
我的 IP 地址是 XXX,对应的 MAC 地址是 XXX。默认情况下,为了让大家充分了解自己,如果一个服务器上配置了多个不同网段的 IP,则会把自己所拥有的每一个 IP 地址的每一个对应的 MAC 信息通告到每一个网络,默认 ARP 信息缓存 5 分钟。 - ARP 查询:交换机转发报文时,会广播 ARP 查询,
谁的 IP 地址是 XXX 啊,给我你的 MAC 地址吧。
所以根据这两个关键点,解决办法有三个:
- 交换机做
IP <--> MAC绑定,缺点是灵活性差,LVS 如果发生变化需要更改。 - 使用
ARPtables,类似于iptables的软件,缺点就是需要安装额外的软件。 - 通过修改内核参数的方式,让服务器不发送
ARP 通告,和不应答ARP 查询,比较轻量,推荐 使用。
工作方式:

要求:
- RS 的 RIP 既可以使用私网地址,也可以是公网地址。
- 因为通过 MAC 转发,RS 跟 Dirctory 要在同一物理网络内(不能由路由器分隔,因为 VIPServer 要通过封装 MAC 地址到 RIPServer)。
- RIP 的网关不能指向 DIP,以确保响应报文不会经由 Director。
- 请求报文要经由 Director,但响应不能经由 Director,而是由 RS 直接发往 Client。
优点:
- 缓解了 Director 节点的压力。
缺点:
- 不支持端口映射。
3. TUN¶
无论是 NAT,还是 DR,都有一个共同的缺点,就是 Director 和 所有的 Realserver 必须同在一个网络内,这样是无法实现异地容灾的。
而下面要说的 TUN 模式,基于隧道封装技术。在 IP 报文的外面再包一层 IP 报文,即使 Director 和 Realserver 相隔十万八千里,只要能路由到达即可。
TUN 不修改请求报文的 IP 首部(源 IP 为 CIP,目标 IP 为 VIP),而是在原 IP 报文之外再封装一个 IP 首部(源 IP 是 DIP,目标 IP 是 RIP),将报文发往挑选出的目标 RS,RS 处理完请求,直接响应给客户端(源 IP 是 VIP,目标 IP 是 CIP)。
工作方式:

要求:
- DIP,VIP,RIP 都应该是公网地址。
- RS 的网关不能,也不可能指向 DIP。
- 请求报文要经由 Director,但响应不能经由 Director。
缺点:
- 不支持端口映射
- Director 只接受进站请求,减少负载。
- RS 的 OS 必须支持隧道功能。
- 因为 MTU 限制,隧道技术会额外花费性能,增大开销。
- 必须人为指定最大 MTU 值,不能是 1500,必须留出隧道首部的位置。
4. FULL-NAT¶
由于 TUN 需要人为指定 MTU,而 DR 又要求每个 RS 都需要改内核参数,而且拓扑很独特,所以,最简单的还是 NAT 类型。
但是 NAT 中各 RS 的网关必须指向 DIP ,中间不能有网关隔离,所以 NAT 不能较远距离的传输,为了解决这样的问题,便研发了 FULL-NAT 类型。
FULL-NAT 和 NAT 类型的区别并不大,NAT 是修改 目标 IP,而 FULL-NAT 是既修改 目标 IP,也修改 源 IP。这样 RS 收到的数据报文的 源 IP 是 DIP,所以 RS 网关可以不用再指向 DIP,这样就大大增加了 NAT 的灵活性。
但是此类型默认是不支持的,需要为内核打补丁。
工作方式:

要求:
- VIP 是公网地址,RIP 和 DIP 是私网地址,且通常不在同一个三层 IP 网络,因此,RIP 的网关一般不会指向 DIP。
- RS 收到的请求报文源地址是 DIP,因此只能响应给 DIP,由 Director 将其发往 Client。
优点:
- 支持端口映射。
- 兼顾 NAT 的优点。
- 可以不在同一个三层 IP 网络,灵活性高。
缺点:
- 请求和响应报文都经由 Director,压力高时易成为瓶颈。
- 支持端口映射。
- 注意:此类型默认是不支持的,需要为内核打补丁。
三、LVS 的配置¶
LVS 配置起来还是很简单的,也是属于 90% 的概念 + 10% 的操作。
1. 安装 LVS¶
首先确认内核是否支持 ip_vs。

安装 LVS 用户空间的工具 ipvsadm。
1 | yum install ipvsadm |
查看安装了哪些文件 rpm -ql ipvdadm。
ipvsadm:
- 主程序包:
/usr/sbin/ipvsadm - 规则保存工具:
/usr/sbin/ipvsadm-save - 规则重载工具:
/usr/sbin/ipvsadm-restore - 配置文件:
/etc/sysconfig/ipvsadm-config - Unit 文件:
/usr/lib/systemd/system/ipvsadm.service
2. ipvsadm 命令的使用¶
管理集群服务的命令¶
- 增、改:
ipvsadm -A|E -t|u|f service-address:port [-s scheduler] [-p [timeout]] - 删:
ipvsadm -D -t|u|f service-address-A:添加集群-E:修改集群-D:删除集群-L:查看集群-t:tcpvip:port-u:udpvip:port-f:防火墙标记 MARK-s:调度算法,默认为WLC-C:清空所有规则-S:保存所有规则到文件-R:从文件载入所有规则-p:持久连接模式
管理集群服务上的 RS 的命令¶
- 增、改:
ipvsadm -a|e -t|u|f service-address -r server-address [-g|i|m] [-w weight] - 删:
ipvsadm -d -t|u|f service-address -r server-address-a:添加一个 RS 主机-e:修改一个 RS 主机-d:删除一个 RS 主机-L:查看 RS-m:masquereadLVSNAT 调度模式-g:GATAWAYDR 调度模式-i:IPIPTUN 调度模式-w:weight 权重
其他命令¶
ipvsadm
-L --rate:速率值-L --stats:总计数器--zero -Z [-t|u|f server-address]:清空上面两个计数器
四、构建 LVS 集群¶
NAT 的构建¶
我们使用 Docker 先来构建 LVS 中 NAT 的模型。
宿主机扮演 Client 角色,请求虚拟机扮演的 Dirctor,在虚机内部利用 Docker 创建 web1、2 两个容器作为 Realserver,由 Dirctor 向后调度到 Realserver。
架构图:

1. 配置容器¶
先创建 mybr0 网桥,网段是 10.10.0.0/24,网关为 10.10.0.1。
1 | [root@centos_1 ~]# docker network create -d bridge --subnet 10.10.0.0/24 --gateway 10.10.0.1 mybr0 |
创建 nginx 的测试页文件
1 | [root@centos_1 ~]# cat /data/nginx1/index.html |
创建容器 nginx1 和 nginx2,分别挂载存储卷,到刚才创建的主页文件目录 nginx1 和 nginx2下。指定使用刚才创建的 mybr0 网桥,并指定 IP 为 10.10.0.11 和 10.10.0.12。
1 | docker run -d --name nginx1 -v /data/nginx1/:/usr/share/nginx/html/ --network mybr0 --ip 10.10.0.11 nginx |
2. 配置 ipvsadm¶
安装 ipvsadm。
1 | [root@centos_1 ~]# yum install ipvsadm |
加载 IP_VS 内核模块。
1 | [root@centos_1 ~]# modprobe ip_vs |
创建集群。
1 | ipvsadm -A -t 192.168.50.11:80 -s wlc # 调度算法 wlc |
集群创建好了,使用宿主机访问 Director,可以看到已经可以访问,是轮询模式。
1 | le@DESKTOP-TTY:~$ while true ; do curl 192.168.50.11 ; sleep .2 ; done |
然后加上权重再看看效果
1 | ipvsadm -e -t 192.168.50.11:80 -r 10.10.0.11 -m -w3 # 权重为 3 |
再次使用宿主机访问,可以看到比例是 3:1
1 | le@DESKTOP-TTY:~$ while true ; do curl 192.168.50.11 ; sleep .2 ; done |
DR 的构建¶
DR 类型的构建使用 3 台虚拟机完成,Director,Realserver 1 和 Realserver 2。
| hostname | HOST IP | VIP |
|---|---|---|
| Director | host: 192.168.50.100 | ens33:0 192.168.50.11 |
| Realserver 1 | RIP: 192.168.50.12 | lo:0 192.168.50.11 |
| Realserver 2 | RIP: 192.168.50.13 | lo:0 192.168.50.11 |
1. 配置 Realserver¶
首先要确定防火墙和 SELinux 已经关闭!!!
经测试,VIP 必须配置在子接口上,如 lo:0。不能配置在 lo 上,不然会调度失败。
Realserver 的配置脚本,在 R1 和 R2 执行。
1 | !/bin/bash |
分别在 RS 1 和 RS 2 上安装 httpd,并放置首页文件,RS 就准备好了。
1 | RS 1 |
2. 配置 Director¶
首先要确定防火墙和 SELinux 已经关闭!!!
经测试,VIP 必须配置在子接口上,如 ens33:0,不然会调度失败,所以 Director 不能直接在物理网卡上配置 IP。需要有一个主机 IP 用来和外界通信,另一个 VIP 配置在子接口上。
Director 的配置脚本,在 Director 节点执行。
1 | !/bin/bash |
3. 测试访问¶
在一台客户端上访问 VIP,可以看到是轮询的方式。
1 | root@mirrors [08:18:50 PM] [~] |
五、防火墙标记和持久连接¶
1. 防火墙标记¶
FWM,是 firewall mark 的缩写,借助于防火墙标记来分类报文,而后 LVS 根据此防火墙标记来将相应报文归类到指定的集群服务中。使用 FWM 可将多个不同的应用定义为同一个集群服务来进行调度。
报文的 firewall mark 是在报文进入到 Director 的 PREROUTING 时打上的指定标记,随后 LVS 就根据此 mark 标记来进行调度。
打标记
1 | iptables -t mangle -A PREROUTING -d $vip -p $proto --dport $port -j MARK --set-mark MARK_NUM[1-99] |
基于 FWM 定义集群
1 | ipvsadm -A -f MARK_NUM -s SCHEDULER |
示例¶
配置 FWM
1 | http 和 sshd 服务定义为一类标签 |
从 Client 访问,查看效果。
1 | [root@Client ~]# curl 192.168.0.11 |
2. 持久连接¶
LVS 的持久连接是指,无论使用哪一种调度方法,都能保证在指定的时间范围内,将来自于同一个 IP 的请求始终被调度到同一个 RS 服务器上。
LVS 的持久连接配置参数为:
1 | ipvsadm -A|E -t|u|f service-address [-s scheduler] [-p [timeout]] |
持久连接的类型:
- PPC(端口持久):每个端口对应定义为一个集群服务,每集群服务单独调度。
- PCC(客户端持久):基于端口
0来定义集群服务,即客户端对 Director 上 VIP 的所有请求统统调度至后端主机,必须定义为持久模式,不然会报错Memory allocation problem。 - PFWM(防火墙标记持久):基于防火墙标记定义集群服务,可实现将多个端口上的应用统一调度,即所谓的 port Affinity
PPC 端口持久:
1 | ipvsadm -A -t 192.168.0.11:80 -s rr -p |
PCC 客户端持久:
1 | 注意 0 端口和 -p 选项需同时使用 |
PFWM 防火墙标记持久:
1 | ipvsadm -A -f 3 -s rr -p |
参考:
https://www.jianshu.com/p/a3d33f903d87
https://www.jianshu.com/p/8a61de3f8be9
