Skip to content

Commit

Permalink
Update docs for v2.0.0 (#918)
Browse files Browse the repository at this point in the history
Replace "ModuleLoader image" with "kmod image".
Adapt to the new worker Pods workflow.
Prefer installation via OLM.
Add events to the troubleshooting section.
Change the theme to something more familiar.

Upstream-Commit: 683b249
  • Loading branch information
qbarrand authored Dec 13, 2023
1 parent c8e5c16 commit c8a3494
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 111 deletions.
5 changes: 2 additions & 3 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ nav:
- Documentation:
- documentation/install.md
- documentation/deploy_kmod.md
- documentation/module_loader_image.md
- documentation/kmod_image.md
- Binary firmwares: documentation/firmwares.md
- Secure boot: documentation/secure_boot.md
- Preflight validation: documentation/preflight_validation.md
Expand All @@ -39,7 +39,6 @@ plugins:
markdown_extensions:
- admonition


theme:
name: mkdocs
name: readthedocs
custom_dir: overrides/
81 changes: 40 additions & 41 deletions docs/mkdocs/documentation/deploy_kmod.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
# Deploying kernel modules

For each `Module`, KMM can create a number of `DaemonSets`:
KMM watches `Node` and `Module` resources in the cluster to determine if a kernel module should be loaded on or unloaded
from a node.
To be eligible for a `Module`, a `Node` must:

- one ModuleLoader DaemonSet per compatible kernel version running in the cluster;
- one device plugin DaemonSet, if configured.
- have labels that match the `Module`'s `.spec.selector` field;
- run a kernel version matching one of the items in the `Module`'s `.spec.moduleLoader.container.kernelMappings`;
- if [ordered upgrade](ordered_upgrade.md) is configured in the `Module`, have a label that matches its
`.spec.moduleLoader.container.version` field.

### ModuleLoader
When KMM needs to reconcile nodes with the desired state as configured in the `Module` resource, it creates worker Pods
on the target node(s) to run the necessary action.
The operator monitors the outcome of those Pods and records that information.
It uses it to label `Node` objects when the module was successfully loaded, and to run the device plugin (if
configured).

The ModuleLoader DaemonSets run ModuleLoader images to load kernel modules.
A ModuleLoader image is an OCI image that contains the `.ko` files and both the `modprobe` and `sleep` binaries.
### Worker Pods

When the ModuleLoader pod is created, it will run `modprobe` to insert the specified module into the kernel.
It will then `sleep` until it is terminated.
When that happens, the `ExecPreStop` hook will run `modprobe -r` to unload the kernel module.
Worker pods run the KMM `worker` binary that

Learn more about [how to build a ModuleLoader image](module_loader_image.md).
- pulls the kmod image configured in the `Module` resource;
- extract it in the Pod's filesystem;
- runs `modprobe` with the right arguments to perform the necessary action.

kmod images are standard OCI images that contains `.ko` files.
Learn more about [how to build a kmod image](kmod_image.md).

### Device plugin

Expand All @@ -23,12 +33,12 @@ DaemonSet in the cluster.
That DaemonSet will target nodes:

- that match the `Module`'s `.spec.selector`;
- on which the kernel module is loaded (where the ModuleLoader pod has the `Ready` condition).
- on which the kernel module is loaded.

## `Module` CRD

The `Module` Custom Resource Definition represents a kernel module that should be loaded on all or select nodes in the
cluster, through a ModuleLoader image.
cluster, through a kmod image.
A Module specifies one or more kernel versions it is compatible with, as well as a node selector.

The compatible versions for a `Module` are listed under `.spec.moduleLoader.container.kernelMappings`.
Expand All @@ -41,14 +51,14 @@ The reconciliation loop for `Module` runs the following steps:
3. for each kernel version:
1. go through `.spec.moduleLoader.container.kernelMappings` and find the appropriate container image name.
If the kernel mapping has `build` or `sign` defined and the container image does not already exist, run the build
and / or signing job as required;
2. create a ModuleLoader `DaemonSet` with the container image determined at the previous step;
and / or signing pod as required;
2. create a worker Pod pulling the container image determined at the previous step and running `modprobe`;
3. if `.spec.devicePlugin` is defined, create a device plugin `DaemonSet` using the configuration specified under
`.spec.devicePlugin.container`;
4. garbage-collect:
1. existing `DaemonSets` targeting kernel versions that are not run by any node in the cluster;
2. successful build jobs;
3. successful signing jobs.
1. obsolete device plugin `DaemonSets` that do not target any node;
2. successful build pods;
3. successful signing pods.

### Soft dependencies between kernel modules

Expand Down Expand Up @@ -90,25 +100,15 @@ spec:
inTreeModuleToRemove: mod_b
```

The ModuleLoader pod will first try to unload the in-tree `mod_b` before loading `mod_a` from the ModuleLoader image.
When the ModuleLoader pod is terminated and `mod_a` is unloaded, `mod_b` will not be loaded again.

### Image pull policy

Many examples in this documentation use the kernel version as image tag for the ModuleLoader image name.
If that image tag is being overridden on the registry, the Kubernetes
[default image pull policy](https://kubernetes.io/docs/concepts/containers/images/#imagepullpolicy-defaulting) that KMM
honors can lead to nodes not pulling the latest version of an image.
Use `.spec.moduleLoader.container.imagePullPolicy` to configure the right
[image pull policy](https://kubernetes.io/docs/concepts/containers/images/#updating-images) for your ModuleLoader
DaemonSets.
The worker Pod will first try to unload the in-tree `mod_b` before loading `mod_a` from the kmod image.
When the worker Pod is terminated and `mod_a` is unloaded, `mod_b` will not be loaded again.

### Example resource

Below is an annotated `Module` example with most options set.
More information about specific features is available in the dedicated pages:

- [in-cluster builds](module_loader_image.md#building-in-cluster)
- [in-cluster builds](kmod_image.md#building-in-cluster)
- [kernel module signing](secure_boot.md)

```yaml
Expand Down Expand Up @@ -204,7 +204,7 @@ spec:
serviceAccountName: sa-device-plugin # Optional
imageRepoSecret: # Optional. Used to pull ModuleLoader and device plugin images
imageRepoSecret: # Optional. Used to pull kmod and device plugin images
name: secret-name
selector:
Expand All @@ -222,12 +222,11 @@ The following `Module` fields support shell-like variable substitution:

The following variables will be substituted:

| Name | Description | Example |
|-------------------------------|----------------------------------------|-------------------------------|
| `KERNEL_FULL_VERSION` | The kernel version we are building for | `5.14.0-70.58.1.el9_0.x86_64` |
| `KERNEL_VERSION` (deprecated) | The kernel version we are building for | `5.14.0-70.58.1.el9_0.x86_64` |
| `MOD_NAME` | The `Module`'s name | `my-mod` |
| `MOD_NAMESPACE` | The `Module`'s namespace | `my-namespace` |
| Name | Description | Example |
|-----------------------|----------------------------------------|-------------------------------|
| `KERNEL_FULL_VERSION` | The kernel version we are building for | `5.14.0-70.58.1.el9_0.x86_64` |
| `MOD_NAME` | The `Module`'s name | `my-mod` |
| `MOD_NAMESPACE` | The `Module`'s namespace | `my-namespace` |

## Security and permissions

Expand All @@ -248,8 +247,8 @@ The authorization model depends on the `Module`'s namespace, as well as its spec
always used;
- if those fields are not set, then:
- if the `Module` is created in the operator's namespace (`openshift-kmm` by default), then KMM will use its
default, powerful `ServiceAccounts` to run the DaemonSets;
- if the `Module` is created in any other namespace, then KMM will run the DaemonSets as the namespace's `default`
default, powerful `ServiceAccounts` to run the worker and device plugin Pods;
- if the `Module` is created in any other namespace, then KMM will run the Pods with the namespace's `default`
`ServiceAccount`, which cannot run privileged workload unless you manually allow it to use the `privileged` SCC.

!!! warning "`openshift-kmm` and some other namespaces that are part of the [cluster payload](https://docs.openshift.com/container-platform/4.12/authentication/understanding-and-managing-pod-security-admission.html#security-context-constraints-psa-opting_understanding-and-managing-pod-security-admission) are considered trusted namespaces"
Expand All @@ -258,8 +257,8 @@ The authorization model depends on the `Module`'s namespace, as well as its spec
`openshift-kmm` namespace will result in KMM automatically running privileged workload on potentially all nodes in
cluster.

To allow any `ServiceAccount` to use the `privileged` SCC and hence to run ModuleLoader and / or device plugin pods,
use the following command:
To allow any `ServiceAccount` to use the `privileged` SCC and hence to run worker and / or device plugin Pods, use the
following command:

```shell
oc adm policy add-scc-to-user privileged -z "${serviceAccountName}" [ -n "${namespace}" ]
Expand Down
24 changes: 2 additions & 22 deletions docs/mkdocs/documentation/firmwares.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
# Firmware support

Kernel modules sometimes need to load firmware files from the filesystem.
KMM supports copying firmware files from the [Module Loader image](module_loader_image.md)
KMM supports copying firmware files from the [kmod image](kmod_image.md)
to the node's filesystem.
The contents of `.spec.moduleLoader.container.modprobe.firmwarePath` are copied into `/var/lib/firmware` on the node
before `modprobe` is called to insert the kernel module.
All files and empty directories are removed from that location before `modprobe -r` is called to unload the kernel
module, when the pod is terminated.

## Configuring the lookup path on nodes

On OpenShift nodes, the set of default lookup paths for firmwares does not include `/var/lib/firmware`.
That path can be added with the [Machine Config Operator](https://docs.openshift.com/container-platform/4.12/post_installation_configuration/machine-configuration-tasks.html)
by creating a `MachineConfig` resource:

```yaml
apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
metadata:
labels:
machineconfiguration.openshift.io/role: worker
name: 99-worker-kernel-args-firmware-path
spec:
kernelArguments:
- 'firmware_class.path=/var/lib/firmware'
```
This will entail a reboot of all worker nodes.
## Building a ModuleLoader image
## Building a kmod image

In addition to building the kernel module itself, include the binary firmware in the builder image.

Expand Down
4 changes: 2 additions & 2 deletions docs/mkdocs/documentation/hub_spoke.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ To do that, [install the regular edition of KMM](./install.md).

### Installing KMM-Hub

#### Using OLM (recommended)
#### With OLM (recommended)

KMM-Hub is available to install from the Red Hat catalog.

Expand Down Expand Up @@ -52,7 +52,7 @@ spec:
sourceNamespace: openshift-marketplace
```
#### Using `oc`
#### With `oc`

The command below installs the bleeding edge version of KMM-Hub.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
# Creating a ModuleLoader image
# Creating a kmod image

Kernel Module Management works with purpose-built ModuleLoader images.
Those are standard OCI images that satisfy a few requirements:
Kernel Module Management works with purpose-built kmod images, which are standard OCI images that contain `.ko` files.
The location of those files must match the following pattern:
```
<prefix>/lib/modules/[kernel-version]/
```

Where:

- `.ko` files must be located under `/opt/lib/modules/${KERNEL_VERSION}`
- the `modprobe` and `sleep` binaries must be in the `$PATH`.
- `<prefix>` should be equal to `/opt` in most cases, as it is the `Module` CRD's default value;
- `kernel-version` must be non-empty and equal to the kernel version the kernel modules were built for.

## `depmod`

It is recommended to run `depmod` at the end of the build process to generate `modules.dep` and map files.
This is especially useful if your ModuleLoader image contains several kernel modules and if one of the modules depend on
This is especially useful if your kmod image contains several kernel modules and if one of the modules depend on
another.
To generate dependencies and map files for a specific kernel version, run `depmod -b /opt ${KERNEL_VERSION}`.

Expand Down Expand Up @@ -53,24 +58,23 @@ RUN depmod -b /opt ${KERNEL_VERSION}

## Building in cluster

KMM is able to build ModuleLoader images in cluster.
KMM is able to build kmod images in cluster.
Build instructions must be provided using the `build` section of a kernel mapping.
The `Dockerfile` for your container image should be copied into a `ConfigMap` object, under the `Dockerfile` key.
The `ConfigMap` needs to be located in the same namespace as the `Module`.

KMM will first check if the image name specified in the `containerImage` field exists.
If it does, the build will be skipped.
Otherwise, KMM creates a [`Build`](https://docs.openshift.com/container-platform/4.12/cicd/builds/build-configuration.html)
Otherwise, KMM will create a [`Build`](https://docs.openshift.com/container-platform/4.12/cicd/builds/build-configuration.html)
object to build your image.

The following build arguments are automatically set by KMM:

| Name | Description | Example |
|-------------------------------|----------------------------------------|-------------------------------|
| `KERNEL_FULL_VERSION` | The kernel version we are building for | `5.14.0-70.58.1.el9_0.x86_64` |
| `KERNEL_VERSION` (deprecated) | The kernel version we are building for | `5.14.0-70.58.1.el9_0.x86_64` |
| `MOD_NAME` | The `Module`'s name | `my-mod` |
| `MOD_NAMESPACE` | The `Module`'s namespace | `my-namespace` |
| Name | Description | Example |
|-----------------------|----------------------------------------|-------------------------------|
| `KERNEL_FULL_VERSION` | The kernel version we are building for | `5.14.0-70.58.1.el9_0.x86_64` |
| `MOD_NAME` | The `Module`'s name | `my-mod` |
| `MOD_NAMESPACE` | The `Module`'s namespace | `my-namespace` |

Once the image is built, KMM proceeds with the `Module` reconciliation.

Expand Down Expand Up @@ -104,16 +108,16 @@ Once the image is built, KMM proceeds with the `Module` reconciliation.
!!! warning "OpenShift's internal container registry is not enabled by default on bare metal clusters"
A common pattern is to push ModuleLoader images to OpenShift's internal image registry once they are built.
A common pattern is to push kmod images to OpenShift's internal image registry once they are built.
That registry is not enabled by default on bare metal installations of OpenShift.
Refer to [Configuring the registry for bare metal](https://docs.openshift.com/container-platform/4.13/registry/configuring_registry_storage/configuring-registry-storage-baremetal.html)
to enable it.
### Using Driver Toolkit (DTK)
[Driver Toolkit](https://docs.openshift.com/container-platform/4.12/hardware_enablement/psap-driver-toolkit.html) is a
convenient base image that contains most tools and libraries required to build ModuleLoader images for the OpenShift
version that is currently running in the cluster.
convenient base image that contains most tools and libraries required to build kmod images for the OpenShift version
that is currently running in the cluster.
It is recommended to use DTK as the first stage of a multi-stage `Dockerfile` to build the kernel modules, and to copy
the `.ko` files into a smaller end-user image such as [`ubi-minimal`](https://catalog.redhat.com/software/containers/ubi9/ubi-minimal).

Expand Down
17 changes: 8 additions & 9 deletions docs/mkdocs/documentation/secure_boot.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ If you have a pre-built image, such as one distributed by a hardware vendor, or
[Signing](#signing-a-pre-built-driver-container) docs for signing with KMM.

Alternatively if you have source code and need to build your image first, please see the
[Build and Sign](#building-and-signing-a-moduleloader-container-image) docs for deploying with KMM.
[Build and Sign](#building-and-signing-a-kmod-image) docs for deploying with KMM.

If all goes well KMM should load your driver into the node's kernel.
If not, see [Common Issues](#debugging--troubleshooting).
Expand Down Expand Up @@ -92,16 +92,16 @@ oc get secret -o yaml <private key secret name> | awk '/key/{print $2; exit}' |

Which should display a key, including `-----BEGIN PRIVATE KEY-----` and `-----END PRIVATE KEY-----` lines

# Signing a pre-built driver container
# Signing kmods in a pre-built image

The YAML below will add the public/private key-pair as secrets with the required key names (`key` for the private key,
`cert` for the public key).
It will then pull down the `unsignedImage` image, open it up, sign the kernel modules listed in `filesToSign`, add them
back and push the resulting image as `containerImage`.

KMM should then deploy the DaemonSet that loads the signed kmods onto all the nodes with that match the selector.
The driver containers should run successfully on any nodes that have the public key in their MOK database, and any
nodes that are not secure-boot enabled (which will just ignore the signature).
KMM should then load the signed kmods onto all the nodes with that match the selector.
The kmods should be successfully loaded on any nodes that have the public key in their MOK database, and any nodes that
are not secure-boot enabled (which will just ignore the signature).
They should fail to load on any that have secure-boot enabled but do not have that key in their MOK database.

## Example
Expand Down Expand Up @@ -143,7 +143,7 @@ spec:
kubernetes.io/arch: amd64
```
# Building and signing a ModuleLoader container image
# Building and signing a kmod image
The YAML below should build a new container image using the
[source code from the repo](https://github.com/rh-ecosystem-edge/kernel-module-management/tree/main/ci/kmm-kmod) (this
Expand Down Expand Up @@ -222,7 +222,6 @@ spec:

# Debugging & troubleshooting

If your driver containers end up in `PostStartHookError` or `CrashLoopBackOff` status, and `oc describe` shows an event:
`modprobe: ERROR: could not insert '<your kmod name>': Required key not available` then the kmods are either not signed,
or signed with the wrong key.
If your worker Pod logs show `modprobe: ERROR: could not insert '<your kmod name>': Required key not available` then the
kmods are either not signed, or signed with the wrong key.

Loading

0 comments on commit c8a3494

Please sign in to comment.