Documentation

Docker support for Bazel

Rules

Overview

These build rules are used for building Docker images. Such images are easy to modify and deploy system image for deploying application easily on cloud providers.

As traditional Dockerfile-based docker builds effectively execute a series of commands inside of Docker containers, saving the intermediate results as layers; this approach is unsuitable for use in Bazel for a variety of reasons.

The docker_build rule constructs a tarball that is compatible with docker save/load, and creates a single layer out of each BUILD rule in the chain.

Basic Example

Consider the following BUILD file in //third_party/debian:

load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build")

filegroup(
    name = "ca_certificates",
    srcs = ["ca_certificates.deb"],
)

# Example when you have all your dependencies in your repository.
# We have an example on how to fetch them from the web later in this
# document.
filegroup(
    name = "openjdk-7-jre-headless",
    srcs = ["openjdk-7-jre-headless.deb"],
)

docker_build(
    name = "wheezy",
    tars = ["wheezy.tar"],
)

The wheezy target in that BUILD file roughly corresponds to the Dockerfile:

FROM scratch
ADD wheezy.tar /

You can then build up subsequent layers via:

docker_build(
    name = "base",
    base = "//third_party/debian:wheezy",
    debs = ["//third_party/debian:ca_certificates"],
)

docker_build(
    name = "java",
    base = ":base",
    debs = ["//third_party/debian:openjdk-7-jre-headless"],
)

Image Configuration

You can set image configuration on these same rules by simply adding (supported) arguments to the rule, for instance:

docker_build(
    name = "my-layer",
    entrypoint = ["foo", "bar", "baz"],
    ...
)

Will have a similar effect as the Dockerfile construct:

ENTRYPOINT ["foo", "bar", "baz"]

For the set of supported configuration options see here

Using

Suppose you have a docker_build target //my/image:helloworld:

docker_build(
    name = "helloworld",
    ...
)

You can build this with bazel build my/image:helloworld.tar. This will produce the file bazel-genfiles/my/image/helloworld.tar. You can load this into my local Docker client by running docker load -i bazel-genfiles/my/image/helloworld.tar, or simply bazel run my/image:helloworld (this last command only update the changed layers and thus is faster).

Upon success you should be able to run docker images and see:

REPOSITORY          TAG                 IMAGE ID       ...
bazel/my_image      helloworld          d3440d7f2bde   ...

You can now use this docker image with the name bazel/my_image:helloworld or tag it with another name, for example: docker tag bazel/my_image:helloworld gcr.io/my-project/my-awesome-image:v0.9

You can do all that at once with specifying the tag on the command line of bazel run: bazel run my/image:helloworld gcr.io/my-project/my-awesome-image:v0.9

Nota Bene: the docker images command will show a really old timestamp because docker_build removes all timestamps from the build to make it reproducible.

Pulling images and deb files from the internet

If you do not want to check in base image in your repository, you can use external repositories. For instance, you could create various layer with external labels:

load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build")

docker_build(
    name = "java",
    base = "@docker_debian//:wheezy",
    debs = ["@openjdk_7_jre_headless//file"],
)

Using the WORKSPACE file to add the actual files:

new_http_archive(
    name = "docker_debian",
    url = "https://codeload.github.com/tianon/docker-brew-debian/zip/e9bafb113f432c48c7e86c616424cb4b2f2c7a51",
    build_file = "debian.BUILD",
    type = "zip",
    sha256 = "515d385777643ef184729375bc5cb996134b3c1dc15c53acf104749b37334f68",
)

http_file(
   name = "openjdk_7_jre_headless",
   url = "http://security.debian.org/debian-security/pool/updates/main/o/openjdk-7/openjdk-7-jre-headless_7u79-2.5.5-1~deb7u1_amd64.deb",
   sha256 = "b632f0864450161d475c012dcfcc37a1243d9ebf7ff9d6292150955616d71c23",
)

With the following debian.BUILD file:

load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build")

# Extract .xz files
genrule(
    name = "wheezy_tar",
    srcs = ["docker-brew-debian-e9bafb113f432c48c7e86c616424cb4b2f2c7a51/wheezy/rootfs.tar.xz"],
    outs = ["wheezy_tar.tar"],
    cmd = "cat $< | xzcat >$@",
)

docker_build(
    name = "wheezy",
    tars = [":wheezy_tar"],
    visibility = ["//visibility:public"],
)

Future work

In the future, we would like to provide better integration with docker repositories: pull and push docker image.

docker_build

docker_build(name, base, data_path, directory, files, mode, tars, debs, symlinks, entrypoint, cmd, env, labels, ports, volumes, workdir, repository)
Implicit output targets
name.tar The full Docker image

A full Docker image containing all the layers, identical to what docker save would return. This is only generated on demand.

name-layer.tar An image of the current layer

A Docker image containing only the layer corresponding to that target. It is used for incremental loading of the layer.

Note: this target is not suitable for direct consumption. It is used for incremental loading and non-docker rules should depends on the docker image (name.tar) instead.

name Incremental image loader

The incremental image loader. It will load only changed layers inside the Docker registry.

Attributes
name Name, required

A unique name for this rule.

base File, optional

The base layers on top of which to overlay this layer, equivalent to FROM.

data_path String, optional

Root path of the files.

The directory structure from the files is preserved inside the docker image but a prefix path determined by `data_path` is removed from the directory structure. This path can be absolute from the workspace root if starting with a `/` or relative to the rule's directory. A relative path may starts with "./" (or be ".") but cannot use go up with "..". By default, the `data_path` attribute is unused and all files are supposed to have no prefix.

directory String, optional

Target directory.

The directory in which to expand the specified files, defaulting to '/'. Only makes sense accompanying one of files/tars/debs.

files List of files, optional

File to add to the layer.

A list of files that should be included in the docker image.

mode String, default to 0555

Set the mode of files added by the files attribute.

tars List of files, optional

Tar file to extract in the layer.

A list of tar files whose content should be in the docker image.

debs List of files, optional

Debian package to install.

A list of debian packages that will be installed in the docker image.

symlinks Dictionary, optional

Symlinks to create in the docker image.

symlinks = { "/path/to/link": "/path/to/target", ... },

user String, optional

The user that the image should run as.

Because building the image never happens inside a docker container, this user does not affect the other actions (e.g., adding files).

entrypoint String or string list, optional

List of entrypoints to add in the image.

cmd String or string list, optional

List of commands to execute in the image.

env Dictionary from strings to strings, optional

Dictionary from environment variable names to their values when running the docker image.

env = { "FOO": "bar", ... },

labels Dictionary from strings to strings, optional

Dictionary from custom metadata names to their values. You can also put a file name prefixed by '@' as a value. Then the value is replaced with the contents of the file.

labels = { "com.example.foo": "bar", "com.example.baz": "@metadata.json", ... },

ports String list, optional

List of ports to expose.

volumes String list, optional

List of volumes to mount.

workdir String, optional

Initial working directory when running the docker image.

Because building the image never happens inside a docker container, this working directory does not affect the other actions (e.g., adding files).

repository String, default to `bazel`

The repository for the default tag for the image.

Image generated by `docker_build` are tagged by default to `bazel/package_name:target` for a `docker_build` target at `//package/name:target`. Setting this attribute to `gcr.io/dummy` would set the default tag to `gcr.io/dummy/package_name:target`.