34 电信功能配置 #
本章将阐释部署了 ATIP 的群集上特定于电信的功能配置。
将使用有关 ATIP 自动置备的一章(第 35 章 “全自动定向网络置备”)中所述的定向网络置备部署方法。
本章涵盖以下主题:
实时内核映像(第 34.1 节 “实时内核映像”):实时内核使用的内核映像。
低延迟和高性能的内核参数(第 34.2 节 “低延迟和高性能的内核参数”):为了在运行电信工作负载时实现最高性能和低延迟而由实时内核使用的内核参数。
CPU 已微调配置(第 34.3 节 “CPU 已微调配置”):实时内核使用的已微调配置。
CNI 配置(第 34.4 节 “CNI 配置”):Kubernetes 群集使用的 CNI 配置。
SR-IOV 配置(第 34.5 节 “SR-IOV”):Kubernetes 工作负载使用的 SR-IOV 配置。
DPDK 配置(第 34.6 节 “DPDK”):系统使用的 DPDK 配置。
vRAN 加速卡(第 34.7 节 “vRAN 加速 (
Intel ACC100/ACC200
)”):Kubernetes 工作负载使用的加速卡配置。大页(第 34.8 节 “大页”):Kubernetes 工作负载使用的大页配置。
CPU 固定配置(第 34.9 节 “CPU 固定配置”):Kubernetes 工作负载使用的 CPU 固定配置。
可感知 NUMA 的调度配置(第 34.10 节 “可感知 NUMA 的调度”):Kubernetes 工作负载使用的可感知 NUMA 的调度配置。
MetalLB 配置(第 34.11 节 “MetalLB”):Kubernetes 工作负载使用的 MetalLB 配置。
专用注册表配置(第 34.12 节 “专用注册表配置”):Kubernetes 工作负载使用的专用注册表配置。
34.1 实时内核映像 #
实时内核映像不一定比标准内核更好。它是针对特定用例进行微调的另一种内核。经过微调的实时内核可以降低延迟,但代价是降低了吞吐量。不建议将实时内核用于一般用途,但在本例中,它是建议用于电信工作负载的内核,因为其中的延迟是一项关键考虑因素。
实时内核有四大特性:
确定性执行:
获得更高的可预测性 — 确保关键业务流程每次都能及时完成并提供高质量的服务,即使在承受繁重的系统负载情况下也能如此。通过围隔出关键系统资源供高优先级流程使用,可以确保为时间敏感型应用程序提供更高的可预测性。
低抖动:
基于高确定性技术的低抖动有助于应用程序与现实世界保持同步。这可以为那些需要持续重复计算的服务提供帮助。
优先级继承:
优先级继承是指当较高优先级的进程在完成其任务之前需要先等待较低优先级的进程完成时,较低优先级的进程可以提升为较高优先级的功能。SUSE Linux Enterprise Real Time 解决了任务关键型进程的优先级倒置问题。
线程中断:
在通用操作系统中以中断模式运行的进程不可抢占。在 SUSE Linux Enterprise Real Time 中,这些中断已由可中断的内核线程封装,并允许用户定义的较高优先级进程抢占硬中断和软中断。
在本例中,如果您已安装
SLE Micro RT
之类的实时映像,则就已经安装了实时内核。可以从 SUSE Customer Center 下载实时内核映像。
34.2 低延迟和高性能的内核参数 #
内核参数必须进行配置,以便实时内核能够正常工作,从而为运行电信工作负载提供最佳性能和低延迟。在为此用例配置内核参数时,有一些重要的概念需要牢记:
使用 SUSE 实时内核时需去除
kthread_cpus
。此参数控制在哪些 CPU 上创建内核线程。它还控制允许哪些 CPU 用于 PID 1 及加载内核模块(由 kmod 用户空间辅助工具加载)。此参数无法被 SUSE 实时内核识别,因此没有任何效果。将
domain,nohz,managed_irq
标志添加到isolcpus
内核参数。如果没有任何标志,isolcpus
相当于只指定domain
标志。这将导致指定的 CPU 无法进行调度,包括内核任务。nohz
标志会停止指定 CPU 上的周期性调度器(如果一个 CPU 上只有一个任务可运行),managed_irq
标志可避免在指定 CPU 上路由受管理的外部(设备)中断。去除
intel_pstate=passive
。此选项将intel_pstate
配置为与通用 cpufreq 调节器一起工作,但缺点是,为了实现这一点,它会禁用硬件管理的 P 状态(HWP
)。为了减少硬件延迟,不建议将此选项用于实时工作负载。将
intel_idle.max_cstate=0 processor.max_cstate=1
替换为idle=poll
。为了避免 C 状态转换,使用idle=poll
选项来禁用 C 状态转换并将 CPU 保持在最高 C 状态。intel_idle.max_cstate=0
选项会禁用intel_idle
,因此使用acpi_idle
,然后acpi_idle.max_cstate=1
会为 acpi_idl 设置最大 C 状态。在 x86_64 架构上,第一个 ACPI C 状态始终是轮询
,但它使用poll_idle()
函数,这可能会因为定期读取时钟并在超时后重新启动do_idle()
中的主循环(也涉及清除和设置TIF_POLL
任务标志)而导致一些细微的延迟。相比之下,idle=poll
在一个紧密的循环中运行,等待任务被重新调度时也会高速运转。这最大限度地减少了由退出空闲状态造成的延迟,但代价是 CPU 在空闲线程中也要保持全速运行。在 BIOS 中禁用 C1E。此选项对于禁用 BIOS 中的 C1E 状态非常重要,可以避免 CPU 在空闲时进入 C1E 状态。C1E 状态是一种低功耗状态,可能会在 CPU 空闲时造成延迟。
添加
nowatchdog
以禁用软锁定检查包 — 作为定时器硬中断情况下运行的定时器。当它到期时(即检测到软锁定时),会显示一个警告(在硬中断情况下),并运行所有延迟目标。即使它永不过期,也会进入计时器列表,略微增加每次计时器中断的开销。此选项还会禁用 NMI 检查包,因此 NMI 无法干扰。添加
nmi_watchdog=0
。此选项仅禁用 NMI 检查包。
以下是一个包含上述调整的内核参数列表的示例:
GRUB_CMDLINE_LINUX="skew_tick=1 BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepages=0 hugepages=40 hugepagesz=1G hugepagesz=2M ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,19,20,39 isolcpus=domain,nohz,managed_irq,1-18,21-38 mce=off nohz=on net.ifnames=0 nmi_watchdog=0 nohz_full=1-18,21-38 nosoftlockup nowatchdog quiet rcu_nocb_poll rcu_nocbs=1-18,21-38 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1"
34.3 CPU 已微调配置 #
使用 CPU 已微调配置可以隔离 CPU 核心供实时内核使用。必须防止操作系统与实时内核使用相同的核心,因为操作系统有可能会使用这些核心,导致提高实时内核的延迟。
要启用和配置此功能,首先应该为我们想要隔离的 CPU 核心创建一个配置文件。在本例中,我们将隔离核心 1-30
和
33-62
。
$ echo "export tuned_params" >> /etc/grub.d/00_tuned
$ echo "isolated_cores=1-18,21-38" >> /etc/tuned/cpu-partitioning-variables.conf
$ tuned-adm profile cpu-partitioning
Tuned (re)started, changes applied.
然后我们需要修改 GRUB 选项来隔离 CPU 核心和其他重要的 CPU 使用参数。请务必根据您的当前硬件规格自定义以下选项:
参数 | 值 | 说明 |
---|---|---|
isolcpus | domain,nohz,managed_irq,1-18,21-38 | 隔离核心 1-18 和 21-38 |
skew_tick | 1 | 此选项允许内核在隔离的 CPU 之间偏斜计时器中断。 |
nohz | on | 此选项允许内核在系统空闲时在单个 CPU 上运行计时器滴答周期。 |
nohz_full | 1-18,21-38 | 内核引导参数是当前用于配置完整 dynticks 及 CPU 隔离的主接口。 |
rcu_nocbs | 1-18,21-38 | 此选项允许内核在系统空闲时在单个 CPU 上运行 RCU 回调。 |
irqaffinity | 0,19,20,39 | 此选项允许内核在系统空闲时在单个 CPU 上运行中断。 |
idle | poll | 最大限度减少退出空闲状态而造成的延迟,但代价是 CPU 在空闲线程中也要保持全速运行。 |
nmi_watchdog | 0 | 此选项仅禁用 NMI 检查包。 |
nowatchdog | 此选项禁用软锁定检查包,该检查包会在定时器硬中断情况下作为定时器运行。 |
我们将使用上面所示的值隔离 60 个核心,并将其中 4 个核心用于操作系统。
以下命令会修改 GRUB 配置并应用上述更改,以便下次引导时使用更改的配置:
编辑 /etc/default/grub
文件,在其中添加上述参数:
GRUB_CMDLINE_LINUX="skew_tick=1 BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepages=0 hugepages=40 hugepagesz=1G hugepagesz=2M ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,19,20,39 isolcpus=domain,nohz,managed_irq,1-18,21-38 mce=off nohz=on net.ifnames=0 nmi_watchdog=0 nohz_full=1-18,21-38 nosoftlockup nowatchdog quiet rcu_nocb_poll rcu_nocbs=1-18,21-38 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1"
更新 GRUB 配置:
$ transactional-update grub.cfg
$ reboot
要验证重引导后是否应用了这些参数,可使用以下命令检查内核命令行:
$ cat /proc/cmdline
另外还有一个脚本可用于调整 CPU 配置,它主要执行以下步骤:
将 CPU 调节器设置为
性能
。取消将计时器迁移到隔离 CPU 的设置。
将 kdaemon 线程迁移到管家 CPU。
将隔离 CPU 延迟设置为尽可能低的值。
将 vmstat 更新延迟到 300 秒。
34.4 CNI 配置 #
34.4.1 Cilium #
Cilium
是 ATIP 的默认 CNI 插件。要在 RKE2 群集上启用 Cilium 作为默认插件,需要在
/etc/rancher/rke2/config.yaml
文件中进行以下配置:
cni:
- cilium
也可以使用命令行参数来指定此配置,即,将 --cni=cilium
添加到
/etc/systemd/system/rke2-server
文件的 server 行中。
要使用下一节(第 34.5 节 “SR-IOV”)中所述的 SR-IOV
网络操作器,请将 Multus
与另一个 CNI 插件(例如 Cilium
或 Calico
)一起用作辅助插件。
cni:
- multus
- cilium
34.5 SR-IOV #
SR-IOV 允许网络适配器等设备在各种 PCIe
硬件功能之间分隔对其资源的访问。可以通过多种方式部署
SR-IOV
,本节将介绍两种不同的方式:
选项 1:使用
SR-IOV
CNI 设备插件和配置映射来正确配置 SR-IOV。选项 2(建议):使用 Rancher Prime 中的
SR-IOV
Helm chart 来轻松完成此部署。
选项 1 - 安装 SR-IOV CNI 设备插件并准备配置映射来正确配置 SR-IOV
为设备插件准备配置映射
使用 lspci
命令获取用于填充配置映射的信息:
$ lspci | grep -i acc
8a:00.0 Processing accelerators: Intel Corporation Device 0d5c
$ lspci | grep -i net
19:00.0 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
19:00.1 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
19:00.2 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
19:00.3 Ethernet controller: Broadcom Inc. and subsidiaries BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet (rev 11)
51:00.0 Ethernet controller: Intel Corporation Ethernet Controller E810-C for QSFP (rev 02)
51:00.1 Ethernet controller: Intel Corporation Ethernet Controller E810-C for QSFP (rev 02)
51:01.0 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:01.1 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:01.2 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:01.3 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.0 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.1 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.2 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
51:11.3 Ethernet controller: Intel Corporation Ethernet Adaptive Virtual Function (rev 02)
配置映射由一个 JSON
文件组成,该文件使用用于发现的过滤器来描述设备,并创建接口组。此处的关键在于理解过滤器和组。过滤器用于发现设备,组用于创建接口。
可以设置过滤器:
vendorID:
8086
(Intel)deviceID:
0d5c
(加速卡)driver:
vfio-pci
(驱动程序)pfNames:
p2p1
(物理接口名称)
还可以设置过滤器来匹配更复杂的接口语法,例如:
pfNames:
["eth1#1,2,3,4,5,6"]
或[eth1#1-6]
(物理接口名称)
对于组,我们可为 FEC
卡创建一个组,为 Intel
卡创建另一个组,甚至可以根据用例创建一个前缀:
resourceName:
pci_sriov_net_bh_dpdk
resourcePrefix:
Rancher.io
可以发现许多组合,并创建资源组来为 Pod 分配一些 VF
。
根据硬件和用例设置用于匹配接口的过滤器和组后,以下配置映射会显示要使用的示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: sriovdp-config
namespace: kube-system
data:
config.json: |
{
"resourceList": [
{
"resourceName": "intel_fec_5g",
"devicetype": "accelerator",
"selectors": {
"vendors": ["8086"],
"devices": ["0d5d"]
}
},
{
"resourceName": "intel_sriov_odu",
"selectors": {
"vendors": ["8086"],
"devices": ["1889"],
"drivers": ["vfio-pci"],
"pfNames": ["p2p1"]
}
},
{
"resourceName": "intel_sriov_oru",
"selectors": {
"vendors": ["8086"],
"devices": ["1889"],
"drivers": ["vfio-pci"],
"pfNames": ["p2p2"]
}
}
]
}
准备
daemonset
文件以部署设备插件。
设备插件支持多种体系结构(arm
、amd
、ppc64le
),因此同一文件可用于不同的体系结构,以便为每个体系结构部署多个
daemonset
。
apiVersion: v1
kind: ServiceAccount
metadata:
name: sriov-device-plugin
namespace: kube-system
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-sriov-device-plugin-amd64
namespace: kube-system
labels:
tier: node
app: sriovdp
spec:
selector:
matchLabels:
name: sriov-device-plugin
template:
metadata:
labels:
name: sriov-device-plugin
tier: node
app: sriovdp
spec:
hostNetwork: true
nodeSelector:
kubernetes.io/arch: amd64
tolerations:
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
serviceAccountName: sriov-device-plugin
containers:
- name: kube-sriovdp
image: rancher/hardened-sriov-network-device-plugin:v3.7.0-build20240816
imagePullPolicy: IfNotPresent
args:
- --log-dir=sriovdp
- --log-level=10
securityContext:
privileged: true
resources:
requests:
cpu: "250m"
memory: "40Mi"
limits:
cpu: 1
memory: "200Mi"
volumeMounts:
- name: devicesock
mountPath: /var/lib/kubelet/
readOnly: false
- name: log
mountPath: /var/log
- name: config-volume
mountPath: /etc/pcidp
- name: device-info
mountPath: /var/run/k8s.cni.cncf.io/devinfo/dp
volumes:
- name: devicesock
hostPath:
path: /var/lib/kubelet/
- name: log
hostPath:
path: /var/log
- name: device-info
hostPath:
path: /var/run/k8s.cni.cncf.io/devinfo/dp
type: DirectoryOrCreate
- name: config-volume
configMap:
name: sriovdp-config
items:
- key: config.json
path: config.json
应用配置映射和
daemonset
后,将部署设备插件,发现接口并将其提供给 Pod 使用。$ kubectl get pods -n kube-system | grep sriov kube-system kube-sriov-device-plugin-amd64-twjfl 1/1 Running 0 2m
检查节点中发现的并可供 Pod 使用的接口:
$ kubectl get $(kubectl get nodes -oname) -o jsonpath='{.status.allocatable}' | jq { "cpu": "64", "ephemeral-storage": "256196109726", "hugepages-1Gi": "40Gi", "hugepages-2Mi": "0", "intel.com/intel_fec_5g": "1", "intel.com/intel_sriov_odu": "4", "intel.com/intel_sriov_oru": "4", "memory": "221396384Ki", "pods": "110" }
FEC
为intel.com/intel_fec_5g
,值为 1。如果您使用设备插件和配置映射但未使用 Helm chart 部署了 SR-IOV,则
VF
为intel.com/intel_sriov_odu
或intel.com/intel_sriov_oru
。
如果此处没有接口,则继续操作就意义不大,因为 Pod 没有可用的接口。请先检查配置映射和过滤器来解决问题。
方式 2(建议)- 使用 Rancher 和 Helm chart 安装 SR-IOV CNI 和设备插件
获取 Helm(如果没有):
$ curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
安装 SR-IOV。
可以通过两种方式完成此操作:使用 CLI
或使用 Rancher UI
。
- 从 CLI 安装 Operator
helm install sriov-crd oci://registry.suse.com/edge/3.1/sriov-crd-chart -n sriov-network-operator helm install sriov-network-operator oci://registry.suse.com/edge/3.1/sriov-network-operator-chart -n sriov-network-operator
- 从 Rancher UI 安装 Operator
安装群集后,如果您可以访问
Rancher UI
,则可以通过Rancher UI
中的应用程序选项卡安装SR-IOV Operator
:
确保选择正确的名称空间来安装 Operator,例如 sriov-network-operator
。
+ image::features_sriov.png[sriov.png]
检查已部署的资源 CRD 和 Pod:
$ kubectl get crd
$ kubectl -n sriov-network-operator get pods
检查节点中的标签。
所有资源都运行后,标签会自动出现在您的节点中:
$ kubectl get nodes -oyaml | grep feature.node.kubernetes.io/network-sriov.capable
feature.node.kubernetes.io/network-sriov.capable: "true"
查看
daemonset
,您会看到新的sriov-network-config-daemon
和sriov-rancher-nfd-worker
处于活动状态并已准备就绪:
$ kubectl get daemonset -A
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
calico-system calico-node 1 1 1 1 1 kubernetes.io/os=linux 15h
sriov-network-operator sriov-network-config-daemon 1 1 1 1 1 feature.node.kubernetes.io/network-sriov.capable=true 45m
sriov-network-operator sriov-rancher-nfd-worker 1 1 1 1 1 <none> 45m
kube-system rke2-ingress-nginx-controller 1 1 1 1 1 kubernetes.io/os=linux 15h
kube-system rke2-multus-ds 1 1 1 1 1 kubernetes.io/arch=amd64,kubernetes.io/os=linux 15h
几分钟后(最多可能需要 10 分钟才会更新),将检测到节点,其上已配置了 SR-IOV
功能:
$ kubectl get sriovnetworknodestates.sriovnetwork.openshift.io -A
NAMESPACE NAME AGE
sriov-network-operator xr11-2 83s
检查已检测到的接口。
发现的接口应使用网络设备的 PCI 地址。请在主机中使用 lspci
命令检查此信息。
$ kubectl get sriovnetworknodestates.sriovnetwork.openshift.io -n kube-system -oyaml
apiVersion: v1
items:
- apiVersion: sriovnetwork.openshift.io/v1
kind: SriovNetworkNodeState
metadata:
creationTimestamp: "2023-06-07T09:52:37Z"
generation: 1
name: xr11-2
namespace: sriov-network-operator
ownerReferences:
- apiVersion: sriovnetwork.openshift.io/v1
blockOwnerDeletion: true
controller: true
kind: SriovNetworkNodePolicy
name: default
uid: 80b72499-e26b-4072-a75c-f9a6218ec357
resourceVersion: "356603"
uid: e1f1654b-92b3-44d9-9f87-2571792cc1ad
spec:
dpConfigVersion: "356507"
status:
interfaces:
- deviceID: "1592"
driver: ice
eSwitchMode: legacy
linkType: ETH
mac: 40:a6:b7:9b:35:f0
mtu: 1500
name: p2p1
pciAddress: "0000:51:00.0"
totalvfs: 128
vendor: "8086"
- deviceID: "1592"
driver: ice
eSwitchMode: legacy
linkType: ETH
mac: 40:a6:b7:9b:35:f1
mtu: 1500
name: p2p2
pciAddress: "0000:51:00.1"
totalvfs: 128
vendor: "8086"
syncStatus: Succeeded
kind: List
metadata:
resourceVersion: ""
如果此处未检测到您的接口,请确保该接口在下一个配置映射中存在:
$ kubectl get cm supported-nic-ids -oyaml -n sriov-network-operator
如果此处未检测到您的设备,请编辑配置映射,添加要发现的正确值(如有必要,请重启动
sriov-network-config-daemon
daemonset)。
创建
NetworkNode 策略
来配置VF
。
将在设备 (rootDevices
) 中创建一些 VF
(numVfs
),并在其上配置驱动程序 deviceType
和
MTU
:
resourceName
字段不得包含任何特殊字符,并且在整个群集中必须唯一。该示例使用
deviceType: vfio-pci
,因为 dpdk
将与
sr-iov
结合使用。如果您不使用 dpdk
,则 deviceType
应为 deviceType: netdevice
(默认值)。
apiVersion: sriovnetwork.openshift.io/v1
kind: SriovNetworkNodePolicy
metadata:
name: policy-dpdk
namespace: sriov-network-operator
spec:
nodeSelector:
feature.node.kubernetes.io/network-sriov.capable: "true"
resourceName: intelnicsDpdk
deviceType: vfio-pci
numVfs: 8
mtu: 1500
nicSelector:
deviceID: "1592"
vendor: "8086"
rootDevices:
- 0000:51:00.0
验证配置:
$ kubectl get $(kubectl get nodes -oname) -o jsonpath='{.status.allocatable}' | jq
{
"cpu": "64",
"ephemeral-storage": "256196109726",
"hugepages-1Gi": "60Gi",
"hugepages-2Mi": "0",
"intel.com/intel_fec_5g": "1",
"memory": "200424836Ki",
"pods": "110",
"rancher.io/intelnicsDpdk": "8"
}
创建 sr-iov 网络(可选操作,仅在需要不同的网络时才执行):
apiVersion: sriovnetwork.openshift.io/v1
kind: SriovNetwork
metadata:
name: network-dpdk
namespace: sriov-network-operator
spec:
ipam: |
{
"type": "host-local",
"subnet": "192.168.0.0/24",
"rangeStart": "192.168.0.20",
"rangeEnd": "192.168.0.60",
"routes": [{
"dst": "0.0.0.0/0"
}],
"gateway": "192.168.0.1"
}
vlan: 500
resourceName: intelnicsDpdk
检查创建的网络:
$ kubectl get network-attachment-definitions.k8s.cni.cncf.io -A -oyaml
apiVersion: v1
items:
- apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
annotations:
k8s.v1.cni.cncf.io/resourceName: rancher.io/intelnicsDpdk
creationTimestamp: "2023-06-08T11:22:27Z"
generation: 1
name: network-dpdk
namespace: sriov-network-operator
resourceVersion: "13124"
uid: df7c89f5-177c-4f30-ae72-7aef3294fb15
spec:
config: '{ "cniVersion":"0.4.0", "name":"network-dpdk","type":"sriov","vlan":500,"vlanQoS":0,"ipam":{"type":"host-local","subnet":"192.168.0.0/24","rangeStart":"192.168.0.10","rangeEnd":"192.168.0.60","routes":[{"dst":"0.0.0.0/0"}],"gateway":"192.168.0.1"}
}'
kind: List
metadata:
resourceVersion: ""
34.6 DPDK #
DPDK
(数据平面开发包)是一组用于实现快速包处理的库和驱动程序。它用于加速各种 CPU
体系结构上运行的包处理工作负载。DPDK 包含以下组件的数据平面库和优化的网络接口控制器 (NIC
) 驱动程序:
队列管理器实现无锁队列。
缓冲区管理器预先分配固定大小的缓冲区。
内存管理器在内存中分配对象池,并使用环来存储空闲对象;确保对象均匀分布在所有
DRAM
通道上。轮询模式驱动程序 (
PMD
) 可以在没有异步通知的情况下运行,从而降低了开销。包框架以一组库的形式提供,可帮助开发包处理解决方案。
以下步骤说明如何启用 DPDK
,以及如何从 NIC
创建供
DPDK
接口使用的 VF
:
安装
DPDK
软件包:
$ transactional-update pkg install dpdk dpdk-tools libdpdk-23
$ reboot
内核参数:
要使用 DPDK,请采用一些驱动程序来启用内核中的某些参数:
参数 | 值 | 说明 |
---|---|---|
iommu | pt | 此选项允许为 DPDK 接口使用 |
intel_iommu | on | 此选项允许为 |
要启用这些参数,请将其添加到 /etc/default/grub
文件中:
GRUB_CMDLINE_LINUX="skew_tick=1 BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepages=0 hugepages=40 hugepagesz=1G hugepagesz=2M ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,19,20,39 isolcpus=domain,nohz,managed_irq,1-18,21-38 mce=off nohz=on net.ifnames=0 nmi_watchdog=0 nohz_full=1-18,21-38 nosoftlockup nowatchdog quiet rcu_nocb_poll rcu_nocbs=1-18,21-38 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1"
更新 GRUB 配置,并重引导系统以应用更改:
$ transactional-update grub.cfg
$ reboot
加载
vfio-pci
内核模块,并在NIC
上启用SR-IOV
:
$ modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
从
NIC
创建一些虚拟功能 (VF
)。
例如,要为两个不同的 NIC
创建 VF
,需要运行以下命令:
$ echo 4 > /sys/bus/pci/devices/0000:51:00.0/sriov_numvfs
$ echo 4 > /sys/bus/pci/devices/0000:51:00.1/sriov_numvfs
将新的 VF 绑定到
vfio-pci
驱动程序:
$ dpdk-devbind.py -b vfio-pci 0000:51:01.0 0000:51:01.1 0000:51:01.2 0000:51:01.3 \
0000:51:11.0 0000:51:11.1 0000:51:11.2 0000:51:11.3
检查是否正确应用了配置:
$ dpdk-devbind.py -s
Network devices using DPDK-compatible driver
============================================
0000:51:01.0 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.1 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.2 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.3 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:01.0 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:11.1 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:21.2 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
0000:51:31.3 'Ethernet Adaptive Virtual Function 1889' drv=vfio-pci unused=iavf,igb_uio
Network devices using kernel driver
===================================
0000:19:00.0 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em1 drv=bnxt_en unused=igb_uio,vfio-pci *Active*
0000:19:00.1 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em2 drv=bnxt_en unused=igb_uio,vfio-pci
0000:19:00.2 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em3 drv=bnxt_en unused=igb_uio,vfio-pci
0000:19:00.3 'BCM57504 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb/200Gb Ethernet 1751' if=em4 drv=bnxt_en unused=igb_uio,vfio-pci
0000:51:00.0 'Ethernet Controller E810-C for QSFP 1592' if=eth13 drv=ice unused=igb_uio,vfio-pci
0000:51:00.1 'Ethernet Controller E810-C for QSFP 1592' if=rename8 drv=ice unused=igb_uio,vfio-pci
34.7 vRAN 加速 (Intel ACC100/ACC200
) #
随着通讯服务提供商从 4G 过渡到 5G 网络,有许多提供商正在采用虚拟化无线接入网络 (vRAN
)
体系结构来提高信道容量及简化基于边缘的服务和应用程序的部署。vRAN
解决方案非常适合用于提供低延迟服务,并可以根据实时流量和网络需求灵活地增加或减少容量。
计算密集程度最高的 4G 和 5G 工作负载之一是 RAN 第 1 层 (L1
)
FEC
,它可以解决不可靠或高干扰信道上的数据传输错误。FEC
技术可以检测并纠正 4G 或 5G 数据中有限数量的错误,因此消除了重新传输的需要。由于 FEC
加速事务不包含特定的单元状态信息,因此可以轻松虚拟化,从而带来了池化优势并可实现轻松的单元迁移。
内核参数
要启用 vRAN
加速,需要启用以下内核参数(如果尚未启用):
参数 | 值 | 说明 |
---|---|---|
iommu | pt | 此选项允许为 DPDK 接口使用 vfio。 |
intel_iommu | on | 此选项允许为 VF 使用 vfio。 |
修改 GRUB 文件 /etc/default/grub
,以将这些参数添加到内核命令行:
GRUB_CMDLINE_LINUX="skew_tick=1 BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepages=0 hugepages=40 hugepagesz=1G hugepagesz=2M ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,19,20,39 isolcpus=domain,nohz,managed_irq,1-18,21-38 mce=off nohz=on net.ifnames=0 nmi_watchdog=0 nohz_full=1-18,21-38 nosoftlockup nowatchdog quiet rcu_nocb_poll rcu_nocbs=1-18,21-38 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1"
更新 GRUB 配置,并重引导系统以应用更改:
$ transactional-update grub.cfg
$ reboot
要校验重引导后是否应用了这些参数,请检查命令行:
$ cat /proc/cmdline
加载 vfio-pci 内核模块以启用
vRAN
加速:
$ modprobe vfio-pci enable_sriov=1 disable_idle_d3=1
获取 Acc100 接口信息:
$ lspci | grep -i acc
8a:00.0 Processing accelerators: Intel Corporation Device 0d5c
将物理接口 (
PF
) 绑定到vfio-pci
驱动程序:
$ dpdk-devbind.py -b vfio-pci 0000:8a:00.0
从物理接口 (
PF
) 创建虚拟功能 (VF
)。
从 PF
创建 2 个 VF
,并按照以下步骤将其绑定到
vfio-pci
:
$ echo 2 > /sys/bus/pci/devices/0000:8a:00.0/sriov_numvfs
$ dpdk-devbind.py -b vfio-pci 0000:8b:00.0
使用建议的配置文件配置 acc100:
$ pf_bb_config ACC100 -c /opt/pf-bb-config/acc100_config_vf_5g.cfg
Tue Jun 6 10:49:20 2023:INFO:Queue Groups: 2 5GUL, 2 5GDL, 2 4GUL, 2 4GDL
Tue Jun 6 10:49:20 2023:INFO:Configuration in VF mode
Tue Jun 6 10:49:21 2023:INFO: ROM version MM 99AD92
Tue Jun 6 10:49:21 2023:WARN:* Note: Not on DDR PRQ version 1302020 != 10092020
Tue Jun 6 10:49:21 2023:INFO:PF ACC100 configuration complete
Tue Jun 6 10:49:21 2023:INFO:ACC100 PF [0000:8a:00.0] configuration complete!
检查从 FEC PF 创建的新 VF:
$ dpdk-devbind.py -s
Baseband devices using DPDK-compatible driver
=============================================
0000:8a:00.0 'Device 0d5c' drv=vfio-pci unused=
0000:8b:00.0 'Device 0d5d' drv=vfio-pci unused=
Other Baseband devices
======================
0000:8b:00.1 'Device 0d5d' unused=
34.8 大页 #
当某个进程使用 RAM
时,CPU
会将 RAM
标记为已由该进程使用。为提高效率,CPU
会以区块的形式分配
RAM
,在许多平台上,默认的区块大小值为 4K
字节。这些区块称为页。页可以交换到磁盘等位置。
由于进程地址空间是虚拟的,CPU
和操作系统需要记住哪些页属于哪个进程,以及每个页存储在哪个位置。页数越多,内存映射的搜索时间就越长。当某个进程使用 1
GB
内存时,需要查找 262144 个项 (1 GB
/ 4
K
)。如果一个页表项占用 8 个字节,则需要查找 2 MB
内存 (262144 * 8)。
当前的大多数 CPU
体系结构都支持大于默认值的页,因此减少了
CPU/操作系统
需要查找的项。
内核参数
要启用大页,应添加以下内核参数:
参数 | 值 | 说明 |
---|---|---|
hugepagesz | 1G | 此选项允许将大页的大小设置为 1 G |
hugepages | 40 | 这是先前定义的大页数量 |
default_hugepagesz | 1G | 这是用于获取大页的默认值 |
修改 GRUB 文件 /etc/default/grub
,以将这些参数添加到内核命令行:
GRUB_CMDLINE_LINUX="skew_tick=1 BOOT_IMAGE=/boot/vmlinuz-6.4.0-9-rt root=UUID=77b713de-5cc7-4d4c-8fc6-f5eca0a43cf9 rd.timeout=60 rd.retry=45 console=ttyS1,115200 console=tty0 default_hugepagesz=1G hugepages=0 hugepages=40 hugepagesz=1G hugepagesz=2M ignition.platform.id=openstack intel_iommu=on iommu=pt irqaffinity=0,19,20,39 isolcpus=domain,nohz,managed_irq,1-18,21-38 mce=off nohz=on net.ifnames=0 nmi_watchdog=0 nohz_full=1-18,21-38 nosoftlockup nowatchdog quiet rcu_nocb_poll rcu_nocbs=1-18,21-38 rcupdate.rcu_cpu_stall_suppress=1 rcupdate.rcu_expedited=1 rcupdate.rcu_normal_after_boot=1 rcupdate.rcu_task_stall_timeout=0 rcutree.kthread_prio=99 security=selinux selinux=1"
更新 GRUB 配置,并重引导系统以应用更改:
$ transactional-update grub.cfg
$ reboot
要验证重引导后是否应用了这些参数,可以检查命令行:
$ cat /proc/cmdline
使用大页
要使用大页,需要挂载它们:
$ mkdir -p /hugepages
$ mount -t hugetlbfs nodev /hugepages
部署 Kubernetes 工作负载,并创建资源和卷:
...
resources:
requests:
memory: "24Gi"
hugepages-1Gi: 16Gi
intel.com/intel_sriov_oru: '4'
limits:
memory: "24Gi"
hugepages-1Gi: 16Gi
intel.com/intel_sriov_oru: '4'
...
...
volumeMounts:
- name: hugepage
mountPath: /hugepages
...
volumes:
- name: hugepage
emptyDir:
medium: HugePages
...
34.9 CPU 固定配置 #
要求
必须根据第 34.3 节 “CPU 已微调配置”一节中所述的性能配置文件微调
CPU
。必须使用 CPU 管理参数配置
RKE2
群集 kubelet,为此请将以下块(示例)添加到/etc/rancher/rke2/config.yaml
文件中:
kubelet-arg:
- "cpu-manager=true"
- "cpu-manager-policy=static"
- "cpu-manager-policy-options=full-pcpus-only=true"
- "cpu-manager-reconcile-period=0s"
- "kubelet-reserved=cpu=1"
- "system-reserved=cpu=1"
在 Kubernetes 上使用 CPU 固定
根据您在工作负载上定义的请求和限制,可以用三种方式通过 kubelet 中定义的静态策略
来使用该功能:
BestEffort
QoS 类:如果您没有为CPU
定义任何请求或限制,则 Pod 将调度到系统中第一个可用的CPU
上。使用
BestEffort
QoS 类的示例如下:spec: containers: - name: nginx image: nginx
Burstable
QoS 类:如果为 CPU 定义的请求不等于限制,或者没有 CPU 请求,请使用此方式。使用
Burstable
QoS 类的示例如下:spec: containers: - name: nginx image: nginx resources: limits: memory: "200Mi" requests: memory: "100Mi"
或
spec: containers: - name: nginx image: nginx resources: limits: memory: "200Mi" cpu: "2" requests: memory: "100Mi" cpu: "1"
Guaranteed
QoS 类:如果为 CPU 定义的请求等于限制,请使用此方式。使用
Guaranteed
QoS 类的示例如下:spec: containers: - name: nginx image: nginx resources: limits: memory: "200Mi" cpu: "2" requests: memory: "200Mi" cpu: "2"
34.10 可感知 NUMA 的调度 #
非统一内存访问或非统一内存体系结构 (NUMA
) 是
SMP
(多处理器)体系结构中使用的物理内存设计,其中内存访问时间取决于相对于处理器的内存位置。在
NUMA
下,与访问非本地内存(即,另一个处理器本地的内存,或者在处理器之间共享的内存)相比,处理器可以更快地访问自身的本地内存。
34.10.1 识别 NUMA 节点 #
要识别 NUMA
节点,请在系统上使用以下命令:
$ lscpu | grep NUMA
NUMA node(s): 1
NUMA node0 CPU(s): 0-63
对于此示例,只有一个 NUMA
节点显示了 64 个 CPU
。
NUMA
需要在 BIOS
中启用。如果
dmesg
中没有引导过程中 NUMA 初始化的记录,则可能表示内核环缓冲区中的
NUMA
相关消息已被重写。
34.11 MetalLB #
MetalLB
是裸机 Kubernetes 群集的负载平衡器实现,使用
L2
和 BGP
等标准路由协议作为广告协议。它是一个网络负载平衡器,可用于向外部公开 Kubernetes 群集中的服务(因为需要在裸机上使用 Kubernetes
服务类型 LoadBalancer
)。
要在 RKE2
群集中启用 MetalLB
,需要执行以下步骤:
使用以下命令安装
MetalLB
:
$ kubectl apply <<EOF -f
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: metallb
namespace: kube-system
spec:
chart: oci://registry.suse.com/edge/3.1/metallb-chart
targetNamespace: metallb-system
version: 0.14.9
createNamespace: true
---
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: endpoint-copier-operator
namespace: kube-system
spec:
chart: oci://registry.suse.com/edge/3.1/endpoint-copier-operator-chart
targetNamespace: endpoint-copier-operator
version: 0.2.1
createNamespace: true
EOF
创建
IpAddressPool
和L2advertisement
配置:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: kubernetes-vip-ip-pool
namespace: metallb-system
spec:
addresses:
- 10.168.200.98/32
serviceAllocation:
priority: 100
namespaces:
- default
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: ip-pool-l2-adv
namespace: metallb-system
spec:
ipAddressPools:
- kubernetes-vip-ip-pool
创建端点服务来公开
VIP
:
apiVersion: v1
kind: Service
metadata:
name: kubernetes-vip
namespace: default
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: rke2-api
port: 9345
protocol: TCP
targetPort: 9345
- name: k8s-api
port: 6443
protocol: TCP
targetPort: 6443
sessionAffinity: None
type: LoadBalancer
检查
VIP
是否已创建并且MetalLB
Pod 是否正在运行:
$ kubectl get svc -n default
$ kubectl get pods -n default
34.12 专用注册表配置 #
可以配置 Containerd
以连接到专用注册表,然后使用专用注册表提取每个节点上的专用映像。
启动时,RKE2
会检查 /etc/rancher/rke2/
中是否存在
registries.yaml
文件,并指示 containerd
使用该文件中定义的任何注册表。如果您希望使用某个专用注册表,请在要使用该注册表的每个节点上以 root 身份创建此文件。
要添加专用注册表,请创建包含以下内容的 /etc/rancher/rke2/registries.yaml
文件:
mirrors:
docker.io:
endpoint:
- "https://registry.example.com:5000"
configs:
"registry.example.com:5000":
auth:
username: xxxxxx # this is the registry username
password: xxxxxx # this is the registry password
tls:
cert_file: # path to the cert file used to authenticate to the registry
key_file: # path to the key file for the certificate used to authenticate to the registry
ca_file: # path to the ca file used to verify the registry's certificate
insecure_skip_verify: # may be set to true to skip verifying the registry's certificate
或者不设置身份验证:
mirrors:
docker.io:
endpoint:
- "https://registry.example.com:5000"
configs:
"registry.example.com:5000":
tls:
cert_file: # path to the cert file used to authenticate to the registry
key_file: # path to the key file for the certificate used to authenticate to the registry
ca_file: # path to the ca file used to verify the registry's certificate
insecure_skip_verify: # may be set to true to skip verifying the registry's certificate
要使注册表更改生效,需要在节点上启动 RKE2 之前配置此文件,或者在配置的每个节点上重启动 RKE2。