posts-go/docs/2022-06-08-dockerfile-go.html

35 lines
3.6 KiB
HTML

<!doctype html><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><link rel=preconnect href=https://fonts.googleapis.com><link rel=preconnect href=https://fonts.gstatic.com crossorigin><link href="https://fonts.googleapis.com/css2?family=Recursive:wght,CASL,MONO@300..800,0..1,0..1&display=swap" rel=stylesheet><link rel=stylesheet href=styles.css><h1>Dockerfile for Go</h1><p>Each time I start a new Go project, I repeat many steps.<br>Like set up <code>.gitignore</code>, CI configs, Dockerfile, ...<p>So I decide to have a baseline Dockerfile like this:<pre><code class=language-Dockerfile>FROM golang:1.18-bullseye as builder
RUN go install golang.org/dl/go1.18@latest \
&amp;&amp; go1.18 download
WORKDIR /build
COPY go.mod .
COPY go.sum .
COPY vendor .
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath .
FROM gcr.io/distroless/base-debian11
COPY --from=builder /build/app /app
ENTRYPOINT [&quot;/app&quot;]
</code></pre><p>I use <a href=https://docs.docker.com/develop/develop-images/multistage-build/>multi-stage build</a> to keep my image size small.<br>First stage is <a href=https://hub.docker.com/_/golang>Go official image</a>,<br>second stage is <a href=https://github.com/GoogleContainerTools/distroless>Distroless</a>.<p>Before Distroless, I use <a href=https://hub.docker.com/_/alpine>Alpine official image</a>,<br>There is a whole discussion on the Internet to choose which is the best base image for Go.<br>After reading some blogs, I discover Distroless as a small and secure base image.<br>So I stick with it for a while.<p>Also, remember to match Distroless Debian version with Go official image Debian version.<pre><code class=language-Dockerfile>FROM golang:1.18-bullseye as builder
</code></pre><p>This is Go image I use as a build stage.<br>This can be official Go image or custom image is required in some companies.<pre><code class=language-Dockerfile>RUN go install golang.org/dl/go1.18@latest \
&amp;&amp; go1.18 download
</code></pre><p>This is optional.<br>In my case, my company is slow to update Go image so I use this trick to install latest Go version.<pre><code class=language-Dockerfile>WORKDIR /build
COPY go.mod .
COPY go.sum .
COPY vendor .
COPY . .
</code></pre><p>I use <code>/build</code> to emphasize that I am building something in that directory.<p>The 4 <code>COPY</code> lines are familiar if you use Go enough.<br>First is <code>go.mod</code> and <code>go.sum</code> because it defines Go modules.<br>The second is <code>vendor</code>, this is optional but I use it because I don't want each time I build Dockerfile, I need to redownload Go modules.<pre><code class=language-Dockerfile>RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath .
</code></pre><p>This is where I build Go program.<p><code>CGO_ENABLED=0</code> because I don't want to mess with C libraries.<br><code>GOOS=linux GOARCH=amd64</code> is easy to explain, Linux with x86-64.<br><code>GOAMD64=v3</code> is new since <a href=https://go.dev/doc/go1.18#amd64>Go 1.18</a>,<br>I use v3 because I read about AMD64 version in <a href=https://gitlab.archlinux.org/archlinux/rfcs/-/blob/master/rfcs/0002-march.rst>Arch Linux rfcs</a>. TLDR's newer computers are already x86-64-v3.<p><code>-tags timetzdata</code> to embed timezone database incase base image does not have.<br><code>-trimpath</code> to support reproduce build.<pre><code class=language-Dockerfile>FROM gcr.io/distroless/base-debian11
COPY --from=builder /build/app /app
ENTRYPOINT [&quot;/app&quot;]
</code></pre><p>Finally, I copy <code>app</code> to Distroless base image.