IPVS调度模式和Keepalived详解

简介

IPVS在内核中实现了传输层负载均衡,是一个L4的交换机。IPVS在一群真实服务器的前面,运行一个LB角色的主机,该主机面向客户端,提供了单一IP地址的虚拟服务。

和netfilter的交互

在Director上,LVS钩子在netfilter框架中的位置,不管是来程、回程报文,均从左侧进入、右侧出去。对于DR/TUN模式,回程报文不经过Director:

 

 

  1. 当Director接收到封包时,如果它的目的地址是VIP,则PREROUTING后路由决策,会导致封包被发送到LOCAL_IN。这是因为VIP是Local地址,ip_vs_in挂钩到LOCAL_IN,是VIP必须在Director上的原因,因为只有目标是本地地址,才能走到LOCAL_IN
  2. LVS在LOCAL_IN注册钩子ip_vs_in,需要注意的是,钩子具有优先级。最低优先级的钩子,最先看见封包。LVS的钩子的优先级,比iptables规则高,因此iptables规则首先应用到封包,然后是LVS
  3. 上一步中,如果ip_vs_in得到封包,它会根据负载均衡算法,选择一个RS,然后如变魔法一般,将封包直接瞬移到POSTROUTING链
  4. LVS不会在FORWARD中寻找ingress封包,仅当NAT模式下,收到来自RS的回程报文时,LVS才和FORWARD链相关,封包在此unNATed(源地址从RS改为Director)。同样需要注意,LVS在任何iptables规则之后看到封包

负载均衡模式

只有fullNAT或者NAT才支持端口映射,也就是说VIP和RIP使用不同的端口。

fullNAT

不是标准内核的一部分。

来程报文: ClientIP: ClientPort – VIP:VPORT ⇨ IPVS Director ⇨ DirectorIPDirectorPort – RIPRPORT

回程报文:RIP: RPORT – DirectorIP: DirectorPort ⇨ IPVS Director ⇨ VIP:VPORT – ClientIPClientPort

和NAT模式相比,IPVS Director在进行DNAT的同时,还进行SNAT。这样获得以下优势:

  1. 客户端可以和Director、真实服务器在同一个局域网内

缺点是:

  1. 真实服务器看不到客户端的真实IP地址。只能由Director通过TCP Options携带真实IP,但是TCP Option只有40字节,可能客户端已经占用导致空间不足

NAT

来程报文: ClientIP: ClientPort – VIP:VPORT ⇨ IPVS Director ⇨ ClientIP: ClientPort – RIPRPORT

回程报文:RIP: RPORT – ClientIP: ClientPort ⇨ IPVS Director ⇨ VIP:VPORT – ClientIP: ClientPort

基于NAT的虚拟服务器,当LB具有两个网络接口时:

  1. 一个接口分配面向外部的IP地址
  2. 另一个接口分配私有的、面向内部的IP地址

LB从外部接口接受流量,然后经过NAT,转发给内部网络中的真实服务器。

该模式的缺陷:

  1. LB是性能瓶颈,它需要转发两个方向的流量
  2. RS上必须进行适当的路由,确保针对ClientIP的路由转发给IPVS Director处理
  3. 客户端必须和RS不在同一网段。这个限制可以通过配置解决:
    1. 在Director上关闭ICMP重定向:
echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects
echo 0 > /proc/sys/net/ipv4/conf/default/send_redirects
echo 0 > /proc/sys/net/ipv4/conf/eth0/send_redirects

2.在RS上,把Director配置为出口包的唯一网关

 

Tunneling

在此模式下,LB通过IP隧道将请求发送给真实服务器。

好处是可扩容,LB将请求转发给真实服务器后,真实服务器直接将响应发送给客户端,不再需要经过LB。由于大部分情况下都是请求包小、应答包大,这种模式下LB的性能压力较小。

该模式的缺陷:对网络结构依赖很大,真实服务器必须支持IP隧道协议。

 

Direct Routing

用户请求LB上的VIP,LB将请求直接转发(通过修改L2封包的方式,L3保持不变)给真实服务器。

好处是可扩容,而且没有Tunneling模式的封包解包开销。

真实服务器要直接应答客户端,则应答包的源IP必须是VIP。这就导致LB和所有真实服务器都需要共享VIP+MAC的组合。不经过合理配置,则真实服务器可能直接接收到请求,绕过LB。

该模式的缺陷:

  1. RS和LVS的VIP,必须使用相同端口
  2. RS和LVS不能在同一机器上,否则该RS访问VIP不经过LVS的负载均衡,而是直接访问自己
  3. RS和LVS必须位于同一VLAN或局域网

 

负载均衡算法

rr

简单的轮询算法,均等地对待每一台服务器,而不管服务器上实际的连接数和系统负载。

wrr

带权重的轮询算法,可以保证处理能力强的服务器处理更多的访问流量?调度器可以自动问询真实服务器的负载情况,并动态地调整其权值。

lc

IPVS表存储了所有活动的连接,基于此信息可以将请求发送给具有最少已建立连接的RS。如果集群系统的真实服务器具有相近的系统性能,采用”最小连接”调度算法可以较好地均衡负载。

wlc

考虑权重的最少连接算法,具有较高权值的服务器将承受较大比例的活动连接负载。

lblc

通常用于缓存集群,将来自同一个目的地址的请求分配给同一台RS,如果RS满载则将这个请求分配给连接数最小的RS。

lblcr

根据请求的目标IP地址找出该目标IP地址对应的服务器组,按“最小连接”原则从该服务器组中选出一台服务器,若

  1. 服务器没有超载, 将请求发送到该服务器
  2. 若服务器超载;则按“最小连接”原则从整个集群中选出一台服务器,将该服务器加入到服务器组中,将请求发送到该服务器

当该服务器组有一段时间没有增减成员,将最忙的服务器从服务器组中删除

sh

源地址哈希调度以源地址为关键字查找一个静态hash表来获得需要的RS

dh

目的地址哈希调度以目的地址为关键字查找一个静态hash表来获得需要的RS

nq

如果存在空闲服务器,则请求转发给它;否则,按seq算法

seq

最短期望延迟(Shortest Expected Delay)。期望延迟公式: (Ci + 1) / Ui ,其中Ci为服务器i的当前连接数,Ui为服务器i的固定服务速率(权重)

ipvsadm命令

管理Linux虚拟服务器的底层命令行工具,可以创建、修改、查看位于Linux内核中的虚拟服务器表

格式:

# 添加或修改虚拟服务
ipvsadm -A|E virtual-service [-s scheduler] [-p [timeout]] [-M netmask] [-b sched-flags]
# 删除虚拟服务
ipvsadm -D virtual-service
# 清空虚拟服务表
ipvsadm -C
# 从标准输入还原虚拟服务表
ipvsadm -R
# 将虚拟服务表导出到标准输出
ipvsadm -S [-n]
# 为虚拟服务添加、编辑一个真实服务器
ipvsadm -a|e virtual-service -r server-address [-g|i|m] [-w weight] [-x upper] [-y lower]
# 为虚拟服务删除一个真实服务器
ipvsadm -d virtual-service -r server-address
# 列出虚拟服务
ipvsadm -L|l [virtual-service] [options]
# 清零包、字节、速率计数器
ipvsadm -Z [virtual-service]
# 设置IPVS连接的超时值,三个值,分别用于TCP会话、接收到FIN后的TCP会话、UDP包
ipvsadm --set tcp tcpfin udp
# 启动连接同步守护进程,state可以为master或backup。连接同步守护进程在内核中运行
# 主守护进程周期性的组播连接的变更,备份守护进程则接收组播消息并创建对应的连接。一旦主宕机,则从具有几乎全部的连接,不会出现已创建连接损坏的情况
ipvsadm --start-daemon state [--mcast-interface interface] [--syncid syncid]
# 停止连接同步守护进程
ipvsadm --stop-daemon state

 

选项
选项 说明
-s alg 负载均衡算法,即分别TCP连接或UDP报到RS的方法
-p [timeout] 指示虚拟服务是否“持久的”。如果指定该选项,则同一客户端的后续请求,被转发给同一个RS
-M netmask 持久虚拟服务的客户端被分组的粒度,可以将某个网络的所有客户端都转发给同一RS
-r server-address 指定真实服务器的地址
-g 使用DR模式
-i 使用隧道模式,即ipip封装
-m 使用NAT模式
-w 指定服务器的相对权重,整数
-t service-address 使用TCP服务
-u service-address 使用UDP服务

 

示例
# 创建TCP虚拟服务207.175.44.110:80,使用轮询调度
ipvsadm -A -t 207.175.44.110:80 -s rr
# 添加两台RS
ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.1:80 -m
ipvsadm -a -t 207.175.44.110:80 -r 192.168.10.2:80 -m
 
 
# 打印连接列表,可以看到IPVS建立的连接的状态
ipvsadm -lc
 
# IPVS connection entries
# pro expire state       source             virtual            destination
# TCP 01:19  FIN_WAIT    zircon:33568       k8s:6443           neon:6443
# TCP 119:57 ESTABLISHED zircon:33570       k8s:6443           xenon:6443
# TCP 01:19  FIN_WAIT    zircon:33569       k8s:6443           radon:6443

 

Keepalived

简介

Keepalived提供负载均衡和高可用的框架。

负载均衡基于IP虚拟服务器(IPVS)内核模块,支持L4负载均衡。Keepalived提供了一组健康检查器,可以动态、自适应的管理上游服务器池。

高可用基于虚拟路由冗余协议(VRRP)。Keepalived实现了一系列钩子,和VRRP状态机进行底层/高层交互。

出于健壮性的考虑,Keepalived被拆分为三个进程:

  1. 一个最小化的父进程,负责监控子进程
  2. 两个子进程,分别负责VRRP、上游服务健康检查

 

安装

通过包管理器安装:

# CentOS
yum install keepalived
# Ubuntu
apt install libipset-dev
apt install keepalived

完成配置工作后,执行下面的命令启动:

systemctl restart keepalived.service

keepalived命令

选项
选项 说明
f, –use-file=FILE 指定配置文件路径
-P, –vrrp 仅仅运行VRRP子系统,也就是仅仅提供HA
-C, –check 仅仅运行健康检查子系统,也就是仅仅提供LB
-l, –log-console 打印日志到控制台,默认打印到syslog
-D, –log-detail 记录详细日志
-S, –log-facility=[0-7] 日志级别
-V, –dont-release-vrrp 进程退出后,不移除VRRP虚IP和VROUTE
-I, –dont-release-ipvs 进程退出后,不移除IPVS拓扑
-n, –dont-fork 前台运行
-d, –dump-conf 导出配置文件

 

配置语法2.0

在Ubuntu 16.04上,配置文件的位置为/etc/keepalived/keepalived.conf。

全局定义
global_defs {
    # 通知邮件配置
    notification_email {
        email
        email
    }
    notification_email_from email
    smtp_server host
    smtp_connect_timeout num
    # LVS Director的名字
    lvs_id string
}

虚拟服务器定义

# 提示此虚拟服务器是一个FWMARK
virtual_server (@IP PORT)|(fwmark num) {
    # 健康检查周期,秒
    delay_loop num
    # 负载均衡算法(调度算法)
    lb_algo rr|wrr|lc|wlc|sh|dh|lblc
    # IPVS模式
    lb_kind NAT|DR|TUN
    (nat_mask @IP)
    # 持久化连接,即会话亲和,让一段时间内同一客户端的连接(短)都发送到同一个RS
    # 持久化连接的超时
    persistence_timeout num
    # 持久化连接的粒度掩码
    persistence_granularity @IP
    # 用于HTTP/SSL_GET的虚拟主机名
    virtualhost string
    # 协议类型
    protocol TCP|UDP
    # 如果所有RS宕机,将下面的服务器加入池中
    sorry_server @IP PORT
    # 定义一个真实服务器(RS)
    real_server @IP PORT {
        # 负载均衡权重
        weight num
        # 通过TCP进行健康检查
        TCP_CHECK {
            connect_port num
            connect_timeout num
        }
    }
    real_server @IP PORT {
        weight num
        # 通过脚本进行健康检查
        MISC_CHECK {
            misc_path /path_to_script/script.sh
            (or misc_path “ /path_to_script/script.sh <arg_list>”)
        }
    }
}
real_server @IP PORT {
    weight num
    # 通过HTTP/HTTPS进行健康检查
    HTTP_GET|SSL_GET {
        # 可以指定多个url块
        url {
            # URL路径
            path alphanum
            # 摘要信息
            digest alphanum
        }
        # 端口
        connect_port num
        # 超时
        connect_timeout num
        # 重试次数
        retry num
        # 重试延迟
        delay_before_retry num
    }
}

 

VRRP定义

# 组
vrrp_sync_group string {
    group {
        string
        string
    }
    # 可以为脚本传递参数,整体用引号包围
    notify_master /path_to_script/script_master.sh
    notify_backup /path_to_script/script_backup.sh
    notify_fault /path_to_script/script_fault.sh
}
 
# 默认情况下,Keepavelid只能对网络故障、Keepalived自身故障进行监控,并依此进行Master切换
# 使用vrrp_script则可以通过脚本进行自定义的(对本节点)健康检查
 
vrrp_script chk {
   script "/bin/bash -c 'curl -m1 -k -s https://127.0.0.1:6443/healthz -o/dev/null'"
   # 每两秒执行一次
   interval 2
   # 如果检测成功(脚本结果为0),且weight大于0,则当前实例的优先级升高
   # 如果检测失败(脚本结果为非0),且weight小于0,则当前实例的优先级降低
   weight -10
   # 连续3次为0才认为成功
   fall 3
   # 连续1次失败则认为失败
   rise 1
}
# 实例
vrrp_instance string {
    # 实例状态
    # 如果所有实例都配置为BACKUP,则初始时高优先级的成为第一个Master
    # 如果一个设置为MASTER其它设置为BACKUP,那么不设置nopreempt时每当Master恢复都会强占成为主
    state MASTER|BACKUP
    # 高优先级的实例恢复正常后,不会去强占,成为Master
    nopreempt
    # 使用的网络接口
    interface string
    # <span style="color: #404040;" data-mce-style="color: #404040;">VRRP通知的IP头上的IP地址</span>
    mcast_src_ip @IP
    # LVS sync_daemon在什么网络接口上运行
    lvs_sync_daemon_interface string
    # 此实例所属的虚拟路由器ID
    virtual_router_id num
    # 优先级,其运行时值可以随着vrrp_script的检测结果动态变化
    # 如果接收到Peer的vrrp广播包,发现自己的优先级最高,则自动切换为Master
    priority num
    # VRRP通知间隔
    advert_int num
    # 当MASTER状态变化时进行邮件通知
    smtp_alert
    # VRRP身份验证配置
    authentication {
        auth_type PASS|AH
        auth_pass string
    }
    # 定义虚IP
    virtual_ipaddress {
        @IP
        @IP
        @IP
    }
    # 排除虚IP
    virtual_ipaddress_excluded {
        @IP
        @IP
        @IP
    }
    # 进入MASTER状态后执行的脚本
    notify_master /path_to_script/script_master.sh
    # 进入BACKUP状态后执行的脚本
    notify_backup /path_to_script/script_backup.sh
    # 进入FAULT状态后执行的脚本
    notify_fault /path_to_script/script_fault.sh
}

 

配置语法1.2

全局定义

global_defs {
    notification_email {
        admin@example1.com
    }
    smtp_server 127.0.0.1 [<PORT>]
    smtp_helo_name <HOST_NAME>
    smtp_connect_timeout 30
    router_id my_hostname    # 机器标识
    vrrp_mcast_group4 224.0.0.18  # VRRP组播地址
    vrrp_mcast_group6 ff02::12
    default_interface eth0   # 静态地址的默认网络接口
    # LVS连接同步守护进程的配置
    #               监听接口     对应VRRP实例      lvs syncd的ID  最大包长度       使用的UDP端口               组播地址
    lvs_sync_daemon <INTERFACE> <VRRP_INSTANCE> [id <SYNC_ID>] [maxlen <LEN>] [port <PORT>] [ttl <TTL>] [group <IP ADDR>]
    lvs_timeouts tcp 7200 tcpfin 120 udp 300  # LVS超时配置
    lvs_flush # 启动时清空所有LVS配置
    vrrp_garp_master_delay 5   # 转变为MASTER后多久发起ARP宣告
    vrrp_garp_master_repeat 5  # 发起ARP宣告的次数
    vrrp_garp_master_refresh 0 # ARP重新宣告的周期,默认0表示不重复宣告
    vrrp_garp_master_refresh_repeat 1 # 重新宣告时发送ARP的次数
    vrrp_version 2 # 使用的VRRP协议版本
}

 

DR模式示例

本示例的Keepalived版本为1.2.24。

关于DR模式

使用DR模式时,必须保证:

  1. RS本机必须有网络接口,配置为VIP。DR模式下LB节点仅仅修改以太网帧的目标MAC地址,IP包不做任何修改,因此RS必须作为IP包的Destination
  2. RS不去响应针对VIP的ARP请求
  3. RS不去发送关于VIP的ARP通知

要保证上面三点,需要调整RS内核的行为。具体实施方法有几种:

添加arptables规则
arptables -A IN -d 10.0.10.1 -j DROP
arptables -A OUT -s 10.0.10.1 -j mangle --mangle-ip-s 10.0.10.1

 

修改内核参数

关于内核参数/proc/sys/net/ipv4/conf/*/arp_ignore

DR模式下,每个真实服务器节点都要在环回网卡上绑定VIP。如果客户端对于VIP的ARP请求广播到了各个真实服务器节点,当:

  1. arp_ignore=0,则各个真实服务器节点都会响应该arp请求,此时客户端就无法正确获取LVS(LB)节点上正确的VIP所在网卡的MAC地址。客户端可能将以太网帧绕过LB直接发给RS
  2. arp_ignore=1,只响应目的IP地址为接收网卡上的本地地址的arp请求

关于内核参数/proc/sys/net/ipv4/conf/*/arp_announce

每台服务器或者交换机中都有一张arp表,该表用于存储对端通信节点IP地址和MAC地址的对应关系。当

  1. 收到一个未知IP地址的arp请求,就会在本机的arp表中新增对端的IP和MAC记录
  2. 当收到一个已知IP地址(arp表中已有记录的地址)的arp请求,则会根据arp请求中的源MAC刷新自己的arp表

如果arp_announce参数配置为0,则网卡在发送arp请求时,可能选择的源IP地址并不是该网卡自身的IP地址,这时候收到该arp请求的其他节点或者交换机上的arp表中记录的该网卡IP和MAC的对应关系就不正确,可能会引发一些未知的网络问题,存在安全隐患。

DR模式下要求arp_ignore参数配置为1,arp_announce参数配置为2:

# 修改所有RS的内核参数
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
 
# 在环回网卡上绑定虚拟IP
ifconfig lo:k8s  10.0.10.1 netmask 255.255.255.255 broadcast 10.0.10.1
ifconfig lo:etcd 10.0.10.1 netmask 255.255.255.255 broadcast 10.0.10.1
ifconfig lo:ceph 10.0.10.1 netmask 255.255.255.255 broadcast 10.0.10.1
keepalived配置
global_defs {
    router_id oxygen
 
    # 增加LVS超时
    # 可以防止TCP连接静置一段时间后服务器返回RST
    # 可通过ipvsadm -l --timeout确保生效
    # 设置超时大于7200,因为TCP保活定时器默认2小时执行,保持行为一致
    lvs_timeouts tcp 7200 tcpfin 120 udp 300
    lvs_sync_daemon eth0 51
}
 
vrrp_instance apiserver {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass gmem
    }
    virtual_ipaddress {
        10.0.10.1/32 brd 10.0.10.1 dev eth0 label eth0:k8s
        10.0.10.2/32 brd 10.0.10.2 dev eth0 label eth0:etcd
        10.0.10.3/32 brd 10.0.10.3 dev eth0 label eth0:ceph
    }
}
 
virtual_server 10.0.10.1 6443 {
    delay_loop 5
    lb_algo wlc
    lb_kind DR
    protocol TCP
    real_server 10.0.5.1 6443 {
        weight 10
        SSL_GET {
            url {
                path /healthz
                status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
 
    }
    real_server 10.0.2.1 6443 {
        weight 10
        SSL_GET {
            url {
                path /healthz
                status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
 
    }
    real_server 10.0.3.1 6443 {
        weight 10
        SSL_GET {
            url {
                path /healthz
                status_code 200
            }
            connect_timeout 3
            nb_get_retry 3
            delay_before_retry 3
        }
 
    }
 
}
 
virtual_server 10.0.10.2 2379 {
    delay_loop 5
    lb_algo wlc
    lb_kind DR
    protocol TCP
    real_server 10.0.5.1 2379 {
        weight 10
        TCP_CHECK {
          connect_timeout 3
        }
    }
    real_server 10.0.2.1 2379 {
        weight 10
        TCP_CHECK {
          connect_timeout 3
        }
    }
    real_server 10.0.3.1 2379 {
        weight 10
        TCP_CHECK {
          connect_timeout 3
        }
    }
 
}
 
virtual_server 10.0.10.3 6789 {
    delay_loop 5
    lb_algo wlc
    lb_kind DR
    protocol TCP
    # 要启用持久化连接,必须配置
    persistence_timeout 300
    real_server 10.0.5.1 6789 {
        weight 10
        TCP_CHECK {
          connect_timeout 3
        }
    }
    real_server 10.0.2.1 6789 {
        weight 10
        TCP_CHECK {
          connect_timeout 3
        }
    }
    real_server 10.0.3.1 6789 {
        weight 10
        TCP_CHECK {
          connect_timeout 3
        }
    }
 
}

在此配置下:

  1. LVS节点无法访问VIP的6443端口
  2. RS节点可以访问VIP的6443端口,但是只会访问本机的服务,不会走负载均衡

常见问题

DR模式相关
连接卡在SYN_RECV

可能原因是:

  1. RS没有配置虚IP
  2. RS上的服务没有监听虚IP所在的网络接口

发表评论