diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..e6248c2 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,43 @@ +name: Build and release Systemd sysext images +on: + push: + tags: + - '*' + +env: + RUNC_VERSION: v1.2.0-rc.2 + CONTAINERD_VERSION: 2.0.0-rc.3 + +jobs: + build: + runs-on: ubuntu-22.04 + permissions: + # allow the action to create a release + contents: write + steps: + # checkout the sources + - uses: actions/checkout@v4 + + # prepare build host + - name: install prerequisites + run: | + set -euxo pipefail + + sudo apt update -qq && sudo apt install -yqq \ + curl \ + jq \ + squashfs-tools \ + xz-utils \ + gawk + + - name: build release artifacts + run: | + TAG=${{ github.ref_name }} ./create_containerd_sysext.sh $CONTAINERD_VERSION containerd-nine + + - name: create a new latest release with all artifacts + uses: softprops/action-gh-release@v1 + with: + files: | + SHA256SUMS + *.raw + *.conf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7998697 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.raw +*.conf +SHA256SUMS diff --git a/README.md b/README.md index 31981b7..d601673 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ -# -containerd-sysext -A systemd-sysext image that installs the latest containerd version +# containerd-sysext + +A systemd-sysext image that installs the latest containerd version. Used to be +able to deploy containerd 2.0 on Flatcar before it is bundled with the OS. + +The scripts in this repo have been adapted from [the Flatcar sysext-bakery](https://github.com/flatcar/sysext-bakery). + +## Releasing + +Simply merge to main and push a new tag. A github release with the +sysext-image as an artifact is automatically created. diff --git a/bake.sh b/bake.sh new file mode 100755 index 0000000..0513df3 --- /dev/null +++ b/bake.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euo pipefail + +OS="${OS-_any}" +FORMAT="${FORMAT:-squashfs}" +ARCH="${ARCH-}" +RELOAD="${RELOAD-}" +SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH-0}" +export SOURCE_DATE_EPOCH + +# This script is to be called as helper by other scripts but can also be used standalone +if [ $# -lt 1 ]; then + echo "Usage: $0 SYSEXTNAME" + echo "The script will make a SYSEXTNAME.raw image of the folder SYSEXTNAME, and create an os-release file in it, run with --help for the list of supported environment variables." + exit 1 +elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + echo "If ARCH is specified as environment variable the sysext image will be required to run on the given architecture." + echo "To build for a specific OS, pass 'OS=myosid' as environment variable (current value is '${OS}'), e.g., 'fedora' as found in 'ID' under '/etc/os-release', or pass 'OS=_any' for any OS." + echo "The '/etc/os-release' file of your OS has to include 'SYSEXT_LEVEL=1.0' as done in Flatcar (not needed for 'OS=_any')." + echo "To specify that systemd should do a daemon reload for the system when the extension is loaded/unloaded, set RELOAD=1 (current value is '${RELOAD}')." + echo "If the mksquashfs tool is missing you can pass FORMAT=btrfs, FORMAT=ext4, or FORMAT=ext2 as environment variable (current value is '${FORMAT}') but the script won't change the ownership of the files in the SYSEXTNAME directory, so make sure that they are owned by root before creating the sysext image to avoid any problems." + echo "To make builds reproducible the SOURCE_DATE_EPOCH environment variable will be set to 0 if not defined." + echo + exit 1 +fi + +SYSEXTNAME="$1" + +if [ "${FORMAT}" != "squashfs" ] && [ "${FORMAT}" != "btrfs" ] && [ "${FORMAT}" != "ext4" ] && [ "${FORMAT}" != "ext2" ]; then + echo "Expected FORMAT=squashfs, FORMAT=btrfs, FORMAT=ext4, or FORMAT=ext2, got '${FORMAT}'" >&2 + exit 1 +fi + +# Map to valid values for https://www.freedesktop.org/software/systemd/man/os-release.html#ARCHITECTURE= +if [ "${ARCH}" = "amd64" ] || [ "${ARCH}" = "x86_64" ]; then + ARCH="x86-64" +elif [ "${ARCH}" = "aarch64" ]; then + ARCH="arm64" +fi + +mkdir -p "${SYSEXTNAME}/usr/lib/extension-release.d" +{ + echo "ID=${OS}" + if [ "${OS}" != "_any" ]; then + echo "SYSEXT_LEVEL=1.0" + fi + if [ "${ARCH}" != "" ]; then + echo "ARCHITECTURE=${ARCH}" + fi + if [ "${RELOAD}" = 1 ]; then + echo "EXTENSION_RELOAD_MANAGER=1" + fi +} > "${SYSEXTNAME}/usr/lib/extension-release.d/extension-release.${SYSEXTNAME}" +rm -f "${SYSEXTNAME}".raw +if [ "${FORMAT}" = "btrfs" ]; then + # Note: We didn't chown to root:root, meaning that the file ownership is left as is + mkfs.btrfs --mixed -m single -d single --shrink --rootdir "${SYSEXTNAME}" "${SYSEXTNAME}".raw + # This is for testing purposes and makes not much sense to use because --rootdir doesn't allow to enable compression +elif [ "${FORMAT}" = "ext4" ] || [ "${FORMAT}" = "ext2" ]; then + # Assuming that 1 GB is enough + truncate -s 1G "${SYSEXTNAME}".raw + # Note: We didn't chown to root:root, meaning that the file ownership is left as is + mkfs."${FORMAT}" -E root_owner=0:0 -d "${SYSEXTNAME}" "${SYSEXTNAME}".raw + resize2fs -M "${SYSEXTNAME}".raw +else + VER=$({ mksquashfs -version || true ; } | head -n1 | cut -d " " -f 3) + VERMAJ=$(echo "${VER}" | cut -d . -f 1) + VERMIN=$(echo "${VER}" | cut -d . -f 2) + ARG=(-all-root -noappend) + if [ "${VERMAJ}" -gt 4 ] && [ "${VERMIN}" -gt 6 ]; then + ARG+=('-xattrs-exclude' '^btrfs.') + fi + mksquashfs "${SYSEXTNAME}" "${SYSEXTNAME}".raw "${ARG[@]}" +fi +echo "Created ${SYSEXTNAME}.raw" diff --git a/create_containerd_sysext.sh b/create_containerd_sysext.sh new file mode 100755 index 0000000..fdbf9c0 --- /dev/null +++ b/create_containerd_sysext.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +set -euo pipefail + +export ARCH="${ARCH-amd64}" +export RUNC_VERSION="${RUNC_VERSION-v1.2.0-rc.1}" +SCRIPTFOLDER="$(dirname "$(readlink -f "$0")")" +ONLY_CONTAINERD="${ONLY_CONTAINERD:-0}" + +if [ $# -lt 2 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + echo "Usage: $0 VERSION SYSEXTNAME" + echo "The script will download the containerd release tar ball and create a sysext squashfs image with the name SYSEXTNAME.raw in the current folder." + echo "A temporary directory named SYSEXTNAME in the current folder will be created and deleted again." + echo "All files in the sysext image will be owned by root." + echo "The necessary systemd services will be created by this script" + echo "To use arm64 pass 'ARCH=arm64' as environment variable (current value is '${ARCH}')." + "${SCRIPTFOLDER}"/bake.sh --help + exit 1 +fi + +VERSION="$1" +SYSEXTNAME="$2" + +rm -f "containerd-${VERSION}.tgz" +curl -o "containerd-${VERSION}.tgz" -fsSL "https://github.com/containerd/containerd/releases/download/v${VERSION}/containerd-${VERSION}-linux-${ARCH}.tar.gz" +rm -f runc${ARCH} +curl -o runc -fsSL "https://github.com/opencontainers/runc/releases/download/${RUNC_VERSION}/runc.${ARCH}" +rm -rf "${SYSEXTNAME}" +mkdir -p "${SYSEXTNAME}" +tar --force-local -xf "containerd-${VERSION}.tgz" -C "${SYSEXTNAME}" +rm "containerd-${VERSION}.tgz" +mkdir -p "${SYSEXTNAME}"/usr/bin +mv "${SYSEXTNAME}"/bin/* "${SYSEXTNAME}"/usr/bin/ +chmod +x runc +mv runc "${SYSEXTNAME}"/usr/bin +rmdir "${SYSEXTNAME}"/bin +mkdir -p "${SYSEXTNAME}/usr/lib/systemd/system" + +cat > "${SYSEXTNAME}/usr/lib/systemd/system/containerd.service" <<-'EOF' + [Unit] + Description=containerd container runtime + After=network.target + [Service] + Delegate=yes + Environment=CONTAINERD_CONFIG=/usr/share/containerd/config.toml + ExecStart=/usr/bin/containerd --config ${CONTAINERD_CONFIG} + KillMode=process + Restart=always + # (lack of) limits from the upstream docker service unit + LimitNOFILE=1048576 + LimitNPROC=infinity + LimitCORE=infinity + TasksMax=infinity + [Install] + WantedBy=multi-user.target +EOF + mkdir -p "${SYSEXTNAME}/usr/lib/systemd/system/multi-user.target.d" + { echo "[Unit]"; echo "Upholds=containerd.service"; } > "${SYSEXTNAME}/usr/lib/systemd/system/multi-user.target.d/10-containerd-service.conf" + mkdir -p "${SYSEXTNAME}/usr/share/containerd" + cat > "${SYSEXTNAME}/usr/share/containerd/config.toml" <<-'EOF' + version = 2 + # set containerd's OOM score + oom_score = -999 + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] + # setting runc.options unsets parent settings + runtime_type = "io.containerd.runc.v2" + [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] + SystemdCgroup = true +EOF +sed 's/SystemdCgroup = true/SystemdCgroup = false/g' "${SYSEXTNAME}/usr/share/containerd/config.toml" > "${SYSEXTNAME}/usr/share/containerd/config-cgroupfs.toml" + +RELOAD=1 "${SCRIPTFOLDER}"/bake.sh "${SYSEXTNAME}" +rm -rf "${SYSEXTNAME}" + +mv "${SYSEXTNAME}.raw" "${SYSEXTNAME}-${VERSION}.raw" + +cat > containerd.conf << EOF + [Transfer] + Verify=false + [Source] + Type=url-file + Path=https://github.com/ninech/containerd-sysext/releases/${TAG}/download/ + MatchPattern=${SYSEXTNAME}-@v.raw + [Target] + InstancesMax=3 + Type=regular-file + Path=/opt/extensions/containerd + CurrentSymlink=/etc/extensions/containerd.raw +EOF + +sha256sum *.raw | tee SHA256SUMS