高可用之Keepalived热备了解及实践

Keepalived 是什么?

  1. Keepalived起初是为 LVS (Linux虚拟服务器)设计的用来检测集群系统中各个服务节点的状态, 根据TCP/IP的第345层的交换机制检测每个服务的状态; 如果其中某个服务节点出现异常或者工作出现故障, 会将该节点从系统集群中剔除, 这个过程是自动完成的, 无须人工干预, 人工仅需要修复出现故障的服务节点;

  2. Keepalived支持 VRRP(虚拟路由冗余协议), 主要是为了解决静态路由出现单点故障, 通过VRRP实现不间断持续可用性, 确保服务稳定运行; 因此Keepalived既拥有服务器状态检测和隔离功能又拥有 HA cluster (高可用集群)功能.

  3. Keepalived可以用来检测节点部署的WEB, 数据库等服务, 除了实现服务器自身故障自动转移同时还可以检测在节点部署的服务是否正常运行, 防止因当前节点负载过高导致相关服务进程被Kill掉或服务意外崩溃导致服务暂停. 实现针对单一或多个应用程序的高可用.

工作原理

Keepalived根据设置的权重进行选举出一台Master服务器, 并分配一个指定的虚拟IP来进行通信, 如果Master服务器出现断网, 重启, 关机, 本机的Keepalived crash或者其他崩溃的情况时, Backup服务器们检测到Master服务器通信不正常, 因此判定Master服务器故障, 然后Backup服务器们通过设置的权重重选出新的Master服务器并指定一样的虚拟IP充当Master服务器将服务恢复正常.

选举策略

Keepalived是根据 VRRP 协议完全通过权重大小进行选举Master服务器, 权重最大0~255的会被选举为Master服务器, 下面几种情况会产生选举:

  1. Keepalived启动的时候.
  2. Master服务器断网, 重启, 关机, 本机的Keepalived crash或者其他崩溃的情况时.
  3. 有新的备份服务器加入且权重最大的时候.

搭建Keepalived环境

配置文件简介

Keepalived官方使用说明英文原版PDF地址
keepalived只有一个配置文件 keepalived.conf , 里面主要包括以下几个配置区域,
分别是 global_defs , static_ipaddress , static_routes , vrrp_script , vrrp_instancevirtual_server

  1. global_defs 主要是配置故障发生时的通知对象以及机器标识

    • notification_email 故障发生时给发送警告邮件, 可以设置多个, 每个一行
    • notification_email_from 通知邮件的发件人
    • smtp_server 通知邮件的 smtp 发件服务器地址
    • smtp_connect_timeout 连接发件服务器的超时时间, 单位秒
    • enable_traps 开启 SNMP陷阱
    • router_id 标识当前节点的字符串, 通常为主机名, 但不一定必须. 故障发生时, 邮件通知会用到该字符串
    • lvs_sync_daemon 绑定lvs syncd的网卡
    • enable_script_security 开启安全脚本执行
    • script_user 设置执行脚本的 Linux 用户
    global_defs {
        notification_email {
          admin@localhost.localdomain
          devops@localhost.localdomain
        }
        notification_email_from monitor@localhost.localdomain
        smtp_server localhost.localdomain
        smtp_connect_timeout 30
        router_id BaseServer_1
        vrrp_mcast_group4 
        lvs_sync_daemon enp0s3 BASE_1
        enable_script_security
        script_user 
    }
    

    global_defs 设置的邮件信息不适合用于实际使用,所以一般需要自行编写脚本来发送邮件.
    script_user 此项如不存在即不设置安全用户, 则必须保证 keepalived_script 用户存在

  2. static_ipaddressstatic_routes 分别表示当前节点的IP和路由信息.

    通常情况下服务器已经配置IP和路由, 这两个节点可以不用配置

    static_ipaddress {
        192.168.123.5/24 brd 192.168.123.255 dev eth0 scope global
        # 192.168.123.5/24 表示静态IP: 192.168.123.5 和子网掩码: 255.255.255.0 
        # dev eth0 表示网卡设备名称
        # scope global 全局作用域有效
    }
    static_routes {
        192.168.123.0/24 via 192.168.123.1 dev eth0
        # 设置外部路由, 192.168.123.1 - 192.168.123.254 都是从 192.168.123.1 出去
    }
    

    这相当于Keepalived在启动时执行:

    /sbin/ip addr add 192.168.123.5/24 brd 192.168.123.255 dev eth0 scope global
    /sbin/ip route add 192.168.123.0/24 via 192.168.123.1 dev eth0
    

    在停止时执行:

    /sbin/ip addr del 192.168.123.5/24 brd 192.168.123.255 dev eth0 scope global
    /sbin/ip route del 192.168.123.0/24 via 192.168.123.1 dev eth0
    

    一般在购买的服务器已经自动配置好了静态IP和网关, 所以这两个部分可以忽略不设置

  3. vrrp_script 健康检查
    当检查失败时会将 vrrp_instancepriority 减少相应的值.

    vrrp_script chk_http_port {
        script "echo hello"
        interval 1
        weight -10
        timeout
        rise
        fall
        user
        init_fail
    }
    
    参数说明
    script要执行的脚本
    interval脚本执行的间隔, 单位
    weight要修改的权重大小, +-priority
    timeout脚本执行多少秒没有返回视为失败
    rise成功多少次后上升权重
    fall成功多少次后下降权重
    user执行脚本的用户
    init_fail假设脚本最初处于失败状态

    如果脚本执行失败(这里是: echo hello ), 相应的 vrrp_instance 中的优先级会减少10个点

  4. vrrp_instancevrrp_sync_group区域
    vrrp_instance是用来定义对外提供服务的vIP区域及其相关属性, 是检测节点本身状态的主要功能.
    vrrp_sync_group是用来定义vrrp_intance组, 使得这个组内成员动作一致.

    例如两个vrrp_instance同属于一个vrrp_intance组, 那么当其中一个vrrp_instance出现故障切换时
    另一个vrrp_instance也会一同切换, 无论该节点本身是否出现故障.

    vrrp_sync_group 区域配置详情:

    vrrp_sync_group vGroup_1 {
    group {
        inside_network   # vrrp_instance 节点的名称
        outside_network  # 每个可以移动的IP
        ...
    }
    
    notify_master /script/master.sh
    notify_backup /script/backup.sh
    notify_fault "/script/fault.sh vGroup_1"
    notify /script/notify.sh
    smtp_alert
    }
    
    参数说明
    notify_master当该节点切换为Master时执行的脚本
    notify_backup当该节点切换为Backup时执行的脚本
    notify_fault当该节点切换故障/出错时执行的脚本
    notify当前节点切换(不管切换成功还是失败)时执行的脚本
    smtp_alert是否开启邮件通知

    smtp_alert 使用全局区域的邮件设置来发通知
    notify 会在前面三个脚本执行完成时进行调用, 并且会传递如下三个参数:

    $1 = 切换后会传递是 GROUP 还是 INSTANCE
    $2 = 节点在 GROUP 或者 INSTANCE 设置的名称
    $3 = 切换的目标节点状态(MASTER/BACKUP/FAULT)

    vrrp_instance 区域配置详情

    vrrp_instance BASE_1 {
        state MASTER
        interface eth0
        use_vmac <VMAC_INTERFACE>
        dont_track_primary
        track_interface {
            enp0s3
            eth1
        }
        mcast_src_ip <IPADDR>
        garp_master_delay 10
        virtual_router_id 1
        priority 100
        advert_int 1
        authentication {
            auth_type PASS
            auth_pass 1111
        }
        virtual_ipaddress {
            192.168.123.150/24 brd 192.168.123.255 dev enp0s3
        }
        virtual_routes {
            192.168.123.0/24 via 192.168.123.1 dev enp0s3
        }
        track_script {
            chk_http_port
        }
        nopreempt
        preempt_delay 300
        debug
        notify_master <STRING>|<QUOTED-STRING>
        notify_backup <STRING>|<QUOTED-STRING>
        notify_fault <STRING>|<QUOTED-STRING>
        notify <STRING>|<QUOTED-STRING>
        smtp_alert
    }
    
    参数说明
    stateMaster/Backup 具体抢占IP是根据选举权重决定
    interface主网卡,用来发VRRP
    use_vmac否使用VRRP的虚拟MAC地址
    dont_track_primary忽略VRRP网卡错误,默认未设置
    track_interface监控列表中的网卡,任何一个不通就会切换到FAULT状态(可选项)
    mcast_src_ip修改VRRP组播包的源地址,默认源地址为Master的IP
    garp_master_delay当切为主状态后多久更新ARP缓存,默认5秒
    virtual_router_id用来区分多个instanceVRRP组播, 0~255之间
    priority选举权重0~255之间,不在此区间会识别为默认值100
    advert_intVRRP包的间隔时间/健康检查, 即多久进行一次Master选举
    authentication认证区域[PASS|HA(IPSEC)],PASS:密码只识别前8
    virtual_ipaddress被选举为Master后设置的虚拟IP
    virtual_routes被选举为Master后设置的虚拟路由
    virtual_ipaddress_excluded发送的VRRP包里不包含的IP地址,减少回应VRRP包的个数.网卡绑定IP比较多的时候有用
    nopreempt允许一个低权重节点作为Master,即便有priority更高的节点启动
    preempt_delayMaster启动多久之后进行接管资源(vIP/Route信息等),前提没有设置nopreempt选项

    当使用track_script时可以不用加nopreempt,只需要加上preempt_delay 5
    这里的间隔时间要大于vrrp_script中定义的时长
    注意: 同一网段中virtual_router_id的值不能重复, 否则会出错, 相关错误信息如下
    Tips: 可以用tcpdump -nn -i any net 224.0.0.0/8命令来查看该网络中所存在的vrid

    Keepalived_vrrp[27120]: ip address associated with VRID not present in received packet :
    one or more VIP associated with VRID mismatch actual MASTER advert
    bogus VRRP packet received on enp0s3 !!!
    receive an invalid ip number count associated with VRID!
    VRRP_Instance(xxx) ignoring received advertisment...
    
  5. virtual_server_groupvirtual_server区域

    virtual_server_group 一般在超大型的LVS中用到, 一般情况下用不到, 暂时略过.
    virtual_server 将后端某一台real_server上的某个服务可以属于多个Virtual Server并且只做一次健康检查

    这两项配置主要是针对LVS+Keepalived, 如果使用的Nginx代替LVS 无需进行配置.

    virtual_server区域配置详情

    virtual_server 192.168.123.150 80 { 
        #虚拟服务地址和端口,使用空格分隔,其中地址为VIP
        delay_loop 1     
        lb_algo rr  
        lb_kind NAT 
        persistence_timeout 30  
        persistence_granularity 255.255.255.0
        protocol TCP
        sorry_server 192.168.123.150 65000
    
        real_server 192.168.123.151 80 {
            weight 1 
            HTTP_GET { 
                url {      
                    path /index.php
                    status_code 200
                }
                connect_timeout 2
                nb_get_retry 1
                delay_before_retry 0
            }
        }
    }
    
    参数说明
    delay_loop健康检查时间间隔,单位
    lb_algo负载均衡LB调度算法
    lb_kind节点模型,有NAT/DR/TUN三种
    persistence_timeout持久会话保持时长
    persistence_granularity指定持久连接的掩码粒度
    protocol监控服务的协议类型1.3.0版本之后支持UDP
    virtualhostHTTP_GETSSL_GET配置请求header
    real_server真正提供服务的服务器, 一般是当前节点的的IP和具体服务器的端口
    sorry_server当所有real server宕掉时,sorry server顶替,一般由本机提供一个web提示网站正在维护
    weight节点权重
    notify_upreal server启动时运行的脚本
    notify_downreal server宕机时运行的脚本
    HTTP_GET健康状况检查的检查方式: [HTTP_GET|SSL_GET|TCP_CHECK|MISC_CHECK]
    urlHTTP_GET|SSL_GET时检查的路由,可以有多个
    path要检查的路径, 例如/index.php
    digest状态码: [status_code|digest|digest+status_code]
    digest值用keepalivedgenhash命令生成,一般使用status_code就可以了
    connect_port源端口在指定的端口上连接远程服务器
    connect_timeout响应超时,需要重新连接. 单位
    nb_get_retry重试次数, 单位numerical
    delay_before_retry重试的时间间隔
    1. 如果使用的内核版本不是2.2,则nat_mask已过时, 该标志使您能够定义反向NAT粒度
    2. 关于lb_kind的节点模型详细说明可以参看该文档
    3. 关于LVS的调度算法详细说明可以参看该文档

搭建环境实践

  • 对外提供服务的前端服务器, nginx反代, 拟定IP: 192.168.123.150
  • 主web服务器, 提供nginx, php功能, 拟定IP: 192.168.123.151
  • 备web服务器, 提供nginx, php功能, 拟定IP: 192.168.123.152
  • 拟定Keepalived虚拟IP为: 192.168.123.155

数据库服务器也可以进行双机热备, 读写分离等, 后面单独发布文章.

  1. 安装 Nginx
    文档TODO中
  2. 安装 PHP7.4.6
    文档TODO中
安装 Keepalived

安装Keepalived非常容易, 在Linux各大发行版官方仓库中都包含该软件, 只要使用的发行版不是太旧, 可以获得比较新的版本, 该软件在Debian Buster官方仓库中的版本为: 2.0.10. 使用以下命令分别在主备服务器安装Keepalived

# 查看仓库中的 keepalived 版本, 看是否符合预期
sudo apt show keepalived
# 安装 keepalived
sudo apt install keepalived -y
# 执行 keepalived --vresion 查看是否正常输出
sudo keepalived --vresion
实践
  1. 制定配置文件
  • 开始配置之前需要准备发送邮件的脚本, 由于默认配置的邮件不适合实际使用, 所以手写了发送邮件的程序 sendmail, 主要包含如下功能:

    Usage: parameter [-s | -u | -c| -a | -h]
    
    -s                Fault server name
    -u                Send to user name
    -a                Send to email address
    -c                Email text
    -t                Email title
    -h                Print help info
    --help
    
    Please input fault server name, send to user and send to address.
    
  • 编写发送邮件的脚本keepalived_sendmail.sh, 大致内容如下:

    #!/bin/bash
    # Author: AENO - 南岭第一烤橘王
    
    # 脚本执行时间, 可以当做切换时间
    change_time=$(date "+%Y-%m-%d %H:%M:%S")
    # 节点标识, 将作为发件人用户
    server_id="$2"
    # 发送到的邮箱用户
    to_user="mail"
    # 发送到的邮箱地址
    to_user_address="mail@admin.cn"
    # 标题
    title="$1->$2 State: $3"
    # 消息正文, 支持 HTML 标签
    comment="<h3>热备节点状态切换通知<h3> <p>当前节点: $2</p> <p>所属区域: $1</p> <p>当前状态: $3</p> <p>切换时间: $change_time</p>"
    
    function send_mail() {
        /home/base/sendmail -s $server_id -u $to_user -a $to_user_address -t "$title" -c "$comment"
    }
    
    function write_log() {
        cat <<EOF | tee -a /home/base/keepalived_change.log
    
    #######################################################
    Change_TIME    : $change_time
    SERVER_ID      : $server_id
    SERVER_STATE   : $title 
    SENDMAIL_STATE : $1
    #######################################################
    EOF
    
    }
    
    function main() {
        send_mail
        write_log $?
    }
    
    main
    
  • 编写用于监测站点运行状况的脚本, 当该节点web服务出现异常时也需要切换到备用服务器, 大致如下:

    #!/bin/bash
    # Author: AENO
    
    # 检测站点状态码
    function site_state_check() {
        status_code=$(curl -sILw "%{http_code}" -o /dev/null http://$1/index.php)
        if [[ $status_code != 200 ]]; then
            send_fault_notify $@
            exit 1
        else
            send_fault_recovery $@
            exit 0
        fi
    }
    
    # 故障发生通知
    function send_fault_notify() {
        # 如果故障锁定文件不存在, 则创建故障锁定文件并同时发送故障通知
        if [[ ! -f /tmp/fault.lock ]]; then
            touch /tmp/fault.lock
            bash /home/base/keepalived_sendmail.sh 故障通知 $2 "$2--故障通知!!!"
        fi
    }
    
    # 故障恢复通知
    function send_fault_recovery() {
        if [[ -f /tmp/fault.lock ]]; then
            rm -rf /tmp/fault.lock
            bash /home/base/keepalived_sendmail.sh 故障恢复通知 $2 "$2--故障恢复通知!"
        fi
    }
    
    function main() {
        site_state_check $@
    }
    
    main $@
    

开始制定主服务器配置

```CoffeeScript
# 全局设置
global_defs {
    # 开启安全脚本执行
    enable_script_security
    # 设置执行脚本的用户
    script_user base
}
# 健康检测
vrrp_script site_check {
    script "/usr/bin/bash /home/base/site_check.sh 192.168.123.151 Master-BASE_1"
    interval 1
    weight -30
    timeout 1
    # rise 2
    fall 3
    # 执行该脚本的用户
    user base

    # 如果主服务器执行 site_check.sh 失败 3 次权重下降 30, 
    # 下降后选举权重低于备用服务器将发生切换
    # 当再次成功执行后, 权限恢复, 重新切换回主服务器
}
# 检测服务器本身状态
vrrp_instance BASE_1 {
    # 将 Base_1 设置标示为主服务器抢占IP, 最终决定权在选举权限设置的priority大小
    state MASTER 
    # 设置发送 VRRP 包的网卡, 填写主网卡设备(负责通信的网卡设备名称)
    interface enp0s3
    virtual_router_id 51
    # 当切为主状态后多久更新ARP缓存,默认5秒
    garp_master_delay 5
    # 选举权重 默认100
    priority 50
    # 发送 VRRP 包的间隔时间
    advert_int 1
    # 认证区域
    authentication {
        auth_type PASS
        auth_pass 12345678
    }
    # 虚拟IP  --- vIP
    virtual_ipaddress {
        192.168.123.155/24 brd 192.168.123.255 dev enp0s3 scope global
    }
    # 健康检查, 调用前面设置的健康检查具体规则
    track_script {
    site_check
    }
    # debug
    notify "/usr/bin/bash /home/base/keepalived_sendmail.sh"
    # 由于 global_defs 设置的邮件警告环境不太适合实际使用,
    # 因而没有进行配置, 所以这里的邮件发送选项不需要设置, 使用上面的脚本通知即可
    # smtp_alert
}
```

制定备服务器配置

```CoffeeScript
# 全局设置
global_defs {
    # 开启安全脚本执行
    enable_script_security
    # 设置执行脚本的用户
    script_user base
}
# 健康检测
vrrp_script site_check {
    script "/usr/bin/bash /home/base/site_check.sh 192.168.123.152 BUCKUP-BASE_2"
    interval 1
    # weight -30
    timeout 1
    # rise 2
    fall 3
    # 执行该脚本的用户
    user base

    # 如果主服务器执行 site_check.sh 失败 3 次权重下降 30, 
    # 下降后选举权重低于备用服务器将发生切换
    # 当再次成功执行后, 权限恢复, 重新切换回主服务器
}
# 检测服务器本身状态
vrrp_instance BASE_2 {
    # 将 Base_1 设置标示为主服务器抢占IP, 最终决定权在选举权限设置的priority大小
    state BACKUP 
    # 设置发送 VRRP 包的网卡, 填写主网卡设备(负责通信的网卡设备名称)
    interface enp0s3
    virtual_router_id 51
    # 当切为主状态后多久更新ARP缓存,默认5秒
    garp_master_delay 5
    # 选举权重 默认100
    priority 40
    # 发送 VRRP 包的间隔时间
    advert_int 1
    # 认证区域
    authentication {
        auth_type PASS
        auth_pass 12345678
    }
    # 虚拟IP  --- vIP
    virtual_ipaddress {
        192.168.123.155/24 brd 192.168.123.255 dev enp0s3 scope global
    }
    # 健康检查, 调用前面设置的健康检查具体规则
    track_script {
    site_check
    }
    # debug
    notify "/usr/bin/bash /home/base/keepalived_sendmail.sh"
    # 由于 global_defs 设置的邮件警告环境不太适合实际使用,
    # 因而没有进行配置, 所以这里的邮件发送选项不需要设置, 使用上面的脚本通知即可
    # smtp_alert
}
```

备用服务器其实只需要做如下修改即可

  1. 将监测脚本修改为 site_check.sh 192.168.123.152 BUCKUP-BASE_2 进行监测和发送通知
  2. 注释 weight -30, 当备用服务器站点监测故障时此时主备都无法提供服务, 但备用服务器不用修改权重, 因为此时不适合再参与选举
  3. 修改备用服务器的选举权重 priority 40 确保小于主服务器
  1. 测试热备效果, 校验配置是否正确

开始之前, 分别在两台web服务器上创建index.php确保可以正常访问.

前端服务器访问:
前端服务器
服务器访问
web1服务器
服务器访问
web2服务器

  • 测试当服务器网络中断后前端服务器访问:

    nmcli con down static
    

    测试访问结果如下:
    image.png
    查看邮件通知情况, 可以查看到BACKUP--BASE_2成功切换为服务器, 继续提供服务.
    image.png

  • 重新启动服务器的网络连接, 然后停止Nginx服务后再测试访问前端服务器查看效果.

    sudo systemctl stop nginx
    

    可以查看到已经从服务器成功切换到服务器了
    image.png
    此时查看邮件:
    image.png

    可以查看到邮件也收到了

    1. 第二封表示重新启动服务器后服务器成功通过选举成为Master服务器
    2. 第三封表示服务器网络恢复后,服务器重新被选举为BACKUP服务器
    3. 第四封表示服务器停止Nginx健康检测脚本检测到服务器无法继续提供服务,发送了故障邮件
    4. 第五封表示服务器停止服务后健康检测脚本检测到并降低了服务器的选举权重被重选为BACKUP服务器
    5. 第六封表示服务器停止服务后健康检测脚本降低服务器权重BASE_2被重选选举为MASTER服务器,并继续提供服务
  • 重新启动服务器的Nginx并停止服务器的Nginx查看访问效果和邮件通知

    base@base1:~$ sudo systemctl start nginx
    base@base2:~$ sudo systemctl stop nginx
    

    可以查看到服务器待Nginx恢复服务后健康检测脚本重新提升了服务器选择权重成功通过竞选重新成为MASTER服务器
    image.png
    从邮件通知来看也是可以明显查看到切换状态,正确收到BASE_1故障恢复通知并竞选为MASTER, 而服务器BASE_2被重新选举回BACKUP服务器,同时也收到了因服务器停止Nginx服务后发送的故障邮件.
    image.png

总结

  1. 通过前面的部署及测试可以更加详细了解Keepalived热备切换流程.
  2. 服务器本身出现故障(网络中断)时可以及时被备用服务器的Keepalived侦测到并快速切换为Master服务器继续提供服务, 很好的保证了服务的高可用性.
  3. 通过使用健康检测功能加上检测脚本: site_check.sh成功检测服务器提供的服务故障后成功将其降权, 然后选举服务器为MASTER服务器将vIP漂移到BASE_2继续提供服务.
  4. 无论是服务器和服务器哪个发生了故障, 都可以通过设置的邮件通知脚本收到故障邮件便于及时处理发生故障的服务器使其恢复,.

一般情况下, 因为当前两个节点部署的服务是完全一样的, 服务的公共配置文件和WEB站点的目录应该由NFS共享, 然后做软链接便于统一管理, 当前实践只是测试运行效果并非生产环境所以没有这样操作.

# Debian   Linux  

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×