Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 139 additions & 10 deletions content/guides/golang/build-images.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ func IntMin(a, b int) int {

To build a container image with Docker, a `Dockerfile` with build instructions is required.

Before creating a Dockerfile, you need to choose a base image. You can either use the [Go Docker Official Image](https://hub.docker.com/_/golang) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog).

Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/).

{{< tabs >}}
{{< tab name="Using Docker Hardened Images" >}}
Docker Hardened Images (DHIs) are available for Go on [Docker Hub](https://hub.docker.com/hardened-images/catalog/dhi/golang). Unlike using the Docker Official Image, you must first mirror the Go image into your organization and then use it as your base image. Follow the instructions in the [DHI quickstart](/dhi/get-started/) to create a mirrored repository for Go.

Mirrored repositories must start with `dhi-`, for example: `FROM <your-namespace>/dhi-golang:<tag>`. In the following Dockerfile, the `FROM` instruction uses `<your-namespace>/dhi-golang:1.25` as the base image.

Begin your `Dockerfile` with the (optional) parser directive line that instructs BuildKit to
interpret your file according to the grammar rules for the specified version of the syntax.

Expand All @@ -107,13 +117,38 @@ You then tell Docker what base image you would like to use for your application:
```dockerfile
# syntax=docker/dockerfile:1

FROM golang:1.19
FROM <your-namespace>/dhi-golang:1.25
```

Docker images can be inherited from other images. Therefore, instead of creating
your own base image from scratch, you can use the DHI Go image that already
has all necessary tools and libraries to compile and run a Go application.

{{< /tab >}}
{{< tab name="Using the official image" >}}

Using the Docker Official Image is straightforward. In the following Dockerfile, you'll notice that the `FROM` instruction uses `golang:1.25` as the base image.

This is the official image for Go. This image is [available on the Docker Hub](https://hub.docker.com/_/golang).

Begin your `Dockerfile` with the (optional) parser directive line that instructs BuildKit to
interpret your file according to the grammar rules for the specified version of the syntax.

You then tell Docker what base image you would like to use for your application:

```dockerfile
# syntax=docker/dockerfile:1

FROM golang:1.25
```

Docker images can be inherited from other images. Therefore, instead of creating
your own base image from scratch, you can use the official Go image that already
has all necessary tools and libraries to compile and run a Go application.

{{< /tab >}}
{{< /tabs >}}

> [!NOTE]
>
> If you are curious about creating your own base images, you can check out the following section of this guide: [creating base images](/manuals/build/building/base-images.md#create-a-base-image).
Expand Down Expand Up @@ -171,7 +206,7 @@ directory inside the image.
RUN go mod download
```

At this point, you have a Go toolchain version 1.19.x and all your Go
At this point, you have a Go toolchain version 1.25.x and all your Go
dependencies installed inside the image.

The next thing you need to do is to copy your source code into the image. You’ll
Expand Down Expand Up @@ -209,10 +244,46 @@ CMD ["/docker-gs-ping"]

Here's the complete `Dockerfile`:

{{< tabs >}}
{{< tab name="Using Docker Hardened Images" >}}

```dockerfile
# syntax=docker/dockerfile:1

FROM <your-namespace>/dhi-golang:1.25

# Set destination for COPY
WORKDIR /app

# Download Go modules
COPY go.mod go.sum ./
RUN go mod download

# Copy the source code. Note the slash at the end, as explained in
# https://docs.docker.com/reference/dockerfile/#copy
COPY *.go ./

# Build
RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping

# Optional:
# To bind to a TCP port, runtime parameters must be supplied to the docker command.
# But we can document in the Dockerfile what ports
# the application is going to listen on by default.
# https://docs.docker.com/reference/dockerfile/#expose
EXPOSE 8080

# Run
CMD ["/docker-gs-ping"]
```

{{< /tab >}}
{{< tab name="Using the official image" >}}

```dockerfile
# syntax=docker/dockerfile:1

FROM golang:1.19
FROM golang:1.25

# Set destination for COPY
WORKDIR /app
Expand All @@ -239,6 +310,9 @@ EXPOSE 8080
CMD ["/docker-gs-ping"]
```

{{< /tab >}}
{{< /tabs >}}

The `Dockerfile` may also contain comments. They always begin with a `#` symbol,
and must be at the beginning of a line. Comments are there for your convenience
to allow documenting your `Dockerfile`.
Expand All @@ -252,7 +326,7 @@ that you may have used:
# syntax=docker/dockerfile:1
# A sample microservice in Go packaged into a container image.

FROM golang:1.19
FROM golang:1.25

# ...
```
Expand Down Expand Up @@ -287,8 +361,8 @@ The following is just an example of what these messages may look like.
=> CACHED docker-image://docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14 0.0s
=> [internal] load build definition from Dockerfile 0.0s
=> [internal] load .dockerignore 0.0s
=> [internal] load metadata for docker.io/library/golang:1.19 0.7s
=> [1/6] FROM docker.io/library/golang:1.19@sha256:5d947843dde82ba1df5ac1b2ebb70b203d106f0423bf5183df3dc96f6bc5a705 0.0s
=> [internal] load metadata for docker.io/library/golang:1.25 0.7s
=> [1/6] FROM docker.io/library/golang:1.25@sha256:5d947843dde82ba1df5ac1b2ebb70b203d106f0423bf5183df3dc96f6bc5a705 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 6.08kB 0.0s
=> CACHED [2/6] WORKDIR /app 0.0s
Expand Down Expand Up @@ -415,19 +489,58 @@ These two issues can be solved by using [multi-stage builds](/manuals/build/buil
In a nutshell, a multi-stage build can carry over the artifacts from one build stage into another,
and every build stage can be instantiated from a different base image.

Thus, in the following example, you are going to use a full-scale official Go
image to build your application. Then you'll copy the application binary into
Thus, in the following example, you are going to use a full-scale Go
image (either DHI or official) to build your application. Then you'll copy the application binary into
another image whose base is very lean and doesn't include the Go toolchain or
other optional components.

The `Dockerfile.multistage` in the sample application's repository has the
following content:

{{< tabs >}}
{{< tab name="Using Docker Hardened Images" >}}

```dockerfile
# syntax=docker/dockerfile:1

# Build the application from source
FROM <your-namespace>/dhi-golang:1.25 AS build-stage

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY *.go ./

RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping

# Run the tests in the container
FROM build-stage AS run-test-stage
RUN go test -v ./...

# Deploy the application binary into a lean image
FROM gcr.io/distroless/base-debian11 AS build-release-stage

WORKDIR /

COPY --from=build-stage /docker-gs-ping /docker-gs-ping

EXPOSE 8080

USER nonroot:nonroot

ENTRYPOINT ["/docker-gs-ping"]
```

{{< /tab >}}
{{< tab name="Using the official image" >}}

```dockerfile
# syntax=docker/dockerfile:1

# Build the application from source
FROM golang:1.19 AS build-stage
FROM golang:1.25 AS build-stage

WORKDIR /app

Expand Down Expand Up @@ -456,6 +569,9 @@ USER nonroot:nonroot
ENTRYPOINT ["/docker-gs-ping"]
```

{{< /tab >}}
{{< /tabs >}}

Since you have two Dockerfiles now, you have to tell Docker what Dockerfile
you'd like to use to build the image. Tag the new image with `multistage`. This
tag (like any other, apart from `latest`) has no special meaning for Docker,
Expand All @@ -481,9 +597,22 @@ base image that you have used in the second stage of the build is very barebones
There's much more to multi-stage builds, including the possibility of multi-architecture builds,
so feel free to check out [multi-stage builds](/manuals/build/building/multi-stage.md). This is, however, not essential for your progress here.

## Summary

In this section, you learned how you can build a container image for your Go
application using Docker.

Related information:

- [Dockerfile reference](/reference/dockerfile.md)
- [.dockerignore file](/reference/dockerfile.md#dockerignore-file)
- [Docker Compose overview](/manuals/compose/_index.md)
- [Compose file reference](/reference/compose-file/_index.md)
- [Docker Hardened Images](/dhi/)

## Next steps

In this module, you met your example application and built and container image
for it.

In the next module, youll take a look at how to run your image as a container.
In the next module, you'll take a look at how to run your image as a container.