Kernel Module Management Application

The app-kernel-module-management application enables dynamic loading of out-of-tree kernel modules at runtime, with modules distributed independently of the platform. Kernel modules can be optionally built on demand into container images or provided as pre-built container images. These images are used to deploy and load the required modules onto target Kubernetes nodes. Custom Kubernetes resources are used to define which kernel modules to load, from which container images, and the specific nodes on which the modules should be deployed.

Install App-kernel-module-management Application

Perform the following steps to install the app-kernel-module-management application.

Procedure

  1. Upload the application package by running the following command:

    ~(keystone_admin)]$ system application-upload /usr/local/share/applications/helm/kernel-module-management-*.tgz
    
  2. Create and apply the Helm overrides.

    The kernel-module-management application requires a Docker registry to manage the module images it builds. The application will use this registry to pull and push the images.

    The registry may be an external or a local registry, as shown in the following example:

    1. Set the Docker credentials.

      USERNAME="sysinv"
      PASSWORD=$(keyring get sysinv services)
      DOCKER_CREDENTIALS=$(echo -n "${USERNAME}":"${PASSWORD}" | base64)
      
    2. Create docker-config.json.

      cat >docker-config.json<<EOF
      {
        "auths": {
            "https://registry.local:9001": {
               "auth": "$DOCKER_CREDENTIALS"
            }
        }
      }
      EOF
      
      dconfigjson=$(cat docker-config.json | base64 -w 0)
      
    3. Create the override file with registry information.

      cat >kmm-app-override.yaml<<EOF
      dockerRegistrySecretName: "kmm-registry-secret"
      dockerConfigJson: "$dconfigjson"
      EOF
      
    4. Apply the overrides.

      ~(keystone_admin)]$ system helm-override-update kernel-module-management kernel-module-management kernel-module-management --values kmm-app-override.yaml
      
  3. After uploading the application and applying the overrides, apply the application using the following command.

    ~(keystone_admin)]$ system application-apply kernel-module-management
    

    After the application is successfully applied, the two pods will be running as shown below:

    (keystone_admin)]$ kubectl get pods -n kernel-module-management
    NAME                                       READY   STATUS    RESTARTS   AGE
    kmm-operator-controller-86dfc6bff8-swvf7   1/1     Running   0          97s
    kmm-operator-webhook-788f76bd7c-64t6t      1/1     Running   0          97s
    

Building and Loading Kernel Modules

Create Kernel Module Images

To create your own out-of-tree kernel module it is necessary to have an image with the kernel headers to build your own module files against it. For more details, see kernel-module-management docs.

FROM ubuntu as builder
ARG KERNEL_FULL_VERSION
RUN apt-get update && apt-get install -y bc \
bison \
flex \
libelf-dev \
gnupg \
wget \
git \
make \
gcc \
linux-headers-${KERNEL_FULL_VERSION}

The StarlingX project provides a pre-built container image with the tooling and source code to enable you to build your kernel modules inside the StarlingX ecosystem. This pre-built image also includes a hello_world module example that can be used to test the kernel-module-management app. The structure used in the example module can be followed as a basis for creating your own kernel modules.

To create your own module image it is necessary to create a Dockerfile building the wanted modules, as well as the source code for the kernel module itself. The final image only requires the .ko files from the built module, therefore a multi-stage build strategy is advised to create a lighter image, without unnecessary packages and files.

This Dockerfile example uses two stages. The first stage builds the module(s) and the second one builds an image containing the module files and required tools.

FROM docker.io/starlingx/kmm-builder:stx.12.0-v1.0.0 as builder
ARG KERNEL_FULL_VERSION
WORKDIR /usr/src
RUN ["git", "clone", "https://opendev.org/starlingx/app-kernel-module-management.git"]
WORKDIR /usr/src/app-kernel-module-management/tools/builder-image/debian/docker/hello-world-module
RUN make KERNEL_DIR=/lib/modules/${KERNEL_FULL_VERSION}/build && mkdir /out && cp *.ko /out/

FROM ubuntu
ARG KERNEL_FULL_VERSION
RUN apt-get update && apt-get install -y kmod && rm -rf /var/lib/apt/lists/*
COPY --from=builder /out/*.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
RUN depmod -b /opt ${KERNEL_FULL_VERSION}

With this Dockerfile created, it is possible to build the image passing the current StarlingX kernel version “6.12.0-1-amd64” as a build argument when running docker build.

Warning

The KERNEL_FULL_VERSION variable should always match the current StarlingX kernel version, otherwise the module will not be loaded by the application.

Warning

With new versions of StarlingX, the kernel version could change, creating the necessity to rebuild the module image matching the new kernel and kmm-builder image versions, before reapplying it. Otherwise, the module will experience an outage while the system builds the new version.

Load Module images

One way to load the module in the application is pre-building the module image in a lab and then shipping it out to all the sites to avoid the overhead of building separately for each site. For these cases, just push the built image to the registry in use and create a CRD Module.

Note

In addition to using a public container registry, in a Distributed Cloud environment the module image can also be pushed to the Central Cloud registry and pulled from it by the subclouds.

cat << 'EOF' > hello_world_mod.yaml
apiVersion: kmm.sigs.x-k8s.io/v1beta1
kind: Module
metadata:
  name: kmm-hello-world
  namespace: kernel-module-management
spec:
  moduleLoader:
    container:
      modprobe:
        moduleName: hello_world_dmesg
      kernelMappings:
        - literal: "6.12.0-1-amd64"
          containerImage: registry.local:9001/kmm/kmm-hello-world:stx.12.0-6.12.0-1-amd64
          registryTLS:
            insecure: false
            insecureSkipTLSVerify: true

  imageRepoSecret:
    name: "kmm-registry-secret"

  selector:
    kubernetes.io/os: linux
    kubernetes.io/hostname: controller-0

  tolerations:
    - key: "services"
      operator: "Equal"
      value: "disabled"
      effect: "NoExecute"
EOF

Note

As kernel versions change, the kernelMappings also need to be updated to match the version(s) in use.

Note

The example above selects by hostname, another common pattern is selecting by node type using the selector node-role.kubernetes.io/worker: “”. As seen on KMM docs.

To load the module, apply the CRD created by running the following command:

~(keystone_admin)]$ kubectl apply -f hello_world_mod.yaml

Warning

See kernel-module-management official documentation for more details on how to construct a Module custom resource. It is not possible to select documentation for a specific KMM version, so the docs do not take into consideration which version of the application is running. Some configurations may not behave as expected.

Building and loading the modules

You can create a CRD Module file and a ConfigMap as an additional step when you want the kernel-module-management application to build the module image. The ConfigMap will carry the Dockerfile content mentioned previously, and the CRD Module will reference the newly created ConfigMap.

cat << 'EOF' > hello_world_cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: kmm-hello-world-cm
  namespace: kernel-module-management
data:
  dockerfile: |
    FROM docker.io/starlingx/kmm-builder:stx.12.0-v1.0.0 as builder
    ARG KERNEL_FULL_VERSION
    WORKDIR /usr/src
    RUN ["git", "clone", "https://opendev.org/starlingx/app-kernel-module-management.git"]
    WORKDIR /usr/src/app-kernel-module-management/tools/builder-image/debian/docker/hello-world-module
    RUN make KERNEL_DIR=/lib/modules/${KERNEL_FULL_VERSION}/build && mkdir /out && cp *.ko /out/

    FROM ubuntu
    ARG KERNEL_FULL_VERSION
    RUN apt-get update && apt-get install -y kmod
    COPY --from=builder /out/*.ko /opt/lib/modules/${KERNEL_FULL_VERSION}/
    RUN depmod -b /opt ${KERNEL_FULL_VERSION}
EOF
cat << 'EOF' > hello_world_mod.yaml
apiVersion: kmm.sigs.x-k8s.io/v1beta1
kind: Module
metadata:
  name: kmm-hello-world
  namespace: kernel-module-management
spec:
  moduleLoader:
    container:
      modprobe:
        moduleName: hello_world_dmesg
      kernelMappings:
        - literal: "6.12.0-1-amd64"
          containerImage: registry.local:9001/kmm/kmm-hello-world:stx.12.0-6.12.0-1-amd64
          build:
            buildArgs:
              - name: KERNEL_FULL_VERSION
                value: "6.12.0-1-amd64"
            baseImageRegistryTLS:
              insecure: false
              insecureSkipTLSVerify: true
            dockerfileConfigMap:
              name: kmm-hello-world-cm
          registryTLS:
            insecure: false
            insecureSkipTLSVerify: true

  imageRepoSecret:
    name: "kmm-registry-secret"

  selector:
    kubernetes.io/os: linux
    kubernetes.io/hostname: controller-0

  tolerations:
    - key: "services"
      operator: "Equal"
      value: "disabled"
      effect: "NoExecute"
EOF

To load the module, apply the CRD and ConfigMap created by running the following command:

~(keystone_admin)]$ kubectl apply -f hello_world_cm.yaml -f hello_world_mod.yaml

The kernel module build progress can be tracked using the kubectl logs command.

~(keystone_admin)]$ kubectl logs -n kernel-module-management kmm-hello-world-build-<POD_ID> -f

Note

Successful build pods are garbage-collected immediately, while failed build pods are always preserved and must be deleted manually by the administrator for the build to be restarted.

Regardless of whether the module was loaded with a pre-built image or built using the app, it is possible to confirm that the module is correctly loaded by running the following dmesg:

sudo dmesg

[34352.320647] systemd-sysv-generator[2076803]: Overwriting existing symlink /run/systemd/generator.late/radosgw.service with real service.
[102022.711368] e1000: enp0s3 NIC Link is Down
[102028.836500] e1000: enp0s3 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX
[102422.922297] e1000: enp0s3 NIC Link is Down
[102422.922706] e1000 0000:00:03.0 enp0s3: Reset adapter
[102425.122913] e1000: enp0s3 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX
[102582.800513] e1000: enp0s3 NIC Link is Down
[102586.903818] e1000: enp0s3 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX
[103642.175972] Hello, world!

Unload Kernel Module

To unload the kernel module, run the following command in the same path where the previous module files were created:

~(keystone_admin)]$ kubectl delete -f hello_world_cm.yaml -f hello_world_mod.yaml

For the scenario with a pre-built image, run the following command:

~(keystone_admin)]$ kubectl delete -f hello_world_mod.yaml

Confirm that the kernel module was successfully unloaded by running dmesg again and checking for the Goodbye message logged.

sudo dmesg

[102425.122913] e1000: enp0s3 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX
[102582.800513] e1000: enp0s3 NIC Link is Down
[102586.903818] e1000: enp0s3 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX
[103642.175972] Hello, world!
[104469.925530] Goodbye, world!

Uninstall App-kernel-module-management Application

To uninstall the app-kernel-module-management application, run the following command:

~(keystone_admin)]$ system application-remove kernel-module-management

Delete the uploaded application by running the following command:

~(keystone_admin)]$ system application-delete kernel-module-management