添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

关于IPVS,可以参考这个网站: http://www.austintek.com/LVS/LVS-HOWTO/

关于IPVS在内核中的实现,可以参考: IPVS模式下ClusterIP泄露宿主机端口的问题

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

和netfilter的交互

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

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

    fullNAT

    不是标准内核的一部分。

    来程报文: ClientIP: ClientPort - VIP:VPORT ⇨ IPVS Director DirectorIP : DirectorPort - RIP RPORT

    回程报文:RIP: RPORT - DirectorIP: DirectorPort ⇨ IPVS Director ⇨ VIP : VPORT - ClientIP : ClientPort

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

  • 客户端可以和Director、真实服务器在同一个局域网内
  • 真实服务器 看不到客户端的真实IP 地址。只能由Director通过TCP Options携带真实IP,但是TCP Option只有40字节,可能客户端已经占用导致空间不足
  • 来程报文: ClientIP: ClientPort - VIP:VPORT ⇨ IPVS Director ⇨ ClientIP: ClientPort - RIP RPORT

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

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

  • 一个接口分配面向外部的IP地址
  • 另一个接口分配私有的、面向内部的IP地址
  • LB从外部接口接受流量,然后经过NAT,转发给内部网络中的真实服务器。

    该模式的缺陷:

  • LB是性能瓶颈,它需要转发两个方向的流量
  • RS上必须进行适当的路由 ,确保针对ClientIP的路由转发给IPVS Director处理
  • 客户端必须和RS不在同一网段 。这个限制可以通过配置解决:
  • 在Director上关闭ICMP重定向:
    Tunneling

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

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

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

    Direct Routing

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

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

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

    该模式的缺陷:

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

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

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

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

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

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

    lblcr

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

  • 服务器没有 超载, 将请求发送到该服务器
  • 若服务器超载;则按“最小连接”原则从整个集群中选出一台服务器,将该服务器加入到服务器组中,将请求发送到该服务器
  • 当该服务器组有一段时间没有增减成员,将 最忙的服务器从服务器组中删除

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

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

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

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

    ipvsadm命令

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    # 添加或修改虚拟服务
    ipvsadm - A | E virtual - service [ - s scheduler ] [ - p [ timeout ] ] [ - M netmask ] [ - b sched - flags ]
    # virtual-service可以是:
    #  -t, --tcp-service service-address
    #  -u, --udp-service service-address
    # 示例
    ipvsadm - A - t 10.0.10.100 : 80 - s rr - p 600
    # 删除虚拟服务
    ipvsadm - D virtual - service
    # 清空虚拟服务表
    ipvsadm - C
    # 从标准输入还原虚拟服务表
    ipvsadm - R
    # 将虚拟服务表导出到标准输出
    ipvsadm - S [ - n ]
    # 为虚拟服务添加、编辑一个真实服务器
    # 封包转发方式:
    # -g --gatewaying 直接路由
    # -i, --ipip  隧道(IPIP封装)
    # -m, --masquerading NAT
    ipvsadm - a | e virtual - service - r server - address [ - g | i | m ] [ - w weight ] [ - x upper ] [ - y lower ]
    # 示例
    ipvsadm - a - t 10.0.10.100 : 80 - r 10.1.0.10 : 80 - m
    # 为虚拟服务删除一个真实服务器
    ipvsadm - d virtual - service - r server - address
    # 示例
    ipvsadm - d - t 10.0.10.100 : 80 - r 10.1.0.10 : 80
    # 列出虚拟服务
    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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 创建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提供负载均衡和高可用的框架。

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

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

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

  • 一个最小化的父进程,负责监控子进程
  • 两个子进程,分别负责VRRP、上游服务健康检查
  • 通过包管理器安装:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    # 提示此虚拟服务器是一个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
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    # 组
    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
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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模式时,必须保证:

  • RS本机必须有网络接口,配置为VIP。DR模式下LB节点仅仅修改以太网帧的目标MAC地址,IP包不做任何修改,因此RS必须作为IP包的Destination
  • RS不去响应针对VIP的ARP请求
  • RS不去发送关于VIP的ARP通知
  • 要保证上面三点,需要调整RS内核的行为。具体实施方法有几种:

    添加arptables规则
    修改内核参数

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

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

  • arp_ignore=0,则各个真实服务器节点都会响应该arp请求,此时客户端就无法正确获取LVS(LB)节点上正确的VIP所在网卡的MAC地址。客户端可能将以太网帧绕过LB直接发给RS
  • arp_ignore=1,只响应目的IP地址为接收网卡上的本地地址的arp请求
  • 关于内核参数/proc/sys/net/ipv4/conf/*/arp_announce:

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 修改所有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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    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
    }
    }
    }

    Leave a Reply Cancel reply

    Your email address will not be published. Required fields are marked *