2021-12-18 16:52:46 +00:00
|
|
|
# Dockerfile for Go
|
|
|
|
|
2023-08-05 18:56:25 +00:00
|
|
|
Each time I start a new Go project, I repeat many steps. Like set up
|
|
|
|
`.gitignore`, CI configs, Dockerfile, ...
|
2021-12-18 16:52:46 +00:00
|
|
|
|
|
|
|
So I decide to have a baseline Dockerfile like this:
|
|
|
|
|
|
|
|
```Dockerfile
|
2023-02-08 08:29:08 +00:00
|
|
|
FROM golang:1.20-bullseye as builder
|
2022-06-08 10:42:03 +00:00
|
|
|
|
2023-02-08 08:29:08 +00:00
|
|
|
RUN go install golang.org/dl/go1.20@latest \
|
|
|
|
&& go1.20 download
|
2021-12-18 16:52:46 +00:00
|
|
|
|
|
|
|
WORKDIR /build
|
|
|
|
|
|
|
|
COPY go.mod .
|
|
|
|
COPY go.sum .
|
|
|
|
COPY vendor .
|
|
|
|
COPY . .
|
|
|
|
|
2023-05-18 16:44:36 +00:00
|
|
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath -ldflags="-s -w" .
|
2021-12-18 16:52:46 +00:00
|
|
|
|
|
|
|
FROM gcr.io/distroless/base-debian11
|
|
|
|
|
|
|
|
COPY --from=builder /build/app /app
|
|
|
|
|
|
|
|
ENTRYPOINT ["/app"]
|
|
|
|
```
|
2021-12-19 04:45:22 +00:00
|
|
|
|
2023-08-05 18:56:25 +00:00
|
|
|
I use
|
|
|
|
[multi-stage build](https://docs.docker.com/develop/develop-images/multistage-build/)
|
|
|
|
to keep my image size small. First stage is
|
|
|
|
[Go official image](https://hub.docker.com/_/golang), second stage is
|
|
|
|
[Distroless](https://github.com/GoogleContainerTools/distroless).
|
2021-12-19 04:45:22 +00:00
|
|
|
|
2023-08-05 18:56:25 +00:00
|
|
|
Before Distroless, I use
|
|
|
|
[Alpine official image](https://hub.docker.com/_/alpine), There is a whole
|
|
|
|
discussion on the Internet to choose which is the best base image for Go. After
|
|
|
|
reading some blogs, I discover Distroless as a small and secure base image. So I
|
|
|
|
stick with it for a while.
|
2021-12-19 04:45:22 +00:00
|
|
|
|
2023-08-05 18:56:25 +00:00
|
|
|
Also, remember to match Distroless Debian version with Go official image Debian
|
|
|
|
version.
|
2021-12-19 04:52:45 +00:00
|
|
|
|
2021-12-19 04:45:22 +00:00
|
|
|
```Dockerfile
|
2023-02-08 08:29:08 +00:00
|
|
|
FROM golang:1.20-bullseye as builder
|
2021-12-19 04:45:22 +00:00
|
|
|
```
|
|
|
|
|
2023-08-05 18:56:25 +00:00
|
|
|
This is Go image I use as a build stage. This can be official Go image or custom
|
|
|
|
image is required in some companies.
|
2022-06-08 10:42:03 +00:00
|
|
|
|
|
|
|
```Dockerfile
|
2023-02-08 08:29:08 +00:00
|
|
|
RUN go install golang.org/dl/go1.20@latest \
|
|
|
|
&& go1.20 download
|
2022-06-08 10:42:03 +00:00
|
|
|
```
|
|
|
|
|
2023-08-05 18:56:25 +00:00
|
|
|
This is optional. In my case, my company is slow to update Go image so I use
|
|
|
|
this trick to install latest Go version.
|
2021-12-19 04:45:22 +00:00
|
|
|
|
|
|
|
```Dockerfile
|
|
|
|
WORKDIR /build
|
|
|
|
|
|
|
|
COPY go.mod .
|
|
|
|
COPY go.sum .
|
|
|
|
COPY vendor .
|
|
|
|
COPY . .
|
|
|
|
```
|
|
|
|
|
|
|
|
I use `/build` to emphasize that I am building something in that directory.
|
|
|
|
|
2023-08-05 18:56:25 +00:00
|
|
|
The 4 `COPY` lines are familiar if you use Go enough. First is `go.mod` and
|
|
|
|
`go.sum` because it defines Go modules. The second is `vendor`, this is optional
|
|
|
|
but I use it because I don't want each time I build Dockerfile, I need to
|
|
|
|
redownload Go modules.
|
2021-12-19 04:45:22 +00:00
|
|
|
|
|
|
|
```Dockerfile
|
2023-05-18 16:44:36 +00:00
|
|
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath -ldflags="-s -w" .
|
2021-12-19 04:45:22 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
This is where I build Go program.
|
2022-06-19 11:03:14 +00:00
|
|
|
|
2023-06-10 04:15:10 +00:00
|
|
|
- `CGO_ENABLED=0` because I don't want to mess with C libraries.
|
|
|
|
- `GOOS=linux GOARCH=amd64` is easy to explain, Linux with x86-64.
|
2023-08-05 18:56:25 +00:00
|
|
|
- `GOAMD64=v3` is new since [Go 1.18](https://go.dev/doc/go1.18#amd64), I use v3
|
|
|
|
because I read about AMD64 version in
|
|
|
|
[Arch Linux rfcs](https://gitlab.archlinux.org/archlinux/rfcs/-/blob/master/rfcs/0002-march.rst).
|
2023-06-10 04:17:23 +00:00
|
|
|
TLDR's newer computers are already x86-64-v3.
|
2023-08-05 18:56:25 +00:00
|
|
|
- `-tags timetzdata` to embed timezone database in case base image does not
|
|
|
|
have.
|
2023-06-10 04:15:10 +00:00
|
|
|
- `-trimpath` to support reproduce build.
|
|
|
|
- `-ldflags="-s -w"` to strip debugging information.
|
2022-06-19 11:03:14 +00:00
|
|
|
|
2023-08-17 09:48:31 +00:00
|
|
|
Also there are some experiment:
|
|
|
|
|
|
|
|
- `GOMEMLIMIT=1024MiB`: soft memory limit.
|
|
|
|
|
2021-12-19 04:45:22 +00:00
|
|
|
```Dockerfile
|
|
|
|
FROM gcr.io/distroless/base-debian11
|
|
|
|
|
|
|
|
COPY --from=builder /build/app /app
|
|
|
|
|
|
|
|
ENTRYPOINT ["/app"]
|
|
|
|
```
|
|
|
|
|
|
|
|
Finally, I copy `app` to Distroless base image.
|
2023-05-18 16:44:36 +00:00
|
|
|
|
2023-05-18 16:48:07 +00:00
|
|
|
## Thanks
|
2023-05-18 16:44:36 +00:00
|
|
|
|
|
|
|
- [How to start a Go project in 2023](https://boyter.org/posts/how-to-start-go-project-2023/)
|
|
|
|
- [Shrink your Go binaries with this one weird trick](https://words.filippo.io/shrink-your-go-binaries-with-this-one-weird-trick/)
|
2023-08-17 09:48:31 +00:00
|
|
|
- [GOMEMLIMIT is a game changer for high-memory applications](https://weaviate.io/blog/gomemlimit-a-game-changer-for-high-memory-applications)
|