apiVersion: v1
kind: Namespace
metadata:
name: openshift-kmm
了解内核模块管理 (KMM) 操作符以及如何使用它在 OpenShift Container Platform 集群上部署树外内核模块和设备插件。
内核模块管理 (KMM) 操作符管理、构建、签名和部署 OpenShift Container Platform 集群上的树外内核模块和设备插件。
KMM 添加了一个新的 `Module` CRD,它描述了树外内核模块及其关联的设备插件。您可以使用 `Module` 资源配置如何加载模块,为内核版本定义 `ModuleLoader` 镜像,并包含针对特定内核版本的模块构建和签名说明。
KMM 旨在同时适应多个内核版本,适用于任何内核模块,从而实现无缝节点升级并减少应用程序停机时间。
作为集群管理员,您可以使用 OpenShift CLI 或 Web 控制台安装内核模块管理 (KMM) 操作符。
KMM 操作符在 OpenShift Container Platform 4.12 及更高版本上受支持。在 4.11 版本上安装 KMM 不需要额外的步骤。有关在 4.10 及更早版本上安装 KMM 的详细信息,请参阅“在早期版本的 OpenShift Container Platform 上安装内核模块管理操作符”部分。
作为集群管理员,您可以使用 OpenShift Container Platform Web 控制台安装内核模块管理 (KMM) 操作符。
登录到 OpenShift Container Platform Web 控制台。
安装内核模块管理操作符
在 OpenShift Container Platform Web 控制台中,单击**操作符** → **OperatorHub**。
从可用操作符列表中选择**内核模块管理操作符**,然后单击**安装**。
从**已安装命名空间**列表中,选择 `openshift-kmm` 命名空间。
单击**安装**。
要验证 KMM 操作符是否已成功安装
导航到**操作符** → **已安装操作符**页面。
确保**内核模块管理操作符**列在 `openshift-kmm` 项目中,且**状态**为**InstallSucceeded**。
在安装过程中,操作符可能会显示**失败**状态。如果安装稍后成功并显示**InstallSucceeded**消息,则可以忽略**失败**消息。 |
要排除操作符安装问题
导航到**操作符** → **已安装操作符**页面,并检查**操作符订阅**和**安装计划**选项卡中**状态**下的任何故障或错误。
导航到**工作负载** → **Pod**页面,并检查 `openshift-kmm` 项目中 Pod 的日志。
作为集群管理员,您可以使用 OpenShift CLI 安装内核模块管理 (KMM) 操作符。
您有一个正在运行的 OpenShift Container Platform 集群。
您已安装 OpenShift CLI ( `oc` )。
您已以具有 `cluster-admin` 权限的用户身份登录到 OpenShift CLI。
在 `openshift-kmm` 命名空间中安装 KMM
创建以下 `Namespace` CR 并保存 YAML 文件,例如 `kmm-namespace.yaml`
apiVersion: v1
kind: Namespace
metadata:
name: openshift-kmm
创建以下 `OperatorGroup` CR 并保存 YAML 文件,例如 `kmm-op-group.yaml`
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: kernel-module-management
namespace: openshift-kmm
创建以下 `Subscription` CR 并保存 YAML 文件,例如 `kmm-sub.yaml`
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: kernel-module-management
namespace: openshift-kmm
spec:
channel: release-1.0
installPlanApproval: Automatic
name: kernel-module-management
source: redhat-operators
sourceNamespace: openshift-marketplace
startingCSV: kernel-module-management.v1.0.0
通过运行以下命令创建订阅对象
$ oc create -f kmm-sub.yaml
要验证操作符部署是否成功,请运行以下命令
$ oc get -n openshift-kmm deployments.apps kmm-operator-controller
NAME READY UP-TO-DATE AVAILABLE AGE
kmm-operator-controller 1/1 1 1 97s
操作符可用。
KMM 运算符支持 OpenShift Container Platform 4.12 及更高版本。对于 4.10 及更低版本,您必须创建一个新的SecurityContextConstraint
对象并将其绑定到运算符的ServiceAccount
。作为集群管理员,您可以使用 OpenShift CLI 安装内核模块管理 (KMM) 运算符。
您有一个正在运行的 OpenShift Container Platform 集群。
您已安装 OpenShift CLI ( `oc` )。
您已以具有 `cluster-admin` 权限的用户身份登录到 OpenShift CLI。
在 `openshift-kmm` 命名空间中安装 KMM
创建以下Namespace
CR 并保存 YAML 文件,例如kmm-namespace.yaml
文件。
apiVersion: v1
kind: Namespace
metadata:
name: openshift-kmm
创建以下SecurityContextConstraint
对象并保存 YAML 文件,例如kmm-security-constraint.yaml
文件。
allowHostDirVolumePlugin: false
allowHostIPC: false
allowHostNetwork: false
allowHostPID: false
allowHostPorts: false
allowPrivilegeEscalation: false
allowPrivilegedContainer: false
allowedCapabilities:
- NET_BIND_SERVICE
apiVersion: security.openshift.io/v1
defaultAddCapabilities: null
fsGroup:
type: MustRunAs
groups: []
kind: SecurityContextConstraints
metadata:
name: restricted-v2
priority: null
readOnlyRootFilesystem: false
requiredDropCapabilities:
- ALL
runAsUser:
type: MustRunAsRange
seLinuxContext:
type: MustRunAs
seccompProfiles:
- runtime/default
supplementalGroups:
type: RunAsAny
users: []
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- projected
- secret
通过运行以下命令将SecurityContextConstraint
对象绑定到运算符的ServiceAccount
。
$ oc apply -f kmm-security-constraint.yaml
$ oc adm policy add-scc-to-user kmm-security-constraint -z kmm-operator-controller -n openshift-kmm
创建以下 `OperatorGroup` CR 并保存 YAML 文件,例如 `kmm-op-group.yaml`
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: kernel-module-management
namespace: openshift-kmm
创建以下 `Subscription` CR 并保存 YAML 文件,例如 `kmm-sub.yaml`
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: kernel-module-management
namespace: openshift-kmm
spec:
channel: release-1.0
installPlanApproval: Automatic
name: kernel-module-management
source: redhat-operators
sourceNamespace: openshift-marketplace
startingCSV: kernel-module-management.v1.0.0
通过运行以下命令创建订阅对象
$ oc create -f kmm-sub.yaml
要验证操作符部署是否成功,请运行以下命令
$ oc get -n openshift-kmm deployments.apps kmm-operator-controller
NAME READY UP-TO-DATE AVAILABLE AGE
kmm-operator-controller 1/1 1 1 97s
操作符可用。
在大多数情况下,无需修改内核模块管理 (KMM) 运算符的默认配置。但是,您可以使用以下步骤修改运算符设置以适应您的环境。
运算符配置设置在运算符命名空间中的kmm-operator-manager-config
ConfigMap
中。
要修改设置,请通过输入以下命令来编辑ConfigMap
数据。
$ oc edit configmap -n "$namespace" kmm-operator-manager-config
healthProbeBindAddress: :8081
job:
gcDelay: 1h
leaderElection:
enabled: true
resourceID: kmm.sigs.x-k8s.io
webhook:
disableHTTP2: true # CVE-2023-44487
port: 9443
metrics:
enableAuthnAuthz: true
disableHTTP2: true # CVE-2023-44487
bindAddress: 0.0.0.0:8443
secureServing: true
worker:
runAsUser: 0
seLinuxType: spc_t
setFirmwareClassPath: /var/lib/firmware
参数 | 描述 |
---|---|
|
定义运算符监控 kubelet 健康探针的地址。建议值为 |
|
定义成功构建的 Pod 在删除之前应保留的持续时间。此设置没有推荐值。有关此设置的有效值的更多信息,请参阅 ParseDuration。 |
|
确定是否使用 leader election 来确保任何时候只运行 KMM 运算符的一个副本。更多信息,请参阅 Leases。建议值为 |
|
确定 leader election 用于持有 leader 锁的资源名称。建议值为 |
|
如果为 |
|
定义运算符监控 webhook 请求的端口。建议值为 |
|
确定是否使用 对于身份验证和授权,控制器需要具有以下规则的
要抓取指标(例如,使用 Prometheus),客户端需要具有以下规则的
建议值为 |
|
如果为 |
|
确定指标服务器的绑定地址。如果未指定,则默认为 |
|
确定指标是通过 HTTPS 而不是 HTTP 提供服务。建议值为 |
|
确定工作容器安全上下文的 |
|
确定工作容器安全上下文的 |
|
将内核的固件搜索路径设置到节点上的 |
修改设置后,使用以下命令重启控制器。
$ oc delete pod -n "<namespace>" -l app.kubernetes.io/component=kmm
|
更多信息,请参阅 安装内核模块管理运算符。
迁移到较新版本或如果它们在节点上引入一些不良副作用时,必须卸载内核模块。
要卸载使用 KMM 从节点加载的模块,请删除相应的Module
资源。然后,KMM 会根据需要创建 worker Pod 来运行modprobe -r
并从节点卸载内核模块。
卸载 worker Pod 时,KMM 需要加载内核模块时使用的所有资源。这包括 为避免 KMM 无法从节点卸载内核模块的情况,请确保在集群中仍然存在 |
如 固件搜索路径中所述,Linux 内核接受firmware_class.path
参数作为固件的搜索路径。
KMM worker Pod 可以通过在尝试加载 kmod 之前写入 sysfs 来在节点上设置此值。
要定义固件搜索路径,请在运算符配置中将worker.setFirmwareClassPath
设置为/var/lib/firmware
。
有关worker.setFirmwareClassPath
路径的更多信息,请参阅 配置内核模块管理运算符。
内核模块管理 (KMM) 监控集群中的Node
和Module
资源,以确定是否应在节点上加载或卸载内核模块。
要符合模块的条件,节点必须包含以下内容:
与模块的.spec.selector
字段匹配的标签。
与模块的.spec.moduleLoader.container.kernelMappings
字段中的一个项目匹配的内核版本。
如果在模块中配置了有序升级 (ordered_upgrade.md
),则包含与.spec.moduleLoader.container.version
字段匹配的标签。
当 KMM 将节点与Module
资源中配置的所需状态协调时,它会在目标节点上创建工作 Pod 来运行必要的操作。KMM 运算符监控 Pod 的结果并记录信息。运算符使用此信息在成功加载模块时标记Node
对象,并在配置的情况下运行设备插件。
工作 Pod 运行执行以下任务的 KMM worker
二进制文件:
拉取Module
资源中配置的 kmod 镜像。Kmod 镜像是包含.ko
文件的标准 OCI 镜像。
将镜像提取到 Pod 的文件系统中。
使用指定的参数运行modprobe
来执行必要的操作。
Module
自定义资源定义 (CRD) 代表一个内核模块,可以通过 kmod 镜像加载到集群中的所有或部分选定节点上。Module
自定义资源 (CR) 指定一个或多个与其兼容的内核版本和节点选择器。
Module
资源的兼容版本列在.spec.moduleLoader.container.kernelMappings
下。内核映射可以匹配literal
版本,也可以使用regexp
同时匹配多个版本。
Module
资源的协调循环运行以下步骤:
列出所有与.spec.selector
匹配的节点。
构建在这些节点上运行的所有内核版本的集合。
对于每个内核版本:
遍历.spec.moduleLoader.container.kernelMappings
并查找相应的容器镜像名称。如果内核映射已定义build
或sign
,并且容器镜像尚不存在,则根据需要运行构建、签名 Pod 或两者。
创建一个工作 Pod 来拉取上一步中确定的容器镜像并运行modprobe
。
如果定义了.spec.devicePlugin
,则使用.spec.devicePlugin.container
下指定的配置创建一个设备插件 DaemonSet。
对以下内容运行garbage-collect
:
不针对任何节点的已过时的设备插件DaemonSets
。
成功的构建 Pod。
成功的签名 Pod。
某些配置要求以特定顺序加载多个内核模块才能正常工作,即使这些模块不通过符号直接依赖于彼此。这些称为软依赖关系。depmod
通常不知道这些依赖关系,并且它们不会出现在它生成的文件中。例如,如果mod_a
对mod_b
有软依赖关系,则modprobe mod_a
将不会加载mod_b
。
您可以通过使用modulesLoadingOrder
字段在 Module 自定义资源定义 (CRD) 中声明软依赖关系来解决这些情况。
# ...
spec:
moduleLoader:
container:
modprobe:
moduleName: mod_a
dirName: /opt
firmwarePath: /firmware
parameters:
- param=1
modulesLoadingOrder:
- mod_a
- mod_b
在上面的配置中,工作 Pod 将首先尝试卸载树内mod_b
,然后再从 kmod 镜像加载mod_a
。当工作 Pod 终止并且mod_a
被卸载时,mod_b
将不会再次加载。
列表中的第一个值(最后加载)必须等效于 |
加载内核模块是一个高度敏感的操作。加载后,内核模块具有所有可能的权限来对节点执行任何类型的操作。 |
内核模块管理 (KMM) 创建一个特权工作负载以在节点上加载内核模块。该工作负载需要允许使用privileged
SecurityContextConstraint
(SCC) 资源的ServiceAccounts
。
该工作负载的授权模型取决于Module
资源的命名空间及其规范。
如果设置了.spec.moduleLoader.serviceAccountName
或.spec.devicePlugin.serviceAccountName
字段,则始终使用它们。
如果没有设置这些字段,则:
如果在运算符的命名空间(默认为openshift-kmm
)中创建Module
资源,则 KMM 使用其默认的、强大的ServiceAccounts
来运行工作 Pod 和设备插件 Pod。
如果在任何其他命名空间中创建Module
资源,则 KMM 使用命名空间的default
ServiceAccount
运行 Pod。除非您手动启用它以使用privileged
SCC,否则Module
资源无法运行特权工作负载。
设置 RBAC 权限时,请记住,任何在 |
要允许任何ServiceAccount
使用privileged
SCC 并运行工作 Pod 或设备插件 Pod,您可以使用oc adm policy
命令,如下例所示:
$ oc adm policy add-scc-to-user privileged -z "${serviceAccountName}" [ -n "${namespace}" ]
您可以使用内核模块管理 (KMM) 构建可以按需加载或卸载到内核中的内核模块。这些模块扩展了内核的功能,无需重新启动系统。模块可以配置为内置或动态加载。
动态加载的模块包括树内模块和树外 (OOT) 模块。树内模块是 Linux 内核树内部的,也就是说,它们已经是内核的一部分。树外模块位于 Linux 内核树之外。它们通常用于开发和测试目的,例如测试以树内方式交付的内核模块的新版本,或处理不兼容性。
KMM 加载的某些模块可能会替换节点上已加载的树内模块。要在加载您的模块之前卸载树内模块,请将.spec.moduleLoader.container.inTreeModulesToRemove
字段的值设置为要卸载的模块。以下示例演示了所有内核映射的模块替换:
# ...
spec:
moduleLoader:
container:
modprobe:
moduleName: mod_a
inTreeModulesToRemove: [mod_a, mod_b]
在这个示例中,moduleLoader
pod 使用 inTreeModulesToRemove
在从 moduleLoader
镜像加载 mod_a
之前卸载树内模块 mod_a
和 mod_b
。当 moduleLoader
pod 终止并且 mod_a
被卸载时,mod_b
不会再次加载。
以下是针对特定内核映射的模块替换示例。
# ...
spec:
moduleLoader:
container:
kernelMappings:
- literal: 6.0.15-300.fc37.x86_64
containerImage: "some.registry/org/my-kmod:${KERNEL_FULL_VERSION}"
inTreeModulesToRemove: [<module_name>, <module_name>]
以下是带注释的 Module
示例
apiVersion: kmm.sigs.x-k8s.io/v1beta1
kind: Module
metadata:
name: <my_kmod>
spec:
moduleLoader:
container:
modprobe:
moduleName: <my_kmod> (1)
dirName: /opt (2)
firmwarePath: /firmware (3)
parameters: (4)
- param=1
kernelMappings: (5)
- literal: 6.0.15-300.fc37.x86_64
containerImage: some.registry/org/my-kmod:6.0.15-300.fc37.x86_64
- regexp: '^.+\fc37\.x86_64$' (6)
containerImage: "some.other.registry/org/<my_kmod>:${KERNEL_FULL_VERSION}"
- regexp: '^.+$' (7)
containerImage: "some.registry/org/<my_kmod>:${KERNEL_FULL_VERSION}" (8)
build:
buildArgs: (9)
- name: ARG_NAME
value: <some_value>
secrets:
- name: <some_kubernetes_secret> (10)
baseImageRegistryTLS: (11)
insecure: false
insecureSkipTLSVerify: false (12)
dockerfileConfigMap: (13)
name: <my_kmod_dockerfile>
sign:
certSecret:
name: <cert_secret> (14)
keySecret:
name: <key_secret> (15)
filesToSign:
- /opt/lib/modules/${KERNEL_FULL_VERSION}/<my_kmod>.ko
registryTLS: (16)
insecure: false (17)
insecureSkipTLSVerify: false
serviceAccountName: <sa_module_loader> (18)
devicePlugin: (19)
container:
image: some.registry/org/device-plugin:latest (20)
env:
- name: MY_DEVICE_PLUGIN_ENV_VAR
value: SOME_VALUE
volumeMounts: (21)
- mountPath: /some/mountPath
name: <device_plugin_volume>
volumes: (22)
- name: <device_plugin_volume>
configMap:
name: <some_configmap>
serviceAccountName: <sa_device_plugin> (23)
imageRepoSecret: (24)
name: <secret_name>
selector:
node-role.kubernetes.io/worker: ""
1 | 必需。 |
2 | 可选。 |
3 | 可选:将此路径的内容复制到 kmm-operator-manager-config 配置映射的 worker.setFirmwareClassPath (预设为 /var/lib/firmware )中指定的路径。此操作发生在调用 modprobe 插入内核模块之前。 |
4 | 可选。 |
5 | 至少需要一个内核项。 |
6 | 对于每个运行与正则表达式匹配的内核的节点,KMM 检查您是否包含了标签或摘要。如果您没有在容器镜像中指定标签或摘要,则验证 Webhook 将返回错误,并且不会应用模块。 |
7 | 对于任何其他内核,请使用 my-kmod ConfigMap 中的 Dockerfile 构建镜像。 |
8 | 包含客户 kmod 的容器镜像。此容器应包含 cp 二进制文件。 |
9 | 可选。 |
10 | 可选:可以在构建环境的 /run/secrets/some-kubernetes-secret 中获取 some-kubernetes-secret 的值。 |
11 | 此字段无效。在构建 kmod 镜像或在 kmod 镜像中签署 kmod 时,有时可能需要从使用不受信任的证书颁发机构 (CA) 签署的证书的服务注册表中提取基础镜像。为了让 KMM 信任该 CA,它还必须通过替换集群的 CA 捆绑包来信任新的 CA。 请参阅“其他资源”以了解如何替换集群的 CA 捆绑包。 |
12 | 可选:避免使用此参数。如果设置为 true ,则构建将在使用普通 HTTP 拉取 Dockerfile FROM 指令中的镜像时跳过任何 TLS 服务器证书验证。 |
13 | 必需。 |
14 | 必需:包含具有密钥“cert”的公共安全启动密钥的密钥。 |
15 | 必需:包含具有密钥“key”的私有安全启动密钥的密钥。 |
16 | 可选:避免使用此参数。如果设置为 true ,则允许 KMM 使用普通 HTTP 检查容器镜像是否已存在。 |
17 | 可选:避免使用此参数。如果设置为 true ,则 KMM 在检查容器镜像是否已存在时将跳过任何 TLS 服务器证书验证。 |
18 | 可选。 |
19 | 可选。 |
20 | 必需:如果存在设备插件部分。 |
21 | 可选。 |
22 | 可选。 |
23 | 可选。 |
24 | 可选:用于拉取模块加载器和设备插件镜像。 |
某些内核模块依赖于随节点操作系统一起提供的其他内核模块。为了避免将这些依赖项复制到 kmod 镜像中,内核模块管理 (KMM) 将 /usr/lib/modules
挂载到构建和工作程序 pod 的文件系统中。
通过从 /opt/usr/lib/modules/
创建到 /usr/lib/modules/
的符号链接,depmod
可以使用构建节点文件系统上的树内 kmod 来解析依赖项。
在运行时,工作程序 pod 将提取整个镜像,包括
符号链接。该符号链接指向工作程序 pod 中的 /usr/lib/modules/
,该 pod 从节点的文件系统挂载。然后,modprobe
可以遵循该链接并根据需要加载树内依赖项。
在以下示例中,host
是 /opt/usr/lib/modules/
下的符号链接名称。
ARG DTK_AUTO
FROM ${DTK_AUTO} as builder
#
# Build steps
#
FROM ubi9/ubi
ARG KERNEL_FULL_VERSION
RUN dnf update && dnf install -y kmod
COPY --from=builder /usr/src/kernel-module-management/ci/kmm-kmod/kmm_ci_a.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
COPY --from=builder /usr/src/kernel-module-management/ci/kmm-kmod/kmm_ci_b.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
# Create the symbolic link
RUN ln -s /lib/modules/${KERNEL_FULL_VERSION} /opt/lib/modules/${KERNEL_FULL_VERSION}/host
RUN depmod -b /opt ${KERNEL_FULL_VERSION}
在 KMM 加载内核模块的节点上, |
内核模块管理 (KMM) 使用专用构建的 kmod 镜像,这些镜像是包含 .ko
文件的标准 OCI 镜像。.ko
文件的位置必须匹配以下模式:
。
使用 .ko
文件时,请记住以下几点
在大多数情况下,
应等于 /opt
。这是 Module
CRD 的默认值。
kernel-version
不能为空,并且必须等于为其构建内核模块的内核版本。
建议在构建过程结束时运行 depmod
以生成 modules.dep
和 .map
文件。如果您的 kmod 镜像包含多个内核模块,并且其中一个模块依赖于另一个模块,则此方法尤其有用。
您必须拥有 Red Hat 订阅才能下载 |
通过运行以下命令为特定内核版本生成 modules.dep
和 .map
文件
$ depmod -b /opt ${KERNEL_FULL_VERSION}+`.
如果您在 OpenShift Container Platform 上构建镜像,请考虑使用驱动程序工具包 (DTK)。
有关更多信息,请参阅 使用授权构建。
apiVersion: v1
kind: ConfigMap
metadata:
name: kmm-ci-dockerfile
data:
dockerfile: |
ARG DTK_AUTO
FROM ${DTK_AUTO} as builder
ARG KERNEL_FULL_VERSION
WORKDIR /usr/src
RUN ["git", "clone", "https://github.com/rh-ecosystem-edge/kernel-module-management.git"]
WORKDIR /usr/src/kernel-module-management/ci/kmm-kmod
RUN KERNEL_SRC_DIR=/lib/modules/${KERNEL_FULL_VERSION}/build make all
FROM registry.redhat.io/ubi9/ubi-minimal
ARG KERNEL_FULL_VERSION
RUN microdnf install kmod
COPY --from=builder /usr/src/kernel-module-management/ci/kmm-kmod/kmm_ci_a.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
COPY --from=builder /usr/src/kernel-module-management/ci/kmm-kmod/kmm_ci_b.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
RUN depmod -b /opt ${KERNEL_FULL_VERSION}
KMM 可以在集群中构建 kmod 镜像。请遵循以下准则
使用内核映射的 build
部分提供构建指令。
将容器镜像的 Dockerfile
复制到 ConfigMap
资源的 dockerfile
密钥下。
确保 ConfigMap
位于与 Module
相同的命名空间中。
KMM 检查 containerImage
字段中指定的镜像名称是否存在。如果存在,则跳过构建。
否则,KMM 将创建一个 Build
资源来构建您的镜像。构建镜像后,KMM 将继续进行 Module
调和。请参见以下示例。
# ...
- regexp: '^.+$'
containerImage: "some.registry/org/<my_kmod>:${KERNEL_FULL_VERSION}"
build:
buildArgs: (1)
- name: ARG_NAME
value: <some_value>
secrets: (2)
- name: <some_kubernetes_secret> (3)
baseImageRegistryTLS:
insecure: false (4)
insecureSkipTLSVerify: false (5)
dockerfileConfigMap: (6)
name: <my_kmod_dockerfile>
registryTLS:
insecure: false (7)
insecureSkipTLSVerify: false (8)
1 | 可选。 |
2 | 可选。 |
3 | 将在构建 pod 中作为 /run/secrets/some-kubernetes-secret 挂载。 |
4 | 可选:避免使用此参数。如果设置为 true ,则允许构建使用普通 HTTP 拉取 Dockerfile FROM 指令中的镜像。 |
5 | 可选:避免使用此参数。如果设置为 true ,则构建将在使用普通 HTTP 拉取 Dockerfile FROM 指令中的镜像时跳过任何 TLS 服务器证书验证。 |
6 | 必需。 |
7 | 可选:避免使用此参数。如果设置为 true ,则允许 KMM 使用普通 HTTP 检查容器镜像是否已存在。 |
8 | 可选:避免使用此参数。如果设置为true ,KMM 将跳过任何 TLS 服务器证书验证,以检查容器镜像是否存在。 |
成功的构建 Pod 会立即被垃圾回收,除非在 Operator 配置中设置了job.gcDelay
参数。失败的构建 Pod 将始终保留,必须由管理员手动删除才能重新启动构建。
驱动程序工具包 (DTK) 是构建 kmod 加载程序镜像的便捷基础镜像。它包含当前在集群中运行的 OpenShift 版本的工具和库。
将 DTK 用作多阶段Dockerfile
的第一阶段。
构建内核模块。
将.ko
文件复制到较小的最终用户镜像中,例如ubi-minimal
。
要在集群内构建中利用 DTK,请使用DTK_AUTO
构建参数。创建Build
资源时,KMM 会自动设置该值。请参见以下示例。
ARG DTK_AUTO
FROM ${DTK_AUTO} as builder
ARG KERNEL_FULL_VERSION
WORKDIR /usr/src
RUN ["git", "clone", "https://github.com/rh-ecosystem-edge/kernel-module-management.git"]
WORKDIR /usr/src/kernel-module-management/ci/kmm-kmod
RUN KERNEL_SRC_DIR=/lib/modules/${KERNEL_FULL_VERSION}/build make all
FROM ubi9/ubi-minimal
ARG KERNEL_FULL_VERSION
RUN microdnf install kmod
COPY --from=builder /usr/src/kernel-module-management/ci/kmm-kmod/kmm_ci_a.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
COPY --from=builder /usr/src/kernel-module-management/ci/kmm-kmod/kmm_ci_b.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
RUN depmod -b /opt ${KERNEL_FULL_VERSION}
在启用安全启动的系统上,所有内核模块 (kmod) 都必须使用注册到机器所有者密钥 (MOK) 数据库中的公钥/私钥对进行签名。作为发行版一部分分发的驱动程序应已由发行版的私钥签名,但对于树外构建的内核模块,KMM 支持使用内核映射的sign
部分对内核模块进行签名。
有关使用安全启动的更多详细信息,请参阅生成公钥和私钥对
正确 (DER) 格式的公钥私钥对。
至少一个启用安全启动的节点,其 MOK 数据库中注册了公钥。
预构建的驱动程序容器镜像,或在集群内构建一个所需的源代码和Dockerfile
。
要使用 KMM 内核模块管理 (KMM) 对内核模块进行签名,需要证书和私钥。有关如何创建这些密钥的详细信息,请参阅生成公钥和私钥对。
有关如何提取公钥和私钥对的详细信息,请参阅使用私钥签名内核模块。使用步骤 1 到 4 将密钥提取到文件中。
创建包含证书的sb_cert.cer
文件和包含私钥的sb_cert.priv
文件。
$ openssl req -x509 -new -nodes -utf8 -sha256 -days 36500 -batch -config configuration_file.config -outform DER -out my_signing_key_pub.der -keyout my_signing_key.priv
使用以下方法之一添加文件:
直接添加文件作为密钥
$ oc create secret generic my-signing-key --from-file=key=<my_signing_key.priv>
$ oc create secret generic my-signing-key-pub --from-file=cert=<my_signing_key_pub.der>
通过 base64 编码添加文件
$ cat sb_cert.priv | base64 -w 0 > my_signing_key2.base64
$ cat sb_cert.cer | base64 -w 0 > my_signing_key_pub.base64
将编码文本添加到 YAML 文件
apiVersion: v1
kind: Secret
metadata:
name: my-signing-key-pub
namespace: default (1)
type: Opaque
data:
cert: <base64_encoded_secureboot_public_key>
---
apiVersion: v1
kind: Secret
metadata:
name: my-signing-key
namespace: default (1)
type: Opaque
data:
key: <base64_encoded_secureboot_private_key>
1 | namespace - 将default 替换为有效的命名空间。 |
应用 YAML 文件
$ oc apply -f <yaml_filename>
添加密钥后,必须检查密钥以确保它们已正确设置。
检查以确保公钥密钥已正确设置。
$ oc get secret -o yaml <certificate secret name> | awk '/cert/{print $2; exit}' | base64 -d | openssl x509 -inform der -text
这应该显示一个具有序列号、颁发者、主题等的证书。
检查以确保私钥密钥已正确设置。
$ oc get secret -o yaml <private key secret name> | awk '/key/{print $2; exit}' | base64 -d
这应该显示包含在-----BEGIN PRIVATE KEY-----
和-----END PRIVATE KEY-----
行中的密钥。
如果您有预构建的镜像(例如,硬件供应商分发的镜像或在其他地方构建的镜像),请使用此过程。
以下 YAML 文件将公钥/私钥对添加为具有所需密钥名称的密钥 - 私钥为key
,公钥为cert
的密钥。然后,集群下载unsignedImage
镜像,打开它,签名filesToSign
中列出的内核模块,将它们添加回来,并将生成的镜像作为containerImage
推送。
然后,KMM 将签名的 kmod 加载到与选择器匹配的所有节点上。kmod 将成功加载到 MOK 数据库中具有公钥的任何节点上,以及任何未启用安全启动的节点上,这些节点将忽略签名。
keySecret
和certSecret
密钥已在与其余资源相同的命名空间中创建。
应用 YAML 文件
---
apiVersion: kmm.sigs.x-k8s.io/v1beta1
kind: Module
metadata:
name: example-module
spec:
moduleLoader:
serviceAccountName: default
container:
modprobe: (1)
moduleName: '<module_name>'
kernelMappings:
# the kmods will be deployed on all nodes in the cluster with a kernel that matches the regexp
- regexp: '^.*\.x86_64$'
# the container to produce containing the signed kmods
containerImage: <image_name> (2)
sign:
# the image containing the unsigned kmods (we need this because we are not building the kmods within the cluster)
unsignedImage: <image_name> (3)
keySecret: # a secret holding the private secureboot key with the key 'key'
name: <private_key_secret_name>
certSecret: # a secret holding the public secureboot key with the key 'cert'
name: <certificate_secret_name>
filesToSign: # full path within the unsignedImage container to the kmod(s) to sign
- /opt/lib/modules/4.18.0-348.2.1.el8_5.x86_64/kmm_ci_a.ko
imageRepoSecret:
# the name of a secret containing credentials to pull unsignedImage and push containerImage to the registry
name: repo-pull-secret
selector:
kubernetes.io/arch: amd64
1 | 要加载的 kmod 的名称。 |
2 | 容器镜像的名称。例如,quay.io/myuser/my-driver:<kernelversion 。 |
3 | 未签名镜像的名称。例如,quay.io/myuser/my-driver:<kernelversion 。 |
如果您有源代码并且必须先构建镜像,请使用此过程。
以下 YAML 文件使用存储库中的源代码构建新的容器镜像。生成的镜像将使用临时名称保存回注册表中,然后使用sign
部分中的参数对该临时镜像进行签名。
临时镜像名称基于最终镜像名称,并设置为<containerImage>:<tag>-<namespace>_<module name>_kmm_unsigned
。
例如,使用以下 YAML 文件,内核模块管理 (KMM) 将构建一个名为example.org/repository/minimal-driver:final-default_example-module_kmm_unsigned
的镜像,其中包含未签名的 kmod 并将其推送到注册表。然后,它创建一个名为example.org/repository/minimal-driver:final
的第二个镜像,其中包含签名的 kmod。正是这个第二个镜像被工作 Pod 拉取,并包含要在集群节点上加载的 kmod。
签名后,您可以安全地从注册表中删除临时镜像。如有需要,它将被重新构建。
keySecret
和certSecret
密钥已在与其余资源相同的命名空间中创建。
应用 YAML 文件
---
apiVersion: v1
kind: ConfigMap
metadata:
name: example-module-dockerfile
namespace: <namespace> (1)
data:
Dockerfile: |
ARG DTK_AUTO
ARG KERNEL_VERSION
FROM ${DTK_AUTO} as builder
WORKDIR /build/
RUN git clone -b main --single-branch https://github.com/rh-ecosystem-edge/kernel-module-management.git
WORKDIR kernel-module-management/ci/kmm-kmod/
RUN make
FROM registry.access.redhat.com/ubi9/ubi:latest
ARG KERNEL_VERSION
RUN yum -y install kmod && yum clean all
RUN mkdir -p /opt/lib/modules/${KERNEL_VERSION}
COPY --from=builder /build/kernel-module-management/ci/kmm-kmod/*.ko /opt/lib/modules/${KERNEL_VERSION}/
RUN /usr/sbin/depmod -b /opt
---
apiVersion: kmm.sigs.x-k8s.io/v1beta1
kind: Module
metadata:
name: example-module
namespace: <namespace> (1)
spec:
moduleLoader:
serviceAccountName: default (2)
container:
modprobe:
moduleName: simple_kmod
kernelMappings:
- regexp: '^.*\.x86_64$'
containerImage: <final_driver_container_name>
build:
dockerfileConfigMap:
name: example-module-dockerfile
sign:
keySecret:
name: <private_key_secret_name>
certSecret:
name: <certificate_secret_name>
filesToSign:
- /opt/lib/modules/4.18.0-348.2.1.el8_5.x86_64/kmm_ci_a.ko
imageRepoSecret: (3)
name: repo-pull-secret
selector: # top-level selector
kubernetes.io/arch: amd64
1 | 将default 替换为有效的命名空间。 |
2 | 默认的serviceAccountName 没有运行特权模块所需的权限。有关创建服务帐户的信息,请参阅本节“附加资源”中的“创建服务帐户”。 |
3 | 用作DaemonSet 对象中的imagePullSecrets ,以及构建和签名功能的拉取和推送。 |
在集线器和辐条场景中,许多辐条集群连接到中央强大的集线器集群。内核模块管理 (KMM) 依赖于 Red Hat Advanced Cluster Management (RHACM) 才能在集线器和辐条环境中运行。
KMM 通过解耦 KMM 功能与集线器和辐条环境兼容。提供了一个ManagedClusterModule
自定义资源定义 (CRD) 来包装现有的Module
CRD 并将其扩展到选择辐条集群。还提供了 KMM-Hub,这是一个新的独立控制器,可在集线器集群上构建镜像和签名模块。
在集线器和辐条设置中,辐条是专注的、资源受限的集群,由集线器集群集中管理。辐条运行 KMM 的单集群版本,其中资源密集型功能已禁用。为了使 KMM 适应此环境,应将辐条上运行的工作负载减少到最低限度,而集线器负责处理费时的任务。
构建内核模块镜像和签名.ko
文件应在集线器上运行。模块加载器和设备插件DaemonSets
的调度只能在辐条上进行。
KMM 项目提供 KMM-Hub,这是一个专用于 Hub 集群的 KMM 版本。KMM-Hub 监控运行在 Spoke 上的所有内核版本,并确定集群中应该接收内核模块的节点。
KMM-Hub 运行所有计算密集型任务,例如镜像构建和 kmod 签名,并准备精简的 Module
,通过 RHACM 传输到 Spoke。
KMM-Hub 不能用于在 Hub 集群上加载内核模块。要加载内核模块,请安装 KMM 的常规版本。 |
您可以使用以下方法之一安装 KMM-Hub
使用 Operator Lifecycle Manager (OLM)
创建 KMM 资源
如果您想以编程方式安装 KMM-Hub,可以使用以下资源创建 Namespace
、OperatorGroup
和 Subscription
资源
---
apiVersion: v1
kind: Namespace
metadata:
name: openshift-kmm-hub
---
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: kernel-module-management-hub
namespace: openshift-kmm-hub
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: kernel-module-management-hub
namespace: openshift-kmm-hub
spec:
channel: stable
installPlanApproval: Automatic
name: kernel-module-management-hub
source: redhat-operators
sourceNamespace: openshift-marketplace
ManagedClusterModule
CRD使用 ManagedClusterModule
自定义资源定义 (CRD) 来配置在 Spoke 集群上部署内核模块。此 CRD 是集群范围的,它包含一个 Module
规范,并添加了以下附加字段
apiVersion: hub.kmm.sigs.x-k8s.io/v1beta1
kind: ManagedClusterModule
metadata:
name: <my-mcm>
# No namespace, because this resource is cluster-scoped.
spec:
moduleSpec: (1)
selector: (2)
node-wants-my-mcm: 'true'
spokeNamespace: <some-namespace> (3)
selector: (4)
wants-my-mcm: 'true'
1 | moduleSpec :包含 moduleLoader 和 devicePlugin 部分,类似于 Module 资源。 |
2 | 选择 ManagedCluster 中的节点。 |
3 | 指定应在哪个命名空间中创建 Module 。 |
4 | 选择 ManagedCluster 对象。 |
如果 .spec.moduleSpec
中存在构建或签名指令,则这些 Pod 将在操作符命名空间中的 Hub 集群上运行。
当 .spec.selector
匹配一个或多个 ManagedCluster
资源时,KMM-Hub 将在相应的命名空间中创建一个 ManifestWork
资源。ManifestWork
包含一个精简的 Module
资源,保留了内核映射,但删除了所有 build
和 sign
子部分。包含以标签结尾的镜像名称的 containerImage
字段将替换为其摘要等效项。
在 Spoke 上安装内核模块管理 (KMM) 后,无需进一步操作。从 Hub 创建 ManagedClusterModule
对象可在 Spoke 集群上部署内核模块。
您可以通过 RHACM Policy
对象在 Spoke 集群上安装 KMM。除了从 OperatorHub 安装 KMM 并以轻量级 Spoke 模式运行它之外,Policy
还配置了 RHACM 代理管理 Module
资源所需的额外 RBAC。
使用以下 RHACM策略在 Spoke 集群上安装 KMM
---
apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
name: install-kmm
spec:
remediationAction: enforce
disabled: false
policy-templates:
- objectDefinition:
apiVersion: policy.open-cluster-management.io/v1
kind: ConfigurationPolicy
metadata:
name: install-kmm
spec:
severity: high
object-templates:
- complianceType: mustonlyhave
objectDefinition:
apiVersion: v1
kind: Namespace
metadata:
name: openshift-kmm
- complianceType: mustonlyhave
objectDefinition:
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
name: kmm
namespace: openshift-kmm
spec:
upgradeStrategy: Default
- complianceType: mustonlyhave
objectDefinition:
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: kernel-module-management
namespace: openshift-kmm
spec:
channel: stable
config:
env:
- name: KMM_MANAGED (1)
value: "1"
installPlanApproval: Automatic
name: kernel-module-management
source: redhat-operators
sourceNamespace: openshift-marketplace
- complianceType: mustonlyhave
objectDefinition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kmm-module-manager
rules:
- apiGroups: [kmm.sigs.x-k8s.io]
resources: [modules]
verbs: [create, delete, get, list, patch, update, watch]
- complianceType: mustonlyhave
objectDefinition:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: klusterlet-kmm
subjects:
- kind: ServiceAccount
name: klusterlet-work-sa
namespace: open-cluster-management-agent
roleRef:
kind: ClusterRole
name: kmm-module-manager
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps.open-cluster-management.io/v1
kind: PlacementRule
metadata:
name: all-managed-clusters
spec:
clusterSelector: (2)
matchExpressions: []
---
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
name: install-kmm
placementRef:
apiGroup: apps.open-cluster-management.io
kind: PlacementRule
name: all-managed-clusters
subjects:
- apiGroup: policy.open-cluster-management.io
kind: Policy
name: install-kmm
1 | 在 Spoke 集群上运行 KMM 时,需要此环境变量。 |
2 | 可以自定义 spec.clusterSelector 字段以仅定位选定的集群。 |
在对节点进行维护操作(包括根据需要重新引导节点)时,使用此过程升级内核模块。为了最大程度地减少对集群中运行的工作负载的影响,请一次一个节点地顺序运行内核升级过程。
此过程需要了解使用内核模块的工作负载,并且必须由集群管理员管理。 |
升级前,在内核模块使用的所有节点上设置 kmm.node.kubernetes.io/version-module.<module_namespace>.<module_name>=$moduleVersion
标签。
终止节点上所有用户应用程序工作负载,或将其移动到另一个节点。
卸载当前加载的内核模块。
确保在卸载内核模块之前,用户工作负载(在集群中访问内核模块的应用程序)未在节点上运行,并且在新内核模块版本加载后,工作负载已恢复在节点上运行。
确保卸载节点上 KMM 管理的设备插件。
更新 Module
自定义资源 (CR) 中的以下字段
containerImage
(到相应的内核版本)
version
更新必须是原子的;也就是说,必须同时更新 containerImage
和 version
字段。
终止正在升级的节点上使用内核模块的任何工作负载。
删除节点上的 kmm.node.kubernetes.io/version-module.<module_namespace>.<module_name>
标签。运行以下命令以从节点卸载内核模块
$ oc label node/<node_name> kmm.node.kubernetes.io/version-module.<module_namespace>.<module_name>-
如果需要,作为集群管理员,执行内核模块升级所需的任何其他维护。
如果不需要其他升级,您可以通过将 kmm.node.kubernetes.io/version-module.<module_namespace>.<module_name>
标签值更新为在 Module
中设置的新 $moduleVersion
来跳过步骤 3 到 6。
运行以下命令以向节点添加 kmm.node.kubernetes.io/version-module.<module_namespace>.<module_name>=$moduleVersion
标签。$moduleVersion
必须等于 Module
CR 中 version
字段的新值。
$ oc label node/<node_name> kmm.node.kubernetes.io/version-module.<module_namespace>.<module_name>=<desired_version>
由于 Kubernetes 在标签名称方面的限制, |
恢复节点上利用内核模块的任何工作负载。
重新加载节点上 KMM 管理的设备插件。
内核模块管理 (KMM) 通常是 Day 2 运算符。内核模块仅在 Linux (RHCOS) 服务器完全初始化后才加载。但是,在某些情况下,必须在早期阶段加载内核模块。Day 1 功能允许您使用机器配置运算符 (MCO) 在 Linux systemd
初始化阶段加载内核模块。
Day 1 功能支持有限数量的用例。主要用例是在 NetworkManager 服务初始化之前允许加载树外 (OOT) 内核模块。它不支持在 initramfs
阶段加载内核模块。
以下是 Day 1 功能所需的条件
内核中未加载内核模块。
树内内核模块已加载到内核中,但可以卸载并替换为 OOT 内核模块。这意味着树内模块未被任何其他内核模块引用。
为了使 Day 1 功能正常工作,节点必须具有功能正常的网络接口,即该接口的树内内核驱动程序。OOT 内核模块可以是将替换功能网络驱动程序的网络驱动程序。
树外 (OOT) 内核模块的加载利用了机器配置运算符 (MCO)。流程顺序如下
将 MachineConfig
资源应用于现有的运行集群。为了识别需要更新的必要节点,您必须创建一个合适的 MachineConfigPool
资源。
MCO逐个节点地应用重新引导。在任何重新引导的节点上,都会部署两个新的 systemd
服务:pull
服务和 load
服务。
load
服务配置为在 NetworkConfiguration
服务之前运行。该服务尝试拉取预定义的内核模块镜像,然后使用该镜像卸载树内模块并加载树外内核模块。
pull
服务配置为在 NetworkManager 服务之后运行。该服务检查预配置的内核模块镜像是否位于节点的文件系统上。如果存在,则服务正常退出,服务器继续引导过程。如果不存在,则将其拉取到节点上,然后重新引导节点。
KMM 提供了一个 API 来为 Day 1 功能创建 MCO YAML 清单。
ProduceMachineConfig(machineConfigName, machineConfigPoolRef, kernelModuleImage, kernelModuleName string) (string, error)
返回的输出是待应用的 MCO YAML 清单的字符串表示。应用此 YAML 的责任在于客户。
参数如下:
machineConfigName
MCO YAML 清单的名称。此参数设置为 MCO YAML 清单元数据的 name
参数。
machineConfigPoolRef
用于标识目标节点的 MachineConfigPool
名称。
kernelModuleImage
包含树外内核模块的容器镜像的名称。
kernelModuleName
树外内核模块的名称。此参数用于卸载树内内核模块(如果已加载到内核中)和加载树外内核模块。
API 位于 KMM 源代码的 pkg/mcproducer
包下。无需运行 KMM 运算符即可使用 Day 1 功能。您只需将 pkg/mcproducer
包导入到他们的运算符/实用程序代码中,调用 API 并将生成的 MCO YAML 应用到集群即可。
MachineConfigPool
标识受应用的 MCO 影响的节点集合。
kind: MachineConfigPool
metadata:
name: sfc
spec:
machineConfigSelector: (1)
matchExpressions:
- {key: machineconfiguration.openshift.io/role, operator: In, values: [worker, sfc]}
nodeSelector: (2)
matchLabels:
node-role.kubernetes.io/sfc: ""
paused: false
maxUnavailable: 1
1 | 匹配 MachineConfig 中的标签。 |
2 | 匹配节点上的标签。 |
OCP 集群中存在预定义的 MachineConfigPools
worker
:针对集群中的所有工作节点
master
:针对集群中的所有主节点
定义以下 MachineConfig
来定位主 MachineConfigPool
metadata:
labels:
machineconfiguration.opensfhit.io/role: master
定义以下 MachineConfig
来定位工作 MachineConfigPool
metadata:
labels:
machineconfiguration.opensfhit.io/role: worker
如果驱动程序容器中的 kmod 未签名或使用错误的密钥签名,则容器可能进入 PostStartHookError
或 CrashLoopBackOff
状态。您可以通过在容器上运行 oc describe
命令来验证,在这种情况下,它会显示以下消息
modprobe: ERROR: could not insert '<your_kmod_name>': Required key not available
内核模块有时需要从文件系统加载固件文件。KMM 支持将固件文件从 kmod 镜像复制到节点的文件系统。
在运行 modprobe
命令插入内核模块之前,.spec.moduleLoader.container.modprobe.firmwarePath
的内容将复制到节点上的 /var/lib/firmware
路径。
在 pod 终止时,运行 modprobe -r
命令卸载内核模块之前,将删除该位置的所有文件和空目录。
在 OpenShift Container Platform 节点上,固件的默认查找路径集不包括 /var/lib/firmware
路径。
使用 Machine Config Operator 创建包含 /var/lib/firmware
路径的 MachineConfig
自定义资源 (CR)。
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
labels:
machineconfiguration.openshift.io/role: worker (1)
name: 99-worker-kernel-args-firmware-path
spec:
kernelArguments:
- 'firmware_class.path=/var/lib/firmware'
1 | 您可以根据需要配置标签。对于单节点 OpenShift,请使用 control-pane 或 master 对象。 |
应用 MachineConfig
CR 后,节点将自动重新引导。
除了构建内核模块本身之外,还要在构建器镜像中包含二进制固件。
FROM registry.redhat.io/ubi9/ubi-minimal as builder
# Build the kmod
RUN ["mkdir", "/firmware"]
RUN ["curl", "-o", "/firmware/firmware.bin", "https://artifacts.example.com/firmware.bin"]
FROM registry.redhat.io/ubi9/ubi-minimal
# Copy the kmod, install modprobe, run depmod
COPY --from=builder /firmware /firmware
在 Module
自定义资源 (CR) 中设置 .spec.moduleLoader.container.modprobe.firmwarePath
apiVersion: kmm.sigs.x-k8s.io/v1beta1
kind: Module
metadata:
name: my-kmod
spec:
moduleLoader:
container:
modprobe:
moduleName: my-kmod # Required
firmwarePath: /firmware (1)
1 | 可选:将 /firmware/* 复制到节点上的 /var/lib/firmware/ 。 |
您可以在 Day 0 到 Day 2 操作期间安装一些内核模块 (kmod),而无需使用内核模块管理 (KMM)。这可以帮助将 kmod 过渡到 KMM。
使用以下标准来确定合适的 kmod 安装。
节点在集群中变为 Ready
所需的最基本 kmod。这些类型的 kmod 示例包括:
在引导过程中挂载 rootFS 所需的存储驱动程序
机器访问引导节点上的 machine-config-server
以拉取点火并加入集群所需的网络驱动程序
节点在集群中变为 Ready
不需要但节点处于 Ready
状态时无法卸载的 kmod。
此类 kmod 的一个示例是替换旧的树内驱动程序的树外 (OOT) 网络驱动程序,以便在 NetworkManager
依赖它的同时充分利用 NIC 的潜力。当节点处于 Ready
状态时,由于 NetworkManager
的依赖关系,您无法卸载驱动程序。
可以动态加载到内核或从中移除的 kmod,而不会干扰集群基础设施(例如连接性)。
这些类型的 kmod 的示例包括:
GPU 运算符
辅助网络适配器
现场可编程门阵列 (FPGA)
当在集群中安装 Day 0 kmod 时,分层将通过 Machine Config Operator (MCO) 应用,OpenShift Container Platform 升级不会触发节点升级。
只有在向驱动程序添加新功能时才需要重新编译驱动程序,因为节点的操作系统将保持不变。
当驱动程序允许时,您可以利用 KMM 来管理 kmod 的 Day 0 到 Day 2 生命周期,而无需重新引导。
如果升级需要重新引导节点,例如,当需要重建 |
使用以下任一选项进行生命周期管理。
在对KMM安装问题进行故障排除时,您可以监控日志以确定问题发生在哪个阶段。然后,检索与该阶段相关的诊断数据。
您可以使用oc logs
命令读取Operator日志,如下例所示。
$ oc logs -fn openshift-kmm deployments/kmm-operator-controller
$ oc logs -fn openshift-kmm deployments/kmm-operator-webhook-server
$ oc logs -fn openshift-kmm-hub deployments/kmm-operator-hub-controller
$ oc logs -fn openshift-kmm deployments/kmm-operator-hub-webhook-server
使用以下方法查看KMM事件。
每当KMM启动kmod镜像构建或观察其结果时,它都会发布事件。这些事件附加到Module
对象,并且可在oc describe module
命令输出的末尾找到,如下例所示。
$ oc describe modules.kmm.sigs.x-k8s.io kmm-ci-a
[...]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal BuildCreated 2m29s kmm Build created for kernel 6.6.2-201.fc39.x86_64
Normal BuildSucceeded 63s kmm Build job succeeded for kernel 6.6.2-201.fc39.x86_64
Normal SignCreated 64s (x2 over 64s) kmm Sign created for kernel 6.6.2-201.fc39.x86_64
Normal SignSucceeded 57s kmm Sign job succeeded for kernel 6.6.2-201.fc39.x86_64
每当KMM成功加载或卸载节点上的内核模块时,它都会发布事件。这些事件附加到Node
对象,并且可在oc describe node
命令输出的末尾找到,如下例所示。
$ oc describe node my-node
[...]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
[...]
Normal ModuleLoaded 4m17s kmm Module default/kmm-ci-a loaded into the kernel
Normal ModuleUnloaded 2s kmm Module default/kmm-ci-a unloaded from the kernel
oc adm must-gather
命令是收集支持包并向Red Hat支持提供调试信息的推荐方法。通过使用以下部分中所述的适当参数运行命令来收集特定信息。
收集KMM Operator控制器管理器的数据
设置MUST_GATHER_IMAGE
变量
$ export MUST_GATHER_IMAGE=$(oc get deployment -n openshift-kmm kmm-operator-controller -ojsonpath='{.spec.template.spec.containers[?(@.name=="manager")].env[?(@.name=="RELATED_IMAGE_MUST_GATHER")].value}')
$ oc adm must-gather --image="${MUST_GATHER_IMAGE}" -- /usr/bin/gather
如果您在自定义命名空间中安装了KMM,请使用 |
运行must-gather
工具
$ oc adm must-gather --image="${MUST_GATHER_IMAGE}" -- /usr/bin/gather
查看Operator日志
$ oc logs -fn openshift-kmm deployments/kmm-operator-controller
I0228 09:36:37.352405 1 request.go:682] Waited for 1.001998746s due to client-side throttling, not priority and fairness, request: GET:https://172.30.0.1:443/apis/machine.openshift.io/v1beta1?timeout=32s
I0228 09:36:40.767060 1 listener.go:44] kmm/controller-runtime/metrics "msg"="Metrics server is starting to listen" "addr"="127.0.0.1:8080"
I0228 09:36:40.769483 1 main.go:234] kmm/setup "msg"="starting manager"
I0228 09:36:40.769907 1 internal.go:366] kmm "msg"="Starting server" "addr"={"IP":"127.0.0.1","Port":8080,"Zone":""} "kind"="metrics" "path"="/metrics"
I0228 09:36:40.770025 1 internal.go:366] kmm "msg"="Starting server" "addr"={"IP":"::","Port":8081,"Zone":""} "kind"="health probe"
I0228 09:36:40.770128 1 leaderelection.go:248] attempting to acquire leader lease openshift-kmm/kmm.sigs.x-k8s.io...
I0228 09:36:40.784396 1 leaderelection.go:258] successfully acquired lease openshift-kmm/kmm.sigs.x-k8s.io
I0228 09:36:40.784876 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="Module" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="Module" "source"="kind source: *v1beta1.Module"
I0228 09:36:40.784925 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="Module" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="Module" "source"="kind source: *v1.DaemonSet"
I0228 09:36:40.784968 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="Module" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="Module" "source"="kind source: *v1.Build"
I0228 09:36:40.785001 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="Module" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="Module" "source"="kind source: *v1.Job"
I0228 09:36:40.785025 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="Module" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="Module" "source"="kind source: *v1.Node"
I0228 09:36:40.785039 1 controller.go:193] kmm "msg"="Starting Controller" "controller"="Module" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="Module"
I0228 09:36:40.785458 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="PodNodeModule" "controllerGroup"="" "controllerKind"="Pod" "source"="kind source: *v1.Pod"
I0228 09:36:40.786947 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="PreflightValidation" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidation" "source"="kind source: *v1beta1.PreflightValidation"
I0228 09:36:40.787406 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="PreflightValidation" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidation" "source"="kind source: *v1.Build"
I0228 09:36:40.787474 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="PreflightValidation" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidation" "source"="kind source: *v1.Job"
I0228 09:36:40.787488 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="PreflightValidation" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidation" "source"="kind source: *v1beta1.Module"
I0228 09:36:40.787603 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="NodeKernel" "controllerGroup"="" "controllerKind"="Node" "source"="kind source: *v1.Node"
I0228 09:36:40.787634 1 controller.go:193] kmm "msg"="Starting Controller" "controller"="NodeKernel" "controllerGroup"="" "controllerKind"="Node"
I0228 09:36:40.787680 1 controller.go:193] kmm "msg"="Starting Controller" "controller"="PreflightValidation" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidation"
I0228 09:36:40.785607 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="imagestream" "controllerGroup"="image.openshift.io" "controllerKind"="ImageStream" "source"="kind source: *v1.ImageStream"
I0228 09:36:40.787822 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="preflightvalidationocp" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidationOCP" "source"="kind source: *v1beta1.PreflightValidationOCP"
I0228 09:36:40.787853 1 controller.go:193] kmm "msg"="Starting Controller" "controller"="imagestream" "controllerGroup"="image.openshift.io" "controllerKind"="ImageStream"
I0228 09:36:40.787879 1 controller.go:185] kmm "msg"="Starting EventSource" "controller"="preflightvalidationocp" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidationOCP" "source"="kind source: *v1beta1.PreflightValidation"
I0228 09:36:40.787905 1 controller.go:193] kmm "msg"="Starting Controller" "controller"="preflightvalidationocp" "controllerGroup"="kmm.sigs.x-k8s.io" "controllerKind"="PreflightValidationOCP"
I0228 09:36:40.786489 1 controller.go:193] kmm "msg"="Starting Controller" "controller"="PodNodeModule" "controllerGroup"="" "controllerKind"="Pod"
收集KMM Operator Hub控制器管理器的数据
设置MUST_GATHER_IMAGE
变量
$ export MUST_GATHER_IMAGE=$(oc get deployment -n openshift-kmm-hub kmm-operator-hub-controller -ojsonpath='{.spec.template.spec.containers[?(@.name=="manager")].env[?(@.name=="RELATED_IMAGE_MUST_GATHER")].value}')
$ oc adm must-gather --image="${MUST_GATHER_IMAGE}" -- /usr/bin/gather -u
如果您在自定义命名空间中安装了KMM,请使用 |
运行must-gather
工具
$ oc adm must-gather --image="${MUST_GATHER_IMAGE}" -- /usr/bin/gather -u
查看Operator日志
$ oc logs -fn openshift-kmm-hub deployments/kmm-operator-hub-controller
I0417 11:34:08.807472 1 request.go:682] Waited for 1.023403273s due to client-side throttling, not priority and fairness, request: GET:https://172.30.0.1:443/apis/tuned.openshift.io/v1?timeout=32s
I0417 11:34:12.373413 1 listener.go:44] kmm-hub/controller-runtime/metrics "msg"="Metrics server is starting to listen" "addr"="127.0.0.1:8080"
I0417 11:34:12.376253 1 main.go:150] kmm-hub/setup "msg"="Adding controller" "name"="ManagedClusterModule"
I0417 11:34:12.376621 1 main.go:186] kmm-hub/setup "msg"="starting manager"
I0417 11:34:12.377690 1 leaderelection.go:248] attempting to acquire leader lease openshift-kmm-hub/kmm-hub.sigs.x-k8s.io...
I0417 11:34:12.378078 1 internal.go:366] kmm-hub "msg"="Starting server" "addr"={"IP":"127.0.0.1","Port":8080,"Zone":""} "kind"="metrics" "path"="/metrics"
I0417 11:34:12.378222 1 internal.go:366] kmm-hub "msg"="Starting server" "addr"={"IP":"::","Port":8081,"Zone":""} "kind"="health probe"
I0417 11:34:12.395703 1 leaderelection.go:258] successfully acquired lease openshift-kmm-hub/kmm-hub.sigs.x-k8s.io
I0417 11:34:12.396334 1 controller.go:185] kmm-hub "msg"="Starting EventSource" "controller"="ManagedClusterModule" "controllerGroup"="hub.kmm.sigs.x-k8s.io" "controllerKind"="ManagedClusterModule" "source"="kind source: *v1beta1.ManagedClusterModule"
I0417 11:34:12.396403 1 controller.go:185] kmm-hub "msg"="Starting EventSource" "controller"="ManagedClusterModule" "controllerGroup"="hub.kmm.sigs.x-k8s.io" "controllerKind"="ManagedClusterModule" "source"="kind source: *v1.ManifestWork"
I0417 11:34:12.396430 1 controller.go:185] kmm-hub "msg"="Starting EventSource" "controller"="ManagedClusterModule" "controllerGroup"="hub.kmm.sigs.x-k8s.io" "controllerKind"="ManagedClusterModule" "source"="kind source: *v1.Build"
I0417 11:34:12.396469 1 controller.go:185] kmm-hub "msg"="Starting EventSource" "controller"="ManagedClusterModule" "controllerGroup"="hub.kmm.sigs.x-k8s.io" "controllerKind"="ManagedClusterModule" "source"="kind source: *v1.Job"
I0417 11:34:12.396522 1 controller.go:185] kmm-hub "msg"="Starting EventSource" "controller"="ManagedClusterModule" "controllerGroup"="hub.kmm.sigs.x-k8s.io" "controllerKind"="ManagedClusterModule" "source"="kind source: *v1.ManagedCluster"
I0417 11:34:12.396543 1 controller.go:193] kmm-hub "msg"="Starting Controller" "controller"="ManagedClusterModule" "controllerGroup"="hub.kmm.sigs.x-k8s.io" "controllerKind"="ManagedClusterModule"
I0417 11:34:12.397175 1 controller.go:185] kmm-hub "msg"="Starting EventSource" "controller"="imagestream" "controllerGroup"="image.openshift.io" "controllerKind"="ImageStream" "source"="kind source: *v1.ImageStream"
I0417 11:34:12.397221 1 controller.go:193] kmm-hub "msg"="Starting Controller" "controller"="imagestream" "controllerGroup"="image.openshift.io" "controllerKind"="ImageStream"
I0417 11:34:12.498335 1 filter.go:196] kmm-hub "msg"="Listing all ManagedClusterModules" "managedcluster"="local-cluster"
I0417 11:34:12.498570 1 filter.go:205] kmm-hub "msg"="Listed ManagedClusterModules" "count"=0 "managedcluster"="local-cluster"
I0417 11:34:12.498629 1 filter.go:238] kmm-hub "msg"="Adding reconciliation requests" "count"=0 "managedcluster"="local-cluster"
I0417 11:34:12.498687 1 filter.go:196] kmm-hub "msg"="Listing all ManagedClusterModules" "managedcluster"="sno1-0"
I0417 11:34:12.498750 1 filter.go:205] kmm-hub "msg"="Listed ManagedClusterModules" "count"=0 "managedcluster"="sno1-0"
I0417 11:34:12.498801 1 filter.go:238] kmm-hub "msg"="Adding reconciliation requests" "count"=0 "managedcluster"="sno1-0"
I0417 11:34:12.501947 1 controller.go:227] kmm-hub "msg"="Starting workers" "controller"="imagestream" "controllerGroup"="image.openshift.io" "controllerKind"="ImageStream" "worker count"=1
I0417 11:34:12.501948 1 controller.go:227] kmm-hub "msg"="Starting workers" "controller"="ManagedClusterModule" "controllerGroup"="hub.kmm.sigs.x-k8s.io" "controllerKind"="ManagedClusterModule" "worker count"=1
I0417 11:34:12.502285 1 imagestream_reconciler.go:50] kmm-hub "msg"="registered imagestream info mapping" "ImageStream"={"name":"driver-toolkit","namespace":"openshift"} "controller"="imagestream" "controllerGroup"="image.openshift.io" "controllerKind"="ImageStream" "dtkImage"="quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:df42b4785a7a662b30da53bdb0d206120cf4d24b45674227b16051ba4b7c3934" "name"="driver-toolkit" "namespace"="openshift" "osImageVersion"="412.86.202302211547-0" "reconcileID"="e709ff0a-5664-4007-8270-49b5dff8bae9"