LVS是Linux Virtual Server的简称,也就是Linux虚拟服务器, 是一个由章文嵩博士发起的自由软件项目,它的官方站点是www.linuxvirtualserver.org。现在LVS已经是 Linux标准内核的一部分,在Linux2.4内核以前,使用LVS时必须要重新编译内核以支持LVS功能模块,但是从Linux2.4内核以后,已经完>全内置了LVS的各个功能模块,无需给内核打任何补丁,可以直接使用LVS提供的各种功能。

目录

LVS的作用

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

LVS的体系结构

使用LVS架设的服务器集群系统有三个部分组成:

(1)最前端的负载均衡层,用Load Balancer表示;

(2)中间的服务器集群层,用Server Array表示;

(3)最底端的数据共享存储层,用Shared Storage表示;

在用户看来,所有的内部应用都是透明的,用户只是在使用一个虚拟服务器提供的高性能服务。

结构图

LVS负载均衡机制

(1)LVS是四层负载均衡,也就是说建立在OSI模型的第四层——传输层之上,传输层上有我们熟悉的TCP/UDP,LVS支持TCP/UDP的负载均衡。因为LVS是四层负载均衡,因此它相对于其它高层负载均衡的解决办法,比如DNS域名轮流解析、应用层负载的调度、客户端的调度等,它的效率是非常高的。

(2)LVS的转发主要通过修改IP地址(NAT模式,分为源地址修改SNAT和目标地址修改DNAT)、修改目标MAC(DR模式)来实现。

①NAT模式:网络地址转换

结构图

NAT(Network Address Translation)是一种外网和内网地址映射的技术。NAT模式下,网络数据报的进出都要经过LVS的处理。LVS需要作为RS(真实服务器)的网关。当包到达LVS时,LVS做目标地址转换(DNAT),将目标IP改为RS的IP。RS接收到包以后,仿佛是客户端直接发给它的一样。RS处理完,返回响应时,源IP是RS IP,目标IP是客户端的IP。这时RS的包通过网关(LVS)中转,LVS会做源地址转换(SNAT),将包的源地址改为VIP,这样,这个包对客户端看起来就仿佛是LVS直接返回给它的。客户端无法感知到后端RS的存在

②DR模式:直接路由

结构图

DR模式下需要LVS和RS集群绑定同一个VIP(RS通过将VIP绑定在loopback实现),但与NAT的不同点在于:请求由LVS接受,由真实提供服务的服务器(RealServer, RS)直接返回给用户,返回的时候不经过LVS。详细来看,一个请求过来时,LVS只需要将网络帧的MAC地址修改为某一台RS的MAC,该包就会被转发到相应的RS处理,注意此时的源IP和目标IP都没变,LVS只是做了一下移花接木。RS收到LVS转发来的包时,链路层发现MAC是自己的,到上面的网络层,发现IP也是自己的,于是这个包被合法地接受,RS感知不到前面有LVS的存在。而当RS返回响应时,只要直接向源IP(即用户的IP)返回即可,不再经过LVS

(3)DR负载均衡模式数据分发过程中不修改IP地址,只修改mac地址,由于实际处理请求的真实物理IP地址和数据请求目的IP地址一致,所以不需要通过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成为瓶颈。因此,DR模式具有较好的性能,也是目前大型网站使用最广泛的一种负载均衡手段。

配置准备

前端LVS采用主从模式,Nginx为后端realserver负债均衡。

服务 IP地址
LVS + keepalived + nginx 192.168.100.101
LVS + keepalived + nginx 192.168.100.102
Tomcat 192.168.100.103
Tomcat 192.168.100.104

配置主负载服务器

安装keeplived

yum安装

1
2
3
4
$ yum install -y curl gcc openssl-devel libnl3-devel net-snmp-devel  #安装依赖包
$ yum install -y keepalived
$ systemctl start keepalived
$ systemctl enable keepalived

编译安装

1.官网下载Keeplived的最新版本,解压并安装

1
2
3
4
5
6
7
$ wget https://www.keepalived.org/software/keepalived-2.0.7.tar.gz
$ tar xvf keepalived-2.0.7.tar.gz
$ cd keepalived-2.0.7
$ ./configure --prefix=/usr/local/keepalived
$ make && make install
$ systemctl start keepalived
$ systemctl enable keepalved

完成后会在以下路径生成:

1
2
3
/usr/local/etc/keepalived/keepalived.conf
/usr/local/etc/sysconfig/keepalived
/usr/local/sbin/keepalived

2.初始化及启动

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# keepalived启动脚本变量引用文件,默认文件路径是/etc/sysconfig/,也可以不做软链接,直接修改启动脚本中文件路径即可(安装目录下)
$ cp /usr/local/keepalived/etc/sysconfig/keepalived  /etc/sysconfig/keepalived 
 
# 将keepalived主程序加入到环境变量(安装目录下)
$ cp /usr/local/keepalived/sbin/keepalived /usr/sbin/keepalived
 
# keepalived启动脚本(源码目录下),放到/etc/init.d/目录下就可以使用service命令便捷调用
$ cp /usr/local/src/keepalived-2.0.7/keepalived/etc/init.d/keepalived  /etc/init.d/keepalived

# 将配置文件放到默认路径下
$ mkdir /etc/keepalived
$ cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf

配置keepalived

配置Master服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$ vim /etc/keepalived/keepalived.conf 
! Configuration File for keepalived

global_defs {    #lvs组设置
   router_id LVS1    #id_唯一标识
}
vrrp_instance VI_1 {    #vrrp设置,VI_1自定义名称
    state MASTER    #master设置,必须大写
    interface eth0    #网卡设置,绑定vip的子接口,lvs需要绑定在网卡上,realserver绑定在回环口。区别:lvs对访问为外,realserver为内不易暴露本机信息。
    virtual_router_id 51    #router_id,vrrp唯一标识,一致
    priority 100    #优先级,越大越优先
    advert_int 1    #组播信息发送间隔,两个节点设置必须一样
    authentication {    #设置验证信息,两个节点必须一致
        auth_type PASS
        auth_pass 123456
    }
    virtual_ipaddress {    #设置vip
        192.168.100.200
    }
}
virtual_server 192.168.100.200 80 {    #设置vip连接下的realserver
    delay_loop 1    #每1秒检测回环口
    lb_algo rr    #设置调度算法为默认rr轮询
    lb_kind DR    #设置lvs默认为DR直接路由模式。建议lvs都是内网ip部署,最后vip用路由器映射到外网,最为安全!
    protocol TCP    #启用TCP检测模式
    real_server 192.168.100.101 80 {    #realserver的ip地址
    weight 1    #权重,最大越高,lvs就越优先访问。
    TCP_CHECK {    #TCP模式检测后端连接
       connect_timeout 3    #超时3秒
       retry 2    #重连次数2次
       delay_before_retry 3    #重连间隔时间3秒
       connect_port 80    #检测后端realserver的端口
}
        }
    real_server 192.168.100.102 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 3
            retry 3
            delay_before_retry 3
            connect_port 80
          }
     }
}

配置Backup服务器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
$ vim /etc/keepalived/keepalived.conf 
! Configuration File for keepalived

global_defs {
   router_id LVS2
}
vrrp_instance VI_1 {
    state BACKUP
    interface eth0    
    virtual_router_id 51
    priority 80
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 123456     #与backup的认证密码
    }
    virtual_ipaddress {
        192.168.100.200
    }
}
virtual_server 192.168.100.200 80 {
    delay_loop 1
    lb_algo rr
    lb_kind DR
    protocol TCP
    real_server 192.168.100.101 80 {
    weight 1
    TCP_CHECK {
     connect_timeout 3
       retry 2
       delay_before_retry 3
       connect_port 80
}
        }
    real_server 192.168.100.102 80 {
        weight 1
        TCP_CHECK {
           connect_timeout 3
            retry 3
            delay_before_retry 3
            connect_port 80
          }
     }
}

安装ipvsadm

yum安装

1
$ yum -y install ipvsadm

查看是否加载ip_vs模块

1
2
3
4
$ lsmod | grep ip_vs
ip_vs                 145497  0 
nf_conntrack          139224  1 ip_vs
libcrc32c              12644  3 xfs,ip_vs,nf_conntrack

如果没有显示手动加载

1
$ modprobe  ip_vs

配置lvs网卡设置下的vip

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ vim /etc/init.d/lvs
#!/bin/bash  
#   
# Script to start LVS DR real server.   
# description: LVS DR real server   
# chkconfig: 3 67 22
#
   
.  /etc/rc.d/init.d/functions
VIP=192.168.100.200   #这里根据须要改为本身的VIP地址
host=`/bin/hostname`
case "$1" in  
start)   
       # Start LVS-DR real server on this machine.   
        /sbin/ifconfig lo down   
        /sbin/ifconfig lo up   
        echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore   
        echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce   
        echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore   
        echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
        /sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up  
        /sbin/route add -host $VIP dev lo:0
;;  
stop)
        # Stop LVS-DR real server loopback device(s).  
        /sbin/ifconfig lo:0 down   
        echo 0 > /proc/sys/net/ipv4/conf/lo/arp_ignore   
        echo 0 > /proc/sys/net/ipv4/conf/lo/arp_announce   
        echo 0 > /proc/sys/net/ipv4/conf/all/arp_ignore   
        echo 0 > /proc/sys/net/ipv4/conf/all/arp_announce
;;  
status)
        # Status of LVS-DR real server.  
        islothere=`/sbin/ifconfig lo:0 | grep $VIP`   
        isrothere=`netstat -rn | grep "lo:0" | grep $VIP`   
        if [ ! "$islothere" -o ! "isrothere" ];then   
            # Either the route or the lo:0 device   
            # not found.   
            echo "LVS-DR real server Stopped."   
        else   
            echo "LVS-DR real server Running."   
        fi   
;;   
*)   
            # Invalid entry.   
            echo "$0: Usage: $0 {start|status|stop}"   
            exit 1   
;;   
esac

运行脚本,并添加到开机启动

1
2
3
4
$ chmod +x /etc/init.d/lvs
$ service lvs start
$ chkconfig --add lvs
$ chkconfig lvs on 

安装nginx

yum安装nginx

1
2
3
$ yum -y install nginx
$ systemctl start nginx
$ systemctl enable nginx

编译安装nginx

官网下载nginx最新版本,并安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ wget https://nginx.org/download/nginx-1.17.0.tar.gz
$ tar -zxf nginx-1.17.0.tar.gz
$ cd nginx-1.17.0
#添加用户和组
$ groupadd www
$ useradd -g www www
$ ./configure \
    --user=www \
    --group=www \
    --prefix=/usr/local/nginx \
    --with-http_ssl_module \
    --with-http_stub_status_module \
    --with-http_realip_module \
    --with-threads
$ make && make install

验证安装

1
$ /usr/local/nginx/sbin/nginx -V

创建软连接

1
$ ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx

创建service脚本

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
$ vim /etc/init.d/nginx
#!/bin/sh
#
# nginx - this script starts and stops the nginx daemon
#
# chkconfig:   - 85 15
# description:  NGINX is an HTTP(S) server, HTTP(S) reverse \
#               proxy and IMAP/POP3 proxy server
# processname: nginx
# config:      /usr/local/nginx/conf/nginx.conf
# config:      /etc/sysconfig/nginx
# pidfile:     /usr/local/nginx/logs/nginx.pid
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0
nginx="/usr/local/nginx/sbin/nginx"
prog=$(basename $nginx)
NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"
[ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
lockfile=/var/lock/subsys/nginx
make_dirs() {
   # make required directories
   user=`$nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`
   if [ -z "`grep $user /etc/passwd`" ]; then
       useradd -M -s /bin/nologin $user
   fi
   options=`$nginx -V 2>&1 | grep 'configure arguments:'`
   for opt in $options; do
       if [ `echo $opt | grep '.*-temp-path'` ]; then
           value=`echo $opt | cut -d "=" -f 2`
           if [ ! -d "$value" ]; then
               # echo "creating" $value
               mkdir -p $value && chown -R $user $value
           fi
       fi
   done
}
start() {
    [ -x $nginx ] || exit 5
    [ -f $NGINX_CONF_FILE ] || exit 6
    make_dirs
    echo -n $"Starting $prog: "
    daemon $nginx -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}
stop() {
    echo -n $"Stopping $prog: "
    killproc $prog -QUIT
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $lockfile
    return $retval
}
restart() {
    configtest || return $?
    stop
    sleep 1
    start
}
reload() {
    configtest || return $?
    echo -n $"Reloading $prog: "
    killproc $nginx -HUP
    RETVAL=$?
    echo
}
force_reload() {
    restart
}
configtest() {
  $nginx -t -c $NGINX_CONF_FILE
}
rh_status() {
    status $prog
}
rh_status_q() {
    rh_status >/dev/null 2>&1
}
case "$1" in
    start)
        rh_status_q && exit 0
        $1
        ;;
    stop)
        rh_status_q || exit 0
        $1
        ;;
    restart|configtest)
        $1
        ;;
    reload)
        rh_status_q || exit 7
        $1
        ;;
    force-reload)
        force_reload
        ;;
    status)
        rh_status
        ;;
    condrestart|try-restart)
        rh_status_q || exit 0
            ;;
    *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"
        exit 2
esac

设置开机自启

1
2
3
4
$ chmod a+x /etc/init.d/nginx
$ service nginx start
$ chkconfig --add nginx
$ chkconfig nginx on

测试LVS

查看LVS信息

1
2
3
4
5
6
7
$ ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.100.200:80 rr
  -> 192.168.100.101:80           Route   1      0          0         
  -> 192.168.100.102:80           Route   1      0          0      

通过浏览器多次访问http://192.168.100.200

第一次访问

第二次访问

通过访问发现负载均衡生效

关闭Master服务器上的nginx服务

1
$ systemctl stop nginx

第二次访问

发现依然可以访问,高可用生效

配置动静分离

安装Tomcat

配置JDK

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ wget https://repo.huaweicloud.com/java/jdk/8u181-b13/jdk-8u181-linux-x64.tar.gz
$ mkdir /usr/local/java/
$ tar -zxvf jdk-8u181-linux-x64.tar.gz -C /usr/local/java/
$ vim /etc/profile
# 在末尾添加
export JAVA_HOME=/usr/local/java/jdk1.8.0_181
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

$ source /etc/profile
$ java -version

yum安装Tomcat

1
2
3
$ yum -y install tomcat
$ systemctl start tomcat
$ systemctl enable tomcat

编译安装Tomcat

官网下载Tomcat的最新版本,解压并安装

1
2
3
4
$ wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.46/bin/apache-tomcat-9.0.46.tar.gz
$ tar -zxvf apache-tomcat-9.0.0.M18.tar.gz
$ mkdir /usr/local/tomcat/
$ mv ./apache-tomcat-9.0.0.M18#usr/local/tomcat/

创建service脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ vim /etc/init.d/tomcat
#!/bin/bash
#
# tomcat startup script for the Tomcat server
#
# chkconfig: 345 80 20
# description: start the tomcat deamon
#
# Source function library
. /etc/rc.d/init.d/functions
prog=tomcat
JAVA_HOME=/usr/local/java/jdk1.8.0_171
export JAVA_HOME
CATALANA_HOME=/usr/local/tomcat
export CATALANA_HOME
case "$1" in
start)
    echo "Starting Tomcat..."
    $CATALANA_HOME/bin/startup.sh
    ;;
stop)
    echo "Stopping Tomcat..."
    $CATALANA_HOME/bin/shutdown.sh
    ;;
restart)
    echo "Stopping Tomcat..."
    $CATALANA_HOME/bin/shutdown.sh
    sleep 2
    echo -n "Starting Tomcat..."
    $CATALANA_HOME/bin/startup.sh
    ;;
*)
    echo "Usage: $prog {start|stop|restart}"
    ;;
esac

设置开机自启

1
2
3
4
chmod a+x /etc/init.d/tomcat
service tomcat start
chkconfig --add tomcat
chkconfig tomcat on

配置Nginx代理Tomcat

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
$ vim /usr/local/nginx/conf/nginx.conf
user  nginx;
worker_processes  1;
 
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
 
 
events {
    worker_connections  1024;
}
 
 
http {
 
    server {
                listen 80;
                location  / {
                        proxy_pass http://blance;
                }
        }
 
    upstream blance{
            server 192.168.100.103:8080;
            server 192.168.100.104:8080;
    }
 
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
 
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 
    access_log  /var/log/nginx/access.log  main;
 
    sendfile        on;
    #tcp_nopush     on;
 
    keepalive_timeout  65;
 
    #gzip  on;
 
    include /etc/nginx/conf.d/*.conf;
}

测试动静分离

通过浏览器多次访问https://192.168.100.200 第一次访问 第二次访问 两次访问到的IP不同,动静分离完成