LVS+Keepalived+Nginx实现高可用

架构说明

接到系统迁移的任务,之前的架构是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架构部署成功。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354