美文网首页
LVS+Keepalived+Nginx实现高可用

LVS+Keepalived+Nginx实现高可用

作者: ArthurIsUsed | 来源:发表于2020-06-19 09:41 被阅读0次

    架构说明

    接到系统迁移的任务,之前的架构是kmzyw, kmb2b两套系统隔开,各启用一套nginx。迁移后计划将两套系统放同一段内网,相互调用对方的资源时就不用走公网,可加快程序的响应。若再采用单Nginx架构,Nginx挂掉后,整个系统讲不可用。即,需要实现Nginx集群化,再采用某种机制,将上游的流量以轮训的方式将其转到Nginx上,Nginx将流量代理给后端的tomcat。

    要实现Nginx的高可用,就不得不提LVS+Keepalived。其中Lvs负责将外网请求交由集群中的Nginx进行处理;keepalived则监控lvs群组,根据监控情况,若lvs群组中的master出现宕机情况,则将宕机服务器从ipvsadm移除掉,即将VIP漂移到backup机上。

    LVS

    LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统,

    NAT模式

    结构图如下



    当用户访问服务器集群提供的服务时,指向虚拟IP地址(负载均衡器的外部IP地址)的请求包到达负载均衡器。负载均衡器检查包的目的地址和端口号。如果根据虚拟服务器规则表匹配虚拟服务器服务,则通过调度算法从集群中选择一个真实的服务器,并将连接添加到记录已建立连接的哈希表中。然后,数据包的目的地址和端口被重写为所选服务器的地址和端口,然后数据包被转发到服务器。当传入的数据包属于这个连接并且在哈希表中找到所选的服务器时,该数据包将被重写并转发到所选的服务器。当应答包返回时,负载平衡器将包的源地址和端口重写为虚拟服务的源地址和端口。在连接终止或超时后,连接记录将在散列表中删除。

    • 举个简单例子,如下。



    • 虚拟IP为202.103.106.5, 所有去IP=202.103.106.5 & port=80的流量都会被负载均衡到真实IP=172.16.0.2 & port=80IP=172.16.0.3 & port=8000
    • 流量进入包: SOURCE=202.100.1.2:3456, DEST=202.103.106.5:80,LVS会选择后端一个真实的地址,例如172.16.0.3:8000。
    • 此时包会改写成SOURCE=202.100.1.2:3456, DEST=172.16.0.3:8000,并转发给后端服务器。
    • 后端服务器回报给LVS: SOURCE=172.16.0.3:8000,DEST=202.100.1.2:3456。
    • LVS改写包:SOURCE=202.103.106.5:80,DEST=202.100.1.2:3456,并将数据包传给client。

    LVS的NAT模式,此模式耗CPU,当流量比较大的时候,下游的设备超过20台时,负载均衡器会是瓶颈。

    安装LVS的服务器跟后端真实服务器不在同一个网段,采用NAT模式。如虚拟IP设置10.6.9.0/24,但是后端real_server的IP是172.2.16.0/24。

    Tun模式

    IP隧道(IP封装)是一种将IP数据报封装在IP数据报中的技术,它允许以一个IP地址为目标的数据报被包装并重定向到另一个IP地址。LVS如何使用IP隧道,如下图



    通过IP隧道的虚拟服务器与通过NAT的虚拟服务器最大的不同在于,前者的负载均衡器通过IP隧道向真实服务器发送请求,后者的负载均衡器通过网络地址转换向真实服务器发送请求。

    当用户访问服务器集群提供的虚拟服务时,一个以虚拟IP地址(虚拟服务器的IP地址)为目的地的包就会到达。负载均衡器检查包的目的地址和端口。如果它们与虚拟服务匹配,则根据连接调度算法从集群中选择一个真正的服务器,并将该连接添加到记录连接的哈希表中。然后,负载平衡器将包封装在IP数据报中,并将其转发给所选的服务器。当一个进入的数据包属于这个连接并且可以在哈希表中找到所选的服务器时,该数据包将再次被封装并转发到该服务器。当服务器接收到封装的包时,它对包进行封装并处理请求,最后根据它自己的路由表直接将结果返回给用户。在连接终止或超时后,连接记录将从散列表中删除。下图展示了工作流。


    Tun模式解决负载均衡器与后端服务不在同一个网段(地区)的问题,但要求后端设备支持IP隧道技术,linux内核也需要特定版本(待查)。

    DR模式

    虚拟IP地址由真实服务器和负载均衡器共享。负载均衡器也有一个配置了虚拟IP地址的接口,用于接受请求包,它直接将包路由到所选的服务器。所有真正的服务器都有其非arp别名接口,并配置了虚拟IP地址,或者将以虚拟IP地址为目标的数据包重定向到本地套接字,以便真正的服务器可以在本地处理数据包。负载平衡器和真正的服务器必须有一个接口通过集线器/交换机进行物理连接。通过直接路由实现的虚拟服务器架构如下图所示:



    当用户访问服务器集群提供的虚拟服务时,指定为虚拟IP地址(虚拟服务器的IP地址)的数据包到达。负载平衡器(LinuxDirector)检查包的目的地址和端口。如果它们与虚拟服务匹配,则通过调度算法从集群中选择一个真正的服务器,并将连接添加到记录连接的哈希表中。然后,负载均衡器直接将它转发到所选的服务器。当进入的数据包属于这个连接并且在哈希表中找到所选的服务器时,该数据包将再次直接路由到该服务器。当服务器接收到转发的数据包时,服务器发现该数据包是用于其别名接口上的地址或本地套接字的,因此它处理请求并最终直接将结果返回给用户。在连接终止或超时后,连接记录将从散列表中删除。

    通常采用DR模式,Keepalived+LVS采用一主一备,下游的Nginx可以横行扩展,只需在keepalived.conf添加real server即可。

    直接路由工作流如下图所示:


    负载均衡器简单地改变数据帧的MAC地址,以选择的服务器,并在局域网上抑制它。这就是为什么负载均衡器和每个服务器必须通过LAN的一个不间断区段直接相互连接。

    安装LVS的服务器跟后端真实服务器在同一个网段,采用DR模式。如LVS的服务器是10.6.9.201, 设置的虚拟IP是10.6.9.199,后端nginx的地址是10.6.9.201-203。

    Keepalived

    keepalive (KA)是一个设备向另一个设备发送的消息,用于检查两者之间的链路是否在运行,或者防止链路被断开。keepalived是采用VRRP(Virtual Router Redundancy Protocol)虚拟路由冗余协议,实现主从的平滑迁移。

    这里主要是用于real server的健康检测,还有,若其中一台keeaplived挂了,另外一台由Slave变成Master

    [root@lvs1 ~]# yum install -y curl gcc openssl-devel libnl3-devel net-snmp-devel
    [root@lvs1 ~]# yum install keepalived
    [root@lvs1 ~]# vim /etc/keepalived/keepalived.conf
    
    ! Configuration File for keepalived
    
    global_defs {
        notification_email {
            youli@kangmei.com.cn
        }
        notification_email_from Alexandre.Cassen@firewall.loc
        smtp_server smtp.kangmei.com.cn
        smtp_connect_timeout 30
        router_id LVS_DEVEL
    }
    
    vrrp_instance VI_1 {
        state MASTER    #从服务器是:BACKUP,注:需要大写
        interface eth0    #网卡名称,ip a可查
        virtual_router_id 51  # 虚拟路由IP,主备须一致
        priority 100       # 优先级,master需要大于backup,于此,master=100, backup=90
        advert_int 1    # 设定MASTER与BACKUP负载均衡器之间同步检查的时间间隔,单位是秒
        authentication {     # 设置验证类型和密码
            auth_type PASS    # 验证类型,PASS与AH两种
            auth_pass 1111     #  #设置验证密码,在同一个vrrp_instance下,MASTER与BACKUP必须使用相同的密码才能正常通信
        }
        virtual_ipaddress {     # 虚拟IP,用于对外连接
            172.20.16.99
        }
    }
    
    virtual_server 172.20.16.99 80 {   # 真实IP + port
        delay_loop 6    # 健康时间检查,单位秒
        lb_algo rr    #  负载均衡调度算法,和使用的LVS的调度算法保持原则一致
        lb_kind DR    #  LVS实现负载均衡的机制 ,都在同一段内网,采用DR(direct route)模式
        persistence_timeout 50    # 会话保持时间,单位是秒。
        protocol TCP    # 指定转发协议类型,有TCP和UDP两种
    
        real_server 172.20.16.102 80 {
            weight 3
            TCP_CHECK {      # TCP 机制的健康检查
                connect_timeout 3    # 超时时间,单位秒
                nb_get_retry 3    # 重试次数
                delay_before_retry 3   # 时间间隔
                connect_port 80
            }
        }
        real_server 172.20.16.103 80 {
            weight 3
            TCP_CHECK {
                connect_timeout 10
                nb_get_retry 3
                delay_before_retry 3
                connect_port 80
            }
        }
        real_server 172.20.16.104 80 {
            weight 3
            TCP_CHECK {
                connect_timeout 10
                nb_get_retry 3
                delay_before_retry 3
                connect_port 80
            }
        }
    }
    

    persistence_timeout 50,有了这个会话保持功能,用户的请求会被一直分发到某个服务节点,直到超过这个会话的保持时间。需要注意的是,这个会话保持时间是最大无响应超时时间,也就是说,用户在操作动态页面时,如果50秒内没有执行任何操作,那么接下来的操作会被分发到另外的节点,但是如果用户一直在操作动态页面,则不受50秒的时间限制。

    • 配置好keepalived后,启动服务,说明keepalived已正常启动。


    • centos7.10系统下 keepalived开机自启动

      • systemctm enable keepalived.service
    • Centos7.10系统下启动keepalived

      • systemctl start keepavlied.service
    • Centos7.10系统下keepalived的日志位置
      *tail -n 200 -f /var/log/messages

    Nginx

    • 在 172.20.16.102-104上配置realserver
    [root@Nginx1 nginx]# cd /etc/init.d/
    [root@Nginx1 nginx]# vim realserver.sh
    
    #虚拟的vip 根据自己的实际情况定义
    SNS_VIP=172.20.16.99
    /etc/rc.d/init.d/functions
    case "$1" in
    start)
           ifconfig lo:0 $SNS_VIP netmask 255.255.255.255 broadcast $SNS_VIP
           /sbin/route add -host $SNS_VIP dev lo:0
           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
           sysctl -p >/dev/null 2>&1
           echo "RealServer Start OK"
           ;;
    stop)
           ifconfig lo:0 down
           route del $SNS_VIP >/dev/null 2>&1
           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
           echo "RealServer Stoped"
           ;;
    *)
           echo "Usage: $0 {start|stop}"
           exit 1
    esac
    exit 0
    
    
    [root@Nginx1 nginx]# chmod 755 /etc/init.d/realserver.sh
    [root@Nginx1 nginx]# chmod 755 /etc/rc.d/init.d/functions
    
    [root@Nginx1 nginx]#  /etc/init.d/realserver.sh start
    RealServer Start OK
    
    • 通过ip a查看是否启动成功

    • 安装nginx

    yum install -y pcre pcre-devel zlib zlib-devel gcc-c++  gcc  openssl openssl-devel 
    cd /usr/local/src/
    wget http://nginx.org/download/nginx-1.12.1.tar.gz
    tar -xf nginx-1.12.1.tar.gz
    cd /nginx-1.12.1
    ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module 
    make && make install
    
    • 配置nginx,并验证是否可正常打开
    [root@nginx3 conf]# cat /root/demo/demo.html 
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset='utf-8'>
        <title>Hello world</title>
    </head>
        <body>
        <p style='color:red;'>Hello World!</p>
        </body>
    </html>
    
    
    [root@nginx3 conf]#vim nginx.conf
    
            location / {
                root   /root/demo/;
                index  demo.html;
            }
    
    • 依次替换172.20.16.102-104


    • Nginx开机自启动

    [root@kmzyw-nginx02 ~]# vim /etc/ini.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/localnginx/conf/nginx.conf
    # 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/localnginx/conf/nginx.conf"
    
    [ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx
    
    
    make_dirs() {
       # make required directories
       user=`$nginx -V 2>&1 | grep "configure arguments:.*--user=" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`
       if [ -n "$user" ]; then
          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
        fi
    }
    
    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
    
    
    [root@kmzyw-nginx02 ~]# chkconfig --add /etc/ini.d/nginx
    [root@kmzyw-nginx02 ~]# chkconfig nginx on
    [root@kmzyw-nginx02 ~]# chkconfig --list
    

    ipvsadm

    keepalived启动后, ipvsadm -Ln命令只能看到一条转发记录,查看教程与配置,发现TCP_CHECK与左花括号之间需要加空格

    更新配置,重启keepalived后,可看到3条转发记录

    web打开http://172.20.16.99/demo.html可以看到,keepalived部署成功

    停掉主keepalived、nginx中的任意两台都可以通过改URL打开页面。

    排错

    • ipvsadm 只能看到一条记录
      • TCP_CHECK与左花括号之间需要空格
    • centos7下安装keepalived,启动报错, "unknow keyword nat_mask"、"unknow keyword 'nb_get_retry'"
      • 注意版本,新版本取消了nat_mask,nb_get_retry更改为retry
    • 在master上无法'nc -z -v virtul IP port'
      • 正常,在后端的nginx,backupd都正常,如:nc -z -v 10.6.9.199 80返回正常,master提示timeout。同样,可以curl http://10.6.9.199:80 返回 Wellcome to Nginx!说明LVS+keepalived+Nginx架构部署成功。

    相关文章

      网友评论

          本文标题:LVS+Keepalived+Nginx实现高可用

          本文链接:https://www.haomeiwen.com/subject/iavjxktx.html