Build, Test, and Run Go Microservices with Bazel

In my previous article, I talked about Go microservices with monorepo. I also mentioned Bazel to build and test them. In this article, let me show you how to use Bazel and Gazelle for it.

Bazel is a free software tool used for the automation of building and testing software. The company Google uses the build tool Blaze internally and released an open-sourced port of the Blaze tool as Bazel, named as an anagram of Blaze.

Bazel supports many programming languages such as Java, C++, Go, Android, and iOS. We only need to put the WORKSPACE file on the root of the project, then let Gazelle, a Bazel build file generator for Bazel projects, generate BAZEL.build files for your project.

Installation

First, install Bazelisk, the recommended way to install Bazel.

macOS

brew install bazelisk

Windows

choco install bazelisk

Linux

  1. Go to https://github.com/bazelbuild/bazelisk/releases.
  2. Download the binary file. Eg; wget https://github.com/bazelbuild/bazelisk/releases/download/v1.16.0/bazelisk-linux-amd64.
  3. Move the binary file to the system. Eg; sudo mv bazelisk-linux-amd64 /usr/local/bin/bazel.
  4. Register the binary to the system path. Eg; export PATH="/usr/local/bin/bazel:$PATH", you can put this code at the end of your ~/.bash_profile file.

Node Package Manager

npm install -g @bazel/bazelisk

Go

  1. go install github.com/bazelbuild/bazelisk@latest.
  2. export PATH=$PATH:$(go env GOPATH)/bin. You can put this code at the end of your ~/.bash_profile / ~/.zprofile file.

WORKSPACE

Create a WORKSPACE file on your root project. We're going to run Gazelle with Bazel. You can visit Gazelle GitHub for more information. Here is a WORKSPACE that I used.

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

http_archive(
    name = "io_bazel_rules_go",
    sha256 = "56d8c5a5c91e1af73eca71a6fab2ced959b67c86d12ba37feedb0a2dfea441a6",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.37.0/rules_go-v0.37.0.zip",
        "https://github.com/bazelbuild/rules_go/releases/download/v0.37.0/rules_go-v0.37.0.zip",
    ],
)

http_archive(
    name = "bazel_gazelle",
    sha256 = "ecba0f04f96b4960a5b250c8e8eeec42281035970aa8852dda73098274d14a1d",
    urls = [
        "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.29.0/bazel-gazelle-v0.29.0.tar.gz",
        "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.29.0/bazel-gazelle-v0.29.0.tar.gz",
    ],
)


load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies")
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository")

############################################################
# Define your own dependencies here using go_repository.
# Else, dependencies declared by rules_go/gazelle will be used.
# The first declaration of an external repository "wins".
############################################################

go_rules_dependencies()

go_register_toolchains(version = "1.19.5")

gazelle_dependencies()

BUILD.bazel

Now, let's create BUILD.bazel files. We only need to create a BUILD.bazel file here and let Gazelle build the rest. Still on the root project, here was my BUILD.bazel.

load("@bazel_gazelle//:def.bzl", "gazelle")

# gazelle:prefix github.com/aristorinjuang/microservices-go
gazelle(name = "gazelle")

You can replace the after prefix with your Git repository, then run bazel run //:gazelle. Gazelle can detect your project and write BAZEL.build based on it. We don't need to worry.

Test

Before we build the project, of course, we need to test them all first. For a single Go project or monolith, we can test it by running the command go test ./.... On a project of microservices with monorepo, we need to run that command on every sub-project. With Bazel, we only need to run the command bazel test //... to test them all. Here was my result.

INFO: Analyzed 16 targets (0 packages loaded, 0 targets configured).
INFO: Found 13 targets and 3 test targets...
INFO: Elapsed time: 0.417s, Critical Path: 0.02s
INFO: 1 process: 1 internal.
INFO: Build completed successfully, 1 total action
//article/pkg:pkg_test                                          (cached) PASSED in 0.9s
//image/pkg:pkg_test                                            (cached) PASSED in 1.9s
//user/pkg:pkg_test                                             (cached) PASSED in 1.4s

Executed 0 out of 3 tests: 3 tests pass.
There were tests whose specified size is too big. Use the --test_verbose_timeout_warnings command line option to see which ones these are.

Build

For a single Go project or monolith, we can build it by running the command go build -o ./article/cmd/app ./article/cmd/app as an example. On a project of microservices with monorepo, to build all services, we only need to run bazel build //....

Bazel will generate four folders that we can ignore on Git:

Let's say we're looking for the binary of the Article service. We can find it on bazel-bin/article/cmd/app/app_/app.

Run

Let's say we want to run the Article service with Bazel. We can do it by running the command bazel run //article/cmd/app:app.

You can find the completed code for this article and the previous one at https://github.com/aristorinjuang/microservices-go.

Related Articles