Z.S.K.'s Records

Kubernetes学习(k8s基于InfiniBand实现HPC高性能容器网络组网方案实践二)

上回简单介绍了下技术背景,这次真正进入实践阶段,之前提到过,sriov是需要硬件支持, 一般开启sriov需要在IBOS中进行设置,这个根据服务器型号不同而操作不同, 具体可参考官网.

k8s集群

现有的k8s集群使用的容器网络为flannel,k8s版本为v1.15.9, 这里要特别说明一下,1.15.9的版本相对来说已经较旧了,后面部署sriov-network-operator遇到好几个坑都是因为版本问题,后面会细说,其它没什么特别的.

硬件开启SRIOV功能

由于服务器的型号不一样,操作方法不一样,但是万变不离其综,在IBOS中多找找,一般都可以找到.

参考: https://docs.mellanox.com/pages/viewpage.action?pageId=19798214

安装驱动

1
2
3
4
5
wget  'http://www.mellanox.com/page/mlnx_ofed_eula?mtag=linux_sw_drivers&mrequest=downloads&mtype=ofed&mver=MLNX_OFED-4.5-1.0.1.0&mname=MLNX_OFED_LINUX-4.5-1.0.1.0-rhel7.4-x86_64.tgz'

yum install -y tcsh pciutils lsof python-devel gtk2 atk cairo tcl gcc-gfortran tk python-devel redhat-rpm-config rpm-build

tar xf /home/xx/MLNX_OFED_LINUX-4.5-1.0.1.0-rhel7.4-x86_64.tgz -C /home/xx/ && cd /home/xx/MLNX_OFED_LINUX-4.5-1.0.1.0-rhel7.4-x86_64 && ./mlnxofedinstall --add-kernel-support --skip-repo

在安装完驱动之后机器需要重启

1
2
# 重启完之后ib的驱动会自动启动,且一般默认开机自启
systemctl status openibd

这时需要为 ib 端口配置一个 IP 地址,以方便网络的识别和诊 断,配置方法和以太网端口的 IP 配置方法一样,具体步骤如下:

步骤 1: 在节点的/etc/sysconfig/network-scripts/创建 ifcfg-ib0 网络配置文件,IP 地址和子网掩码 按照 LLD 规划设计,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# cat /etc/sysconfig/network-scripts/ifcfg-ib0 
IPADDR=10.4.52.5
NETMASK=255.255.255.0
STARTMODE=auto
CONNECTED_MODE=no
TYPE=InfiniBand
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ib0
DEVICE=ib0
ONBOOT=yes

步骤 2: 启动 ib0 端口,命令为 ifup ib0

步骤 3: 按照以上两步完成所有节点 IB 网络的 IP 配置

开启vf

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
# mst status
# 输出
MST modules:
------------
MST PCI module is not loaded
MST PCI configuration module is not loaded

PCI Devices:
------------

83:00.0

# 启动 MST (Mellanox Software Tools)
# mst start
Starting MST (Mellanox Software Tools) driver set
Loading MST PCI module - Success
Loading MST PCI configuration module - Success
Create devices
-W- Missing "lsusb" command, skipping MTUSB devices detection
Unloading MST PCI module (unused) - Success

# mst status
MST modules:
------------
MST PCI module is not loaded
MST PCI configuration module loaded

MST devices:
------------
/dev/mst/mt4119_pciconf0 - PCI configuration cycles access.
domain:bus:dev.fn=0000:83:00.0 addr.reg=88 data.reg=92
Chip revision is: 00

# 配置SRIOV是否开启、虚拟化数量,需要重启机器
# mlxconfig -d /dev/mst/mt4119_pciconf0 set SRIOV_EN=1 NUM_OF_VFS=8

Device #1:
----------

Device type: ConnectX5
Name: N/A
Description: N/A
Device: /dev/mst/mt4119_pciconf0 # 这里是设备

Configurations: Next Boot New
SRIOV_EN True(1) True(1)
NUM_OF_VFS 8 8

Apply new Configuration? (y/n) [n] : y
Applying... Done!
-I- Please reboot machine to load new configurations.

# 重启机器
# systemctl reboot


#注意:机器启动后还无法通过lspci看到VFS,只有在MLNX OFED驱动程序上启用SR-IOV后,你才能看到它们

# 设置固件中VFS数量,上面是配置设备的
# echo 8 > /sys/class/net/ib0/device/sriov_numvfs
# 注意,vfs最好不要超过64个

# 查看
ibstat
ibdev2netdev -v
# 会发现除了ib0外其它的ib网卡都是DOWN状态

# 查看PCI设备
lspci | grep Mellanox
# 能看到有8个Mellanox, 其中7个为Virtual Function

开启ib0 vf网卡的state从disable为enable, 每次机器重启后,都会变回到disable,因此这条语句需要加入到开机启动中,

1
for ((i = 0 ; i < 8 ; i++ )); do ip link set dev ib0 vf $i state enable; done

参考: https://codimd.mcl.math.ncu.edu.tw/s/SyG8zjJpE

使用ip看到所有VF的mac地址都是全0,但实际这个是ip命令的bug,需要通过以下的方式查看具体的mac地址

1
2
[root@sh-office-10-5-40-94 bin]# cat cd/virtfn0/net/ib1/address
20:00:08:a5:fe:80:00:00:00:00:00:00:dc:56:31:60:6a:ac:03:bd

Ibstat 显示除了mlx5_0的 state为active外,其它的7个(mlx5_1-mlx5_8)的state都为Down,目前这个是正常的

配置子网管理器

IB 网络使用子网管理器(SM, Subnet Manager)管理网络路由,子网管理器可以运行在 服务器节点或具有管理功能的 IB 交换机上,由于IB交换机作者没有权限,因此这里直接部署在两台服务器节点上,由于子网管理器也要考虑单点问题,而SM本身就支持主备切换,同时只 有一台处于 Active 状态,因此部署两台,一主一备,

MLNX_OFED 驱动当中集成了子网管理器 OpenSM,安装 MLNX_OFED 驱动后, OpenSM 已默认安装。启动子网管理器的方法如下: 步骤 1: 运行下述命令来创建 opensm.conf 配置文件:

1
opensm --create-config /etc/opensm/opensm.conf

步骤 2 基于上述初始化创建,OpenSM 使用默认的路由算法 minihop。当网络使用 Fat-Tree 或 Up-Down 等其它拓扑组网时,需要在 opensm.conf 配置文件中将 routing_engine 修改为 对应的路由算法

修改默认配置:

1
2
3
4
5
6
7
8
9
# vim /etc/opensm/opensm.conf
# 修改以下内容,两台sm都需要修改
routing_engine updn
sm_priority 13 # 范围为0-15, 这里两台sm要不一样,数值大的为master,另一台即为back
# 保存后启动
systemctl start opensm
# 开机启动
systemctl enable opensm
# 能够启动说明没有问题,如果无法启动可查看相关日志

检查网络

检查 IB 端口状态 检查 IB 端口状态是否 up,端口速率是否达到标称值,通过 ibstat 命令来进行检查,下 面打印结果为 mlx5_0 端口 up,状态为 active,速率为 100Gbps

部署

准备工作[可选]

由于本人集群中有一些psp的限制 ,需要以下操作

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
# psp.yaml
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
name: sriov-network-psp
spec:
allowPrivilegeEscalation: true
allowedHostPaths:
- pathPrefix: /
fsGroup:
ranges:
- max: 65535
min: 1
rule: RunAsAny
hostNetwork: true
hostPID: true
hostPorts:
- max: 9796
min: 9796
privileged: true
requiredDropCapabilities:
- ALL
runAsUser:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
ranges:
- max: 65535
min: 1
rule: RunAsAny
volumes:
- configMap
- emptyDir
- projected
- secret
- downwardAPI
- persistentVolumeClaim
- hostPath

# kubectl apply -f psp.yaml -n kube-system
# 修改deploy/role.yaml, 在Role/Rules域下新增以下
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames:
- sriov-network-psp

部署multus

由于在一个k8s中要支持多个容器网络,CNCF有几款项目可以实现这个功能,而multus即为比较好的解决方案

multus的主要作用是: 在给pod分配ip时,会先经过multus,由multus根据pod是否存在指定的annotations来分配相关的网络,同时,需要指定一个默认的容器网络(这里是flannel), multus其实就是个网络代理

这里要注意的是,multus里的部署yaml文件中crd的版本因k8s的版本不同而不同,在v1.16之前的版本为v1beta1,之后为v1,请特别注意

v1.16版本之前的使用以下的yaml文件

1
https://github.com/k8snetworkplumbingwg/multus-cni/tree/master/images/deprecated

请根据实际情况配置cm,如下:

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
apiVersion: v1
data:
cni-conf.json: |
{
"name": "multus-cni-network",
"type": "multus",
"capabilities": {
"portMappings": true
},
"delegates": [
{
"cniVersion": "0.2.0",
"name": "flannel",
"plugins": [
{
"type": "flannel",
"name": "cbr0",
"delegate": {
"isDefaultGateway": true,
"hairpinMode": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
],
"kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig"
}
kind: ConfigMap
name: multus-cni-config
namespace: kube-system

最终在`/etc/cni/net.d/00-multus.conf的文件如下

1
{ "name": "multus-cni-network", "type": "multus", "capabilities": {"portMappings": true}, "kubeconfig": "/etc/cni/net.d/multus.d/multus.kubeconfig", "delegates": [ { "cniVersion": "0.2.0", "name": "cbr0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] } ] }

参考: https://github.com/k8snetworkplumbingwg/multus-cni

部署whereabouts

whereabounts是用于管理ip分配,这样多台node节点上的ip能够保证不出现重复

1
2
3
# kubectl apply -f https://raw.githubusercontent.com/openshift/whereabouts-cni/master/doc/daemonset-install.yaml
# kubectl apply -f https://raw.githubusercontent.com/openshift/whereabouts-cni/master/doc/whereabouts.cni.cncf.io_ippools.yaml
# kubectl apply -f https://raw.githubusercontent.com/openshift/whereabouts-cni/master/doc/whereabouts.cni.cncf.io_overlappingrangeipreservations.yaml

参考:https://github.com/openshift/whereabouts-cni

部署sriov-network-operator

sriov-network-operator是openshift开源的一个包含了cni及device-plugin的部署operator,方便直接.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. centos先安装依赖
yum install skopeo golang

# 2. 下载git源码,使用release-4.7版本
git clone https://github.com/k8snetworkplumbingwg/sriov-network-operator
git checkout release-4.7

# 3. 修改deploy/operator.yaml中的RELEASE_VERSION从4.3.0为4.7.0
# 4. 这里由于k8s的版本为1.15.9,不支持crd v1的版本,需要修改Makefile中的CRD_OPTIONS ?= "crd:crdVersions={v1},trivialVersions=true"为CRD_OPTIONS ?= "crd:crdVersions={v1beta1},trivialVersions=true"

# 5. 由于集群限制,需要添加psp, 在deploy目录下添加psp.yaml, 修改role, 见上文

# 6. 给ib机器添加label,值为空即可
kubectl label nodes spring-30 node-role.kubernetes.io/worker=
kubectl label nodes spring-30 feature.node.kubernetes.io/custom-rdma.available='true'
# 7. 默认部署在kube-system ns下最为方便,但不推荐,原因看注意事项
export GOPROXY=https://goproxy.cn
# 8. 修改Makefile ns
make deploy-setup-k8s

安装完成

需要注意以下事项:

  1. 8ks v1.17之前的版本不支持在除kube-system之外的ns在pod中指定system-node-critical priorityClass,而官方镜像sriov-network-operator及sriov-network-config-daemon镜像中的yaml写死了system-node-critical priorityClass,要部署在其它ns中对于v1.17之前的版本需要特殊处理,需要重新打sriov-network-operator跟sriov-network-config-daemon的镜像,因为bindata/manifests目录被打进了镜像中,要么通过挂载的方式进行覆盖或者重新打镜像,操作如下:

    1
    2
    3
    4
    5
    6
    7
    8
    # 首先通过上述操作修改了crdversion,由v1--> v1beta1, bindata下的文件会自动生成
    # bindata下涉及到 priorityClassName: "system-node-critical"的文件有,可以去掉
    # bindata/manifests/daemon/daemonset.yaml
    # bindata/manifests/plugins/sriov-cni.yaml
    # bindata/manifests/plugins/sriov-device-plugin.yaml
    # deploy/operator.yaml

    # 重新Docker build

make deploy-setup-k8s部署完之后查看sriovnetworknodestates.sriovnetwork.openshift.io

1
kubectl -n kube-system get sriovnetworknodestates.sriovnetwork.openshift.io spring-30 -oyaml

输出如下:

发布nodepolicy.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: sriovnetwork.openshift.io/v1
kind: SriovNetworkNodePolicy
metadata:
name: policy-ib0
namespace: kube-system
spec:
resourceName: "mlnx_ib0"
nodeSelector:
feature.node.kubernetes.io/custom-rdma.available: "true"
priority: 10
numVfs: 8
nicSelector:
vendor: "15b3"
deviceID: "1017"
rootDevices:
- 0000:83:00.0
pfNames: [ "ib0" ]
isRdma: true
linkType: ib

说明:

1
2
3
# 1. 自定义resourceName名,这个字段后续会绑定在 NetworkAttachmentDefinition 上
# 2. nicSelector字段信息都由 get sriovnetworknodestates.sriovnetwork.openshift.io 信息获得
kubectl apply -f nodeploicy.yaml -n kube-system

部署NetworkAttachmentDefinition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: sriovnetwork.openshift.io/v1
kind: SriovIBNetwork
metadata:
name: sriovnetwork
namespace: kube-system
spec:
ipam: |
{
"type": "whereabouts",
"range": "192.168.10.0/24",
"gateway": "192.168.10.1",
"routes": [
{
"dst": "0.0.0.0/0"
},
{
"dst": "192.168.0.0/24",
"gw": "192.168.10.1"
}
]
}
vlan: 0
resourceName: mlnx_ib0

192.168.10.1默认为网关,上述的配置ip地址192.168.10.2开始分配, 从whereabouts默认是将ip信息保存在kubernetes中,部署完whereabouts会在node上的/etc/cni/net.d/whereabounts.d下生成存储信息及认证,因此可以省略使用etcd,方便.

新建的SriovIBNetwork对象默认会创建一个同名的networkattachmentdefinition对象

1
kubectl get networkattachmentdefinition.k8s.cni.cncf.io -n kube-system

验证

使用以下的yaml进行验证

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
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-pod-4
labels:
app: sriov
spec:
replicas: 2
selector:
matchLabels:
app: sample-pod-4
template:
metadata:
labels:
app: sample-pod-4
annotations:
k8s.v1.cni.cncf.io/networks: kube-system/sriovnetwork
spec:
containers:
- image: nvidia/cuda-11.2.2-devel-centos7:v0.3 #自定义镜像,里面包含cuda及ib_write_bw工具,未尾有Dockerfile
imagePullPolicy: IfNotPresent
name: mlnx-inbox-ctr
securityContext:
capabilities:
add: [ "IPC_LOCK" ]
resources:
requests:
openshift.io/mlnx_ib0: '1'
nvidia.com/gpu: 1
limits:
openshift.io/mlnx_ib0: '1'
nvidia.com/gpu: 1
command:
- sh
- -c
- sleep inf

发布到镜像后可以看到容器具有两个ip

同时查看pod的yaml,可以看到annotation中存在两个ip地址

可以使用ib的测试工具来验证网络的传输速度

在其中一个pod充当服务端,执行以下命令:

1
2
3
4
5
# kubectl exec -it sample-pod-65b94586b4-8k784 -- bash

root@sample-pod-65b94586b4-8k784:/tmp# ibdev2netdev
mlx5_9 port 1 ==> net1 (Up)
root@sample-pod-65b94586b4-8k784:/tmp# ib_write_bw -a -d mlx5_9 &

在另一个pod中执行充当客户端,执行以下命令;

1
2
3
4
5
# kubectl exec -it sample-pod-65b94586b4-8xn6m -- bash

root@sample-pod-65b94586b4-8xn6m:/tmp# ibdev2netdev
mlx5_7 port 1 ==> net1 (Up)
root@sample-pod-65b94586b4-8xn6m:/tmp# ib_write_bw -a -F 192.168.101.1 -d mlx5_7 --report_gbits

会发现,速率接近100G/s, 符合IB网卡的理想速度.

测试使用的Dockerfile

1
2
3
4
FROM nvidia/cuda:11.2.2-devel-centos7

COPY ib_write_bw /bin/ib_write_bw
COPY ibdev2netdev /bin/ibdev2netdev

部署开启webhook,非必须

Operator也可以选择开启webhook, 功能作用不大,默认也是关闭的,如果想开启的话,参考下面的方式

1
2
3
4
kubectl -n kube-system create secret tls operator-webhook-service --cert=server.pem --key=serverkey.pem
kubectl -n kube-system create secret tls network-resources-injector-secret --cert=server.pem --key=serverkey.pem
export ENABLE_ADMISSION_CONTROLLER=true
export WEBHOOK_CA_BUNDLE=$(base64 -w 0 < cacert.pem)

上述的server.pem、serverkey.pem、cacert.pem可通过openssl生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 一条命令生成 ca根证书及密钥
openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout privkey.pem -out cacert.pem -days 3650
# 按提示输入需要的信息

## 使用ca根证书签发证书

# 生成私钥
openssl genrsa -out serverkey.pem 1024

# 生成证书csr文件
openssl req -new -key serverkey.pem -out servercsr.pem

# 使用证书
openssl ca -in servercsr.pem -out server.pem -cert cacert.pem -keyfile privkey.pem -days 3650

# 然后在部署operator时export以下变量即可
export ENABLE_ADMISSION_CONTROLLER=true
export WEBHOOK_CA_BUNDLE=$(base64 -w 0 < cacert.pem)
make deploy-setup-k8s

整体都跑起来了,但是其中还是遇到这么多的坑,下次做过完整总结

参考文章:

转载请注明原作者: 周淑科(https://izsk.me)

 wechat
Scan Me To Read on Phone
I know you won't do this,but what if you did?