Getting Started with the Go Chainguard Image

Tutorial on the distroless Go Chainguard Image

The Go images based on Wolfi and maintained by Chainguard provide distroless images that are suitable for building Go workloads.

Chainguard offers a minimal runtime image designed for running Go workloads, and a development image that contains a shell and the standard Go build tooling.

We’ll demonstrate two ways that you can build the Go image. The first example will show how to build the Go Chainguard Image with ko. ko enables you to build images from Go programs and push them to container registries without requiring a Dockerfile. The second example will show how to create a multi-stage Docker build that uses the glibc-dynamic runtime image along with the Go Chainguard Image.

If you would like to follow along with both examples, you’ll need both ko and Docker installed, which you can achieve by following the official installation guides for your setup:

Before building the image, follow the prerequisite step below to set up a demo application.

What is distroless Distroless images are minimalist container images containing only essential software required to build or execute an application. That means no package manager, no shell, and no bloat from software that only makes sense on bare metal servers.
What is Wolfi Wolfi is a community Linux undistro created specifically for containers. This brings distroless to a new level, including additional features targeted at securing the software supply chain of your application environment: comprehensive SBOMs, signatures, daily updates, and timely CVE fixes.
Chainguard Images Chainguard Images are a mix of distroless and development images based on Wolfi. Nightly builds make sure images are up-to-date with the latest package versions and patches from upstream Wolfi.

Prerequisite — Setting up a Demo Application in Go

We’ll start by creating a basic command-line Go application to serve as a demo. This image will use the go-containerregistry library to print out the digest of the latest Go container image.

First, create a directory for your app. You can use any meaningful name and path for you, our example will use go-digester/.

mkdir ~/go-digester/ && cd $_

Next, initialize your app by creating a new module and installing dependencies.

go mod init go-digester
go mod tidy
go get github.com/google/go-containerregistry
go get github.com/google/go-containerregistry/pkg/v1/remote
go get github.com/google/go-containerregistry/pkg/name

With these in place, create a file to serve as the entrypoint. We’ll use main.go. You can edit this file in whatever code editor you would like. We’ll use Nano as an example.

nano main.go

The following Go code defines a light CLI app that prints the digest of the latest Go Chainguard Image:

package main

import (
	"fmt"

	"github.com/google/go-containerregistry/pkg/name"
	"github.com/google/go-containerregistry/pkg/v1/remote"
)

func main() {
	image := "cgr.dev/chainguard/go"
	ref, err := name.ParseReference(image)
	if err != nil {
		panic(err)
	}
	desc, err := remote.Get(ref)
	if err != nil {
		panic(err)
	}
	fmt.Printf("The digest of %s is %s\n", image, desc.Digest)
}

Save and close the file. Then, you can run the code with the go command to be sure you are satisfied with the functionality.

go run .

The output should be the printed digest of the latest Go Chainguard Image.

The digest of cgr.dev/chainguard/go is sha256:d4a845840e227b5454b67d00ee6ccdaaf2954eab88f47fa9ecac946011513db0

With the program running as expected, you’re ready to move onto either or both examples of building the image.

Example 1 — Minimal Go Chainguard Image Built with ko

In this example, we’ll build a distroless Go Chainguard Image with ko from the demo app we created in the prerequisite step.

ko offers fast container image builds for Go applications. It builds images by executing go build on your local machine, and because of this, you are not required to have Docker installed to build the image. Additionally, ko produces SBOMs by default, supporting a holistic approach to software security.

First, you’ll need to set up the environment variable (KO_DOCKER_REPO) that identifies where ko should push images that it builds. This is usually a remote registry like GitHub Container registry or Docker Hub, but you can publish to your local machine for testing and demonstration purposes.

export KO_DOCKER_REPO=ko.local

Next, ensuring that you are in the same directory as your main.go file, build the image with ko:

ko build .

Once you run this command, you’ll receive output similar to the following.

2023/03/05 17:44:03 Using base distroless.dev/static:latest@sha256:4a5fda9b2aa55b49971d220cc4ba3d73998084e37e437f23721836112015c2d4 for go-digester
...
2023/03/05 17:44:05 Added tag latest
ko.local/go-digester-edc0ed689c7fb820a565f76425bed013:33523bfd5a136392f92905ffe5a076681baac3060d48c2b9ff2f787a7cc90dfd

At this point, your image is built. Because the output of ko build is an image reference, you can pass it to other tools like Docker. You can learn more about deployment with ko and Kubernetes integration by reading the respective documentation on the official site.

We’ll demonstrate running the above built image with Docker.

Note: To follow along, be sure that you copy and paste the last line of output from your last command that begins ko.local/go-digester-...

docker run --rm ko.local/go-digester-edc0ed689c7fb820a565f76425bed013:33523bfd5a136392f92905ffe5a076681baac3060d48c2b9ff2f787a7cc90dfd

Here, you’ll expect to receive the same output as before that shows the digest of the image.

The digest of cgr.dev/chainguard/go is sha256:d4a845840e227b5454b67d00ee6ccdaaf2954eab88f47fa9ecac946011513db0

Now that you have built the Go Chainguard Image with ko, you can continue onto advanced usage, or you can complete the multistage setup in Example 2.

Example 2 — Multistage Docker Build for Go Chainguard Image

Because Go applications are compiled and the toolchain is not typically required in a runtime image, we suggest the usage of a multi-stage Docker build that uses the glibc-dynamic runtime image. In some cases, the static image may be used as well for an even smaller image, but extra care must be taken to ensure the Go binary is statically-compiled.

For this multi-stage build, we’ll use two FROM lines in our Dockerfile. To create this Dockerfile, you can use any code editor of your choice, we’ll use Nano for demonstation purposes.

nano Dockerfile

The following Dockerfile will:

  1. Start a build stage based on the go:latest image;
  2. Declare the working directory;
  3. Copy the script and the text file that’s being read;
  4. Set up the application as entry point for this image.
FROM cgr.dev/chainguard/go AS builder
COPY . /app
RUN cd /app && go build -o go-digester .

FROM cgr.dev/chainguard/glibc-dynamic
COPY --from=builder /app/go-digester /usr/bin/
CMD ["/usr/bin/go-digester"]

Save the file when you’re finished.

You can now build the image with Docker. If you receive an error, try again with sudo.

docker build . -t digester

Once the build is finished, run the image.

docker run --rm digester

You should get output similar to what you got before.

The digest of cgr.dev/chainguard/go is sha256:d4a845840e227b5454b67d00ee6ccdaaf2954eab88f47fa9ecac946011513db0

You have successfully completed the multi-stage Go Chainguard Image. At this point, you can continue to advanced usage.

Advanced Usage

If your project requires a more specific set of packages that aren't included within the general-purpose Go Chainguard Image, you'll first need to check if the package you want is already available on the wolfi-os repository.

Note: If you're building on top of an image other than the wolfi-base image, the image will run as a non-root user. Because of this, if you need to install packages with apk install you need to use the USER root directive.

If the package is available, you can use the wolfi-base image in a Dockerfile and install what you need with apk, then use the resulting image as base for your app. Check the "Using the wolfi-base Image" section of our images quickstart guide for more information.

If the packages you need are not available, you can build your own apks using melange. Please refer to this guide for more information.

Last updated: 2024-01-24 11:07