Skip to content

Latest commit

 

History

History
210 lines (150 loc) · 5.67 KB

scala.md

File metadata and controls

210 lines (150 loc) · 5.67 KB

Images containing Scala applications

Users are typically migrating from scala_image in rules_docker.

You can request the *_deploy.jar output of a scala_binary target, which is a single, self-contained launcher that includes all the dependencies. This can then be added to a container with a base image such as gcr.io/distroless/java17 and then executed directly as java -jar <your jar>.

Example

For this example, we will use App.scala like below:

App.scala

object App {
  def main(args: Array[String]): Unit = {
    println("Hello, world!")
  }
}

In this example, I will not use bzlmod and fall back to the WORKSPACE file, as rules_scala doesn't support bzlmod yet. This file setups the rules_scala according to the documentation so that we can build scala targets. Next, it configures aspect_bazel_lib so that we can have access to tar rule needed later. Finally, it configures rules_oci and pulls the base image with Java 17.

WORKSPACE

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

http_archive(
    name = "bazel_skylib",
    sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
        "https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
    ],
)

load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")

bazel_skylib_workspace()

http_archive(
    name = "io_bazel_rules_scala",
    sha256 = "3b00fa0b243b04565abb17d3839a5f4fa6cc2cac571f6db9f83c1982ba1e19e5",
    strip_prefix = "rules_scala-6.5.0",
    url = "https://github.com/bazelbuild/rules_scala/releases/download/v6.5.0/rules_scala-v6.5.0.tar.gz",
)

load("@io_bazel_rules_scala//:scala_config.bzl", "scala_config")

scala_config(scala_version = "2.13.12")

load("@io_bazel_rules_scala//scala:scala.bzl", "scala_repositories")

scala_repositories()

load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")

rules_proto_dependencies()

rules_proto_toolchains()

load("@io_bazel_rules_scala//scala:toolchains.bzl", "scala_register_toolchains")

scala_register_toolchains()

load("@io_bazel_rules_scala//testing:scalatest.bzl", "scalatest_repositories", "scalatest_toolchain")

scalatest_repositories()

scalatest_toolchain()

http_archive(
    name = "aspect_bazel_lib",
    sha256 = "6d758a8f646ecee7a3e294fbe4386daafbe0e5966723009c290d493f227c390b",
    strip_prefix = "bazel-lib-2.7.7",
    url = "https://github.com/aspect-build/bazel-lib/releases/download/v2.7.7/bazel-lib-v2.7.7.tar.gz",
)

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

# Required bazel-lib dependencies

aspect_bazel_lib_dependencies()

# Register bazel-lib toolchains

aspect_bazel_lib_register_toolchains()

http_archive(
    name = "rules_oci",
    sha256 = "647f4c6fd092dc7a86a7f79892d4b1b7f1de288bdb4829ca38f74fd430fcd2fe",
    strip_prefix = "rules_oci-1.7.6",
    url = "https://github.com/bazel-contrib/rules_oci/releases/download/v1.7.6/rules_oci-v1.7.6.tar.gz",
)

load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies")

rules_oci_dependencies()

load("@rules_oci//oci:repositories.bzl", "LATEST_CRANE_VERSION", "oci_register_toolchains")

oci_register_toolchains(
    name = "oci",
    crane_version = LATEST_CRANE_VERSION,
)

# You can pull your base images using oci_pull like this:
load("@rules_oci//oci:pull.bzl", "oci_pull")

oci_pull(
    name = "distroless_java",
    digest = "sha256:161a1d97d592b3f1919801578c3a47c8e932071168a96267698f4b669c24c76d",
    image = "gcr.io/distroless/java17",
)

Now, let's create BUILD.bazel step by step. First, create a scala_binary target for our app. It is safe to add dependencies, but they were omitted here for simplicity.

BUILD.bazel

load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary")

scala_binary(
    name = "app",
    srcs = ["App.scala"],
    main_class = "App",
)

After that, we can package that binary into a layer using tar

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

tar(
    name = "layer",
    srcs = [":app_deploy.jar"],
)

Next, construct the image from base image and our new layer, using oci_image rule. The entrypoint is set to java -jar /app_deploy.jar so that the image can be run directly.

load("@rules_oci//oci:defs.bzl", "oci_image")

oci_image(
    name = "image",
    base = "@distroless_java",
    entrypoint = ["java", "-jar", "/app_deploy.jar"],
    tars = [":layer"],
)

Finally, create a tarball from oci_image that can be loaded by a runtime such as docker. We specify repo_tags so that the image can be loaded by a registry.

load("@rules_oci//oci:defs.bzl", "oci_load")

oci_load(
    name = "load",
    image = ":image",
    repo_tags = ["my-repository:latest"],
)

Test if it works:

$ bazel run //:load
...
$ docker run --rm my-repository:latest
Hello, world!

Complete BUILD.bazel file

BUILD.bazel

load("@io_bazel_rules_scala//scala:scala.bzl", "scala_binary")
load("@aspect_bazel_lib//lib:tar.bzl", "tar")
load("@rules_oci//oci:defs.bzl", "oci_image", "oci_tarball")

scala_binary(
    name = "app",
    srcs = ["App.scala"],
    main_class = "App",
)

tar(
    name = "layer",
    srcs = [":app_deploy.jar"],
)

oci_image(
    name = "image",
    base = "@distroless_java",
    entrypoint = ["java", "-jar", "/app_deploy.jar"],
    tars = [":layer"],
)

oci_tarball(
    name = "tarball",
    image = ":image",
    repo_tags = ["my-repository:latest"],
)