How to Build an Ubuntu Kernel with GitHub Actions

How to Build an Ubuntu Kernel with GitHub Actions

Keeping a finger on the pulse of kernel development is crucial for any DevOps professional. Canonical, the powerhouse behind Ubuntu, tracks kernel mainline development via its mainline-crack Git repository. In this blog post, we will explore the construction of an efficient GitHub Actions pipeline that automates the build process and distribution of Ubuntu kernel deb packages.

Canonical provides a convenient debian/rules makefile that simplifies the building of the Ubuntu kernel. This makefile is the key tool utilized in our example. To learn more about debian/rules, check out debmake-doc.

CI Pipeline Overview

The pipeline encompasses two primary jobs: build and publish. The build job is executed within a custom Docker container, named custom-linux-builder-jammy:latest, which is based on Ubuntu 22.04 and equipped with all necessary dependencies for Ubuntu kernel compilation. The publish job, reliant on the build job, is responsible for deploying the generated packages to a repository.

Let’s start with the overview of the entire CI pipeline:

Name: CI 
on: [push, workflow_dispatch] 
  
jobs: 
  build: 
	runs-on: [ubuntu-22-04, x64, heavy, self-hosted] 
	container: custom-linux-builder-jammy:latest 
	steps: 
  	- uses: actions/checkout@v3 
  
  	- name: Cleanup 
    	run: | 
      	debian/rules clean 
      	rm -rf ../*.deb /packages 
  
  	- name: Dependencies 
    	run: | 
      	apt-get update && \ 
      	mk-build-deps \ 
        	--tool 'apt-get -o Debug::pkgProblemResolver=yes 
--no-install-recommends --yes' \ 
        	--install --remove debian/control 
  
  	- name: Build 
    	run: | 
      	debian/rules do_tools=true binary-generic binary 
      	mkdir /packages && cp -f ../*deb /packages/. 
  
  	- uses: actions/upload-artifact@v3 
    	with: 
      	name: linux_package 
      	path: /packages/ 
  
  publish: 
	runs-on: [self-hosted, ubuntu-22-04] 
	needs: [build] 
	steps: 
  	- name: Get Secrets 
    	uses: hashicorp/vault-action@v2.4.1 
    	id: secrets 
    	with: 
      	url: <VAULT_INSTANCE_URL> 
      	token: ${{ secrets.VAULT_TOKEN }} 
      	secrets: | 
        	<PATH_TO_PRIVATE_KEY>| key ; 
  
  	- uses: actions/download-artifact@v3 
    	with: 
      	name: linux_package 
  
  	- name: Send to Repo 
    	uses: appleboy/scp-action@master 
    	with: 
      	host: <REPOSITORY_URL> 
      	username: <REPOSITORY_USERNAME> 
      	key: ${{ steps.secrets.outputs.key }} 
      	port: <PORT_NUMBER> 
      	source: "*.deb" 
      	target: <PATH_IN_REPOSITORY>

Deep Dive

Let’s take a deep dive and check out the GitHub Actions pipeline in detail.

Build Phase

The build phase is the core of the pipeline, and is responsible for the Ubuntu kernel compilation and packaging. The pipeline uses Docker image custom-linux-builder-jammy:latest, which is based on Ubuntu 22.04 and contains all necessary dependencies for building the Ubuntu kernel.

Checkout

The actions/checkout@v3 step ensures that the pipeline operates on the latest version of the codebase. This means that any recent revisions will be incorporated into the build.

Cleanup

The Cleanup step removes any residuals from previous workflows. It cleans the source tree and removes any leftover .deb packages from previous builds, ensuring you’re starting fresh. Here are the relevant commands:

debian/rules clean 
rm -rf ../*.deb

Dependencies

The Dependencies step prepares and installs the dependencies listed in debian/control. It ensures that the build environment has all the tools and dependencies it needs before starting to perform the kernel compilation. The code for this step is as follows:

apt-get update && \ 
mk-build-deps \ 
  --tool 'apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' \ 
  --install --remove debian/control

Build

debian/rules do_tools=true binary-generic binary 
mkdir /packages && cp -f ../*deb /packages/.

The Build step handles the actual compilation and packaging of the Ubuntu kernel. It executes the debian/rules script with the specified parameters, including do_tools=true, binary-generic, and binary.

The order of these targets is important. The do_tools=true parameter enables the build of linux common tools (bpftool, cpupower, perf, etc.) binary-generic is executed first, so binary target can utilize results from binary-generic.

Once the build is complete, the resulting Debian packages are stored in the parent directory (../) and then copied to the /packages directory for future use.

Upload Artifact

After the build is successfully completed, the actions/upload-artifact@v3 step takes over and uploads the newly generated Debian packages as an artifact named “linux_package”. This sets up the final stage of the process, the publish job.

Publish

The final stage in our pipeline is the publish job. This job uploads the previously generated kernel packages to the repository. The steps are relatively simple:

  1. Secure secrets: First, we will utilize the hashicorp/vault-action to fetch secrets from a HashiCorp Vault instance. This gives secure access to the resources required in the next steps of this job.
  2. Retrieve the artifact: This step retrieves the artifact generated by the previous “build” job via actions/download-artifact@v3.
  3. Deploy to repository: This step uses appleboy/scp-action to securely transfer the Debian packages to the desired remote repository.

By using these steps in the Publish job, the Ubuntu kernel deb packages are successfully uploaded to the specified repository, ensuring easy access and efficient distribution.

Conclusion

With this simple GitHub Actions pipeline, building and publishing Ubuntu kernels becomes an automated and efficient process, with less chance of human error. We hope this guide will help you to streamline your DevOps processes so you can boost your productivity!

How to Build an Ubuntu Kernel with GitHub Actions

Subscribe
to our newsletter

Get the latest industry trends, exclusive insights, and Gcore
updates delivered straight to your inbox.