注意
本教程针对于 Centos 8 系统, ARM 机器架构, HuaWei KunPeng 920,
其他系统 以及 X86 机器可以参考!! 原理都是通的....
配置 Bonding
Bonding 技术
bonding(绑定)是一种linux系统下的网卡绑定技术,可以把服务器上n个物理网卡在系统内部抽象(绑定)成一个逻辑上的网卡,能够提升网络吞吐量、实现网络冗余、负载等功能,有很多优势。
bonding技术是linux系统内核层面实现的,它是一个内核模块(驱动)。使用它需要系统有这个模块, 我们可以modinfo命令查看下这个模块的信息, 一般来说都支持。
Bonding 的 7 中模式
bonding技术提供了七种工作模式,在使用的时候需要指定一种,每种有各自的优缺点.
Bonding的模式一共有7种:
defineBOND_MODE_ROUNDROBIN 0 (balance-rr) 模式网卡的负载均衡模式, 需要配置交换机做端口聚合
defineBOND_MODE_ACTIVEBACKUP 1 (active-backup)模式网卡的容错模式
defineBOND_MODE_XOR 2 (balance-xor) 模式需要交换机支持
defineBOND_MODE_BROADCAST 3 (broadcast) 模式
defineBOND_MODE_8023AD 4 (802.3ad) 动态链路聚合模式需要配置交换机
defineBOND_MODE_TLB 5 (balance-tlb)自适应传输负载均衡模式
defineBOND_MODE_ALB 6 (balance-alb)网卡虚拟化方式
bonding模块的所有工作模式可以分为两类:多主型工作模式和主备型工作模式,(balance-rr) 和 (broadcast) 属于多主型工作模式, 而 (active-backup) 属于主备型工作模式。(balance-xor)自适应传输负载均衡模式(balance-tlb)和自适应负载均衡模式(balance-alb)也属于多主型工作模式,IEEE (802.3ad)动态链路聚合模式。
详细介绍这7种模式
balance-rr (mode=0)
轮转(Round-robin)策略:从头到尾顺序的在每一个slave接口上面发送数据包。本模式提供负载均衡和容错的能力。
active-backup(mode=1)
活动-备份(主备)策略:在绑定中,只有一个slave被激活。当且仅当活动的slave接口失败时才会激活其他slave。为了避免交换机发生混乱此时绑定的MAC地址只有一个外部端口上可见。在bongding的2.6.2及其以后的版本中,主备模式下发生一次故障迁移时,bonding将在新激活的slave上会送一个或者多个gratuitous ARP。bonding的主salve接口上以及配置在接口上的所有VLAN接口都会发送gratuitous ARP,只要这些接口上配置了至少一个IP地址。VLAN接口上发送的的gratuitous ARP将会附上适当的VLAN id。本模式提供容错能力,primary option,documented below会影响本模式的行为。
balance-xor(mode=2)
XOR策略:基于所选择的传送hash策略。
本模式提供负载均衡和容错的能力。
broadcast(mode=3)
广播策略:在所有的slave接口上传送所有的报文。本模式提供容错能力。
802.3ad(mode=4)
IEEE 802.3ad 动态链路聚合。创建共享相同的速率和双工模式的聚合组。能根据802.3ad规范利用所有的slave来建立聚合链路。Salve的出站选择取决于传输的hash策略,默认策略是简单的XOR策略,而hash策略则可以通xmit_hash_policy选项加以改变。需要注意的是:不是所有的传输策略都与802.3ad兼容,尤其是802.3ad标准的43.2.4章节中关于 packet mis-ordering要求的地方。不同个体的实现往往出现很大的不兼容。
先决条件:
1. 每个slave的基本驱动支持Ethtool获取速率和双工状态。
2. 交换机支持IEEE 802.3ad动态链路聚合。大多数的交换机都需要使用某种配置方式来启用802.3ad模式。
balance-tlb(mode=5)
自适应传输负载均衡:信道绑定不需要特殊的交换机支持。出口流量的分布取决于当前每个slave的负载(计算相对速度)。进口流量从当前的slave的接收。如果接收salve出错,其他的slave接管失败的slave的MAC地址继续接收。
先决条件:
每个slave的基本驱动支持Ethtool获取速率状态。
balance-alb(mode=6)
自适应负载均衡:包括balance-tlb(模式5)以及用于IPV4流量的接收负载均衡,并且不需要特殊的交换机支持。接收负载均衡通过ARP协商实现。bonding的驱动拦截本机发出的ARP Replies(ARP回应报文),并且用bond的某一个slave的硬件地址改写ARP报文的源地址,使得本服务器对不同的设备使用不同的硬件地址。本服务器建立的连接的接收流量也是负载均衡的。当本机发送ARP Request时,bonding驱动通过ARP报文复制并保存节点的IP信息。当从其他节点接收到ARP Reply,bonding驱动获取节点的硬件地址并且会回应一个包含绑定好的slave的硬件地址的ARP Reply给发送的节点。用ARP协商的负载均衡的有一个问题是每次用bond的硬件地址广播ARP报文,那么其他节点发送的数据全部集中在一个slave上,处理ARP更新给其他所有节点的时候,每个节点会重新学习硬件地址,导致流量重新分配。当新加入一个slave或者一个非激活的slave重新激活的时候也会导致接收流量重新分配。接收流量负载是串行(轮转)的分配在bond的一组速率最高的slave上。
当一个链路重连或者一个新的slave加入的时候,bond会重新初始化ARP Replies给所有的客户端。updelay参数的值必须等于或者大于交换机的forwarding delay,以免ARP Replies被交换机阻塞。
先决条件:1. 每个slave的基本驱动支持Ethtool获取速率状态。 2. 基本驱动支持当设备打开时重新设置硬件地址。也要求每一个slave具有唯一的硬件地址。如果curr_active_slave失败,它的硬件地址被新选上的curr_active_slave硬件地址来替换。
配置步骤
准备 工作
# 查看 系统内核 是否支持bonding
cat /boot/config-`uname -r` | grep -i bonding
# 若 CONFIG_BONDING=m , 则表示支持
# 加载bonding 内核模块
modprobe bonding
# 查看系统 是否 加载 bonding 模块
lsmod | grep bonding
以 创建 bondind 模式 1 为例, 其他 模式同理, bonding 名随意,
操作步骤
# 1. 创建 mode = 1, 名称为 bond1 的绑定
nmcli con add type bond con-name bond1 ifname bond1 mode active-backup # 模式以 具体的模式为准
# 2. 将 enp131s0f0 增加到bond1
nmcli connection add type bond-slave ifname enp131s0f0 master bond1
# 3. 将 enp131s0f1 增加到bond1
nmcli connection add type bond-slave ifname enp131s0f1 master bond1
# 4. 启动从属接口 enp131s0f0
nmcli connection up bond-slave-enp131s0f0 # 具体接口名 以第 2 步骤命令执行返回结果中的具体接口名为准
# 5. 启动从属接口 enp131s0f1
nmcli connection up bond-slave-enp131s0f1 # 具体接口名 以第 3 步骤命令执行返回结果中的具体接口名为准
# 6. 启动绑定
nmcli connection up bond1 # bonding 名 以 第 1 步骤命令 所创建的 bonding 名 为准
其他
# 查看系统 bonding 状态文件, 在 /proc/net/bonding/ 目录下每个 bonding 存在一个状态文件。
# 且 该 状态文件 是会 根据 bonding 状态 自动 变化的
cat /proc/net/bonding/bond1
# 查看系统 bonding 配置文件: 文件名规则与 网卡设备连接名 相同。
cat /etc/sysconfig/network-scripts/ifcfg-bond1
# 系统启动时, 自动加载的 bond 配置文件
/etc/modprobe.d/bonding.conf
# 添加 自配置 命令
# 在 /etc/rc.d/rc.local 文件中添加如下:
ifenslave bond0 eth0 eth1 # bonding 名 和 接口名 以 实际的名称为准
# bonding mode=4 可选添加的 配置参数
BONDING_OPTS="mode=802.3ad miimon=100 lacp_rate=fast arp_validate=0"
BONDING_OPTS="mode=4 miimon=100 xmit_hash_policy=layer3+4"
自动化 Bonding 创建 Python 脚本
注意
1. 系统中 需存在 nmcli 命令
2. 系统中 需安装 python, 且为 版本 3
auto_bonding_v1.py
#!/usr/bin/python
#coding=utf-8
import subprocess
import shlex
import os
import time
__author__ = "shiwei"
__time__ = "2020/12/31"
__version__ = "v1"
mode_li = ["balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb"]
mode_name = None # 将要创建的 bonding 名字
mode_num = None # 将要创建的 bonding 级别
mode_int_li = [] # 该 bonding 所要绑定的 接口列表
exists_son_int_li = [] # ifconfig 命令下 已存在的 非 bongding 接口列表
exists_ifcfg_li = [] # /etc/sysconfig/network-scripts/ 目录下已存在的 bond 子接口 列表
bonding_son_int_li = [] # 该 bonding 从属子接口名字列表
def execute_cmd(cmd_str):
""" 执行 bash 命令, 通过 subprocess.Popen 方法
"""
sh_cmd = shlex.split(cmd_str)
print("")
# 不支持管道符
Popen_obj = subprocess.Popen(sh_cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
# s.stdin.write(b"import os
")
# s.stdin.write(b"print(os.environ)")
# s.stdin.close()
res = Popen_obj.stdout.read().decode("UTF-8")
Popen_obj.stdout.close()
# print(res)
time.sleep(0.46) # 休息 0.36 秒,等待系统缓冲
return res
def execute_cmd_02(cmd_str):
""" 执行 bash 命令, 通过 subprocess.run 方法
"""
ret = subprocess.run(cmd_str, stdout=subprocess.PIPE,shell=True)
res = ret.stdout
res = str(res, encoding = "UTF-8")
#res = res[:-1].split("
")
# print("结果为:", res)
time.sleep(0.46) # 休息 0.36 秒,等待系统缓冲
return res
def create_mode_name():
""" 创建 Bonding 模式名
"""
while 1:
try:
user_mode_str = input("请输入所要配置 Bonding 模式【0-6】>").strip()
user_mode_num = int(user_mode_str)
if not user_mode_num in range(0, 7):
raise IndexError("没有这个模式啦....")
# assert isinstance(user_mode_num, int), "类型错误!"
break
except Exception as e:
print(e, "
请重新输入【0-6】>")
cur_name = "bond" + user_mode_str
# 获取当前系统所有已创建的 Bond 模式名
cmd_str = r"""ls /proc/net/bonding/ | xargs -n 1"""
bonding_name = execute_cmd_02(cmd_str)
regis_li = bonding_name[:-1].split("
") # 已创建 bonding 名列表
while 1:
if cur_name in regis_li:
cur_name += "v1"
else:
break
global mode_num, mode_name
mode_num = user_mode_num
mode_name = cur_name
def print_interface(cur_int_li):
""" 打印接口列表信息
"""
print(f" 0 > 退出")
for index, val in enumerate(cur_int_li, 1):
print(f" {index} > {val}")
def sel_interface():
""" 选择该 Bonding 索要绑定的接口
"""
new_cmd_str=r"""cat /proc/net/dev | grep -v lo | grep -v virbr | sed '3,$p' -n | cut -d: -f1 | xargs -n 1"""
int_li_str = execute_cmd_02(new_cmd_str)
# if int_li_str:
cur_int_li = int_li_str[:-1].split("
")
exists_son_int_li = cur_int_li
index_li = [] # 所选择的接口列表
#
# print("当前系统接口列表如下
:", cur_int_li)
# print_interface(cur_int_li)
while 1:
print("当前系统接口列表如下:")
print_interface(cur_int_li)
index = input(f"请选择当前 bonding 所要绑定的接口,输入数字【0-{len(cur_int_li)}】>")
try:
index = int(index)
if not index in range(0, len(cur_int_li) + 1):
raise IndexError("没有这个接口啦....")
if not index:
break
else:
if index not in index_li:
index_li.append(index)
mode_int_li.append(exists_son_int_li[index - 1])
except Exception as e:
print(e, "
请重新输入【0-{len(cur_int_li)}】>")
def get_bond_sub_li(cmd_str):
""" 执行添加 从属接口命令, 根据返回返回值获取 bonding 子接口名字
"""
res = execute_cmd_02(cmd_str)
if res:
sub_int_name = res[:-1].split('"')[1]
bonding_son_int_li.append(sub_int_name)
def struct_cmd():
""" 创建 bonding , 执行命令
"""
exists_ifcfg__cmd = r"""find /etc/sysconfig/network-scripts/ -maxdepth 1 -type f -perm 644 -name 'ifcfg-bond-*' | xargs -i basename {} | awk 'BEGIN{FS="ifcfg-"}{print $2}'"""
ifcfg_li = execute_cmd_02(exists_ifcfg__cmd)
exists_ifcfg_li = ifcfg_li[:-1].split("
")
global mode_li, mode_name, mode_num
print("已存在的 子接口 为:", exists_ifcfg_li)
cmd_bonding_li = [] # 命令列表
# 第一步, 创建 bonding 接口
first_cmd = "nmcli con add type bond con-name " + mode_name + " ifname " + mode_name + " mode " + mode_li[mode_num]
cmd_bonding_li.append(first_cmd)
execute_cmd_02(first_cmd)
time.sleep(0.5)
# 第二步, 添加 子接口
# 返回子接口信息示例: "Connection 'bond-slave-enp189s0f2' (325727bb-a6be-451e-83a2-2221916c4eb9) successfully added."
for interface in mode_int_li:
cmd = r"nmcli connection add type bond-slave ifname " + interface + " master " + mode_name
get_bond_sub_li(cmd)
cmd_bonding_li.append(cmd)
# 第三步, 依次启动从属接口
for bond_sub in bonding_son_int_li:
cmd = r"nmcli connection up " + bond_sub
execute_cmd_02(cmd)
cmd_bonding_li.append(cmd)
# 第四步, 启动 bonding 接口
fourth_cmd = r"nmcli connection up " + mode_name
execute_cmd_02(fourth_cmd)
cmd_bonding_li.append(fourth_cmd)
# 打印 执行命令
for index, item in enumerate(cmd_bonding_li, 1):
print(f"{index} > , {item}")
if __name__ == "__main__":
create_mode_name()
print("bonding 名字为: ", mode_name)
sel_interface()
if len(mode_int_li) >= 2:
struct_cmd()
python 自创 switch 语句
#!/bin/env python
#coding=utf-8
class switch(object):
def __init__(self, value):
self.value = value
self.fall = False
def __iter__(self):
yield self.match
raise StopIteration
def match(self, *args):
if self.fall or not args:
return True
elif self.value in args: # changed for v1.5, see below
self.fall = True
return True
else:
return False
v = 'ten'
for case in switch(v):
if case('one'):
print 1
break
if case('two'):
print 2
break
if case('ten'):
print 10
break
if case('eleven'):
print 11
break
if case(): # default, could also just omit condition or 'if True'
print "something else!"
# No need to break here, it'll stop anyway
c = 'z'
for case in switch(c):
if case('a'): pass # only necessary if the rest of the suite is empty
if case('b'): pass
# ...
if case('y'): pass
if case('z'):
print "c is lowercase!"
break
if case('A'): pass
# ...
if case('Z'):
print "c is uppercase!"
break
if case(): # default
print "I dunno what c was!"
import string
c = 'A'
for case in switch(c):
if case(*string.lowercase): # note the * for unpacking as arguments
print "c is lowercase!"
break
if case(*string.uppercase):
print "c is uppercase!"
break
if case('!', '?', '.'): # normal argument passing style also applies
print "c is a sentence terminator!"
break
if case(): # default
print "I dunno what c was!"