LVS NAT模式和DR模式搭建实现负载均衡

1、LVS简介

LVS的英文全称是Linux Virtual Server,即Linux虚拟服务器。它是我们国家的章文嵩博士的一个开源项目。在linux内存2.6中,它已经成为内核的一部分,在此之前的内核版本则需要重新编译内核。

LVS主要用于多服务器的负载均衡。LVS由用户空间的ipvsadm和内核空间的IPVS组成,ipvsadm用来定义规则,IPVS利用ipvsadm定义的规则工作。它工作在网络层,可以实现高性能,高可用的服务器集群技术。它廉价,可把许多低性能的服务器组合在一起形成一个超级服务器。它易用,配置非常简单,且有多种负载均衡的方法。它稳定可靠,即使在集群的服务器中某台服务器无法正常工作,也不影响整体效果。另外可扩展性也非常好。

LVS工作原理图

在这里插入图片描述
PREROUTING:数据包进入路由表之前
INPUT:通过路由表后目的地为本机
FORWARD:通过路由表后,目的地不为本机
OUTPUT:由本机产生,向外转发
POSTROUTIONG:发送到网卡接口之前

1.当客户端发起请求到达Director Server内核空间,首先会进入PREROUTING链。
2.检测数据包目的地址如果是本机地址,将数据包数据发往INPUT链,否则将发往FORWARD链处理。
3.LVS由用户空间的ipvsadm和内核空间的IPVS组成,ipvsadm用来定义规则,IPVS利用ipvsadm定义的规则工作。IPVS工作在INPUT链上,当数据包到达INPUT链,首先会被IPVS检查,检查数据包请求的目的地址及端口是否有规则,如果没有对其定义规则经过INPUT链发往用户空间,交由用户空间的进程对其处理。
4.如果数据包的请求目的地址及端口在定义规则里面,那么将按照定义的规则对数据报文请求源的IP地址(或MAC地址和端口号)为后端接收请求服务器配置,进行更改后,发送POSTROUTIONG链。
5.最后由POSTROUTIONG链发往后端服务器处理。

LVS组成部分

1.Load Balancer:负载均衡层。这是LVS的核心部分,它好比我们网站MVC模型的Controller。它负责将客户的请求按照一定的算法分发到下一层不同的服务器进行处理,自己本身不做具体业务的处理。另外该层还可用监控下一层的状态,如果下一层的某台服务器不能正常工作了,它会自动把其剔除,恢复后又可用加上。该层由一台或者几台Director Server组成。

2.Server Array:服务器群组层,该层负责具体业务。可有WEB Server、mail Server、FTP Server、DNS Server等组成。注意,其实上层的Director Server也可以当Real server用的。

3.Shared Storage:数据共享层。主要是提高上一层数据和为上一层保持数据一致。

LVS常见术语
名称 解释
ipvsadm 用户空间的命令行工具,用来管理集群服务和集群服务的RS等
IPVS 工作于内核上的netfilter INPUT HOOK之上的程序,可根据用户定义的集群实现请求转发
VS Virtual Server 虚拟服务器
Director, Balancer 负载均衡器、分发器
RS Real Server 后端接收请求服务器
CIP Client IP 客户端IP
VIP Diector Virtual IP 负载均衡器虚拟IP
DIP Diector 负载均衡器IP
RIP Real Server IP 后端处理请求服务器IP
LVS工作模式

NAT : 修改请求报文的目标IP,多目标IP的DNAT
DR : 操纵封装新的MAC地址
TUN : 在原请求IP报文之外新加一个IP首部
FULLNAT : 修改请求报文的源和目标IP
(加粗的主要说明)

2、NAT工作模式

NAT(Network Address Translation): 网络地址转换模式。地址转换器有能被外界访问到的合法IP地址,它修改来自专有网络的流出包的地址。外界看起来包是来自地址转换器本身,当外界包送到转换器时,它能判断出应该将包送到内部网的哪个节点。优点是节省IP 地址,能对内部进行伪装;缺点是效率低,因为返回给请求方的数据包经过调度器。在这里插入图片描述
数据包流向分析:
1.客户端发起请求数据包到Director Server内核空间,首先进入PREROUTING链。
2.PREROUTING链检测数据包请求目的IP地址是本机,将数据包发送到INPUT链处理。
3.INPUT链中IPVS对其检查请求的服务是否有定义集群服务规则,如果有则修改请求目的IP地址为RIP,将数据包发往POSTROUTING链。
4.POSTROUTING选择链路,将数据包转发到Real Server。
5.Real Server接收到Rirector Server转发的数据包请求,发现请求目的IP是自己的,并且本地有这个程序服务,从而处理构建响应报文,返回给客户端Client,此时的源IP是RIP,目的IP是CIP。
6.Director Server在响应数据报文给客户端前,会将源IP地址改为VIP,这时数据包源IP为VIP,目的IP为CIP。

NAT模式小结

1.Real Server必须使用私有地址,网关必须指向DIP。
2.DIP和RIP必须在同一网段内。
3.所有请求和响应报文都需要经过Director Server,容易造成性能瓶颈。
4.支持端口映射。

NAT模式搭建Nginx负载均衡实例

实验配置信息

名称 网卡地址 软件环境
Director Server DIP : 192.168.37.100 (eth160) —— VIP : 192.168.14.5 (eth224) ipvsadm
Real Server 1 RIP : 192.168.14.10 (eth160) nginx
Real Server 2 RIP : 192.168.14.20 (eth160) nginx
Real Server 3 RIP : 192.168.14.30 (eth160) nginx

(所有Server都需要关闭iptables防火墙和关闭selinux)

Director Server 配置

1、添加VIP实体网卡接口 eth224

[root@localhost network-scripts]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.37.100  netmask 255.255.255.0  broadcast 192.168.37.255
        inet6 fe80::b3c9:b9e1:88ca:5ef1  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:82:4e:a3  txqueuelen 1000  (Ethernet)
        RX packets 337  bytes 29305 (28.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 240  bytes 30685 (29.9 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens224: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.14.5  netmask 255.255.255.0  broadcast 192.168.14.255
        inet6 fe80::4f2f:5904:b464:131a  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:82:4e:ad  txqueuelen 1000  (Ethernet)
        RX packets 181  bytes 15780 (15.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 119  bytes 9270 (9.0 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

2、添加ipvsadm规则

[root@localhost network-scripts]# ipvsadm -A -t 192.168.37.100:80 -s wrr
[root@localhost network-scripts]# ipvsadm -a -t 192.168.37.100:80 -r 192.168.14.10:80 -w 1 -m
[root@localhost network-scripts]# ipvsadm -a -t 192.168.37.100:80 -r 192.168.14.20:80 -w 1 -m
[root@localhost network-scripts]# ipvsadm -a -t 192.168.37.100:80 -r 192.168.14.30:80 -w 1 -m
[root@localhost network-scripts]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.37.100:80 wrr
  -> 192.168.14.10:80             Masq    1      0          0         
  -> 192.168.14.20:80             Masq    1      0          0         
  -> 192.168.14.30:80             Masq    1      0          0 

3、开启路由转发功能

[root@localhost network-scripts]# cat /proc/sys/net/ipv4/ip_forward
0
[root@localhost network-scripts]# echo 1 > /proc/sys/net/ipv4/ip_forward
Real Server 配置

1、安装nginx及开启服务

[root@localhost ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1213/master         
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      1419/nginx: master  
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1123/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      1213/master         
tcp6       0      0 :::80                   :::*                    LISTEN      1419/nginx: master  
tcp6       0      0 :::22                   :::*                    LISTEN      1123/sshd  

2、修改网络网关指向Director Server的VIP 重启网卡服务

[root@localhost ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens160

TYPE="Ethernet"
PROXY_METHOD="none"
BROWSER_ONLY="no"
BOOTPROTO="static"
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
IPV6_ADDR_GEN_MODE="stable-privacy"
NAME="ens160"
UUID="b1e89bbb-1479-445c-9421-1269e84c0710"
DEVICE="ens160"
ONBOOT="yes"
IPADDR=192.168.14.10
NETMASK=255.255.255.0
GATEWAY=192.168.14.5
[root@localhost ~]# systemctl restart network 
验证流程
[root@localhost network-scripts]# curl http://192.168.37.100
web2 192.168.14.20
[root@localhost network-scripts]# curl http://192.168.37.100
web1 192.168.14.10
[root@localhost network-scripts]# curl http://192.168.37.100
web3 192.168.14.30
[root@localhost network-scripts]# curl http://192.168.37.100
web2 192.168.14.20

(提前修改了每台Real Server的nginx的index.html以便测试直观)

3、DR工作模式

DR:Direct Routing 直接路由
LVS DR : 请求服务器MAC地址转换
在这里插入图片描述
如上图所示,Director Server和Real Server都配置同一个IP(即VIP),Director将该IP配置在对外的eth网卡的上,RealServer则配置在内部本地的lo网卡上。设置内核参数arp_ignore为1(目的是让数据包发出arp广播请求时,只有Driector Sercer 响应,因为Real Server存在同IP会将arp请求也发过去),所有Real Server对arp请求保持静默。而Director Server收到数据包后根据调度算法,选择对应的Real Server,将目标MAC地址修改为Real Server的MAC地址,并将数据包发送到Real Server。这时Real Server收到数据包处理返回响应数据报文,因为Real Server Lo网卡上也有配置VIP,设置内核参数arp_announce为2(目的是让数据包相应回客户端时,可以修改其源目标地址为VIP地址,不然返回的是Real Server的RIP客户端就不认识从而丢弃),返回数据包给客户端。

数据包流转过程

在这里插入图片描述
1.用户访问网站请求时,通过DNS域名解析得到目的IP为VIP,目的端口为80端口,于是客户端和VIP80端口建立连接。当请求到达VIP所属局域网内,在同一网段内,两个主机通信是靠二层的物理地址而不是IP地址,因此需要IP地址转成物理地址。因此会发起ARP请求,获取VIP服务器对应的MAC地址。(Linux有个特性,如果两台Real Server网卡上也有此VIP的地址也会响应ARP请求),但是我们不想Real Server响应请求,只想通过Rirector Server来想要arp请求。所以需要修改RS的内核参数arp_ignore为1,让Real Server保持静默不响应Arp请求。
2.当用户请求到达Director Server,首先会到达内核空间的PREROUTING链,此时请求报文的源IP是CIP,目的IP是VIP。
3.在PREROUTING链检测报文数据请求的目标IP是本机则将数据包发送至INPUT链。
4.IPVS对比数据包请求的服务是否为集群服务,若是,将请求报文中的源MAC地址改成DIP的MAC地址,目标MAC地址改成RIP的MAC地址。然后将数据包发送给POSTROUTING链,注意请求报文源IP和目的IP并未修改,修改的是请求的源MAC地址和目的MAC地址。
5.由于DS和RS在同一网络中,所有是通过第二层来传输。在POSTROUTING链检查请求的目的MAC地址是RIP的MAC地址,将数据包发至Real Server。
6.Client的请求被Director转发到Real Server后,Real Server发现自己lo网卡上有配置VIP(请求的目标IP正是VIP),所以接收请求并处理,生成响应数据报文返回,Linux默认会使用Real Server上的实体网卡eth的RIP作为响应报文的源IP地址,但是客户端收到后发现并没有请求过这个地址会将数据包丢弃,所以需要修改内核参数arp_announce为2,目的是修改响应报文的源IP为VIP。
7.响应报文通过二层链路传输,最终送达至客户端。

DR模式小结

1.客户端请求数据包在Director Server上对目的MAC地址修改进行转发,请求源IP地址和目的IP地址不做任何改动。
2.修改Real Server的内核参数arp_ignore为1抑制ARP请求的响应,达到只能通过Director Server响应从而实现负载均衡。
3.修改Real Server的内核参数arp_announce为2修改响应报文的源IP为VIP,使客户端能够确认数据包为源发起请求响应的数据包。
4.因为DR模式是通过改写MAC地址进行转发的,所以Director Server和Real Server必须在同一物理网络内。
5.所有请求报文通过Director Server进行处理转发,但是响应报文不能经过Director Server。
6.所有Real Server的lo接口上必须配置VIP地址。
7.不支持IP地址转换,也不支持端口映射。
8.Real Server的网关不能指向DIP(同NAT模式不同)。

DR模式搭建Mysql负载均衡实例

实验配置信息

名称 网卡地址 软件环境
Director Server DIP : 192.168.14.110 (eth160) —— VIP : 192.168.14.100 (eth160:0) ipvsadm
Real Server 1 RIP : 192.168.14.70 (eth160) —— VIP : 192.168.14.100 (lo:0) mysql
Real Server 2 RIP : 192.168.14.80 (eth160) —— VIP : 192.168.14.100 (lo:0) mysql
Client Server CIP : 192.168.14.10 (eth160) mysql

(所有Server都需要关闭iptables防火墙和关闭selinux)

Director Server 配置

1、在Director Server的物理网卡eth160上添加网卡别名eth160:0 (即VIP)

[root@localhost ~]# ifconfig ens160:0 192.168.14.100/24 up
[root@localhost ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.14.110  netmask 255.255.255.0  broadcast 192.168.14.255
        inet6 fe80::330f:985:5b6a:8660  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:10:57:22  txqueuelen 1000  (Ethernet)
        RX packets 442  bytes 38521 (37.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 323  bytes 32143 (31.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens160:0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.14.100  netmask 255.255.255.0  broadcast 192.168.14.255
        ether 00:0c:29:10:57:22  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

2、添加路由 指定访问 192.168.14.100 这个地址走 ens160:0 网卡接口转发出去

[root@localhost ~]# route add -host 192.168.14.100 dev ens160:0
[root@localhost ~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.14.2    0.0.0.0         UG    100    0        0 ens160
192.168.14.0    0.0.0.0         255.255.255.0   U     100    0        0 ens160
192.168.14.100  0.0.0.0         255.255.255.255 UH    0      0        0 ens160

3、添加ipvsadm规则

[root@localhost ~]# ipvsadm -A -t 192.168.14.100:3306 -s wrr
[root@localhost ~]# ipvsadm -a -t 192.168.14.100:3306 -r 192.168.14.70:3306 -g -w 1
[root@localhost ~]# ipvsadm -a -t 192.168.14.100:3306 -r 192.168.14.80:3306 -g -w 1
[root@localhost ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.14.100:3306 wrr
  -> 192.168.14.70:3306           Route   1      0          0         
  -> 192.168.14.80:3306           Route   1      0          0
Real Server 配置

1、安装mysql及开启服务

[root@localhost ~]# netstat -nltp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN      1665/mysqld         
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1044/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1131/master         
tcp6       0      0 :::22                   :::*                    LISTEN      1044/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      1131/master  

2、在本地网卡lo上添加网卡别名lo:0 (即VIP)

[root@localhost ~]# ifconfig lo:0 192.168.14.100 netmask 255.255.255.255 broadcast 192.168.14.100 up
[root@localhost ~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.14.80  netmask 255.255.255.0  broadcast 192.168.14.255
        inet6 fe80::4f44:9531:f37f:47b7  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:5e:09:6c  txqueuelen 1000  (Ethernet)
        RX packets 380  bytes 32158 (31.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 230  bytes 27165 (26.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 268  bytes 23300 (22.7 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 268  bytes 23300 (22.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo:0: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 192.168.14.100  netmask 255.255.255.255
        loop  txqueuelen 1000  (Local Loopback)

(netmask 设为255统一表示这个地址只和自己在一个网段 从而避免接收arp广播请求)

3、添加路由 指定访问 192.168.14.100 这个地址走 lo:0 网卡接口转发出去

[root@localhost ~]# route add -host 192.168.14.100 dev lo:0
[root@localhost ~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.14.2    0.0.0.0         UG    100    0        0 ens160
192.168.14.0    0.0.0.0         255.255.255.0   U     100    0        0 ens160
192.168.14.100  0.0.0.0         255.255.255.255 UH    0      0        0 lo

4、设置系统内核参数 更改arp自主响应和响应源地址的自定义
arp_ignore 置为1 (客户端发起的arp广播请求只会有Director Server进行响应,Real Server保持静默)
arp_announce 置为2 (更改响应数据包源IP地址自定义即VIP,默认使用实体网卡eth160IP地址)

[root@localhost ~]# cd /proc/sys/net/ipv4/conf/ens160/
[root@localhost ens160]# ll arp*
-rw-r--r-- 1 root root 0 3月  25 14:53 arp_accept
-rw-r--r-- 1 root root 0 3月  25 14:53 arp_announce
-rw-r--r-- 1 root root 0 3月  25 14:53 arp_filter
-rw-r--r-- 1 root root 0 3月  25 14:53 arp_ignore
-rw-r--r-- 1 root root 0 3月  25 14:53 arp_notify
[root@localhost ens160]# cat arp_ignore 
0
[root@localhost ens160]# cat arp_announce 
0
[root@localhost ens160]# echo 1 > arp_ignore 
[root@localhost ens160]# echo 2 > arp_announce 
[root@localhost ens160]# cd ../all/
[root@localhost all]# ll arp*
-rw-r--r-- 1 root root 0 3月  25 14:54 arp_accept
-rw-r--r-- 1 root root 0 3月  25 14:54 arp_announce
-rw-r--r-- 1 root root 0 3月  25 14:54 arp_filter
-rw-r--r-- 1 root root 0 3月  25 14:54 arp_ignore
-rw-r--r-- 1 root root 0 3月  25 14:54 arp_notify
[root@localhost all]# cat arp_ignore 
0
[root@localhost all]# cat arp_announce 
0
[root@localhost all]# echo 1 > arp_ignore 
[root@localhost all]# echo 2 > arp_announce

(默认只更改all里面的就可以了,all指所有网卡设备,当all/ 和{interface}/ 下两者同时比较,取较大一个值生效)

验证流程

1、Real Server设置mysql权限

mysql> GRANT ALL ON *.* TO lvs_user@'192.168.14.%' IDENTIFIED BY 'redhat';
Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

(允许lvs_user用户且在192.168.14.X网段下通过密码redhat访问所有数据库所有表*.* 刷新数据库权限)
2、测试数据准备 RS1 和 RS2 提前生成不同数据

#### RS1
mysql> select * from company.staff;
+------+------------+------+
| ID   | Name       | Age  |
+------+------------+------+
| 1001 | houguang   |   23 |
| 1002 | xuzhiqiong |   22 |
| 1003 | laocao     |   23 |
+------+------------+------+
3 rows in set (0.00 sec)
————————————————————————————————————
#### RS2
mysql> select * from company.staff;
+------+-------+------+
| ID   | Name  | Age  |
+------+-------+------+
| 3001 | wang  |   23 |
| 3002 | zhang |   24 |
| 3003 | li    |   25 |
+------+-------+------+
3 rows in set (0.00 sec)

3、使用Client Server测试连接mysql 查询数据

[root@localhost ~]# mysql -u lvs_user -p -h 192.168.14.100
Enter password: 
mysql> select * from company.staff;
+------+------------+------+
| ID   | Name       | Age  |
+------+------------+------+
| 1001 | houguang   |   23 |
| 1002 | xuzhiqiong |   22 |
| 1003 | laocao     |   23 |
+------+------------+------+
3 rows in set (0.00 sec)

mysql> quit
Bye
[root@localhost ~]# mysql -u lvs_user -p -h 192.168.14.100
mysql> select * from company.staff;
+------+-------+------+
| ID   | Name  | Age  |
+------+-------+------+
| 3001 | wang  |   23 |
| 3002 | zhang |   24 |
| 3003 | li    |   25 |
+------+-------+------+
3 rows in set (0.00 sec)

本文是自己需要提升一下自己对LVS NAT模式和DR模式知识储备,从而学习分析并分享学习心得顺便巩固所学,如有问题或者更好的意见和建议欢迎指出,共同进步~~~

猜你喜欢

转载自blog.csdn.net/Hou_guang/article/details/115176496