Skip to content

Commit

Permalink
Don't use OCI Image tools to build Oak Containers system image
Browse files Browse the repository at this point in the history
While we use Docker to generate the base system images, that's just a
convenience, and subject to change. We're not interested in any OCI
Image-related functionality for the system image; it's just a tarball.

* Update base building rules
* Fix sysroot dockerfile, libsystemd-dev never got permanently added.
* Add a push helper script that pushes plain tarballs to gstatic
* Use a sha256sum-based system for uploading tarballs
* Remove the "rootfs_only" hack from oci_runtime_bundle

Change-Id: I3fa5c4e0353d4f92b45cfdceb2ab41ce5cf31a45
  • Loading branch information
jblebrun committed Oct 28, 2024
1 parent 9d239a5 commit 74b4de7
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 199 deletions.
51 changes: 27 additions & 24 deletions WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
workspace(name = "oak")

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")

# The `name` argument in all `http_archive` rules should be equal to the
# WORKSPACE name of the corresponding library.
Expand Down Expand Up @@ -270,24 +270,6 @@ oci_pull(
platforms = ["linux/amd64"],
)

# System image for Oak Containers
# We build these (see oak_containers/system_image) and push them to the repo below before
# this snippet can pull them.
# This image is based on debian:stable-20240612
oci_pull(
name = "oak_containers_sysimage_base",
digest = "sha256:4844b899dcb44420d368bfe24dca856d01a8483d6976fbee292227f601d69940",
image = "europe-west2-docker.pkg.dev/oak-ci/oak-containers-sysimage-base/oak-containers-sysimage-base",
)

# Same as previous, for Nvidia GPU support (see
# oak_containers/system_image/README.md). Based on debian:stable-20240612 .
oci_pull(
name = "oak_containers_nvidia_sysimage_base",
digest = "sha256:9e69576783ad3c0a420bcb978dec53da5de6fd1a304b9b0f9d6c6bc6f188e894",
image = "europe-west2-docker.pkg.dev/oak-ci/oak-containers-sysimage-base/oak-containers-nvidia-sysimage-base",
)

load("@aspect_bazel_lib//lib:repositories.bzl", "register_expand_template_toolchains")

register_expand_template_toolchains()
Expand All @@ -296,20 +278,41 @@ load("@//bazel:repositories.bzl", "oak_toolchain_repositories")

oak_toolchain_repositories()

# Expected hashes for our base image tarballs
SYSROOT_SHA256 = "4c13529ab388e5e8570fb7164757b8255a284bc845b713906b968eb71c4f748e"

BASE_IMAGE_SHA256 = "b826bc141a91ae385f9c45a43eb800f691eca92dc537f0dc5d743c51df459ecb"

NVIDIA_BASE_IMAGE_SHA256 = "ba7f59ebfc71c54c90a54e508dc2acb58d1bd55606d57aa7c771d1cf67dc61f2"

# Experimental sysroot for the build toolchain, based on Oak Containers sysimage.
#
# Rebuild it using:
# $ oak_containers/system_image/build-base.sh sysroot
# (See oak_containers/system_image/README.md for more details.)
#
# Upload it using:
# $ xz oak_containers/system_image/target/sysroot.tar
# $ gsutil cp oak_containers/system_image/target/sysroot.tar.xz gs://oak-bins/sysroot/sysroot.tar.xz
# $ oak_containers/system_image/push-base.sh sysroot
#
# (See oak_containers/system_image/README.md for more details.)
http_archive(
name = "oak_cc_toolchain_sysroot",
build_file = "//:toolchain/sysroot.BUILD",
sha256 = "770f449151c0871d67ed793e996e225973b94cec6438fd56666cef8fc4ee5dd4",
url = "https://storage.googleapis.com/oak-bins/sysroot/sysroot.tar.xz",
sha256 = SYSROOT_SHA256,
url = "https://storage.googleapis.com/oak-bins/sysroot/" + SYSROOT_SHA256 + ".tar.xz",
)

http_file(
name = "oak_containers_system_image_base",
downloaded_file_path = "base-image.tar.xz",
sha256 = BASE_IMAGE_SHA256,
url = "https://storage.googleapis.com/oak-bins/base-image/" + BASE_IMAGE_SHA256 + ".tar.xz",
)

http_file(
name = "oak_containers_nvidia_system_image_base",
downloaded_file_path = "nvidia-base-image.tar.xz",
sha256 = NVIDIA_BASE_IMAGE_SHA256,
url = "https://storage.googleapis.com/oak-bins/nvidia-base-image/" + NVIDIA_BASE_IMAGE_SHA256 + ".tar.xz",
)

# Register a hermetic C++ toolchain to ensure that binaries use a glibc version supported by
Expand Down
8 changes: 1 addition & 7 deletions bazel/private/oci_runtime_bundle.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,13 @@ def _oci_runtime_bundle_impl(ctx):
# unpack the container image and then re-pack it into a tar file.
executable = ctx.actions.declare_file("{}.tar.sh".format(ctx.label.name))

rootfs_only = "true" if ctx.attr.rootfs_only else "false"

ctx.actions.expand_template(
template = ctx.file._tpl,
output = executable,
is_executable = True,
substitutions = {
"{{umoci}}": umoci.path,
"{{yq}}": yq.path,
"{{rootfs_only}}": rootfs_only,
},
)

Expand All @@ -54,7 +51,6 @@ _oci_runtime_bundle = rule(
attrs = {
"image": attr.label(allow_single_file = True),
"bundle": attr.output(),
"rootfs_only": attr.bool(),
"_tpl": attr.label(
allow_single_file = True,
default = ":oci_runtime_bundle.sh.tpl",
Expand All @@ -67,20 +63,18 @@ _oci_runtime_bundle = rule(
],
)

def oci_runtime_bundle(name, image, rootfs_only = False, **kwargs):
def oci_runtime_bundle(name, image, **kwargs):
"""Converts an oci_image to a OCI runtime bundle tar.
Args:
name: the target name to produce. Building this target will generate a
"{name}.tar" output file.
image: the oci_image target to convert.
rootfs_only: Only the files under the root fs will be in the final tar.
**kwargs: additional arguments passed to the rule (e.g., visibility).
"""
_oci_runtime_bundle(
name = name,
bundle = "{}.tar".format(name),
rootfs_only = rootfs_only,
image = image,
**kwargs
)
9 changes: 1 addition & 8 deletions bazel/private/oci_runtime_bundle.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ set -eu

readonly UMOCI="{{umoci}}"
readonly YQ="{{yq}}"
readonly ROOTFS_ONLY="{{rootfs_only}}"

# Add tags to the image index generated by oci_image so that it's compatible
# with umoci.
Expand All @@ -45,13 +44,7 @@ trap 'rm -rf -- "${BUNDLE_DIR}"' EXIT
' "${BUNDLE_DIR}/config.json"


BUNDLE_SUBDIR=""
if [[ $ROOTFS_ONLY == "true" ]]
then
BUNDLE_SUBDIR=$("${YQ}" e ".root.path" < "${BUNDLE_DIR}/config.json")
fi

# Pack the runtime bundle into a (reproducible) tar, excluding unnecessary files
# added by umoci.
tar --create --sort=name --mtime="2000-01-01Z" --owner=0 --group=0 --numeric-owner \
--file="$2" --directory="${BUNDLE_DIR}/${BUNDLE_SUBDIR}" --exclude=./umoci.json --exclude='./sha256_*.mtree' .
--file="$2" --directory="${BUNDLE_DIR}" --exclude=./umoci.json --exclude='./sha256_*.mtree' .
128 changes: 11 additions & 117 deletions oak_containers/system_image/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template")
load("@oak//bazel:defs.bzl", "oci_runtime_bundle")
load("@rules_oci//oci:defs.bzl", "oci_image", "oci_push")

load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_files")
load("@rules_pkg//pkg:tar.bzl", "pkg_tar")
load("//bazel/tools/xz:xz.bzl", "xz_compress")
Expand Down Expand Up @@ -68,138 +66,34 @@ pkg_tar(
package_dir = "/usr/bin",
)

oci_image(
name = "oak_containers_system_image_oci_image",
base = "@oak_containers_sysimage_base",
tars = [":rust_bins_tar"],
)

oci_image(
name = "oak_containers_nvidia_system_image_oci_image",
base = "@oak_containers_nvidia_sysimage_base",
tars = [":rust_bins_tar"],
)

oci_runtime_bundle(
pkg_tar(
name = "oak_containers_system_image_tar",
image = ":oak_containers_system_image_oci_image",
rootfs_only = True,
deps = [
":rust_bins_tar",
"@oak_containers_system_image_base//file:base-image.tar.xz",
],
)

xz_compress(
name = "oak_containers_system_image",
out = "oak_containers_system_image.tar.xz",
# This rule will fail unless you run: just oak_containers_sytem_image_binaries
# This restriction will be removed once everything is bazelified.
tags = ["manual"],
target = ":oak_containers_system_image_tar",
)

oci_runtime_bundle(
pkg_tar(
name = "oak_containers_nvidia_system_image_tar",
image = ":oak_containers_nvidia_system_image_oci_image",
rootfs_only = True,
deps = [
":rust_bins_tar",
"@oak_containers_nvidia_system_image_base//file:nvidia-base-image.tar.xz",
],
)

xz_compress(
name = "oak_containers_nvidia_system_image",
out = "oak_containers_nvidia_system_image.tar.xz",
# This rule will fail unless you run: just oak_containers_sytem_image_binaries
# This restriction will be removed once everything is bazelified.
tags = ["manual"],
target = ":oak_containers_nvidia_system_image_tar",
)

### Base Image Update Targets
### These can't yet be run automatically. First, the build-base.sh script must be run.

# This can be generated by running build-base.sh
filegroup(
name = "base_image_tar",
srcs = [
"target/base-image.tar",
],
# This rule will fail unless you run: ./oak_containers/system_image/build-base.sh first
# These rules are only used when we need to push a new base image.
tags = ["manual"],
)

filegroup(
name = "nvidia_base_image_tar",
srcs = [
"target/nvidia-base-image.tar",
],
# This rule will fail unless you run: ./oak_containers/system_image/build-base.sh first
# These rules are only used when we need to push a new base image.
tags = ["manual"],
)

# Defines labels added to :oak_containers_sysimage_base. When built with
# `--stamp`, overrides in `stamp_substitutions` override those in
# `substitutions`; `stamp_substitutions` can access workspace status values
# (https://bazel.build/docs/user-manual#workspace-status) populated by bazel or
# //bazel:workspace_status_command.sh. Labels must conform to
# https://github.com/opencontainers/image-spec/blob/main/annotations.md.
expand_template(
name = "oak_containers_sysimage_base_labels",
out = "oak_containers_sysimage_base_labels.txt",
stamp_substitutions = {"{revision}": "{{STABLE_GIT_COMMIT}}"},
substitutions = {"{revision}": "unknown"},
template = [
"org.opencontainers.image.source=https://github.com/project-oak/oak",
"org.opencontainers.image.revision={revision}",
],
)

oci_image(
name = "oak_containers_sysimage_base",
architecture = "amd64",
labels = ":oak_containers_sysimage_base_labels",
os = "linux",
# This rule will fail unless you run: ./oak_containers/system_image/build-base.sh first
# These rules are only used when we need to push a new base image.
tags = ["manual"],
tars = [":base_image_tar"],
)

oci_image(
name = "oak_containers_nvidia_sysimage_base",
architecture = "amd64",
labels = ":oak_containers_sysimage_base_labels",
os = "linux",
# This rule will fail unless you run: ./oak_containers/system_image/build-base.sh first
# These rules are only used when we need to push a new base image.
tags = ["manual"],
tars = [":nvidia_base_image_tar"],
)

# Helper targets for pushing base images.
#
# In order to set up the necessary credentials:
#
# - Install `gcloud`: https://cloud.google.com/sdk/docs/downloads-interactive
# - `gcloud auth login`
# - `gcloud config set project oak-ci`
# - `gcloud auth configure-docker`
# - `gcloud auth configure-docker europe-west2-docker.pkg.dev`
# After running this target, you will need to update the hash for
# oak_containers_sysimage_base in the WORKSPACE file to use it.
oci_push(
name = "push_base",
image = ":oak_containers_sysimage_base",
remote_tags = ["latest"],
repository = "europe-west2-docker.pkg.dev/oak-ci/oak-containers-sysimage-base/oak-containers-sysimage-base",
# This rule will fail unless you run: ./oak_containers/system_image/build-base.sh first
# These rules are only used when we need to push a new base image.
tags = ["manual"],
)

oci_push(
name = "push_nvidia_base",
image = ":oak_containers_nvidia_sysimage_base",
remote_tags = ["latest"],
repository = "europe-west2-docker.pkg.dev/oak-ci/oak-containers-sysimage-base/oak-containers-nvidia-sysimage-base",
# This rule will fail unless you run: ./oak_containers/system_image/build-base.sh first
# These rules are only used when we need to push a new base image.
tags = ["manual"],
)
27 changes: 1 addition & 26 deletions oak_containers/system_image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,30 +34,10 @@ avoiding the need for Docker when rebuilding a system image container.
To update the base image and push it:

1. ./oak_containers/system_image/build-base.sh
2. bazel run --stamp oak_containers/system_image:push_base
2. ./oak_containers/system_image/push-base.sh vanilla|nvidia

There is also a version of the base image that includes the nvidia drivers.

## Bazel-Based System Image Tools

`just oak_containers_system_image` and some `BUILD` targets

How this works:

1. We expect that `build-base.sh` has already been run, pushed an image, and the
WORKSPACE points to that image, as described above.

2. The script will build the two needed binaries using cargo. Once those crates
are bazelified, we can make them proper targets.

3. The syslogd binary is patched. We can eventually craft some sort of bazel
rule to do this.

4. `oci_image` target pulls the previously built base, and layers the two
binaries onto it.

5. `oci_runtime_bundle` exports the bundle to a tarball that we can use.

## Sysroot

We use this to get a full, consistent set of libraries, tools and compilers, and
Expand All @@ -73,10 +53,5 @@ moment.
- There might be a better way to build the base image. It feels a bit hacky, but
it's working for now.

- When umoci exports a runtime bundle, the files are under ./rootfs. But the
base image built with the old way has all files in the top level. We'll
probably need to mimic that structure. There are lots of ways to do this, but
it's not clear what the most correct one is.

- The version with nvidia drivers is still largely untested and under
development.
4 changes: 2 additions & 2 deletions oak_containers/system_image/base_image.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# System Image for Oak Containers. Contains base Debian plus binaries and
# System Image for Oak Containers. Contains base Debian plus binaries and
# configs to run Oak. This MUST be based on a stable Debian image.
# debian:stable-20240612 - https://hub.docker.com/_/debian/tags
ARG debian_snapshot=sha256:26878d0d3aa5e1980d6f8060b4af32fc48b8edeb1fc4d2d074a13a04b17c95f2
Expand Down Expand Up @@ -36,7 +36,7 @@ RUN apt-get --yes update \
# * /var/cache/ldconfig/aux-cache can be removed; this is safe for all files
# in /var/cache
RUN (LAST_DAY="$(awk -F: '$1=="root"{print $3}' /etc/shadow)"; \
chage -d "$LAST_DAY" messagebus && chage -d "$LAST_DAY" systemd-network) \
chage -d "$LAST_DAY" messagebus && chage -d "$LAST_DAY" systemd-network) \
&& rm -f /etc/{passwd,shadow}- \
&& ln -sf /etc/machine-id /var/lib/dbus/machine-id \
&& find /etc/machine-id /var/log -type f -execdir truncate -s 0 '{}' '+' \
Expand Down
16 changes: 2 additions & 14 deletions oak_containers/system_image/build-base.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,5 @@ then
build_sysroot
fi

set +o xtrace
printf "\n\nIf you want to push this newly created base, run:\n"
printf "\nbazel run oak_containers/system_image:push_base\n"
printf "(and/or) bazel run oak_containers/system_image:push_nvidia_base\n\n"
printf "If you want to use the newly created base, update the hash for\n"
printf "the oak_containers_sysimage_base oci_pull target (or the nvidia flavour\n"
printf "if needed) in WORKSPACE\n\n"
printf "If you haven't done a push before, you'll need to set up gcloud auth:\n"
printf "Install gcloud if needed: https://cloud.google.com/sdk/docs/downloads-interactive\n"
printf "And then:\n\n"
printf " gcloud auth login\n"
printf " gcloud config set project oak-ci\n"
printf " gcloud auth configure-docker\n"
printf " gcloud auth configure-docker europe-west2-docker.pkg.dev\n"
echo "To upload:"
echo " ./oak_containers/system_image/push-base.sh <flavor>"
Loading

0 comments on commit 74b4de7

Please sign in to comment.