Openwrt 通过 keepalived 实现双机热备

事情起因就是因为我老是会折腾 Openwrt,虽然每次都会做配置备份,升级之后也能快速恢复,但是网络还是会受一段时间影响,所以总是会被骂。

于是我就想,能不能建两个 Openwrt 实现主备,在其中一个掉线后能快速切换到另一个,保证网络正常,也能避免挨骂的情况出现。

 1、方案选择

开始我想的是通过切换 dhcp 服务来实现切换,就是通过开关 dhcp 服务来实现,保证网络中只有一个 dhcp 服务。

这对于使用了 dhcp 服务分配IP的客户端来说应该没啥问题,但是如果客户端是 static 静态 IP 分配的话,就有问题了。因为两个 Openwrt 在同一个网络中,DHCP 切换带来还有网关的切换,动态分配 IP 的客户端可以接收正确的网关地址,但是静态分配的机器还要手动修改网关地址,这显然不极客。

于是就想到了 Linux 下常见的主备方案——keepalived。

2、keepalived

keepalived 的详细原理我们不做过多的深入,这里只做简单的介绍。

keepalived 是运行在 LVS(Linux Virtual Server: Linux虚拟服务) 之上的一个用于高可用 (HA) 的软件。

它通过枚举选择出一台服务器做 master 机器,这台机器会被分配到一个指定的虚拟 IP(VIP),外部程序可以通过这个虚拟 IP 访问这台 master 机器。

当这台 master 机器出现故障时,keepalived 会从其它备份机器上重新选举出一台机器做 master,并将之前的虚拟 IP 转移到这台新的 master 机器上。

对于外部应用来说,因为虚拟 IP 没变,所以不需要修改任何配置就能保证服务不会中断。

而触发选举的条件大概有下面几个:

  • keepalived 启动时
  • master 服务出现故障(断网、重启、本机 keepalived crash 等等一切无法与其它备机通信的情况)
  • 有新的备机加入且备机权重最大

3、keepalived 配置说明

全局定义

全局配置又包括两个子配置

  1. 全局定义(global definition)
  2. 静态路由配置(static ipaddress/routes)
# 全局定义 (global definition) 
global_defs {                      
   notification_email {      
   acassen@firewall.loc     
   failover@firewall.loc
   sysadmin@firewall.loc
   }
   notification_email_from Alexandre.Cassen@firewall.loc   
   smtp_server 192.168.200.1                         
   smtp_connect_timeout 30                                  
   router_id LVS_DEVEL     
}
notification_email: 表示 keepalived 在发生诸如切换操作时需要发送 email 通知以及 email 发送给哪些邮件地址邮件地址可以多个每行一个
notification_email_from admin@example.com: 表示发送通知邮件时邮件源地址是谁
smtp_server 127.0.0.1: 表示发送 email 时使用的 smtp 服务器地址这里可以用本地的 sendmail 来实现
smtp_connect_timeout 30: 连接 smtp 连接超时时间
router_id node1: 机器标识,通常配置主机名

# 静态地址和路由配置范例
static_ipaddress {
    192.168.1.1/24 brd + dev eth0 scope global
    192.168.1.2/24 brd + dev eth1 scope global
}
static_routes {
    src $SRC_IP to $DST_IP dev $SRC_DEVICE
    src $SRC_IP to $DST_IP via $GW dev $SRC_DEVICE
}
 这里实际上和系统里面命令配置 IP 地址和路由一样例如 
192.168.1.1/24 brd + dev eth0 scope global 相当于: ip addr add 192.168.1.1/24 brd + dev eth0 scope global
 就是给 eth0 配置 IP 地址路由同理, 一般这个区域不需要配置 
 这里实际上就是给服务器配置真实的 IP 地址和路由的在复杂的环境下可能需要配置一般不会用这个来配置我们可以直接用 vi /etc/sysconfig/network-script/ifcfg-eth1 来配置切记这里可不是 VIP 不要搞混淆了切记切记

VRRPD 配置

包括三个类:

  1. VRRP 同步组(synchroization group)
  2. VRRP 实例(VRRP Instance)
  3. VRRP 脚本
# VRRP 同步组 (synchroization group) 配置范例 
vrrp_sync_group VG_1 {   // 注意 vrrp_sync_group  后面可自定义名称如 lvs_httpd ,httpd
group {
http
mysql
}
notify_master /path/to/to_master.sh
notify_backup /path_to/to_backup.sh
notify_fault "/path/fault.sh VG_1"
notify /path/to/notify.sh
smtp_alert 
}
 其中 http 和 mysql 是实例名和下面的实例名一致 
notify_master /path/to/to_master.sh // 表示当切换到 master 状态时要执行的脚本
notify_backup /path_to/to_backup.sh // 表示当切换到 backup 状态时要执行的脚本
notify_fault "/path/fault.sh VG_1"  // keepalived 出现故障时执行的脚本
notify /path/to/notify.sh  
smtp_alert           // 表示切换时给 global defs 中定义的邮件地址发送邮件通知

# VRRP 实例(instance) 配置范例
vrrp_instance http {  // 注意 vrrp_instance 后面可自定义名称如 lvs_httpd ,httpd
state MASTER
interface eth0
dont_track_primary
track_interface {
eth0
eth1
}
mcast_src_ip <IPADDR>
garp_master_delay 10
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
autp_pass 1234
}
virtual_ipaddress {
#<IPADDR>/<MASK> brd <IPADDR> dev <STRING> scope <SCOPT> label <LABEL>
192.168.200.17/24 dev eth1
192.168.200.18/24 dev eth2 label eth2:1
}
virtual_routes {
# src <IPADDR> [to] <IPADDR>/<MASK> via|gw <IPADDR> dev <STRING> scope <SCOPE> tab
src 192.168.100.1 to 192.168.109.0/24 via 192.168.200.254 dev eth1
192.168.110.0/24 via 192.168.200.254 dev eth1
192.168.111.0/24 dev eth2
192.168.112.0/24 via 192.168.100.254
}
nopreempt
preemtp_delay 300
debug
}

state: state 指定 instance(Initial)的初始状态就是说在配置好后这台 服务器的初始状态就是这里指定的但这里指定的不算还是得要通过竞选通过优先级来确定里如果这里设置为 master 但如若他的优先级不及另外一台 那么这台在发送通告时会发送自己的优先级另外一台发现优先级不如自己的高那么他会就回抢占为 master

interface: 实例绑定的网卡因为在配置虚拟 VIP 的时候必须是在已有的网卡上添加的

dont track primary: 忽略 VRRP 的 interface 错误

track interface: 跟踪接口设置额外的监控里面任意一块网卡出现问题都会进入故障 (FAULT) 状态例如用 nginx 做均衡器的时候内网必须正常工作如果内网出问题了这个均衡器也就无法运作了所以必须对内外网同时做健康检查

mcast src ip: 发送多播数据包时的源 IP 地址这里注意了这里实际上就是在那个地址上发送 VRRP 通告这个非常重要一定要选择稳定的网卡端口来发送这里相当于 heartbeat 的心跳端口如果没有设置那么就用默认的绑定的网卡的 IP 也就是 interface 指定的 IP 地址

garp master delay: 在切换到 master 状态后延迟进行免费的 ARP(gratuitous ARP)请求,默认 5s

virtual router id: 这里设置 VRID 这里非常重要相同的 VRID 为一个组他将决定多播的 MAC 地址

priority 100: 设置本节点的优先级优先级高的为 master

advert int: 设置 MASTER 与 BACKUP 负载均衡之间同步即主备间通告时间检查的时间间隔, 单位为秒,默认 1s

virtual ipaddress: 这里设置的就是 VIP 也就是虚拟 IP 地址他随着 state 的变化而增加删除当 state 为 master 的时候就添加当 state 为 backup 的时候删除这里主要是有优先级来决定的和 state 设置的值没有多大关系这里可以设置多个 IP 地址

virtual routes: 原理和 virtual ipaddress 一样只不过这里是增加和删除路由

lvs sync daemon interface: lvs syncd 绑定的网卡,类似 HA 中的心跳检测绑定的网卡

authentication: 这里设置认证

auth type: 认证方式可以是 PASS 或 AH 两种认证方式

auth pass: 认证密码

nopreempt: 设置不抢占 master,这里只能设置在 state 为 backup 的节点上而且这个节点的优先级必须别另外的高,比如 master 因为异常将调度圈交给了备份 serve,master serve 检修后没问题,如果不设置 nopreempt 就会将调度权重新夺回来,这样就容易造成业务中断问题

preempt delay: 抢占延迟多少秒,即延迟多少秒后竞选 master

debug:debug 级别

notify master:和 sync group 这里设置的含义一样可以单独设置例如不同的实例通知不同的管理人员 http 实例发给网站管理员 mysql 的就发邮件给 DBA

# VRRP 脚本 
# 如下所示为相关配置示例
vrrp_script check_running {
   script "/usr/local/bin/check_running"
   interval 10
   weight 10
}

vrrp_instance http {
   state BACKUP
   smtp_alert
   interface eth0
   virtual_router_id 101
   priority 90
   advert_int 3
   authentication {
   auth_type PASS
   auth_pass whatever
   }
   virtual_ipaddress {
   1.1.1.1
   }
   track_script {
   check_running 
   }
}
# 首先在 vrrp_script 区域定义脚本名字和脚本执行的间隔和脚本执行的优先级变更, 如下所示:
vrrp_script check_running {
            script "/usr/local/bin/check_running"
            interval 10     # 脚本执行间隔
            weight 10       # 脚本结果导致的优先级变更 10 表示优先级 + 10-10 则表示优先级 - 10
            }
# 然后在实例(vrrp_instance) 里面引用有点类似脚本里面的函数引用一样先定义后引用函数名
track_script {
      check_running 
}

注意:
VRRP 脚本 (vrrp_script) 和 VRRP 实例 (vrrp_instance) 属于同一个级别
keepalived 会定时执行脚本并对脚本执行的结果进行分析,动态调整 vrrp_instance 的优先级。一般脚本检测返回的值为 0,说明脚本检测成功,如果为非 0 数值,则说明检测失败
如果脚本执行结果为 0,并且 weight 配置的值大于 0,则优先级相应的增加, 如果 weight 为非 0,则优先级不变
如果脚本执行结果非 0,并且 weight 配置的值小于 0,则优先级相应的减少, 如果 weight 为 0,则优先级不变
其他情况,维持原本配置的优先级,即配置文件中 priority 对应的值。
这里需要注意的是:
1) 优先级不会不断的提高或者降低
2) 可以编写多个检测脚本并为每个检测脚本设置不同的 weight
3) 不管提高优先级还是降低优先级,最终优先级的范围是在[1,254],不会出现优先级小于等于 0 或者优先级大于等于 255 的情况
这样可以做到利用脚本检测业务进程的状态,并动态调整优先级从而实现主备切换。

virtual_server 虚拟主机配置

关于 keeplived 的虚拟主机配置有三种如下所示
virtual server IP port
virtual server fwmark int
virtual server group string

以常用的第一种为例
virtual_server 192.168.1.2 80
含义: 设置一个 virtual server: VIP:Vport

delay_loop 3
含义: 设置 service polling 的 delay 时间即服务轮询的时间间隔

lb_algo rr|wrr|lc|wlc|lblc|sh|dh
含义: 设置 LVS 调度算法

lb_kind NAT|DR|TUN
含义: 设置 LVS 集群模式

persistence_timeout 120
含义: 设置会话保持时间秒为单位即以用户在 120 秒内被分配到同一个后端 realserver, 超过此时间就重新分配

persistence_granularity
含义: 设置 LVS 会话保持粒度 ipvsadm 中的 - M 参数默认是 0xffffffff 即每个客户端都做会话保持

protocol TCP
含义: 设置健康检查用的是 TCP 还是 UDP

ha_suspend
含义: suspendhealthchecker’s activity

virtualhost
含义: HTTP_GET 做健康检查时检查的 web 服务器的虚拟主机即 host 头

sorry_server
含义: 设置 backupserver 就是当所有后端 realserver 节点都不可用时就用这里设置的也就是临时把所有的请求都发送到这里

real_server
含义: 设置后端真实节点主机的权重等设置主要后端有几台这里就要设置几个

weight 1
含义: 设置给每台的权重 0 表示失效 (不知给他转发请求知道他恢复正常) 默认是 1

inhibit_on_failure
含义: 表示在节点失败后把他权重设置成 0 而不是冲 IPVS 中删除

notify_up |
含义: 设置检查服务器正常 (UP) 后要执行的脚本
notify_down |
含义: 设置检查服务器失败 (down) 后要执行的脚本

注: keepalived 检查机制说明
keepalived 健康检查方式有: HTTP_GET|SSL_GET|TCP_CHECK|SMTP_CHECK|MISC_CHECK 几种如下所示

#HTTP/HTTPS 方式 
HTTP_GET|SSL_GET {      # 设置健康检查方式

url {                   # 设置要检查的 URL 可以有多个
path /                  # 设置 URL 具体路径
digest <STRING>         # 检查后的摘要信息这些摘要信息可以通过 genhash 命令工具获取                                   
status_code 200         # 设置返回状态码
}
connect_port 80         # 设置监控检查的端口
bindto  <IPADD>         # 设置健康检查的 IP 地址
connect_timeout   3     # 设置连接超时时间
nb_get_retry  3         # 设置重连次数
delay_before_retry  2   # 设置重连间隔
} 

#TCP 方式  
TCP_CHECK     {
connect_port 80         # 设置监控检查的端口
bindto  <IPADD>         # 设置健康检查的 IP 地址
connect_timeout   3     # 设置连接超时时间
nb_get_retry  3         # 设置重连次数
delay_before_retry  2   # 设置重连间隔
}
#SMTP 方式 (这个可以用来给邮件服务器做集群)
SMTP_CHECK {
host {
connect_ip <IP ADDRESS>
connect_port <PORT>     # 默认检查 25 端口
14 KEEPALIVED
bindto <IP ADDRESS>
}
connect_timeout <INTEGER>
retry <INTEGER>
delay_before_retry <INTEGER>
helo_name <STRING>|<QUOTED-STRING>
} 

#MISC 方式 这个可以用来检查很多服务器只需要自己会些脚本即可
MISC_CHECK {
misc_path <STRING>|<QUOTED-STRING>  # 外部程序或脚本
misc_timeout <INT>                  # 脚本或程序执行超时时间
misc_dynamic                                              
# 这个就很好用了可以非常精确的来调整权重是后端每天服务器的压力都能均衡调配这个主要是通过执行的程序或脚本返回的状态代码来动态调整 weight 值使权重根据真实的后端压力来适当调整不过这需要有过硬的脚本功夫才行哦
# 返回 0 健康检查没问题不修改权重
# 返回 1 健康检查失败权重设置为 0
# 返回 2-255 健康检查没问题但是权重却要根据返回代码修改为返回码 - 2 例如如果程序或脚本执行后返回的代码为 200# 那么权重这回被修改为 200-2
}

4、配置过程

弄懂了keepalived的配置,那么整个搭建过程就会很简单,思路就是通过keepalived配制出一个VIP,然后各个节点的openwrt的dhcp服务将dns、gateway指向这个VIP即可,具体操作细节可查看官方文档