---
title: Go and Bazel to build containers
url: https://www.grahambrooks.com/post/2020-04-05-go-bazel-container/
date: 2020-04-05T00:00:00+00:00
author: Graham Brooks
tags:
- Go
- Continuous Delivery
- containers
---

# Go and Bazel to build containers

Yesterday I spent some time experimenting with building containerized Go applications. Writing a small restful web service in go is really quite straightforward and there are many better examples out there on the web.

What was interesting for me was seeing how Bazel supports a typical workflow building and running containerized web services. In this case using a small Go program.

Bazel is still relatively new but gaining in popularity. Over the last 6 months or so stability and functionality has really improved. Bazel really shines when combined with a mono-repo approach. For this example the bazel configuration feels more than required to achieve the result.

<div>

<div class="title">

Components used in Building the Go service container

</div>

- <a href="https://github.com/grahambrooks/go-bazel-container" icon:github[role="blue">https://github.com/grahambrooks/go-bazel-container</a> Project Source\]

- [Go](https://golang.org/)

- [Bazel Build](https://bazel.build/)

  - <a href="https://github.com/bazelbuild/rules_go" icon:github[role="blue">https://github.com/bazelbuild/rules_go</a> Bazel build rules for Go projects\]

  - <a href="https://github.com/bazelbuild/bazel-gazelle" icon:github[role="blue">https://github.com/bazelbuild/bazel-gazelle</a> Gazelle go dependency management\]

  - <a href="https://github.com/bazelbuild/rules_docker" icon:github[role="blue">https://github.com/bazelbuild/rules_docker</a> Bazel build rules for docker\]

</div>

<div>

<div class="title">

Pre-requisites

</div>

- Install Docker (required for both local and container builds) [Docker](https://docs.docker.com/install/)

- Bazel (required for local build) [Bazel](https://docs.bazel.build/versions/master/install.html)

</div>

<div>

<div class="title">

Up and Running

</div>

    > docker images #<1>
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

    > bazel run //api:api-container --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64 # <2>
    INFO: Analyzed target //api:api-container (0 packages loaded, 0 targets configured).
    INFO: Found 1 target...
    Target //api:api-container up-to-date:
      bazel-bin/api/api-container-layer.tar
    INFO: Elapsed time: 0.605s, Critical Path: 0.01s
    INFO: 0 processes.
    INFO: Build completed successfully, 1 total action
    INFO: Build completed successfully, 1 total action
    932da5156413: Loading layer [==================================================>]  3.062MB/3.062MB
    dffd9992ca39: Loading layer [==================================================>]  15.44MB/15.44MB
    43babe50bd4f: Loading layer [==================================================>]  6.246MB/6.246MB
    84ff92691f90: Loading layer [==================================================>]  10.24kB/10.24kB
    Loaded image ID: sha256:525fa22e2c6beccba45e2a0dbc7370d7d808342785d6d8b825096ce0a338279f
    Tagging 525fa22e2c6beccba45e2a0dbc7370d7d808342785d6d8b825096ce0a338279f as bazel/api:api-container

    > docker images # <3>
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    bazel/api           api-container       525fa22e2c6b        50 years ago        23.1MB

    > docker run -p 8080:8080 bazel/api:api-container <4>

</div>

<div class="callout-list">

1.  Current list of installed images - none

2.  Run bazel to create and tag the container. Not the target flag - because the api will be running in a linux container we need to build a binary that will work in the environement

3.  The container image is now available in my local docker install.

4.  Fire up the application container and expose port 8080

</div>

<div>

<div class="title">

Testing the container

</div>

    > curl http://localhost:8080/hello/Everyone
    {"Message":"Hello","Name":"Everyone"}

</div>

This short blog records my experiments in building a container using Bazel containing a Go application.

# The API

is a simple JSON service that accepts /hello/:name and returns a json response with a message and the name given in the request URL:

<div>

<div class="title">

The full program

</div>

    include::https://raw.githubusercontent.com/grahambrooks/go-bazel-container/master/api/main.go[]

</div>

<div>

<div class="title">

Expected JSON response

</div>

    {
      "mmessage": "Hello",
      "name": ":name"
    }

</div>

# Bazel

Bazel is a little more difficult to explain.

<div>

<div class="title">

Project layout

</div>

    .
    ├── BUILD.bazel <2>
    ├── Dockerfile <6>
    ├── WORKSPACE <1>
    ├── api
    │   ├── BUILD.bazel <3>
    │   └── main.go <4>
    ├── go.mod <5>
    └── go.sum

</div>

<div class="callout-list">

1.  The project workspace. Loads the Bazel [Skylark](https://docs.bazel.build/versions/master/skylark/language.html) components and go dependencies for the project

2.  Helper build for Gazelle dependency management

3.  The API build script including targets for the API, container image and container

4.  API service source

5.  Go module dependencies. If the go.mod file is changed you can update the bazel dependencies with `bazel` `run` `//:gazelle — update-repos` `-from_file=go.mod`

6.  Containerized Bazel build

</div>

It is both a build system and an environment for plugins supporting application build.

<div>

<div class="title">

Bazel build rules for go

</div>

- <https://github.com/bazelbuild/rules_go>

</div>

[Gazelle](https://github.com/bazelbuild/bazel-gazelle) is used to generate Bazel dependencies from the go.mod file. These dependencies appear in the bazel WORKSPACE file in the root of the project and then referenced in the BUILD.bazel files for each module.

# Building a container in a container

Building software in a container has several advantages. Docker container images are just files and those files can be created without docker. The Bazel docker components don’t need docker installed to build an image. Using the same command line

<div>

<div class="title">

Dockerfile

</div>

    include::https://raw.githubusercontent.com/grahambrooks/go-bazel-container/master/Dockerfile[]

</div>

<div>

<div class="title">

Running the containerized build

</div>

    > docker build . # <1>
    Sending build context to Docker daemon  107.5kB
    Step 1/4 : FROM l.gcr.io/google/bazel:latest
     ---> dc530fa1c5ce
    Step 2/4 : WORKDIR /build
     ---> Using cache
     ---> 9e3158821eed
    Step 3/4 : COPY . /build
     ---> 3d309ccc2b35
    Step 4/4 : RUN bazel run //api:api-container <2>
     ---> Running in 7d632d5bb256
    Extracting Bazel installation...
    Starting local Bazel server and connecting to it...

    ... omnitted logs ...

    [0 / 35] [Prepa] Creating source manifest for @io_bazel_rules_docker//container/go/cmd/create_image_config:create_image_config [for host]
    [36 / 45] GoStdlib external/io_bazel_rules_go/linux_amd64_pure_stripped/stdlib%/pkg; 4s processwrapper-sandbox
    [36 / 45] GoStdlib external/io_bazel_rules_go/linux_amd64_pure_stripped/stdlib%/pkg; 14s processwrapper-sandbox
    Target //api:api-container up-to-date:
      bazel-bin/api/api-container-layer.tar <3>
    INFO: Elapsed time: 64.211s, Critical Path: 19.27s
    INFO: 27 processes: 27 processwrapper-sandbox.
    INFO: Build completed successfully, 45 total actions
    INFO: Running command line: bazel-bin/api/api-container.executable
    INFO: Build completed successfully, 45 total actions
    Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? <4>
    The command '/bin/sh -c bazel run //api:api-container --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64' returned a non-zero code: 1

</div>

<div class="callout-list">

1.  Running docker instead of bazel to build the project

2.  Build and runtimes match so we don’t have to specify the platform for the bazel build

3.  The container file is created but because we are running in a container without docker the

4.  image is not loaded or tagged

</div>

The bazel docker rules provide ways to upload the built image - something to look into next …

# Wrapping up

There’s a lot more to experiment with and this article only scratches the surface.

- Building container images without having to install Docker simplifies the build process

- Running the build in a container minimizes local development configuration management and keeps project isolated. This is particularly important when projects depend on different runtimes.
