posts-go/posts/2022-06-08-dockerfile-go.md

94 lines
2.8 KiB
Markdown
Raw Normal View History

2021-12-18 16:52:46 +00:00
# Dockerfile for Go
2021-12-19 04:45:22 +00:00
Each time I start a new Go project, I repeat many steps.
2021-12-18 16:52:46 +00:00
Like set up `.gitignore`, CI configs, Dockerfile, ...
So I decide to have a baseline Dockerfile like this:
```Dockerfile
2022-10-09 10:45:31 +00:00
FROM golang:1.19-bullseye as builder
2022-10-09 10:45:31 +00:00
RUN go install golang.org/dl/go1.19@latest \
&& go1.19 download
2021-12-18 16:52:46 +00:00
WORKDIR /build
COPY go.mod .
COPY go.sum .
COPY vendor .
COPY . .
2022-06-19 11:03:14 +00:00
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath .
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
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).
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:52:45 +00:00
Also, remember to match Distroless Debian version with Go official image Debian version.
2021-12-19 04:45:22 +00:00
```Dockerfile
2022-10-09 10:45:31 +00:00
FROM golang:1.19-bullseye as builder
2021-12-19 04:45:22 +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.
```Dockerfile
2022-10-09 10:45:31 +00:00
RUN go install golang.org/dl/go1.19@latest \
&& go1.19 download
```
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.
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
2022-06-19 11:03:14 +00:00
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath .
2021-12-19 04:45:22 +00:00
```
This is where I build Go program.
2022-06-19 11:03:14 +00:00
2021-12-19 04:45:22 +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.
2022-06-19 11:03:14 +00:00
`GOAMD64=v3` is new since [Go 1.18](https://go.dev/doc/go1.18#amd64),
2021-12-19 04:45:22 +00:00
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). TLDR's newer computers are already x86-64-v3.
2022-06-19 11:03:14 +00:00
`-tags timetzdata` to embed timezone database incase base image does not have.
`-trimpath` to support reproduce build.
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.