使用 HAProxy + Keepalived 构建基于 Docker 的高可用负载均衡服务(二)

本文主要介绍把 HAProxy + Keepalived 服务组合形成单独的镜像,并能够自定义配置项。适合具有 Docker 和 Bash 相关基础的开发、运维等同学。

使用 Docker 组合构建 haproxy-keepalived 服务

未分类

上图为我们要构建的 haproxy-keepalived 服务的工作原理图,原理表述如下:

START:

  • 根据 Bash 脚本和 Docker 容器的环境变量生成 HAProxy 和 Keepalived 的配置文件
  • 在容器内部启动 HAProxy 和 Keepalived 服务

STOP:

  • ENTRYPOINT 使用 trap 接收到传递来的 SIGTERM 信号
  • 使用 kill -ITEM $pid 的方式,结束掉 HAProxy & Keepalived 两个服务。

下边会按照上述步骤介绍如何工作。

生成 HAProxy & Keepalived 配置文件

haproxy_cfg_init.sh
#!/bin/bash

HAPROXY_DIR="/usr/local/etc/haproxy"
TMP_HAPROXY="/tmp/haproxy"

TMP_FILE="${TMP_HAPROXY}/tmp.cfg"
TMP_TEMPLATE="${TMP_HAPROXY}/template.cfg"

TEMPLATE_FILE="/haproxy/template.cfg"
HAPROXY_CFG="${HAPROXY_DIR}/haproxy.cfg"

# mkdir to prevent not exists
mkdir -p ${HAPROXY_DIR}
mkdir -p ${TMP_HAPROXY}
rm -rf ${HAPROXY_DIR}/haproxy.cfg

# delete old haproxy.cfg and touch tmp.cfg
rm -rf ${HAPROXY_CFG}
rm -rf ${TMP_FILE}
touch ${TMP_FILE}

# copy template.cfg to tmp dir
cp ${TEMPLATE_FILE} ${TMP_TEMPLATE}

COUNTER=1
while [ ${COUNTER} -gt 0 ]
do
    haproxy_item=$(printenv haproxy_item${COUNTER})

    if [ -z "${haproxy_item}" ]
    then
        break;
    fi

    # echo haproxy_item to /tmp/haproxy/tmp.cfg
    echo "${haproxy_item${COUNTER}}" >> ${TMP_FILE}

    let COUNTER+=1
done
echo "Success generate tmp.cfg!"

sed -i "/#CUSTOM/r ${TMP_FILE}" ${TMP_TEMPLATE}
echo "Success generate template.cfg"

touch ${HAPROXY_CFG}
envsubst < ${TMP_TEMPLATE} > ${HAPROXY_CFG}

echo "Success generate haproxy.cfg, file content is below:"
echo "----------------haproxy.cfg------------------"
cat ${HAPROXY_CFG}
echo "------------------ End ----------------------"

容器在启动的时候,传入 haproxy_item_${CURSOR} 环境变量,上述脚本根据传入的环境变量与 template.cfg 文件结合,生成最终的 /usr/local/etc/haproxy/haproxy.cfg 配置文件。

大致相似的原理,根据 init_keepalived_conf.sh 脚本,与传入的环境变量结合,使用环境变量替换 keepalived_template.conf 文件中的环境变量,来生成 最终的 /etc/keepalived/keepalived.conf 配置文件。

#!/bin/bash
set -e

TEMPLATE="/keepalived/keepalived_template.conf"
TMP_TEMPLATE="/tmp/keepalived/tmp_keepalived_template.conf"
KEEPALIVED_FILE="/etc/keepalived/keepalived.conf"

# mkdir to prevent not exist
mkdir -p /tmp/keepalived/
mkdir -p /etc/keepalived/

# copy template to /tmp/keepalived/tmp_keepalived_template.conf
cp ${TEMPLATE} ${TMP_TEMPLATE}

envsubst < ${TMP_TEMPLATE} > ${KEEPALIVED_FILE}
echo "Success generate ${KEEPALIVED_FILE}"
echo "----------------------keepalived.conf----------------------"
cat ${KEEPALIVED_FILE}
echo "--------------------------- End ---------------------------"

容器内部启动 HAProxy & Keepalived 服务

查看 Dockerfile 文件,这个 haproxy-keepalived 镜像是基于 haproxy:1.7.9 基础镜像来做的修改。为了符合一个容器启动 HAProxy & Keepalived 两个服务的预期,我们增加了一个 docker-entrypoint-override.sh 的脚本来作为 haproxy-keepalived 镜像的 ENTRYPOINT。

在 docker-entrypoint-override.sh 中,我们分别用如下两条命令启动来启动两个服务:

  • HAProxy: exec /docker-entrypoint.sh “$@” &

    直接后台运行 haproxy:1.7.9 官方镜像的原 docker-entrypoint.sh 来启动 haproxy

  • Keepalived: exec /start_keepalived.sh “$@” &

    后台运行 start_keepalived.sh 脚本来启动 Keepalived 服务

接下来会稍微详细说一下 start_keepalived.sh 脚本:

#!/bin/bash

LANUCH_SCRIPT="keepalived --dont-fork --dump-conf --log-console --log-detail --log-facility 7 --vrrp -f /etc/keepalived/keepalived.conf"

eval $LANUCH_SCRIPT
while true; do
  k_pid=$(pidof keepalived)
  if [ -n "$k_id" ]; then
    break;
  fi

  kill -TERM $(cat /var/run/vrrp.pid)
  kill -TERM $(cat /var/run/keepalived.pid)
  echo "ERROR: Keepalived start failed, attempting restart ."
  eval $LANUCH_SCRIPT
done

echo "Keepalived started successfuly!"

在上述脚本中,我们在运行 LANUCH_SCRIPT 后,又做了一个循环判断。原因是在我们使用这个自定义的 haproxy-keepalived 服务的时候,在重启容器后 Keepalived 并不能正常启动,会有个报错:

Keepalived: Daemon is already running

但实际上,这个 Daemon 我们已经给它 kill 掉了。我们这里再执行一次判断,如果 $k_id 值不为空,则 Keepalived 启动成功;如果 $k_id 值为空,我们会再 kill 一次 Keepalived 的进程,然后重启 Keepalived。

容器停止,Graceful Shutdown

在容器停止的时候,需要做 Graceful Shutdown 的操作,在这里的目标就是:容器关闭前干掉 HAProxy & Keepalived 的进程。

仍然回到 docker-entrypoint-override.sh 脚本,在脚本中我们定义了接收 SIGITERM 信号的处理事件:

trap "stop; exit 0;" SIGTERM SIGINT

# handler for SIGINT & SIGITEM
stop() {
  echo "SIGTERM caught, terminating <haproxy & keepalived> process..."

  # terminate haproxy
  h_pid=$(pidof haproxy)

  kill -TERM $h_pid > /dev/null 2>&1
  echo "HAProxy had terminated."

  # terminate keepalived
  kill -TERM $(cat /var/run/vrrp.pid)
  kill -TERM $(cat /var/run/keepalived.pid)
  echo "Keepalived had terminated."

  echo "haproxy-keepalived service instance is successfuly terminated!"
}

当容器被 stop 或者 kill 的时候,会触发这个 handler 事件。我们在事件中对两个服务做了下简单的处理。

如何使用

首先,准备两台 Linux 虚拟机,假定 IP 分别为:192.168.0.41、192.168.0.42,并找到一个 VIP,假定为 192.168.0.40。

在 192.168.0.41 机器上新建 /data/haproxy-keepalived/docker-compose.yml 文件:

root@haproxy-master:/data/haproxy-keepalived# cat docker-compose.yml
version: '3'
services:

  haproxy-keepalived:
    image: "pelin/haproxy-keepalived"
    privileged: true
    network_mode: host
    restart: always
    environment:
      KEEPALIVED_STATE: "MASTER"
      KEEPALIVED_INTERFACE: "ens18"
      KEEPALIVED_PRIORITY: "105"
      KEEPALIVED_V_ROUTER_ID: "40"
      KEEPALIVED_VIP: "192.168.0.40"
      haproxy_item1: |-
        listen app-1
            bind *:4000
            mode http
            maxconn 300
            balance roundrobin
            server server1 192.168.0.21:4001 maxconn 300 check
            server server2 192.168.0.22:4002 maxconn 300 check
         listen app-2
            bind *:5000
            mode http
            maxconn 300
            balance roundrobin
            server server1 192.168.0.21:5001 maxconn 300 check
            server server2 192.168.0.22:5002 maxconn 300 check

注意上述配置:
KEEPALIVED_STATE             # Keepalived 节点初始状态
KEEPALIVED_INTERFACE         # Keepalived 绑定的网卡
KEEPALIVED_PRIORITY          # 权重
KEEPALIVED_V_ROUTER_ID       # router_id
KEEPALIVED_VIP               # 虚拟 IP

其中,KEEPALIVED_INTERFACE 需要根据实际情况而定。比如笔者用的 Ubuntu 16.04 的主网卡是 ens18,Ubuntu 14.04 的网卡是 eth0。

然后使用如下命令开启服务:

root@haproxy-keepalived:/data/haproxy-keepalived# docker-compose up

在启动后,访问浏览器:192.168.0.41:1080/stats 即可看到有界面出现

同理,在 42 机器上做上述操作,其中 KEEPALIVED_STATE 需要修改为 BACKUP

如何扩展

目前实现的功能:

  • 根据环境变量动态调整 HAProxy & Keepalived 配置
  • 容器 Graceful Shutdown
  • 容器能够正常 Restart,Crashed 后能正常自动重启

TODO:

  • 直接 Bind 配置文件进容器

如果需要自定义一个 haproxy-keepalived 镜像的话,可以参考上边的思路来做。当然,如果场景可以使用 Bind 的方式,那自然是更简单了。

如果可以直接 Bind 配置文件进容器,上述很多脚本都可以省略掉。但是,我们更寄期望于在容器编排工具中,使用环境变量的方式来初始化配置文件。为什么有这样的考虑?

  • 当有多台机器的时候,我们需要一台一台去修改 Bind 的配置文件。虽然可以使用 Ansible 之类的自动化机制来做,但是在这种场景下把配置和服务分离并不太合适;
  • 对以后做服务的自动伸缩机制友好。服务在伸缩之后,通过服务发现我们需要更新我们的 LB 的配置。如何更新?自然是通过我们的中心容器编排工具来自动更新。去触发脚本更新与我们的期望不符。

LICENSE

Source code: https://coding.net/u/Pelin_li/p/haproxy-keepalived/git

Based on MIT LICENSE: https://coding.net/u/Pelin_li/p/haproxy-keepalived/git/blob/master/LICENSE

使用 Haproxy + Keepalived 构建基于 Docker 的高可用负载均衡服务(一)

适合具有 Docker 和 Bash 相关基础的开发、运维等同学。本文没有太过深入的介绍,也并没有用到一些高级特性,仅适合用来作为一个基础科普文来阅读。

背景

最近在搭新的 Coding 内部测试环境,老大说把以前使用 nginx 插件来做的 LB(Load balancing) 全部换成 HAProxy 来做。经过几天的不断“查阅资料”,简单实现了该服务的动态构建。

本文循序渐进按照以下几个层次来讲:

  • HAProxy 与 Keepalived 的简单介绍
    • HAProxy 介绍

    • Keepalived 介绍

  • 搭建 haproxy + keepalived 服务过程

    • 搭建业务服务

    • 使用 HAProxy 做业务服务的高可用和负载均衡

    • 使用 Keepalived 做 HAProxy 服务的高可用

环境准备:

  • 五台 Linux 主机

未分类

  • 一个虚拟的 IP

192.168.0.146(一个内网没人用的 IP)

HAProxy 与 Keepalived 简单介绍

HAProxy 是一个提供高可用、负载均衡和基于 HTTP/TCP 应用代理的解决方案。

Keepalived 是用 C 编写的路由软件,主要目标是为 Linux 系统及基于 Linux 的设施提供强大的高可用性和负载均衡。

在本文中,HAProxy 我们用来做 HTTP/TCP 应用的 HA + LB 方案,Keepalived 被用来做 HAProxy 的 HA 方案。下边是对两者的简单介绍和简单使用。

HAProxy

关于 HAProxy 的一些概念限于篇幅的原因不再详细说了,可以参考这篇文章: https://www.digitalocean.com/community/tutorials/an-introduction-to-haproxy-and-load-balancing-concepts

下边仅会介绍我们会使用到的一些概念。

HAProxy 的 Proxy 配置可以分为如下几个部分( http://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4 ):

  • defaults []
  • frontend
  • backend
  • listen

其中 defaults 包含一些全局的默认环境变量,frontend 定义了如何把数据 forward 到 backend,backend 描述了一组接收 forward 来的数据的后端服务。listen 则把 frontend 和 backend 组合到了一起,定义出了一个完整的 proxy。

未分类

下面是一段简单的 HAProxy 的配置:

listen app1-cluster
    bind *:4000
    mode http
    maxconn 300
    balance roundrobin
    server server1 192.168.0.189:4004 maxconn 300 check
    server server2 192.168.0.190:4004 maxconn 300 check
    server server3 192.168.0.191:4004 maxconn 300 check

listen app2-cluster
    bind *:5000
    mode http
    maxconn 300
    balance roundrobin
    server server1 192.168.0.189:5555 maxconn 300 check
    server server2 192.168.0.190:5555 maxconn 300 check
    server server3 192.168.0.191:5555 maxconn 300 check

我们在 HAProxy 的配置文件中定义了两个 listen 模块,分别监听在 4000、5000 端口。监听在 4000 端口的模块,使用 roundrobin (轮询)负载均衡算法,把请求分发到了三个后端服务。

Keepalived

关于 Keepalived 的详细介绍可以参考: http://keepalived.readthedocs.io/en/latest/introduction.html

Keepalived 是一个用于负载均衡和高可用的路由软件。

其负载均衡(Load balancing)的特性依赖于 Linux 虚拟服务器(LVS)的 IPVS 内核模块,提供了 Layer 4 负载均衡器(TCP 层级,Layer 7 是 HTTP 层级,即计算机网络中的OSI 七层网络模型与 TCP/IP 四层网络模型)。

Keepalived 实现了虚拟冗余路由协议(VRRP, Virtual Redundancy Routing Protoco),VRRP 是路由故障切换(failover)的基础。

简单来说,Keepalived 主要提供两种功能:

  • 依赖 IPVS 实现服务器的健康检查;
  • 实现 VRRPv2 协议来处理路由的故障切换。

我们接下来会用个简单的配置来描述后者的工作原理:

在 haproxy-master 机器上:

vrrp_script chk_haproxy {
    script "killall -0 haproxy" # verify haproxy's pid existance
    interval 2 # check every 2 seconds
    weight -2 # if check failed, priority will minus 2
}

vrrp_instance VI_1 {
    state MASTER # Start-up default state
    interface ens18 # Binding interface
    virtual_router_id 51 # VRRP VRID(0-255), for distinguish vrrp's multicast
    priority 105 # VRRP PRIO
    virtual_ipaddress { # VIP, virtual ip
        192.168.0.146
    }
    track_script { # Scripts state we monitor
        chk_haproxy              
    }
}

在 haproxy-backup 机器上:

vrrp_script chk_haproxy {
   script "killall -0 haproxy"
    interval 2
    weight -2
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens18
    virtual_router_id 51
    priority 100
    virtual_ipaddress {
        192.168.0.146
    }
    track_script {             
        chk_haproxy              
    }
}

我们为两台机器(master、backup)安装了 Keepalived 服务并设定了上述配置。

可以发现,我们绑定了一个虚拟 IP (VIP, virtual ip): 192.168.0.146,在 haproxy-master + haproxy-backup 上用 Keepalived 组成了一个集群。在集群初始化的时候,haproxy-master 机器的 被初始化为 MASTER。

间隔 2 seconds() 会定时执行 <script>脚本 ,每个 会记录脚本的 exit code。

关于 参数的使用:

  • 检测失败,并且 weight 为正值:无操作
  • 检测失败,并且 weight 为负值:priority = priority – abs(weight)
  • 检测成功,并且 weight 为正值:priority = priority + weight
  • 检测成功,并且 weight 为负值:无操作

weight 默认值为 0,对此如果感到迷惑可以参考:HAProxy github code

故障切换工作流程:

  • 当前的 MASTER 节点 <script> 脚本检测失败后,如果“当前 MASTER 节点的 priority” < “当前 BACKUP 节点的 priority” 时,会发生路由故障切换。
  • 当前的 MASTER 节点脚本检测成功,无论 priority 是大是小,不会做故障切换。

其中有几处地方需要注意:

  • 一个 Keepalived 服务中可以有个 0 个或者多个 vrrp_instance
  • 可以有多个绑定同一个 VIP 的 Keepalived 服务(一主多备),本小节中只是写了两个
  • 注意 ,同一组 VIP 绑定的多个 Keepalived 服务的 必须相同;多组 VIP 各自绑定的 Keepalived 服务一定与另外组不相同。否则前者会出现丢失节点,后者在初始化的时候会出错。

关于 Keepalived 各个参数代表含义的问题,可以同时参考下文与 Github 代码文档来看:

  • keepalived工作原理和配置说明: http://outofmemory.cn/wiki/keepalived-configuration

  • Github keepalived.conf.SYNOPSIS: https://github.com/acassen/keepalived/blob/master/doc/keepalived.conf.SYNOPSIS

搭建 haproxy + keepalived 服务过程

搭建业务服务

我们在 host-1、host-2、host-3 三台机器上,每台机器的 4004、5555 端口分别启起来一个服务,服务打印一段字符串,用来模拟业务集群中的三个实例。

如下所示:

  • 192.168.0.189/host-1

4004 端口:

未分类

5555 端口:

未分类

  • 192.168.0.190/host-2

4004 端口:

未分类

5555 端口:

未分类

  • 192.168.0.191/host-3

4004 端口:

未分类

5555 端口:

未分类

如上所示,我们在三台机器上搭建了两个集群,结构如下图所示:

未分类

使用 HAProxy 做业务服务的高可用和负载均衡

我们现在有两台机器:haproxy-master、haproxy-backup,本小节的目标是在这两台机器上分别搭一套相同的 HAProxy 服务。(为了方便,下边直接用 Docker 做了)

我们直接使用了 haproxy:1.7.9 版本的 Docker 镜像,下边是具体的步骤:

coding@haproxy-master:~/haproxy$ tree .
.
├── Dockerfile
└── haproxy.cfg

0 directories, 2 files

coding@haproxy-master:~/haproxy$ cat Dockerfile
FROM haproxy:1.7.9

COPY haproxy.cfg /usr/local/etc/haproxy/

coding@haproxy-master:~/haproxy$ cat haproxy.cfg
global
    daemon
    maxconn 30000
    log 127.0.0.1 local0 info
    log 127.0.0.1 local1 warning

defaults
    mode http
    option http-keep-alive
    option httplog
    timeout connect 5000ms
    timeout client 10000ms
    timeout server 50000ms
    timeout http-request 20000ms

#  custom your own frontends && backends && listen conf
# CUSTOM

listen app1-cluster
    bind *:4000
    mode http
    maxconn 300
    balance roundrobin
    server server1 192.168.0.189:4004 maxconn 300 check
    server server2 192.168.0.190:4004 maxconn 300 check
    server server3 192.168.0.191:4004 maxconn 300 check

listen app2-cluster
    bind *:5000
    mode http
    maxconn 300
    balance roundrobin
    server server1 192.168.0.189:5555 maxconn 300 check
    server server2 192.168.0.190:5555 maxconn 300 check
    server server3 192.168.0.191:5555 maxconn 300 check

listen stats
    bind *:1080
    stats refresh 30s
    stats uri /stats

在完成上述代码后,我们可以构建我们自己的 Docker 镜像,并运行它:

coding@haproxy-master:~/haproxy$ docker build -t custom-haproxy:1.7.9 .
Sending build context to Docker daemon 3.584kB
Step 1/2 : FROM haproxy:1.7.9
1.7.9: Pulling from library/haproxy
85b1f47fba49: Pull complete
3dee1a596b5f: Pull complete
259dba5307a2: Pull complete
9d51568f5880: Pull complete
d2c6077a1eb7: Pull complete
Digest: sha256:07579aed81dc9592a3b7697d0ea116dea7e3dec18e29f1630bc2c399f46ada8e
Status: Downloaded newer image for haproxy:1.7.9
 ---> 4bb854517f75
Step 2/2 : COPY haproxy.cfg /usr/local/etc/haproxy/
 ---> 4e846d42d719
Successfully built 4e846d42d719
Successfully tagged custom-haproxy:1.7.9

coding@haproxy-master:~/haproxy$ docker run -it --net=host --privileged --name haproxy-1 -d custom-haproxy:1.7.9
fb41ab81d2140af062708e5b84668b7127014eb9ae274e4c2761d06e2f6d7950

通过命令 docker ps -a 我们可以看到,容器已经正常运行了,接下来我们在 web 端打开相应的地址可以看到所有服务的状态:

未分类

我们刚才在 haproxy-master 机器上搭建了一个 haproxy 服务,在其 4000、5000 端口绑定了两个业务集群。我们在浏览器访问:http://192.168.0.144:4000/、http://192.168.0.144:5000/ ,并且重复刷新即可看到会打印不同的 JSON 出来。可以证明我们请求确实是分发到了业务集群的几个实例中。

接下来,还要在 haproxy-backup 上搭建一个一模一样的 HAProxy 服务,步骤同上。

使用 Keepalived 做 HAProxy 服务的高可用

在上一小节中,我们在 haproxy-master、haproxy-backup 两台机器上搭了两套相同的 HAProxy 服务。我们希望在一个连续的时间段,只由一个节点为我们提供服务,并且在这个节点挂掉后另外一个节点能顶上。

本小节的目标是在haproxy-master、haproxy-backup 上分别搭 Keepalived 服务,并区分主、备节点,以及关停掉一台机器后保证 HAProxy 服务仍然正常运行。

使用如下命令安装 Keepalived:

coding@haproxy-master:~$ sudo apt-get install keepalived
coding@haproxy-master:~/haproxy$ keepalived -v
Keepalived v1.2.19 (03/13,2017)

我们在 haproxy-master 机器上,增加如下配置:

coding@haproxy-master:~$ cat /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
    script "killall -0 haproxy"
    interval 2                   
}

vrrp_instance VI_1 {
    state MASTER                 
    interface ens18              
    virtual_router_id 51        
    priority 105                
    virtual_ipaddress {         
        192.168.0.146
    }
    track_script {
        chk_haproxy
    }
}

在 haproxy-backup 机器上增加如下配置:

coding@haproxy-backup:~$ cat /etc/keepalived/keepalived.conf
vrrp_script chk_haproxy {
    script "killall -0 haproxy"
    interval 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens18
    virtual_router_id 51
    priority 100
    virtual_ipaddress {
        192.168.0.146
    }
    track_script {
        chk_haproxy
    }
}

在配置结束之后,我们分别重启两台机器上的 Keepalived 服务:

oot@haproxy-master:~# systemctl restart keepalived

(注:不同的发行版命令不同,本文基于 Ubuntu 16.04)

此时我们的 Keepalived 服务就搭建完成了,VIP 是 192.168.0.146。此时我们通过浏览器访问:http://192.168.0.146:1080/stats,即可看到 HAProxy 服务的状态。

我们也可以通过 ssh user@192.168.0.146 看目前 MASTER 节点为哪台机器。ssh 后发现目前的 MASTER 节点为 haproxy-master 机器,我现在把这台机器上的 haproxy 服务停掉:

root@haproxy-master:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fb41ab81d214 custom-haproxy:1.7.9 "/docker-entrypoin..." About an hour ago Up 1 second haproxy-1
root@haproxy-master:~# docker stop fb41ab81d214
fb41ab81d214

然后我们访问:http://192.168.0.146:1080/stats,会在稍微停顿下后仍然能显示出来 haproxy 服务的状态。我们再 ssh user@192.168.0.146 后,发现我们进入了 haproxy-backup 机器上。此时的 MASTER 节点就是 haproxy-backup 机器。

以此来看,我们已经搭建出来了一个简单的 HAProxy + Keepalived 服务了。整体结构图如下所示:

未分类

结尾

有一点需要注意,容易混淆:

区分主节点、备节点是 Keepalived 服务来做的,HAProxy 在所有节点上的配置应该是相同的。

系统部署之keepalived安装

安装文件

  • keepalived-1.3.5.tar.gz

编译安装

./configure
make && make install

修改配置

机器1

/etc/keepalived/keepalived.conf 

! Configuration File for keepalived
global_defs {
    notification_email {
        root@localhost
    }
    notification_email_from www@example.com
    smtp_server mail.example.com
    smtp_connect_timeout 30
    router_id LVS_DEVEL
}
vrrp_script chk_nginx {
    script "/etc/keepalived/check_nginx.sh" 
    interval 2 
    weight -5 
    fall 3  
    rise 2 
}
vrrp_instance VI_1 {
    state MASTER
    interface 网卡接口名
    virtual_router_id 51
    priority 101
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        虚拟地址1
    }
    track_script {
       chk_nginx 
    }
}
vrrp_instance VI_2 {
    state BACKUP
    interface 网卡接口名
    virtual_router_id 52
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        虚拟地址2
    }
    track_script {
       chk_nginx
    }
}

机器2

! Configuration File for keepalived
global_defs {
    notification_email {
        root@localhost
    }
    notification_email_from www@example.com
    smtp_server mail.example.com
    smtp_connect_timeout 30
    router_id LVS_DEVEL
}
vrrp_script chk_nginx {
    script "/etc/keepalived/check_nginx.sh" 
    interval 2 
    weight -5 
    fall 3  
    rise 2 
}
vrrp_instance VI_1 {
    state BACKUP
    interface 网卡接口名
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        虚拟地址1
    }
    track_script {
       chk_nginx 
    }
}
vrrp_instance VI_2 {
    state MASTER
    interface 网卡接口名
    virtual_router_id 52
    priority 101
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        虚拟地址2
    }
    track_script {
       chk_nginx
    }
}

nginx检查脚本

check_nginx.sh 

#!/bin/bash
counter=$(ps -C nginx --no-heading|wc -l)
if [ "${counter}" = "0" ]; then
    /usr/local/nginx/sbin/nginx
    sleep 2
    counter=$(ps -C nginx --no-heading|wc -l)
    if [ "${counter}" = "0" ]; then
        service keepalived stop
    fi
fi

系统服务

/lib/systemd/system/keepalived.service

[Unit]
Description=LVS and VRRP High Availability Monitor
After=syslog.target network.target

[Service]
Type=simple
PIDFile=/usr/local/var/run/keepalived.pid
KillMode=process
EnvironmentFile=-/usr/local/etc/sysconfig/keepalived
ExecStart=/usr/local/sbin/keepalived --dont-fork -D
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

测试

尝试关闭一台keepalived服务,查看另一台网络端口情况

注意事项

  • 防火墙启用状态下执行
firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 
  --in-interface enp0s8 --destination 224.0.0.18 --protocol vrrp -j ACCEPT

firewall-cmd --direct --permanent --add-rule ipv4 filter OUTPUT 0 
  --out-interface enp0s8 --destination 224.0.0.18 --protocol vrrp -j ACCEPT

firewall-cmd --reload

LVS+Keepalived实现前端高可用实现

一、Keepalived简介及VRRP原理

Keepalived 是一个基于VRRP协议来实现的LVS服务高可用方案,可以利用其来避免单点故障。一个LVS服务会有2台服务器运行Keepalived,一台为主服务器(MASTER),一台为备份服务器(BACKUP),但是对外表现为一个虚拟IP,主服务器会发送特定的消息给备份服务器,当备份服务器收不到这个消息的时候,即主服务器宕机的时候, 备份服务器就会接管虚拟IP,继续提供服务,从而保证了高可用性。Keepalived是VRRP的完美实现,因此在介绍keepalived之前,先介绍一下VRRP的原理。
VRRP原理
在一个VRRP虚拟路由器中,有多台物理的VRRP路由器,但是这多台的物理的机器并不能同时工作,而是由一台称为MASTER的负责路由工作,其它的都是BACKUP,MASTER并非一成不变,VRRP让每个VRRP路由器参与竞选,最终获胜的就是MASTER。MASTER拥有一些特权,比如,拥有虚拟路由器的IP地址,我们的主机就是用这个IP地址作为静态路由的。拥有特权的MASTER要负责转发发送给网关地址的包和响应ARP请求。
VRRP通过竞选协议来实现虚拟路由器的功能,所有的协议报文都是通过IP多播(multicast)包(多播地址224.0.0.18)形式发送的。虚拟路由器由VRID(范围0-255)和一组IP地址组成,对外表现为一个周知的MAC地址。所以,在一个虚拟路由 器中,不管谁是MASTER,对外都是相同的MAC和IP(称之为VIP)。客户端主机并不需要因为MASTER的改变而修改自己的路由配置,对客户端来说,这种主从的切换是透明的。
在一个虚拟路由器中,只有作为MASTER的VRRP路由器会一直发送VRRP通告信息(VRRPAdvertisement message),BACKUP不会抢占MASTER,除非它的优先级(priority)更高。当MASTER不可用时(BACKUP收不到通告信息), 多台BACKUP中优先级最高的这台会被抢占为MASTER。这种抢占是非常快速的(<1s),以保证服务的连续性。由于安全性考虑,VRRP包使用了加密协议进行加密。

二、LVS+Keepalived实现前端高可用实现

1、 实验环境

[root@localhost ~]# uname -r
2.6.32-696.el6.x86_64
[root@localhost ~]# rpm -q keepalived
keepalived-1.2.13-5.el6_6.x86_64

时间同步:

[root@node2 ~]# ntpdate 192.168.1.200

各主机添加host能相互解析

关闭iptables及selinux

未分类

2、配置Keepalived

1)、在192.168.1.200及192.168.1.201上安装Keepalived(yum install keepalived -y)

2)、配置Keepalived

192.168.1.200配置文档:

global_defs {               //全局配置段
   notification_email {        //管理员通知邮箱,可不填写

   }
   notification_email_from root
   smtp_server 127.0.0.1       //邮件服务器地址
   smtp_connect_timeout 30
   router_id LVS_10         //主调度路由器名称,需和备份服务器保持一致
}

vrrp_instance VI_1 {         //VRRPD配置段
    state MASTER         //设置MASTER或BACKUP         
    interface eth0         //设置VIP物理地址的接口
    virtual_router_id 51            //虚拟路由ID号,每组需保持一致
    priority 100                    //优先级,越大越有限
    advert_int 1                    //心跳频率(秒)
    authentication {        
        auth_type PASS          //组播认证方式
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.1.250/24 dev eth0 label eth0:1 //VIP配置,可以有多个VIP
    }
}

virtual_server 192.168.1.250 80 {        //LVS段配置,注意端口
    delay_loop 3                       //健康检测时间
    lb_algo wrr                        //算法rr,wrr,lc,wlc等
    lb_kind DR                        //集群工作模式(nat、dr、tunl、fullnat)
    persistence_timeout 0            //会话保持时间
    protocol TCP                     //协议

    real_server 192.168.1.202 80 {    //realserver配置
        weight 1                    //权重
        TCP_CHECK {                 //健康检查(多种方式)
            connect_port 80     //检测端口
            connect_timeout 3   //超时时间
            nb_get_retry 3      //重试次数
            delay_before_retry 3 //重试间隔
        }
    }

    real_server 192.168.1.203 80 {
        weight 1
        TCP_CHECK {
            connect_port 80
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
    }
}

192.168.1.201配置与上面类似更改state及priority

3、配置realserver

192.168.1.202配置:

DR realserver脚本:

#!/bin/bash
#
#script to start LVS DR real server.   
# description: LVS DR real server   
#   
.  /etc/rc.d/init.d/functions
VIP=192.168.1.250 #修改你的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

执行脚本:

[root@node2 ~]# chmod +x realserver.sh 
[root@node2 ~]# ./realserver.sh start

检查相关脚本配置是否正确

ifconfig及cat

在node2上安装httpd后添加测试网页

echo "<h1>node2.psemily.com</h1>" > /var/www/html/index.html

node3类似操作

4、验证

启动192.168.1.200及192.168.1.201上keepalived后访问VIP:192.168.1.250

下图:图一:调度情况,图二为正常访问,图三为192.168.1.202停止httpd后的访问,图四为停止192.168.1.200后,192.168.1.201的日志

未分类

未分类

未分类

未分类

5、Keepalived的健康检测

HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|MISC_CHECK

1)HTTP_GET|SSL_GET

这里有几个要点:

a、两者都有两种检测方式,一种是简单的基于返回码确认;另一种是基于确认后端页面内容hash值,确认前后是否发生变化(是不是感觉有点高端,还有简单的防止页面被篡改的作用,当然,动态页面显然不行);

b、两者都是处理简单的GET请求,基于post返回值确认是否正常,这种方法显然不适用 ,不过POST方式是可以通过MISC_CHECK方式进行支持检测的;

c、两者配置语法上相同,只不过类型名不同而已 。同属于大的web请求范畴,只不过一个走的HTTP协议,一个走的HTTPS协议;

基于状态码检测,配置如下:

real_server 192.168.1.250 80 {
      weight 1
      HTTP_GET {
          url {
          path /index.html
          status_code 200      #http://192.168.1.250/index.html的返回状态码
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }

基于后端页面内容检测,配置如下:

real_server 192.168.1.250 80 {
     weight 1
     HTTP_GET {
       url {
       path /index.html
       digest 1366dcc22ca042f5e6a91232bc8f4c9f #http://192.168.1.202/index.html的digest值
        }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }

digest是由genhash(通过该命令可以获取页面的hash串)生成,语法如下:

[root@localhost keepalived]# genhash -s 192.168.1.202 -p 80 -u /index.html
MD5SUM = 1366dcc22ca042f5e6a91232bc8f4c9f

2)TCP_CHECK

基于TCP的检测,配置如下:

real_server 192.168.1.250 80 {
        weight 100
        TCP_CHECK {
            connect_timeout 3  
            nb_get_retry 3  
            delay_before_retry 3  
            connect_port 80    //检测端口
        }
    }

3)MISC_CHECK

调用外部配置文件进行检测,配置如下:

MISC_CHECK {
    misc_path <STRING>|<QUOTED-STRING># 外部程序或者脚本路径
    misc_timeout <INT># 执行脚本的超时时间
    misc_dynamic #如果设置了misc_dynamic,healthchecker程序的退出状态码会用来动态调整服务器的权重(weight).
    #返回0:健康检查OK,权重不被修改
    #返回1:健康检查失败,权重设为0
    #返回2-255:健康检查OK,权重设置为:退出状态码-2,比如返回255,那么weight=255-2=253
}

脚本是可以选择传参数还是不传参数的,示例如下:

#不传参配置
real_server 192.168.1.250 80 {
    weight 1
    MISC_CHECK {
      misc_path /usr/local/bin/script.sh
    }
}
#传参配置
real_server 192.168.1.250 80 {
    weight 1
    MISC_CHECK {
      misc_path "/usr/local/bin/script.sh  arg1  arg2"
    }
}

6、实验中遇到的问题

实验中用当所有配置都配置完成后,有浏览器访问VIP时,出现了只调度到一个realserver上的情景,过一段时间后能调度到另外一个realserver上,停止其中一个httpd,浏览器访问时出现不能访问,过段时间才能访问运行的另一个realserver上。

查找网上资料后有以下两种方案:

1、修改persistence_timeout 0 将连接保持时间设置为0,修改后用浏览器访问还是没能解决。用curl可以看出效果

2、ipvsadm的时间

[root@localhost keepalived]# ipvsadm -l --timeout
Timeout (tcp tcpfin udp): 900 120 300

修改时间后访问仍然不行

[root@localhost keepalived]# ipvsadm --set 1 1 1

上述两种方法均不能解决浏览器访问调度到一个realserver上的问题,但是用curl能看出效果,在此先做个记录。

lvs详细介绍及lvs和keepalived的使用

LVS简介

LVS介绍

LVS是Linux Virtual Server的缩写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统,属于4层负载均衡

ipvs和ipvsadm的关系

我们使用配置LVS的时候,不能直接配置内核中的ipvs,需要使用ipvs的管理工具ipvsadm进行管理  

LVS术语

未分类

LVS转发原理

LVS负载均衡器接受所有入站请求,并根据调度算法决定哪个realserver处理该请求

LVS调度算法

  • 轮询(rr):按照请求顺序轮流分发到后端RS
  • 加权轮询(wrr):权值高的获得的任务更多
  • 最小连接数(lc):动态的将请求建立到连接数较少的RS上
  • 加权最小连接数(wlc):调度器自动询问RS的真实负载情况,并动态的调整权

LVS调度算法生产环境选型

一般的网络服务,如:http、mail、MySQL等,常用的调度算法为:

  • 基本轮询调度rr算法
  • 加权轮询调度wrr算法
  • 加权最小连接调度wlc算法

LVS转发模式

  • NAT(Network Address Translation)
  • DR(Direct Routing)
  • TUN

LVS-DR模式

转发流程

将所有入站请求转发给后端realserver,后端realserver处理完直接将结果发给客户端

未分类

原理

当用户请求到达Direct Server,此时报文的源IP为CIP、MAC为CIP-MAC,目标IP为VIP、MAC为VIP-MAC
Direct Server根据调度算法确定一台处理请求的realserver,将请求转发给对应的realserver,此时源IP和目标IP均未改变,仅修改了源MAC为DIP-MAC,目标MAC为RIP-MAC
对应的realserver处理完请求,直接将结果发给客户端,此时源IP为VIP、MAC为VIP-MAC,目标IP为CIP、MAC为CIP-MAC

特性

  • 通过在调度器上修改数据包的目的MAC地址实现转发
  • Real-Server和Direct-Server必须在同一网段
  • Real-Server的lo接口必须绑定VIP

为什么要抑制ARP请求

  • 由于后端Real-Server要将VIP绑定到lo网卡上,这就出现了一个问题,客户端请求到达LVS前端路由器的时候,前端路由器会发送一个{目标地址为VIP}的请求报文,所以需要抑制Real-Server的ARP,保证让Direct-Server收到这个报文,而不是realserver收到这个报文

  • 一句话说明抑制Real-Server原因:保证前端路由将目标地址为VIP的报文发给Direct-Server,而不是Real-Server

优势

只有请求报文经过调度器,而Real-Server响应处理后无需经过调度器,因此并发量大的时候效率很高

LVS-NAT模式

转发流程

将所有入站请求转发给后端Real-Server,后端Real-Server处理完再发给Direct-Server,Direct-Server再发给客户端

特性

  • 既有RIP也有VIP
  • Real-Server必须得跟Direct-Server在同一网段
  • Real-Server必须将网关指向Direct-Server

优势

只需要一个公网IP给Direct-Server,Direct-Server始终跟外接打交道

劣势

需要依赖Direct-Server把请求转发给Real-Server,Real-Server处理完把结果发给Direct-Server,Direct-Server再把结果转发出去,并发高的时候会成为瓶颈

LVS三种模式对比

未分类

ipvsadm介绍

ipvsadm参数

添加虚拟服务器
    语法:ipvsadm -A [-t|u|f]  [vip_addr:port]  [-s:指定算法]
    -A:添加
    -t:TCP协议
    -u:UDP协议
    -f:防火墙标记
    -D:删除虚拟服务器记录
    -E:修改虚拟服务器记录
    -C:清空所有记录
    -L:查看
添加后端RealServer
    语法:ipvsadm -a [-t|u|f] [vip_addr:port] [-r ip_addr] [-g|i|m] [-w 指定权重]
    -a:添加
    -t:TCP协议
    -u:UDP协议
    -f:防火墙标记
    -r:指定后端realserver的IP
    -g:DR模式
    -i:TUN模式
    -m:NAT模式
    -w:指定权重
    -d:删除realserver记录
    -e:修改realserver记录
    -l:查看
通用:
    ipvsadm -ln:查看规则
    service ipvsadm save:保存规则

ipvsadm配置LVS负载均衡

需求

用LVS实现后端两台httpd的负载均衡

环境说明

未分类

负载均衡器端

安装LVS
    [root@lb01 ~]#yum -y install ipvsadm 
    [root@lb01 ~]#ipvsadm  
添加绑定VIP
    [root@lb01 ~]#ip addr add 192.168.0.89/24 dev eth0 label eth0:1
配置LVS-DR模式
    [root@lb01 ~]#ipvsadm -A -t 192.168.0.89:80 -s rr
    [root@lb01 ~]#ipvsadm -a -t 192.168.0.89:80 -r 192.168.0.93 -g 
    [root@lb01 ~]#ipvsadm -a -t 192.168.0.89:80 -r 192.168.0.94 -g

Real-Server端

配置测试后端realserver
    配置httpd省略
    [root@realserver-1 ~]#curl 192.168.0.93 #测试realserver-1网站是否正常    
    192.168.0.93
    [root@realserver-2 ~]#curl 192.168.0.94 #测试realserver-2网站是否正常
    192.168.0.94
绑定VIP到lo网卡
    [root@realserver-1 ~]#ip addr add 192.168.0.89/32 dev lo label lo:1  #由于DR模式需要realserver也有VIP
抑制ARP
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce  
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
    [root@realserver-1 ~]#echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
    [root@realserver-1 ~]#echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore  

客户端测试

[root@test ~]#curl 192.168.0.89
192.168.0.93
[root@test ~]#curl 192.168.0.89
192.168.0.94

配置LVS+keepalived

需求

  • LVS给两台httpd做负载均衡
  • keepalived做lvs高可用,同时做Real-Server健康检查,如果发现Real-Server80端口没开,就认为故障,从集群中剔除
  • 在keepalived配置文件内就能配置LVS  

环境说明

未分类

在负载均衡器端配置lvs+keepalived

lb01节点

[root@lb01 ~]# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
##################全局配置##########################
global_defs {
    #如有故障,发邮件地址
    notification_email {      
    9618154@qq.com              #收件人
    }
    notification_email_from Alexandre.Cassen@firewall.loc  #keepalived报警邮件,发件人
     smtp_server 192.168.200.1  #邮件服务器地址
    smtp_connect_timeout 30     #邮件服务器超时时间
    router_id LVS_01             #类似于MySQL的server-id,每个keepalived节点不能相同
}
#################keepalived配置#####################
vrrp_instance VI_1 {
    state MASTER              #keepalived角色,MASTER和BACKUP
    interface eth0            #通信接口,下面的virtual_ipaddress(VIP)绑定到这个网卡
    virtual_router_id 51      #vrrp_instance的唯一标识
    priority 150              #keepalived权重,数值越大权重越大,MASTER应大于BACKUP
    advert_int 1              #发送心跳间隔,如果backup1秒收不到心跳就接管,单位是秒
    authentication {          #每个keepalived节点通过这里设置的验证通信,必须得设置成一样
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.0.89/24       #VIP
    }
}
##################LVS配置##############            
#添加虚拟服务器
#相当于 ipvsadm -A -t 192.168.0.89:80 -s wrr 
virtual_server 192.168.0.89 80 {
    delay_loop 6             #服务健康检查周期,单位是秒
    lb_algo wrr                 #调度算法
    lb_kind DR                 #模式
    nat_mask 255.255.255.0   
    persistence_timeout 50   #回话保持时间,单位是秒
    protocol TCP             #TCP协议转发
#添加后端realserver
#相当于 ipvsadm -a -t 192.168.0.89:80 -r 192.168.0.93:80 -w 1
    real_server 192.168.0.93 80  {    #realserver的真实IP
        weight 1                      #权重
        #健康检查
        TCP_CHECK {
            connect_timeout 8         #超时时间
            nb_get_retry 3            #重试次数
            delay_before_retry 3      #重试间隔
            connect_port 80           #检查realserver的80端口,如果80端口没监听,就会从集群中剔除
        }
    }
    real_server 192.168.0.94 80  {
        weight 1
        TCP_CHECK {
           connect_timeout 8
           nb_get_retry 3
           delay_before_retry 3
           connect_port 80
        }
    }

}

lb02节点

[root@lb01 ~]# vim /etc/keepalived/keepalived.conf
! Configuration File for keepalived
################全局配置###########################
global_defs {
    notification_email {      
    }
    notification_email_from Alexandre.Cassen@firewall.loc  
    smtp_server 192.168.200.1
    smtp_connect_timeout 30
        router_id LVS_02        
}
################keepalived配置#####################
vrrp_instance VI_1 {
    state BACKUP             
    interface eth0         
    virtual_router_id 51      
    priority 100             
    advert_int 1             
    authentication {         
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.0.89/24
    }
}
################lvs配置##########################
virtual_server 192.168.0.89 80 {
    delay_loop 6
    lb_algo wrr             
    lb_kind DR                 
    nat_mask 255.255.255.0
    persistence_timeout 50    
    protocol TCP              
    real_server 192.168.0.93 80  {
        weight 1             
        TCP_CHECK {            
            connect_timeout 8   
            nb_get_retry 3        
            delay_before_retry 3  
             connect_port 80      
        }
    }
    real_server 192.168.0.94 80  {
        weight 1
        TCP_CHECK {
         connect_timeout 8
         nb_get_retry 3
         delay_before_retry 3
         connect_port 80
        }
    }

                }

配置后端Real-Server

确保网站服务是正常的
    curl 192.168.0.93
    192.168.0.93
    curl 192.168.0.94
    192.168.0.94
绑定VIP到lo网卡
    ip addr add 192.168.0.89/32 dev lo label lo:0
抑制ARP
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce  
    [root@realserver-1 ~]#echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce
    [root@realserver-1 ~]#echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
    [root@realserver-1 ~]#echo 1 >/proc/sys/net/ipv4/conf/lo/arp_ignore  

客户端测试

  • 负载均衡是否正常
  • 后端Real-Server出问题是否自动剔除
  • lvs高可用是否正常,提供服务的LVS宕机,vip漂移到另一台LVS继续提供服务

nginx结合keepalived实现web服务器高可用方案

keepalived的作用是检测服务器的状态,如果有一台web服务器死机,或工作出现故障,Keepalived将检测到,并将有故障的服务器从系统中剔除,同时使用其他服务器代替该服务器的工作,当服务器工作正常后Keepalived自动将服务器加入到服务器群中,这些工作全部自动完成,不需要人工干涉,需要人工做的只是修复故障的服务器。

安装Keepalived(http://www.keepalived.org/download.html)

1、上传或下载 keepalived到 /usr/local/src 目录

2、解压安装

cd /usr/local/src  
tar -zxvf keepalived-1.2.18.tar.gz  
cd keepalived-1.2.18  
./configure --prefix=/usr/local/keepalived  
make && make install  

3、将 keepalived 安装成 Linux 系统服务(因为没有使用 keepalived 的默认路径安装(默认是/usr/local),安装完成之后,需要做一些工作)

#复制默认配置文件到默认路径  
mkdir /etc/keepalived  
cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/  
#复制 keepalived 服务脚本到默认的地址  
cp /usr/local/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/  
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/  
ln -s /usr/local/sbin/keepalived /usr/sbin/  
ln -s /usr/local/keepalived/sbin/keepalived /sbin/  

4、设置 keepalived 服务开机启动

chkconfig keepalived on  

5、修改 Keepalived 配置文件

MASTER 节点配置文件(192.168.1.11)

vi /etc/keepalived/keepalived.conf
global_defs {
  ##keepalived自带的邮件提醒需要开启sendmail服务。建议用独立的监控或第三方SMTP
  ##标识本节点的字条串,通常为 hostname
  router_id 192.168.1.11
}
##keepalived会定时执行脚本并对脚本执行的结果进行分析,动态调整vrrp_instance
   的优先级。如果脚本执行结果为0,并且weight配置的值大于0,则优先级相应的增加。
   如果脚本执行结果非0,并且weight配置的值小于 0,则优先级相应的减少。其他情况,
   维持原本配置的优先级,即配置文件中priority对应的值。
vrrp_script chk_nginx {
   script "/etc/keepalived/nginx_check.sh" ## 检测 nginx 状态的脚本路径
   interval 2 ## 检测时间间隔
   weight -20 ## 如果条件成立,权重-20
}
## 定义虚拟路由,VI_1为虚拟路由的标示符,自己定义名称
vrrp_instance VI_1 {
   state MASTER ## 主节点为MASTER,对应的备份节点为BACKUP
   interface eth1 ## 绑定虚拟IP的网络接口,与本机IP地址所在的网络接口相同
   virtual_router_id 11 ## 虚拟路由的ID号,两个节点设置必须一样,建议用IP最后段
   mcast_src_ip 192.168.1.11 ## 本机 IP 地址
   priority 100 ## 节点优先级,值范围0-254,MASTER要比BACKUP高
   nopreempt ## 优先级高的设置 nopreempt 解决异常恢复后再次抢占的问题
   advert_int 1 ## 组播信息发送间隔,两个节点设置必须一样,默认 1s
   ## 设置验证信息,两个节点必须一致
   authentication {
      auth_type PASS
      auth_pass 1111
   }
   ## 将 track_script 块加入 instance 配置块
      track_script {
      chk_nginx ## 执行 Nginx 监控的服务
   }
   ## 虚拟 IP 池, 两个节点设置必须一样
   virtual_ipaddress {
      192.168.1.10  ## 虚拟 ip,可以定义多个
   }
}

BACKUP 节点配置文件(192.168.1.12)  
vi /etc/keepalived/keepalived.conf

global_defs {
  router_id 192.168.1.12
}
vrrp_script chk_nginx {
   script "/etc/keepalived/nginx_check.sh"
   interval 2 
   weight -20 
}
vrrp_instance VI_1 {
   state BACKUP
   interface eth1 
   virtual_router_id 11 
   mcast_src_ip 192.168.1.12
   priority 90 
   advert_int 1 
   authentication {
      auth_type PASS
      auth_pass 1111
   }
   track_script {
      chk_nginx 
   }
   virtual_ipaddress {
      192.168.1.10  
   }
}

6、编写 Nginx 状态检测脚本 /etc/keepalived/nginx_check.sh

脚本:如果nginx停止运行,尝试启动,如果无法启动则杀死本机的keepalived进程,
keepalied将虚拟ip绑定到 BACKUP 机器上。内容如下:

vi /etc/keepalived/nginx_check.sh
!/bin/bash
A=`ps -C nginx –no-header |wc -l`
if [ $A -eq 0 ];then
    /usr/local/nginx/sbin/nginx
    sleep 2
    if [ `ps -C nginx --no-header |wc -l` -eq 0 ];then
        killall keepalived
    fi
fi

7、给脚本赋执行权限

chmod +x /etc/keepalived/nginx_check.sh

8、启动 Keepalived

service keepalived start
Starting keepalived: [ OK ]

9、Keepalived+Nginx的高可用测试

(1)关闭 192.168.1.11 中的 Nginx,Keepalived会将它重新启动

 /usr/local/nginx/sbin/nginx -s stop

(2)关闭 192.168.1.11 中的 Keepalived,VIP 会切换到 192.168.1.12 中

 service keepalived stop

(3)重新启动 192.168.1.11 中的 Keepalived,VIP 又会切回到 192.168.1.11 中来

 service keepalived start

附Keepalived 服务管理命令:

停止:service keepalived stop
启动:service keepalived start
重启:service keepalived restart
查看状态:service keepalived status

配置LVS keepalived主从切换时同步连接状态信息

由于LVS负载均衡器需要保存大量的连接信息,记录每个TCP连接由哪台真实服务器处理。

[root@localhost keepshell]# ipvsadm -L -n -c
IPVS connection entries
pro expire state       source             virtual            destination
TCP 14:58  ESTABLISHED 192.168.80.1:57622 192.168.80.138:8080 192.168.80.135:8080
TCP 14:58  ESTABLISHED 192.168.80.1:57624 192.168.80.138:8080 192.168.80.135:8080
TCP 14:58  ESTABLISHED 192.168.80.1:57621 192.168.80.138:8080 192.168.80.136:8080
TCP 14:58  ESTABLISHED 192.168.80.1:57625 192.168.80.138:8080 192.168.80.136:8080
TCP 14:58  ESTABLISHED 192.168.80.1:57623 192.168.80.138:8080 192.168.80.136:8080
TCP 14:58  ESTABLISHED 192.168.80.1:57626 192.168.80.138:8080 192.168.80.135:8080

当主负载均衡器宕机以后,备机提升为主,但备机缺省没有这些连接信息,会导致客户端的连接失效,为了解决这一问题,LVS在Linux内核实现了同步连接信息的功能.

ipvsadm -L --daemon  查看同步进程信息

ipvsadm --start-daemon master|backup --mcast-interface=网卡名称 --syncid 编号,主备需要一致

ipvsadm --stop-daemon master|backup 停止同步
ipvsadm -L -n -c 查看连接状态信息

下面给出配置

keepalived.conf

! Configuration File for keepalived  

global_defs {  
   notification_email {  
     root@localhost  
   }  
   notification_email_from root@localhost    
   smtp_server localhost    
   smtp_connect_timeout 30    
   router_id  NodeA  
} 


! Configuration File for keepalived  

global_defs {  
   notification_email {  
     root@localhost  
   }  
   notification_email_from root@localhost    
   smtp_server localhost    
   smtp_connect_timeout 30    
   router_id  NodeA  
} 

virtual_server 192.168.80.138 8080 {   
   delay_loop 6                    
   lb_algo rr                       
   lb_kind DR                            
   persistence_timeout 0             
   protocol TCP                      
   real_server 192.168.80.135 8080 {     
       weight 1                        
        HTTP_GET {  
            url {   
              path /index.jsp  
              digest 5cce221db9752be2116860efb866144e  
            }  
            connect_timeout 5  
            nb_get_retry 3  
            delay_before_retry 3  
        }         
   }  

   real_server 192.168.80.136 8080 {      
       weight 1  
        HTTP_GET {  
            url {   
              path /index.jsp  
              digest 345e829e9a900a87a8fce740ef243198  
            }  
            connect_timeout 5  
            nb_get_retry 3  
            delay_before_retry 3  
        }          
   }  
}  

notify_master.sh

#!/bin/bash  
echo $(date "+%Y-%m-%d %H:%M:%S") "The keepalived service is master." >> /home/keepshell/gexin.txt  
ipvsadm --stop-daemon backup  
ipvsadm --start-daemon master --mcast-interface=eno16777736 --syncid 1 

notify_backup.sh

#!/bin/bash  
echo $(date "+%Y-%m-%d %H:%M:%S") "The keepalived service is backup." >> /home/keepshell/gexin.txt  
ipvsadm --stop-daemon master  
ipvsadm --start-daemon backup --mcast-interface=eno16777736 --syncid 1  

notify_stop.sh

#!/bin/bash  
echo $(date "+%Y-%m-%d %H:%M:%S") "The keepalived service is stop." >> /home/keepshell/gexin.txt  
ipvsadm --stop-daemon master  
ipvsadm --stop-daemon backup 

配置keepalived主从切换时发送告警邮件

邮件脚本:

keepalived_notify.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import smtplib
from email.mime.text import MIMEText
from email.header import Header
import sys, time, subprocess



# 第三方 SMTP 服务
mail_host="smtp.exmail.qq.com"  #设置服务器
mail_user="xxx"    #用户名
mail_pass="xxx"   #口令


sender = 'pairobot2@tuandai.com'    # 邮件发送者
receivers = ['xx1@qq.com', 'xx2@163.com']  # 接收邮件,可设置为你的QQ邮箱或者其他邮箱

p = subprocess.Popen('hostname', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
hostname = p.stdout.readline().split('n')[0]

message_to = ''
for i in receivers:
    message_to += i + ';'

def print_help():
    note = '''python script.py role ip vip
    '''
    print(note)
    exit(1)

time_stamp = time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time()))

if len(sys.argv) != 4:
    print_help()
elif sys.argv[1] == 'master':
    message_content = '%s server: %s(%s) change to Master, vIP: %s' %(time_stamp, sys.argv[2], hostname, sys.argv[3])
    subject = '%s change to Master -- keepalived notify' %(sys.argv[2])
elif sys.argv[1] == 'backup':
    message_content = '%s server: %s(%s) change to Backup, vIP: %s' %(time_stamp, sys.argv[2], hostname, sys.argv[3])
    subject = '%s change to Backup -- keepalived notify' %(sys.argv[2])
else:
    print_help()

message = MIMEText(message_content, 'plain', 'utf-8')
message['From'] = Header(sender, 'utf-8')
message['To'] =  Header(message_to, 'utf-8')

message['Subject'] = Header(subject, 'utf-8')

try:
    smtpObj = smtplib.SMTP()
    smtpObj.connect(mail_host, 25)    # 25 为 SMTP 端口号
    smtpObj.login(mail_user,mail_pass)
    smtpObj.sendmail(sender, receivers, message.as_string())
    print("邮件发送成功")
except smtplib.SMTPException as e:
    print("Error: 无法发送邮件")
    print(e)

使用方法:

python script.py{脚本名} role{master|backup} ip{本keepalived服务器IP} vip{虚拟IP}

master keepalived:

global_defs {
        notification_email {
                xx@qq.com
        }

        notification_email_from keepalived@qq.com
        smtp_server 127.0.0.1
        smtp_connect_timeout 30
        router_id LVS_DEVEL
}
## 上面的配置邮件只能发送到本机,mail 可查看


vrrp_script chk_http_port {
        script "</dev/tcp/127.0.0.1/80"
        interval 2
        weight -10
}


vrrp_instance VI_1 {
        state BACKUP        ############ MASTER|BACKUP
        interface ens160
        virtual_router_id 11
        mcast_src_ip 192.168.1.178
        priority 99                  ########### 权值要比 back 高
        advert_int 2

        authentication {
                auth_type PASS
                auth_pass 8u90u3fhE3FQ
        }

        track_script { 
                chk_http_port ### 执行监控的服务 
        }

        virtual_ipaddress {
                192.168.1.96
        }

        notify_master "/bin/python /script/keepalived_notify.py master 192.168.1.178 192.168.1.96"
        notify_backup "/bin/python /script/keepalived_notify.py backup 192.168.1.178 192.168.1.96"

}

backup keepalived:

global_defs {
        notification_email {
                xx@qq.com
        }

        notification_email_from keepalived@qq.com
        smtp_server 127.0.0.1
        smtp_connect_timeout 30
        router_id LVS_DEVEL
}
## 上面的配置邮件只能发送到本机,mail 可查看


vrrp_script chk_http_port {
        script "</dev/tcp/127.0.0.1/80"
        interval 2
        weight -10
}


vrrp_instance VI_1 {
        state BACKUP        ############ MASTER|BACKUP
        interface ens160
        virtual_router_id 11
        mcast_src_ip 192.168.1.174
        priority 100                  ########### 权值要比 back 高
        advert_int 2

        authentication {
                auth_type PASS
                auth_pass 8u90u3fhE3FQ
        }

        track_script { 
                chk_http_port ### 执行监控的服务 
        }

        virtual_ipaddress {
                192.168.1.96
        }

        notify_master "/bin/python /script/keepalived_notify.py master 192.168.1.174 192.168.1.96"
        notify_backup "/bin/python /script/keepalived_notify.py backup 192.168.1.174 192.168.1.96"

}

LVS Keepalived双机高可用负载均衡搭建

应用环境:

LVS负责多台WEB端的负载均衡(LB);Keepalived负责LVS的高可用(HA),这里介绍主备模型。

测试环境:

未分类

未分类

配置步骤:

1. 安装软件

在LVS-1和LVS-2两台主机上安装ipvsadm和keepalived

~]# yum install ipvsadm keepalived -y  

在两台Web主机上安装Nginx

~]# yum install nginx -y    //修改访问主页内容,方便最后测试,这里改为了Nginx Web 1 [IP:12]和Nginx Web 1 [IP:13]

  

2. 配置Keepalived

说明:keepalived底层有关于IPVS的功能模块,可以直接在其配置文件中实现LVS的配置,不需要通过ipvsadm命令再单独配置。

[root@lvs-1 ~]# vim /etc/keepalived/keepalived.conf    // Master配置好的信息如下 
! Configuration File for keepalived

global_defs {
   router_id LVS        ## 不一定要与主机名相同,也不必与BACKUP的名字一致
}

vrrp_instance VI_1 {      
    state MASTER        ## LVS-1配置了为主,另外一台LVS-2配置为BACKUP
    interface eth0       ## 注意匹配网卡名
    virtual_router_id 51    ## 虚拟路由ID(0-255),在一个VRRP实例中主备服务器ID必须一样
    priority 150        ## 优先级值设定:MASTER要比BACKUP的值大
    advert_int 3        ## 通告时间间隔:单位秒,主备要一致
    authentication {      ##认证机制
        auth_type PASS     ## 默认PASS; 有两种:PASS或AH 
        auth_pass 1111     ## 默认1111; 可多位字符串,但仅前8位有效
    }
    virtual_ipaddress {
        138.138.82.222     ## 虚拟IP;可多个,写法为每行一个
    }
}
virtual_server 138.138.82.222 80 {
    delay_loop 3       ## 设置健康状态检查时间
    lb_algo rr        ## 调度算法,这里用了rr轮询算法,便于后面测试查看
    lb_kind DR        ## 这里测试用了Direct Route 模式,
   # persistence_timeout 1  ## 持久连接超时时间,先注释掉,不然在单台上测试时,全部会被lvs调度到其中一台Real Server
    protocol TCP
    real_server 138.138.82.12 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 10    ##设置响应超时时间
            nb_get_retry 3       ##设置超时重试次数
            delay_before_retry 3   ##设置超时重试间隔时间
            connect_port 80
        }
    }
    real_server 138.138.82.13 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 10
            nb_get_retry 3
            delay_before_retry 3
            connect_port 80
        }
    }
}                   

保存,退出;

[root@lvs-2 ~]# vim /etc/keepalived/keepalived.conf     //同样,修改BACKUP上的配置文件,如下
! Configuration File for keepalived

global_defs {
   router_id LVS
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 120
    advert_int 3
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        138.138.82.222
    }
}
virtual_server 138.138.82.222 80 {
    delay_loop 3
    lb_algo rr
    lb_kind DR
   # persistence_timeout 1
    protocol TCP
    real_server 138.138.82.12 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 10
            nb_get_retry 3
            delay_before_retry 3
            connect_port 80
        }
    }
    real_server 138.138.82.13 80 {
        weight 1
        TCP_CHECK {
            connect_timeout 10
            nb_get_retry 3
            delay_before_retry 3
            connect_port 80
        }
    }
}                   

保存,退出;

启动keepalived

[root@lvs-1 ~]# service keepalived start

[root@lvs-2 ~]# service keepalived start

加入开机启动:

[root@lvs-1 ~]# chkconfig keepalived on

[root@lvs-2 ~]# chkconfig keepalived on

查看:

未分类    

未分类   

说明:

此时,Virtual IP是飘在MASTER上面,如果断开MASTER(关闭MASTER上keepalived),Virtual IP就会飘到BACKUP上;

再当MASTER复活(启动keepalived),Virtual IP会再次回到MATSTER上,可通过/var/log/message查看变更信息;

3. 配置WEB端(两台Nginx)

这里直接给出了配置脚本,方便操作;

用法:~]# sh lvs-web.sh start | stop    //新建一个脚本,假定该脚本名为lvs-web.sh
#!/bin/bash 
VIP=138.138.82.222
case "$1" in
start)
           echo "start LVS of RealServer DR" 
           /sbin/ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
           /sbin/route add -host $VIP dev lo:0
           echo "1" &gt;/proc/sys/net/ipv4/conf/lo/arp_ignore
           echo "2" &gt;/proc/sys/net/ipv4/conf/lo/arp_announce
           echo "1" &gt;/proc/sys/net/ipv4/conf/all/arp_ignore
           echo "2" &gt;/proc/sys/net/ipv4/conf/all/arp_announce
       ;;
stop)
           /sbin/ifconfig lo:0 down
           echo "close LVS of RealServer DR" 
           echo "0" &gt;/proc/sys/net/ipv4/conf/lo/arp_ignore
           echo "0" &gt;/proc/sys/net/ipv4/conf/lo/arp_announce
           echo "0" &gt;/proc/sys/net/ipv4/conf/all/arp_ignore
           echo "0" &gt;/proc/sys/net/ipv4/conf/all/arp_announce
           ;;

*)
          echo "Usage: $0 {start|stop}" 
          exit 1
esac
exit 0

保存,退出;

运行脚本:    

~]# sh lvs-web.sh start  //启动脚本

    

4. 测试

测试①

若一切顺利,正常测试如下:

~]# while true ; do curl 138.138.82.222; sleep 1;done    //每秒执行一次curl 138.138.82.222

未分类
    
默认现在访问VIP:138.138.82.222,走的是LVS-1(MASTER)

测试②

断开MASTER:VIP飘到BACKUP上,访问VIP正常,Client 轮询依旧;

复活MASTER:VIP飘回MASTER上,访问VIP正常,Client 轮询依旧;    // 成功实现:LVS的高可用 和 Nginx的负载均衡

测试③

手动断开Nginx,然后再手动启动Nginx:

未分类   

结束.

keepalived VRRP协议与选举算解析

简介

什么是keepalived呢?keepalived是实现高可用的一种轻量级的技术手段,主要用来防止单点故障(单点故障是指一旦某一点出现故障就会导致整个系统架构的不可用)的发生。之所以说keepalived是轻量级的,是相对于corosync + ldirectord来说的。keepalived也可以实现高可用集群,而且配置起来比corosync + ldirectord简单方便很多,keepalived与corosync的工作机制相差很多。corosync + ldirectord实现的功能虽然强大,但配置起来比较麻烦,而keepalived功能虽然简单,但配置起来比较容易。也就是说keepalived可实现corosync + ldirectord实现的功能,只不过前者没有后者功能强大而已。

VRRP协议

在介绍keepalived之前,不得不先介绍下一个协议——VRRP。之所以要介绍这个协议,是因为VRRP协议是keepalived实现的基础。下面先来一块看下这个这协议是干吗用的吧。

未分类

如上图所示,通常,同一网段内的所有主机都设置一条相同的、以网关为下一跳的缺省路由。主机发往其他网段的报文将通过缺省路由发往网关,再由网关进行转发,从而实现主机与外部网络的通信。当网关发生故障时,本网段内所有以网关为缺省路由的主机将无法与外部网络通信,仅能实现内部主机间通信。缺省路由为用户的配置操作提供了方便,但是对缺省网关设备提出了很高的稳定性要求。增加出口网关是提高系统可靠性的常见方法,此时如何在多个出口之间进行选路就成为需要解决的问题。而VRRP正好解决了此问题。

VRRP:Virtual Router Redundancy Protocol,虚拟路由冗余协议。VRRP说白了就是实现地址漂移的,是一种容错协议,在提高可靠性的同时,简化了主机的配置。该协议能够实现将可以承担网关功能的一组路由器加入到备份组中,形成一台虚拟路由器,由VRRP的选举机制决定哪台路由器承担转发任务,局域网内的主机只需将虚拟路由器配置为缺省网关。

在VRRP协议出现之前,为了不让单个路由器成为本地与外部通信的瓶颈,我们需要有多个路由,在此种模式下,我们内部的主机就需要将自己的网关指向不同的路由器,这样的配置对我们的网关管理员来说是很麻烦的,且不容易实现。在VRRP协议出现后,为了不让单个路由器成为本地与外部通信的瓶颈,我们仍需要有多个路由,但可以使用同一个缺省网关,我们只需将内部主机指定一个缺省网关即可。VRRP协议会根据优先级来选择一个正常的路由作为主路由器实现与外部的通信,而其他路由则作为备份路由不参与转发。在此模式下,多个路由器组成虚拟路由器组,物理上是多个路由器组成,但在逻辑上却是表现为只有一个路由。效果如下图所示:

未分类

在上图中,Router A、Router B和Router C组成一个虚拟路由器。各虚拟路由器都有自己的IP地址。局域网内的主机将虚拟路由器设置为缺省网关。Router A、Router B和Router C中优先级最高的路由器作为Master路由器,承担网关的功能。其余两台路由器作为Backup路由器。当master路由器出故障后,backup路由器会根据优先级重新选举新的master路由器承担网关功能。Master 路由器周期性地发送VRRP 报文,在虚拟路由器中公布其配置信息(优先级等)和工作状况。Backup路由器通过接收到VRRP 报文的情况来判断Master 路由器是否工作正常。

VRRP根据优先级来确定备份组中每台路由器的角色(Master 路由器或Backup 路由器)。优先级越高,则越有可能成为Master 路由器。VRRP优先级的可配置的取值范围为1 到254。

为了防止非法用户构造报文攻击备份组,VRRP通过在VRRP报文中增加认证字的方式,验证接收到的VRRP报文。VRRP提供了两种认证方式:

  • simple:简单字符认证。发送VRRP 报文的路由器将认证字填入到VRRP 报文中,而收到VRRP 报文的路由器会将收到的VRRP 报文中的认证字和本地配置的认证字进行比较。如果认证字相同,则认为接收到的报文是真实、合法的VRRP 报文;否则认为接收到的报文是一个非法报文。

  • md5:MD5 认证。发送VRRP 报文的路由器利用认证字和MD5 算法对VRRP 报文进行摘要运算,运算结果保存在Authentication Header(认证头)中。收到VRRP 报文的路由器会利用认证字和MD5 算法进行同样的运算,并将运算结果与认证头的内容进行比较。如果相同,则认为接收到的报文是真实、合法的VRRP 报文;否则认为接收到的报文是一个非法报文。

在有多个路由器组成的虚拟路由中,当我们的内部主机很多时,如果所有主机都使用同一个master路由,会使得其他路由器很清闲,很浪费资源,我们期望我们本地的内部主机平分到各个路由器上,即让我们的内部主机的缺省网关指向不同的路由,从而减轻因只有一个master路由而造成网络带宽拥堵的负担。这就是负载分担VRRP。但这个如何实现呢?先看下面的配置效果图:

未分类

在此情况下,同一台路由器同时加入多个VRRP备份组,在不同备份组中有不同的优先级,从而实现负载分担。

在上图中,有三个备份组存在:

  • 备份组1:对应虚拟路由器1。Router A作为Master路由器,Router B和Router C作为Backup路由器。
  • 备份组2:对应虚拟路由器2。Router B作为Master路由器,Router A和Router C作为Backup路由器。
  • 备份组3:对应虚拟路由器3。Router C作为Master路由器,Router A和Router B作为Backup路由器。

为了实现业务流量在Router A、Router B和Router C之间进行负载分担,需要将局域网内的主机的缺省网关分别设置为虚拟路由器1、2和3。在配置优先级时,需要确保三个备份组中各路由器的VRRP优先级形成交叉对应。为了便于理解,我们假定有三个路由设备Router A、B、C和三台主机Host A、B、C,列举有在不同的虚拟路由组中。对路由器A来说,因在虚拟路由组1中Router A的优先级高于另外两个,因此,Router A 作为 Master 路由器,Router B 和Router C 作为 Backup路由器;同样,对路由器B来说,因在虚拟路由器组2中Router B的优先级高于另外两个,因此,Router B 作为 Master 路由器,Router A 和Router C 作为 Backup路由器;对路由器C来说,因在虚拟路由器组3中Router C的优先级高于另外两个,因此,Router C 作为 Master 路由器,Router A 和Router B 作为 Backup路由器。对不同的主机来说,一旦其master路由器出故障后,会在另外正常的路由器中根据优先级重新选定master路由。如这里假定Host A的默认网关指向Router A,即Host A指向虚拟路由器组1的默认网关,对主机A来说,如果其master路由出现故障,即Router A出现故障,则会从另外两个正常的备份虚拟路由中根据各自的优先级选取高优先级的作为新的master路由,这里就是选取Router B作为其master路由来完成网关功能。假如想了解更多关于VRRP协议相关的信息请查阅相关资料,这里不再过多介绍。

Keepalived

一、配置说明

keepalived的配置位于/etc/keepalived/keepalived.conf,配置文件格式包含多个必填/可选的配置段,部分重要配置含义如下:

  • global_defs: 全局定义块,定义主从切换时通知邮件的SMTP配置。
  • vrrp_instance: vrrp实例配置。
  • vrrp_script: 健康检查脚本配置。

细分下去,vrrp_instance配置段包括:

  • state: 实例角色。分为一个MASTER和一(多)个BACKUP。
  • virtual_router_id: 标识该虚拟路由器的ID,有效范围为0-255。
  • priority: 优先级初始值,竞选MASTER用到,有效范围为0-255。
  • advert_int: VRRP协议通告间隔。
  • interface: VIP所绑定的网卡,指定处理VRRP多播协议包的网卡。
  • mcast_src_ip: 指定发送VRRP协议通告的本机IP地址。
  • authentication: 认证方式。
  • virtual_ipaddress: VIP。
  • track_script: 健康检查脚本。

vrrp_script配置段包括:

  • script: 一句指令或者一个脚本文件,需返回0(成功)或非0(失败),keepalived以此为依据判断其监控的服务状态。
  • interval: 健康检查周期。
  • weight: 优先级变化幅度。
  • fall: 判定服务异常的检查次数。
  • rise: 判定服务正常的检查次数。

二、选举算法

keepalived中优先级高的节点为MASTER。MASTER其中一个职责就是响应VIP的arp包,将VIP和mac地址映射关系告诉局域网内其他主机,同时,它还会以多播的形式(目的地址224.0.0.18)向局域网中发送VRRP通告,告知自己的优先级。网络中的所有BACKUP节点只负责处理MASTER发出的多播包,当发现MASTER的优先级没自己高,或者没收到MASTER的VRRP通告时,BACKUP将自己切换到MASTER状态,然后做MASTER该做的事:1.响应arp包,2.发送VRRP通告。

MASTER和BACKUP节点的优先级如何调整?

首先,每个节点有一个初始优先级,由配置文件中的priority配置项指定,MASTER节点的priority应比BAKCUP高。运行过程中keepalived根据vrrp_script的weight设定,增加或减小节点优先级。规则如下:

  1. 当weight > 0时,vrrp_script script脚本执行返回0(成功)时优先级为priority + weight, 否则为priority。当BACKUP发现自己的优先级大于MASTER通告的优先级时,进行主从切换。

  2. 当weight < 0时,vrrp_script script脚本执行返回非0(失败)时优先级为priority + weight, 否则为priority。当BACKUP发现自己的优先级大于MASTER通告的优先级时,进行主从切换。

  3. 当两个节点的优先级相同时,以节点发送VRRP通告的IP作为比较对象,IP较大者为MASTER。

主从的优先级初始值priority和变化量weight设置非常关键,配错的话会导致无法进行主从切换。比如,当MASTER初始值定得太高,即使script脚本执行失败,也比BACKUP的priority + weight大,就没法进行VIP漂移了。所以priority和weight值的设定应遵循: abs(MASTER priority – BAKCUP priority) < abs(weight)。

另外,当网络中不支持多播(例如某些云环境),或者出现网络分区的情况,keepalived BACKUP节点收不到MASTER的VRRP通告,就会出现脑裂(split brain)现象,此时集群中会存在多个MASTER节点。