| # cos-dkms |
| |
| `cos-dkms` is a tool designed for Google Container-Optimized OS (COS) to |
| dynamically build, install, and manage kernel modules. It is intended to be a |
| drop-in replacement for the upstream Dynamic Kernel Module Support (DKMS) tool, |
| but tailored for the unique environment of COS and GKE. |
| |
| Key features include: |
| |
| * **Cross-compilation**: Compile kernel modules against specific COS kernel |
| versions. |
| * **Toolchain Orchestration**: Automatically download and use the exact |
| compiler toolchain and kernel headers matching the target COS image. |
| * **GCS Caching**: Cache built modules and sources in a Google Cloud Storage |
| (GCS) bucket to speed up deployments and avoid recompilation. |
| * **Module Signing**: Sign compiled modules using local keys or Google Cloud |
| KMS to satisfy COS kernel module signature verification. |
| |
| Code-level documentation is available |
| [here](https://pkg.go.dev/cos.googlesource.com/cos/tools.git/src/pkg/dkms). |
| |
| Documentation for the high-level cos-dkms runner script is available |
| [here](https://cos.googlesource.com/cos/tools/+/refs/heads/master/src/cmd/cos_dkms_runner). |
| |
| -------------------------------------------------------------------------------- |
| |
| ## Table of Contents |
| |
| 1. [Running cos-dkms](#running-cos-dkms) |
| 2. [Actions](#actions) |
| 3. [Configuration (dkms.conf)](#configuration) |
| * [Configuration Examples](#configuration-examples) |
| 4. [Module Signing](#module-signing) |
| 5. [Example Workflows](#workflows) |
| * [Workflow 1: Installing Precompiled Modules](#workflow-precompiled-modules) |
| * [Workflow 2: Installing Modules Compiled by Google for COS](#workflow-google-compiled-modules) |
| * [Workflow 3: Installing Modules with modprobe](#workflow-modprobe) |
| * [Workflow 4: Building and Distributing Modules](#workflow-building-distributing) |
| |
| -------------------------------------------------------------------------------- |
| |
| ## Running cos-dkms {#running-cos-dkms} |
| |
| Because COS is a minimal operating system without a pre-installed compiler |
| toolchain, `cos-dkms` is intended to be run inside a Docker container. Google |
| provides a pre-built container image: `gcr.io/cos-cloud/cos-dkms:latest`. |
| |
| When running this container, the following directories should be mounted: |
| |
| * The module source directory. |
| * The output directory for installed modules. |
| * `/etc/lsb-release` (to allow `cos-dkms` to automatically detect the host's |
| COS build ID and board). |
| * Optionally, a cache directory for kernel headers and toolchains to speed up |
| subsequent runs. |
| |
| ### Basic Docker Template |
| |
| ```bash |
| docker run --rm -it \ |
| -v "/path/to/your/sources:/usr/src/my-package-1.0" \ |
| -v "/path/to/install/output:/var/lib/dkms-install" \ |
| -v /etc/lsb-release:/etc/lsb-release \ |
| gcr.io/cos-cloud/cos-dkms:latest \ |
| [action] [options] my-package/1.0 |
| ``` |
| |
| If you are using cos-dkms to load modules, then you will need to run the |
| container with `--privileged`. For modules which rely on networking, passing |
| `--net=host` may also be required. |
| |
| -------------------------------------------------------------------------------- |
| |
| ## Actions {#actions} |
| |
| `cos-dkms` supports a set of actions to manage the lifecycle of a kernel module. |
| In most cases, you will only need the high-level `install` and `remove` |
| commands, which automatically orchestrate the lower-level actions. |
| |
| ``` |
| +-----------+ calls +---------+ calls +---------+ |
| | Install | -------> | Build | -------> | Add | |
| +-----------+ +---------+ +---------+ |
| | undone by | undone by | undone by |
| v v v |
| +-----------+ +---------+ +---------+ |
| | Uninstall | <------- | Unbuild | <------- | Remove | |
| +-----------+ calls +---------+ calls +---------+ |
| ``` |
| |
| ### High-Level Actions |
| |
| #### `install` |
| |
| Installs all modules of a package into the install tree. |
| |
| * **Behavior**: If the package is not yet added, it runs `add`. If it is not |
| yet built, it runs `build`. It then copies the compiled `.ko` files to the |
| install tree. |
| * **Version Safety**: By default, it checks if the version being installed is |
| older than an already installed version. If so, it skips installation. Use |
| `--force-version-override` to bypass. |
| * **GCS Integration**: If `--gcs-bucket` is provided, then it will attempt to |
| download sources and modules from the given bucket. If `--upload` is passed, |
| then it will attempt to upload the sources and modules to the bucket after |
| they are added and built, respectively. |
| * **Key Flags**: |
| * * `-i`, `--installtree`: The directory where the modules will be installed. |
| Defaults to /lib/modules/${kernel-version}. |
| * `-b`, `--kernelmodulestree`: The directory containing the in-tree kernel |
| modules. Only used with --insert-on-install. Defaults to ${installtree}. |
| The modules in this tree can be loaded as dependencies when inserting |
| modules installed by cos-dkms. If a module is present in both the kernel |
| modules tree and the install tree, then only the one in the install tree |
| will be loaded. |
| The reason to separate this value from the install tree is that the install |
| tree must be writable, while the kernel's module directory may not be |
| writable. This is the case in COS instances, where the /lib/modules |
| directory is read-only. See the examples below for correct usage of |
| --installtree and --kernelmodulestree in COS. |
| * `-I`, `--insert-on-install`: Inserts the modules directly into the |
| running kernel (using `insmod`-like behavior) with specified |
| `--module-arg` parameters. This takes into account the order of the |
| modules listed in the dkms.conf file in order to install earlier modules |
| first, when possible. This allows the installation of module lists with |
| implicit dependencies, such as when one module does not explicitly |
| import symbols from another but does require that the other module be |
| loaded first. |
| * `--module-arg`: Argument to pass to a specific kernel module when loading |
| it with `--insert-on-install`. Multiple args for multiple modules can be |
| specified at the same time by passing a comma separated list or passing |
| --module-arg multiple times. The format is |
| --module-arg=module1.param1=value1,module2.param1=value1, etc. |
| * `-M`, `--modprobe-on-install`: Automatically runs `depmod` and |
| `modprobe` to load the modules into the running kernel after |
| installation. This is useful for more complex installations that require |
| modprobe configurations in /etc/modprobe.d. |
| Note that this doesn't use --kernelmodulestree because depmod and modprobe |
| require all modules to be under a subdirectory of |
| /lib/modules/<kernel version>, so specifying a separate |
| --kernelmodulestree and --installtree would not work as expected. |
| * `--no-depmod`: Skip running `depmod` before `modprobe` (only used with |
| `-M`). |
| |
| #### `remove` |
| |
| Completely removes a package. |
| |
| * **Behavior**: Automatically runs `uninstall` (removes from install tree) and |
| `unbuild` (cleans build artifacts), then removes the package sources from |
| the DKMS source tree. |
| * **GCS Integration**: If `--gcs-bucket` is provided and `--upload` is passed, |
| it also removes the package from the bucket. |
| |
| -------------------------------------------------------------------------------- |
| |
| ### Low-Level Actions |
| |
| #### `add` |
| |
| Adds a package to the local DKMS source tree. |
| |
| * **Behavior**: Looks for source files in the source tree (default |
| `/usr/src`). If not found, it tries to copy them from a local directory |
| named `<package>-<version>`. |
| * **GCS Integration**: If `--gcs-bucket` is provided and no local sources are |
| found, it checks the bucket for sources. If `--upload` is specified, it |
| uploads the sources to the bucket. |
| |
| #### `build` |
| |
| Compiles the modules in the package. |
| |
| * **Behavior**: Copies sources to a build directory, applies patches (if |
| specified in `dkms.conf`), and runs the `MAKE` command. |
| * **GCS Integration**: If `--gcs-bucket` is provided, it attempts to download |
| precompiled modules first. It only builds locally if they are missing. If |
| `--upload` is specified, it uploads the compiled modules to the bucket after |
| they are built. |
| * **Key Flags**: |
| * `--install-build-dependencies`: Automatically downloads the correct |
| kernel headers and compiler toolchain for the target COS version. |
| * `--make-variables=cos-default`: Appends default toolchain variables |
| (like `CC`, `LD`, etc.) matching the COS kernel build environment. |
| * `-j`, `--jobs`: Number of parallel make jobs (default 1). |
| * `--upload`: Uploads the compiled modules to the GCS bucket after a |
| successful build. |
| |
| #### `status` |
| |
| Displays the current status of a package. |
| |
| * **Output Statuses**: |
| * `Added`: Sources are in the DKMS source tree. |
| * `Built`: Sources are added and modules are compiled. |
| * `Installed`: Modules are compiled and copied to the install tree. |
| * `Broken`: Package is in an inconsistent state (e.g., missing sources, |
| invalid config). |
| * **GCS Integration**: Reflects the status in the GCS bucket if `--gcs-bucket` |
| is provided. |
| |
| #### `unbuild` |
| |
| Cleans build artifacts. |
| |
| * **Behavior**: Removes compiled modules from the build directory. |
| Automatically runs `uninstall` first if the package is installed. |
| * **GCS Integration**: If `--gcs-bucket` is provided and `--upload` is passed, |
| it also removes the compiled modules from the bucket. |
| |
| #### `uninstall` |
| |
| Removes compiled modules from the install tree. |
| |
| -------------------------------------------------------------------------------- |
| |
| ### Global Options |
| |
| These flags can be passed to most commands: |
| |
| * `-p`, `--package` / `-m`, `--module`: Name of the package. |
| * `--package-version` / `--module-version`: Version of the package. |
| * `-k`, `--kernel-version`: Target kernel version (defaults to running kernel, |
| or detected from headers if set to `cos-default`). |
| * `-a`, `--arch`: Target architecture (default: running host arch). |
| * `--build-id`: Target COS build ID (e.g., `18244.151.84`). |
| * `--board`: Target COS board (e.g., `lakitu`). |
| * `--gcs-bucket`: GCS bucket path (e.g., `gs://my-bucket/dkms`) to use as a |
| cache. |
| * `-f`, `--force`: Force the action even if `cos-dkms` thinks it is already |
| done. |
| * `-s`, `--sourcetree`: The directory where the module source tree is located. |
| Defaults to /usr/src. |
| |
| Additionally, cos-dkms uses [glog](https://pkg.go.dev/github.com/golang/glog) for |
| logging, so arguments to configure glog logging (logtostderr, stderrthreshold, |
| etc.) are accepted. |
| |
| -------------------------------------------------------------------------------- |
| |
| ## Configuration (dkms.conf) {#configuration} |
| |
| The `dkms.conf` file is a bash script that defines how `cos-dkms` should handle |
| the package. It supports Dell DKMS-compatible directives. |
| |
| ### Environment Variables Available in `dkms.conf` |
| |
| When `dkms.conf` is sourced, the following variables are pre-populated and can |
| be referenced: |
| |
| * `$module`: Package name. |
| * `$module_version`: Package version. |
| * `$kernelver`: Target kernel version. |
| * `$arch`: Target architecture. |
| * `$build_id` / `$board`: COS-specific build metadata. |
| * `$dkms_tree`: Path to the DKMS tree. |
| * `$source_tree`: Path to the source tree. |
| * `$kernel_source_dir`: Path to kernel headers/sources. |
| * `$build_dir`: Path to the temporary build directory. |
| |
| ### Directives |
| |
| Each of these is a top-level variable in the dkms.conf file which controls the |
| behavior of cos-dkms in some way. Each directive has a default value which will |
| be overwritten by any user-specified values in the dkms.conf. The notation |
| `VARIABLE[#]` indicates that `VARIABLE` is a bash array which can contain |
| multiple values. If only one variable in the array is used, then a simple |
| assignment works, e.g., `BUILT_MODULE_NAME=my-module` has the same effect as |
| `BUILT_MODULE_NAME=(my-module)`. |
| |
| #### Package Metadata |
| |
| * `PACKAGE_NAME`: Name of the package (defaults to `--package`). |
| * `PACKAGE_VERSION`: Version of the package (defaults to `--package-version`). |
| |
| #### Build Commands |
| |
| * `MAKE[#]`: The command used to compile the modules. The first entry in this |
| array defaults to `make -C ${kernel_source_dir} M=${build_dir}`. You can |
| specify multiple `MAKE` commands indexed by number, paired with |
| `MAKE_MATCH[#]` to select a command based on the kernel version. |
| * `MAKE_MATCH[#]`: A regex matched against `$kernelver`. The first `MAKE[#]` |
| whose `MAKE_MATCH[#]` matches the target kernel version will be used. |
| |
| #### Module Definitions |
| |
| For packages containing multiple modules, use 0-indexed arrays: |
| |
| * `BUILT_MODULE_NAME[#]`: The filename of the compiled module (without `.ko`). |
| The first entry in this array defaults to the package name. |
| * `BUILT_MODULE_LOCATION[#]`: Directory where the module is built, relative to |
| `$build_dir`. Default is empty (root of build dir). |
| * `DEST_MODULE_NAME[#]`: Destination filename for the module. Defaults to the |
| corresponding `BUILT_MODULE_NAME[#]`. |
| * `DEST_MODULE_LOCATION[#]`: Destination directory in the install tree, |
| relative to `$installtree`. **Must start with `/kernel`**. Defaults to |
| `/kernel/updates`. |
| * `STRIP[#]`: Whether to strip debug symbols for the module (`yes` or `no`). |
| Defaults to `yes`. |
| |
| #### Dependencies and Constraints |
| |
| * `BUILD_DEPENDS[#]`: List of other DKMS packages that must be built before |
| this one. Currently informational. |
| * `BUILD_EXCLUSIVE_KERNEL`: Regex. If the target `$kernelver` does not match, |
| the build will be skipped. |
| * `BUILD_EXCLUSIVE_ARCH`: Regex. If the target `$arch` does not match, the |
| build will be skipped. |
| * `AUTOINSTALL`: If set to `yes`, indicates the module should be rebuilt on |
| kernel upgrades. Currently informational. |
| |
| #### Patches |
| |
| * `PATCH[#]`: Filename of a patch (in `p1` format) located in the `patches/` |
| subdirectory of the package, to be applied before building. |
| * `PATCH_MATCH[#]`: Regex matched against `$kernelver`. The patch will only be |
| applied if it matches. |
| |
| #### Hooks |
| |
| Scripts (located in the package source) to run at various lifecycle stages: |
| |
| * `POST_ADD` |
| * `PRE_BUILD` / `POST_BUILD` |
| * `PRE_INSTALL` / `POST_INSTALL` |
| * `POST_REMOVE` |
| |
| -------------------------------------------------------------------------------- |
| |
| ### Configuration Examples {#configuration-examples} |
| |
| #### Simple Case (Single Module) |
| |
| If your package contains a single module whose name matches the package name, |
| and it builds with a standard `make` command, **you do not need a `dkms.conf` |
| file at all**. The default values are sufficient. |
| |
| #### Typical Case (Multiple Modules & Hooks) |
| |
| A typical `dkms.conf` for a package with multiple modules and custom build |
| steps: |
| |
| ```bash |
| PACKAGE_NAME="my-driver" |
| PACKAGE_VERSION="1.0.0" |
| |
| # First module |
| BUILT_MODULE_NAME[0]="my-core-driver" |
| DEST_MODULE_LOCATION[0]="/kernel/drivers/misc" |
| |
| # Second module (depends on core) |
| BUILT_MODULE_NAME[1]="my-extension-driver" |
| BUILT_MODULE_LOCATION[1]="extensions/" |
| DEST_MODULE_LOCATION[1]="/kernel/drivers/misc" |
| |
| # Custom build command |
| MAKE="make -C ${kernel_source_dir} M=${build_dir} EXTRA_CFLAGS=-DDEBUG" |
| |
| # Hooks |
| PRE_BUILD="setup_env.sh" |
| POST_INSTALL="echo 'Driver installed successfully!'" |
| ``` |
| |
| #### Real-world Case (Lustre Client Drivers) |
| |
| This example shows how to configure a complex package that requires source |
| modification and autotools configuration, and has many modules with strict |
| load-order requirements. Note that it takes advantage of the implicit pairing of |
| the module name/location lists and the module destination defaults to keep the |
| file simple. |
| |
| ```bash |
| # Install build-time dependencies inside the builder container |
| POST_ADD="apt update && DEBIAN_FRONTEND=noninteractive apt install -y python3-dev libtool git flex bison build-essential libmpich-dev clang python3-setuptools llvm binutils lld libkeyutils-dev libmount-dev pkg-config libnl-3-dev libnl-genl-3-dev libyaml-dev libjson-c-dev" |
| |
| # Configure the build directory |
| # Remove gcc-specific flags since COS uses clang. |
| clean_sources="sed -i -e 's/-Wno-stringop-overflow//g' -e 's/-Wno-stringop-truncation//g' -e 's/-Wno-format-truncation//g' config/lustre-toolchain.m4" |
| configure="LLVM=1 ./configure --disable-server --enable-client --disable-strict-errors --with-linux=/lib/modules/${kernelver}/build/" |
| PRE_BUILD="cd ${build_dir} && ${clean_sources} && bash autogen.sh && ${configure}" |
| |
| # List of modules to build and their locations |
| BUILT_MODULE_NAME=( |
| ksocklnd ko2iblnd fid fld libcfs lmv lnet lov lustre |
| mdc mgc obdclass obdecho osc ptlrpc lnet_selftest |
| ) |
| BUILT_MODULE_LOCATION=( |
| lnet/klnds/socklnd lnet/klnds/o2iblnd lustre/fid |
| lustre/fld libcfs/libcfs lustre/lmv lnet/lnet |
| lustre/lov lustre/llite lustre/mdc lustre/mgc |
| lustre/obdclass lustre/obdecho lustre/osc |
| lustre/ptlrpc lnet/selftest |
| ) |
| ``` |
| |
| -------------------------------------------------------------------------------- |
| |
| ## Module Signing {#module-signing} |
| |
| COS enforces kernel module signature verification. Modules that are not signed |
| by a trusted key will fail to load (`Required key not available`). |
| |
| `cos-dkms` can automatically sign compiled modules during the `build` or |
| `install` phase. |
| |
| By default, the `sha256` algorithm is used for signing. You can customize this |
| by passing `--hash-algorithm` (e.g., `sha384`, `sha512`). |
| |
| ### Signing Options |
| |
| - **Local Private Key (Default)**: `cos-dkms` will look for a private key and |
| certificate. |
| * Default paths: `/var/lib/dkms/mok.key` (private key) and |
| `/var/lib/dkms/mok.cert` (certificate). |
| * You can override these using the environment variables |
| `MODULES_SIGN_KEY` and `MODULES_SIGN_CERT`. |
| - **Cloud KMS**: You can also use a key stored in Google Cloud KMS. |
| * Pass the KMS key resource name via the `--cloud-kms-key` flag: |
| `--cloud-kms-key=projects/my-project/locations/global/keyRings/my-keyring/cryptoKeys/my-key/cryptoKeyVersions/1` |
| * You must also provide the public certificate path via |
| `MODULES_SIGN_CERT` or have it at `/var/lib/dkms/mok.cert`. |
| |
| -------------------------------------------------------------------------------- |
| |
| ## Example Workflows {#workflows} |
| |
| ### Workflow 1: Installing Precompiled Modules {#workflow-precompiled-modules} |
| |
| This workflow is intended to be used in production VMs to install modules. It |
| downloads precompiled modules from a GCS bucket cache and installs them. **No |
| compiler toolchain, kernel headers, or build dependencies are needed on the |
| VM.** |
| |
| ``` |
| +-------------------+ Check GCS Cache +-------------------+ |
| | Target VM | ----------------------> | GCS Bucket | |
| | (No Compiler req) | <---------------------- | (Precompiled .ko) | |
| +-------------------+ Download & Load +-------------------+ |
| ``` |
| |
| #### Steps: |
| |
| 1. On the COS VM, run the `cos-dkms` container. |
| 2. Provide the `--gcs-bucket` where the precompiled modules are stored. |
| 3. Use the `--insert-on-install` flag to automatically load them. |
| |
| #### Example Command: |
| |
| ```bash |
| docker run --rm -it \ |
| --privileged --net=host \ |
| -v /etc/lsb-release:/etc/lsb-release \ |
| -v /lib/modules:/host_modules \ |
| gcr.io/cos-cloud/cos-dkms:latest \ |
| install my-driver/1.0.0 \ |
| --gcs-bucket=gs://my-company-cos-drivers \ |
| --kernelmodulestree=/host_modules \ |
| --insert-on-install |
| ``` |
| |
| Using the high-level |
| [cos-dkms runner script](https://cos.googlesource.com/cos/tools/+/refs/heads/master/src/cmd/cos_dkms_runner), |
| this command can be simplified to: |
| |
| ```bash |
| sudo cos-dkms install my-driver/1.0.0 \ |
| --gcs-bucket=gs://my-company-cos-drivers \ |
| --insert-on-install |
| ``` |
| |
| *What happens under the hood:* |
| |
| 1. `cos-dkms` detects the running kernel version, board, and build ID. |
| 2. It checks `gs://my-company-cos-drivers/` for the precompiled `my-driver` |
| modules matching those exact parameters. |
| 3. It downloads the precompiled `.ko` files. |
| 4. It copies them to `/lib/modules/$(uname -r)/kernel/updates` in the container. |
| 5. It dynamically computes the module insertion order from the dkms.conf file |
| and the modules' dependencies (including modules from the read-only |
| kernelmodulestree mapped from the host), then uses `insmod` to load each |
| module in the correct order. |
| |
| -------------------------------------------------------------------------------- |
| |
| ### Workflow 2: Installing Modules Compiled by Google for COS {#workflow-google-compiled-modules} |
| |
| This is a special case of workflow 1 to install modules which are compiled as |
| part of the official COS image build and signed by Google. For these modules, a |
| value of `--gcs-bucket=cos-default` can be passed, and cos-dkms will infer the |
| correct COS artifacts bucket to download modules from. |
| |
| #### Example Command (installing Lustre drivers): |
| |
| ```bash |
| docker run --rm -it \ |
| --privileged --net=host \ |
| -v /etc/lsb-release:/etc/lsb-release \ |
| -v /lib/modules:/host_modules \ |
| gcr.io/cos-cloud/cos-dkms:latest \ |
| install lustre-client-drivers/2.14.0_p224 \ |
| --gcs-bucket=cos-default \ |
| --kernelmodulestree=/host_modules \ |
| --insert-on-install |
| ``` |
| |
| Using the high-level |
| [cos-dkms runner script](https://cos.googlesource.com/cos/tools/+/refs/heads/master/src/cmd/cos_dkms_runner), |
| this command can be simplified to: |
| |
| ```bash |
| sudo cos-dkms install lustre-client-drivers/2.14.0_p224 \ |
| --gcs-bucket=cos-default \ |
| --insert-on-install |
| ``` |
| |
| *What happens under the hood:* |
| |
| 1. `cos-dkms` detects the running kernel version, board, and build ID. |
| 2. It checks the `gs://cos-tools/` bucket for the precompiled modules (in this |
| case, Lustre) matching those exact parameters. |
| 3. It downloads the precompiled `.ko` files. |
| 4. It copies them to `/lib/modules/$(uname -r)/kernel/updates` in the container. |
| 5. It dynamically computes the module insertion order from the dkms.conf file |
| and the modules' dependencies (including modules from the read-only |
| kernelmodulestree mapped from the host), then uses `insmod` to load each |
| module in the correct order. |
| |
| -------------------------------------------------------------------------------- |
| |
| ### Workflow 3: Installing Modules with modprobe {#workflow-modprobe} |
| |
| Some modules require more complex configuration to install properly. In these cases, |
| using modprobe is more robust and configurable than the --insert-on-install option |
| that cos-dkms provides. However, it requires a little more setup on COS than using |
| --insert-on-install because the /lib/modules directory in COS is read-only. |
| |
| At a high level, the workflow for using --modprobe-on-install on COS is to add |
| any necessary modprobe configurations in the host, copy the host's kernel modules |
| to a writable directory, and mount that directory into the cos-dkms container. |
| |
| #### Example Command (installing Lustre drivers with modprobe): |
| |
| ```bash |
| # Write modprobe.d confs as normal; this one deals with the fact that |
| # lustre can't automatically load ksocklnd from the container context. |
| echo 'softdep lustre pre: ksocklnd post:' > /tmp/lustre.conf |
| |
| # Copy the kernel's modules to a rw directory. |
| cp -ra /lib/modules/ /tmp/host_modules |
| |
| # Run cos-dkms, pretending that the container's /lib/modules directory is the real |
| # one from this host. Doing it this way means that depmod/modprobe work as normal. |
| docker run --rm -it --privileged --net=host \ |
| -v /etc/lsb-release:/etc/lsb-release \ |
| -v /tmp/host_modules:/lib/modules \ |
| -v /tmp/lustre.conf:/etc/modprobe.d/lustre.conf \ |
| gcr.io/cos-cloud/cos-dkms:latest \ |
| install lustre-client-drivers --latest --modprobe-on-install \ |
| --gcs-bucket=cos-default \ |
| --logtostderr -v2 |
| ``` |
| |
| *What happens under the hood:* |
| |
| 1. `cos-dkms` detects the running kernel version, board, and build ID. |
| 2. It checks `gs://my-company-cos-drivers/` for the precompiled `my-driver` |
| modules matching those exact parameters. |
| 3. It downloads the precompiled `.ko` files. |
| 4. It copies them to `/lib/modules/$(uname -r)/kernel/updates`. |
| 5. It runs depmod to build the module dependency graph, taking into account |
| the newly compiled modules and the host kernel's modules since they are |
| both in the "expected" /lib/modules/<kernel version> directory. |
| 6. It runs modprobe to install the Lustre kernel drivers, taking into account |
| the modprobe configurations in the containers /etc/modprobe.conf, in this |
| case, the lustre.conf that we wrote in /tmp on the host. |
| |
| -------------------------------------------------------------------------------- |
| |
| ### Workflow 4: Building and Distributing Modules {#workflow-building-distributing} |
| |
| This workflow can be used by developers or in CI/CD pipelines to compile module |
| sources for a specific COS version, sign them, and upload them to a GCS bucket |
| cache for use in Workflow 1. |
| |
| ``` |
| +------------------+ Download Headers +--------------------+ |
| | Builder Machine | <---------------------- | COS Repositories | |
| | (Runs cos-dkms) | & Compiler Toolchain +--------------------+ |
| +------------------+ |
| | |
| | Build & Sign Modules |
| v |
| +------------------+ Upload Artifacts +--------------------+ |
| | Compiled .ko | ----------------------> | GCS Bucket | |
| +------------------+ | (Cache) | |
| +--------------------+ |
| ``` |
| |
| #### Steps: |
| |
| 1. Prepare your module source directory containing the code and a `dkms.conf`. |
| 2. Run `cos-dkms build` with: |
| * Target `lsb-release`: An LSB release from a target COS image. The |
| variables in this file will be used to infer the correct toolchain, |
| kernel version, and arch to allow cross-compiling the modules for a |
| particular COS version. |
| * `--install-build-dependencies`: To fetch the matching COS kernel headers |
| and toolchain. |
| * `--make-variables=cos-default`: To use the official COS compiler flags. |
| * `--gcs-bucket` and `--upload`: To save the results to the cache. |
| * Signature keys (local or KMS) to sign the modules. |
| |
| #### Example Command: |
| |
| ```bash |
| docker run --rm -it \ |
| -v /$PWD/targets/lsb-release:/etc/lsb-release \ |
| -v "$PWD/mok.key:/var/lib/dkms/mok.key" \ |
| -v "$PWD/mok.cert:/var/lib/dkms/mok.cert" \ |
| gcr.io/cos-cloud/cos-dkms:latest \ |
| build my-module/v1.2.3 \ |
| --install-build-dependencies \ |
| --make-variables=cos-default \ |
| --gcs-bucket=gs://my-company/my-cos-drivers \ |
| --upload \ |
| -v2 --logtostderr |
| ``` |
| |
| After running this command, a user can install the modules on a COS VM with a |
| symmetric command: |
| |
| ```bash |
| docker run --rm -it \ |
| --privileged --net=host \ |
| -v /etc/lsb-release:/etc/lsb-release \ |
| -v /lib/modules:/host_modules |
| gcr.io/cos-cloud/cos-dkms:latest \ |
| install my-module/v1.2.3 \ |
| --gcs-bucket=gs://my-company/my-cos-drivers \ |
| --kernelmodulestree=/host_modules \ |
| -v2 --logtostderr |
| ``` |
| |
| *What happens under the hood:* |
| |
| 1. `cos-dkms` downloads the exact `clang` toolchain and kernel headers for the |
| COS version that matches the variables specified in the target LSB release. |
| 2. It copies the module sources from the provided GCS bucket to a build |
| directory and applies any patches. |
| 3. It builds the module using the `MAKE` command from the dkms.conf. |
| 4. It signs all the compiled `.ko` files using the provided `mok.key` and |
| `mok.cert`. |
| 5. It uploads both the source package and the compiled, signed `.ko` files to |
| `gs://my-company/my-cos-drivers/` under a path specific to the kernel |
| version, board, and build ID. |
| |