Bazel docker container image not copying file

10/25/2021

I'm trying to emulate theRUN step you would fine in a docker file shown below in the Bazel docker container image rule but since the container_image rule does not have a copy function I'm trying to use what is available.

RUN GRPC_HEALTH_PROBE_VERSION=v0.3.1 && \
    wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
    chmod +x /bin/grpc_health_probe
  go_image(
      name = "go_auth_image",
      embed = [":go-auth_lib"],
      visibility = ["//visibility:public"],
  )

  container_image(
      name = "go_auth_api",
      visibility = ["//visibility:public"],
      base = ":go_auth_image",
      ports = ["5001", "5002"],
      files = ["grpc_health_probe-linux-amd64"],
      symlinks = {
          "grpc_health_prob-linux-amd64": "/bin/grpc_health_probe",
      },
      cmd = [
          "apk add --no-cache git",
          "chmod +x /bin/grpc_health_probe",
      ],
  )

note: file_map does not seem to be a parameter for container_image

When I deploy this image to k8s the the image runs fine but the when describing the pod the liveness probe (described below) fails.

          livenessProbe:
            exec:
              command: ["/bin/grpc_health_probe", "-addr=:5002"]
            initialDelaySeconds: 5
            periodSeconds: 30
            timeoutSeconds: 2
            successThreshold: 1
            failureThreshold: 3
  Warning  Unhealthy  6m42s                  kubelet            Liveness probe errored: rpc error: code = Unknown desc = failed to exec in container: failed to start exec "b6c89b7ec907e572f80be59e8d4b5cad6535a3479d67a3563a09e0d1d2f7ca03": OCI runtime exec failed: exec failed: container_linux.go:370: starting container process caused: exec: "/bin/grpc_health_probe": stat /bin/grpc_health_probe: no such file or directory: unknown

What is the correct way to setup this probe with Bazel (I've confirmed this works with a Dockerfile setup)

-- Adgezaza
bazel
bazel-rules
docker
kubernetes

3 Answers

10/26/2021

Thanks to @Brian-Silverman help I was able to triage all my issues and landed with this solution.

  go_image(
      name = "go_auth_image",
      embed = [":go-auth_lib"],
      visibility = ["//visibility:public"],
  )

  GRPC_HEALTH_PROBE_VERSION = "v0.4.5"


  container_run_and_commit_layer(
      name = "health_probe",
      image = "@grpc_health_image//image",
      commands = [
          "apk add --no-cache git",
          "wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/%s/grpc_health_probe-linux-amd64" % GRPC_HEALTH_PROBE_VERSION,
          "chmod +x /bin/grpc_health_probe",
      ],
  )

  container_image(
      name = "go_auth_api",
      base = ":go_auth_image",
      layers = [":health_probe",],
      visibility = ["//visibility:public"],
      ports = ["5001"],
  )
-- Adgezaza
Source: StackOverflow

12/21/2021

I've responded to your github issue, but posting the answer here as well for visibility: I would accomplish this in Bazel by making a TAR which contains the grpc_health_probe binary in the location I would like and add that in the tars section of a container_image rule. I try to avoid the RUN-like rules, because they introduce a dependency on the docker toolchain and they are not guaranteed to be hermetic/reproducible. There's an open issue about this here: https://github.com/bazelbuild/rules_docker/issues/1961.

You can declare the binary in question as a dependency in your WORKSPACE (or a macro invoked by your WORKSPACE) like so:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_file")
GRPC_HEALTH_PROBE_VERSION = "v0.4.5"
GRPC_HEALTH_PROBE_SHA256 = "8699c46352d752d8f533cae72728b0e65663f399fc28fb9cd854b14ad5f85f44"
http_file(
    name = "grpc_health_probe_linux_amd64",
    executable = True,  # Here is your `chmod +x` from the `container_run_and_commit`
    sha256 = GRPC_HEALTH_PROBE_SHA256,
    downloaded_file_path = "grpc_health_probe",  # This is necessary to remove `-linux-amd64` from the end of the file name.
    urls = ["https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/{}/grpc_health_probe-linux-amd64".format(GRPC_HEALTH_PROBE_VERSION)],
)

Having done that, and included the rules_docker toolchain configuration, you can then add this binary to a container image with a build file that looks like the following:

load("@rules_pkg//:pkg.bzl", "pkg_tar")
load("@io_bazel_rules_docker//container:container.bzl", "container_image")

# This rule creates a TAR file containing the executable file
# /bin/grpc_health_probe.
pkg_tar(
    name = "health_probe_linux_amd64",
    srcs = ["@grpc_health_probe_linux_amd64//file"],
    package_dir = "/bin",  # specifying the path of the file
)

# Here, we add that health probe to our base image. In this case
# I've used the ubuntu image for a minimal example.
container_image(
    name = "health_probe_image",
    base = "YOUR_BASE_IMAGE_HERE",
    tars = [":health_probe_linux_amd64"],
)

Github reply: https://github.com/bazelbuild/rules_docker/issues/1943#issuecomment-998400122

-- hxtk
Source: StackOverflow

10/26/2021

container_run_and_commit is the closest equivalent to RUN. Something like this is the direct equivalent:

load("@io_bazel_rules_docker//docker/util:run.bzl", "container_run_and_commit")

GRPC_HEALTH_PROBE_VERSION = "v0.3.1"

container_run_and_commit(
    name = "install_stuff",
    image = ":go_auth_image.tar",
    commands = [
        "wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/%s/grpc_health_probe-linux-amd64" % GRPC_HEALTH_PROBE_VERSION,
        "chmod +x /bin/grpc_health_probe",
    ],
)


container_image(
    name = "go_auth_api",
    visibility = ["//visibility:public"],
    base = ":install_stuff",
    ... # everything else you're doing with container_image
)

It runs the commands a builds a new image, and then uses container_image to add things to the result.

However, doing more of the build with bazel will make better use of bazel's cache and be more reproducible. I think that's what you're doing with the grpc_health_probe-linux-amd64 source file. That approach looks something like this:

load("@io_bazel_rules_docker//docker/util:run.bzl", "container_run_and_commit")

container_image(
    name = "add_stuff",
    base = ":go_auth_image",
    ports = ["5001", "5002"],
    files = ["grpc_health_probe-linux-amd64"],
    symlinks = {
        "grpc_health_prob-linux-amd64": "/bin/grpc_health_probe",
    },
)

container_run_and_commit(
    name = "go_auth_api",
    visibility = ["//visibility:public"],
    image = ":add_stuff.tar",
    commands = [
        "apk add --no-cache git",
        "chmod +x /bin/grpc_health_probe",
    ],
)

That uses container_image to add things first, and then runs the commands afterwards.

Also, instead of running chmod +x, you can use pkg_tar to package the file+symlink (it has a symlinks attribute just like container_image), and then set mode = 0755. container_image.tars will take the tar file and add it to the image. In general, pkg_tar gives a lot of flexibility for building up files, and container_image takes a subset of its functionality directly for simple use cases.

container_image.cmd is the equivalent of CMD in a Dockerfile. It's just setting the information when the container is used, not doing anything while building it. I don't think you want to use it at all for this.

-- Brian Silverman
Source: StackOverflow