Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example of building a Dockerfile with local tar file build context #702

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module(
compatibility_level = 1,
)

bazel_dep(name = "aspect_bazel_lib", version = "2.7.2")
bazel_dep(name = "aspect_bazel_lib", version = "2.9.3")
bazel_dep(name = "bazel_features", version = "1.10.0")
bazel_dep(name = "bazel_skylib", version = "1.5.0")
bazel_dep(name = "platforms", version = "0.0.8")
Expand Down
99 changes: 99 additions & 0 deletions examples/dockerfile/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
load("@aspect_bazel_lib//lib:tar.bzl", "mtree_mutate", "mtree_spec", "tar")
load("@aspect_bazel_lib//lib:run_binary.bzl", "run_binary")
load("@bazel_skylib//rules:native_binary.bzl", "native_binary")
load("@configure_buildx//:defs.bzl", "BUILDER_NAME", "TARGET_COMPATIBLE_WITH")
Expand Down Expand Up @@ -87,3 +88,101 @@ assert_oci_image_command(
|| ||
""",
)

# This tar can be arranged in arbitrary ways.
# For example, one could grab pkg_files from elsewhere in the workspace.
# Anything in the tar can be used by a Dockerfile ADD or COPY.
mtree_spec(
name = "buildx_context_tree",
srcs = [
"context.Dockerfile",
"requirements.txt",
] + glob([
"src/*",
]),
)

mtree_mutate(
name = "buildx_context_tree_prefixed",
mtree = ":buildx_context_tree",
strip_prefix = package_name(),
package_dir = "app",
)

tar(
name = "buildx_context",
mtree = ":buildx_context_tree_prefixed",
srcs = [
"context.Dockerfile",
"requirements.txt",
] + glob([
"src/*",
]),
)

# In order to use a local tar as a context, we must pipe the tar into the BuildX binary
# This is a bash operation, so we must wrap the BuildX tool in an sh_binary that does this for us
sh_binary(
name = "buildx_wrapper",
srcs = [
"buildx_wrapper.sh",
],
)

# This is similar to :base except it uses a tar file as a Docker context
run_binary(
name = "base_context",
tool = ":buildx_wrapper",
out_dirs = [ "base_context" ],
srcs = [
":buildx_context",
":buildx",
],
env = {
"BUILDX": "$(location :buildx)",
"BUILDER_NAME": BUILDER_NAME,
"OUTPUT_DIR": "$@",
"BUILDX_CONTEXT": "$(location :buildx_context)",
"DOCKER_FILE": "/app/context.Dockerfile",
"PLATFORM": "linux/amd64",
},
execution_requirements = {
"local": "1",
"requires-network": "1",
},
target_compatible_with = TARGET_COMPATIBLE_WITH,
)

oci_image(
name = "image_context",
base = ":base_context",
)

assert_oci_config(
name = "assert_metadata_context",
cmd_eq = ["/app/src/say.py"],
entrypoint_eq = None,
image = ":image_context",
)

assert_oci_image_command(
name = "assert_cow_says_moo_context",
args = [
"python",
"/app/src/say.py",
],
exit_code_eq = 0,
image = ":image_context",
output_eq = """\
____
| moo! |
====
\\
\\
^__^
(oo)\\_______
(__)\\ )\\/\\
||----w |
|| ||
""",
)
2 changes: 1 addition & 1 deletion examples/dockerfile/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.11.9-bullseye
FROM python:3.11.9-bullseye@sha256:64da8e5fd98057b05db01b49289b774e9fa3b81e87b4883079f6c31fb141b252

ARG DEBIAN_FRONTEND=noninteractive

Expand Down
14 changes: 9 additions & 5 deletions examples/dockerfile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ STOP before committing this atrocity. Here's some good reasons why you should no
- Building the same Dockerfile one month apart will yield different results.
- `FROM python:3.11.9-bullseye` is non-producible.

So you have chosen to walk this path... one thing that can be tricky is to get Bazel artifacts into the build context of Docker. Docker does not like symlinks or backtracking out of the build context. However, we can provide a complete context using [a tar file and piping it into BuildX](https://docs.docker.com/build/concepts/context/#local-tarballs). Since we have full control over tar layout in Bazel, we now have full control over the build context!

This technique offers an easy migration path from `container_run_and_commit` in `rules_docker`, but use it sparingly.

# Resources

https://reproducible-builds.org/
https://github.com/bazel-contrib/rules_oci/issues/35#issuecomment-1285954483
https://github.com/bazel-contrib/rules_oci/blob/main/docs/compare_dockerfile.md
https://github.com/moby/moby/issues/43124
https://medium.com/nttlabs/bit-for-bit-reproducible-builds-with-dockerfile-7cc2b9faed9f
- https://reproducible-builds.org/
- https://github.com/bazel-contrib/rules_oci/issues/35#issuecomment-1285954483
- https://github.com/bazel-contrib/rules_oci/blob/main/docs/compare_dockerfile.md
- https://github.com/moby/moby/issues/43124
- https://medium.com/nttlabs/bit-for-bit-reproducible-builds-with-dockerfile-7cc2b9faed9f
12 changes: 12 additions & 0 deletions examples/dockerfile/buildx_wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash

set -o errexit -o nounset -o pipefail

$BUILDX \
build - \
--no-cache \
--builder $BUILDER_NAME \
--file $DOCKER_FILE \
--platform $PLATFORM \
--output=type=oci,tar=false,dest=$OUTPUT_DIR \
< $BUILDX_CONTEXT
15 changes: 15 additions & 0 deletions examples/dockerfile/context.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM python:3.11.9-bullseye@sha256:64da8e5fd98057b05db01b49289b774e9fa3b81e87b4883079f6c31fb141b252

ARG DEBIAN_FRONTEND=noninteractive

ENV LC_ALL=C.UTF-8 \
LANG=C.UTF-8 \
LANGUAGE=C.UTF-8

ADD /app /app

WORKDIR /app

RUN pip install --root-user-action=ignore -r requirements.txt

CMD ["/app/src/say.py"]
1 change: 1 addition & 0 deletions examples/dockerfile/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cowsay==6.1.0