<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-dark.min.css" /> <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=Inter&family=JetBrains+Mono&family=Martian+Mono&family=Recursive&display=swap" rel="stylesheet" /> </head> <style> /* https://github.com/sindresorhus/github-markdown-css */ .markdown-body { box-sizing: border-box; min-width: 200px; max-width: 980px; margin: 0 auto; padding: 45px; } @media (max-width: 767px) { .markdown-body { padding: 15px; } } .markdown-body { /* font-family: "Inter", sans-serif; */ font-family: "Recursive", sans-serif; } .markdown-body code, .markdown-body pre { /* font-family: "JetBrains Mono", monospace; */ font-family: "Martian Mono", monospace; } </style> <body class="markdown-body"> <div><a href="index">Index</a></div> <h1> <a id="user-content-dockerfile-for-go" class="anchor" aria-hidden="true" href="#dockerfile-for-go" ><span aria-hidden="true" class="octicon octicon-link"></span></a >Dockerfile for Go </h1> <p> Each time I start a new Go project, I repeat many steps. Like set up <code>.gitignore</code>, CI configs, Dockerfile, ... </p> <p>So I decide to have a baseline Dockerfile like this:</p> <div class="highlight highlight-source-dockerfile"> <pre><span class="pl-k">FROM</span> golang:1.20-bullseye as builder <span class="pl-k">RUN</span> go install golang.org/dl/go1.20@latest \ && go1.20 download <span class="pl-k">WORKDIR</span> /build <span class="pl-k">COPY</span> go.mod . <span class="pl-k">COPY</span> go.sum . <span class="pl-k">COPY</span> vendor . <span class="pl-k">COPY</span> . . <span class="pl-k">RUN</span> CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath . <span class="pl-k">FROM</span> gcr.io/distroless/base-debian11 <span class="pl-k">COPY</span> --from=builder /build/app /app <span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre> </div> <p> I use <a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="nofollow" >multi-stage build</a > to keep my image size small. First stage is <a href="https://hub.docker.com/_/golang" rel="nofollow" >Go official image</a >, second stage is <a href="https://github.com/GoogleContainerTools/distroless">Distroless</a >. </p> <p> Before Distroless, I use <a href="https://hub.docker.com/_/alpine" rel="nofollow" >Alpine official image</a >, 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. </p> <p> Also, remember to match Distroless Debian version with Go official image Debian version. </p> <div class="highlight highlight-source-dockerfile"> <pre><span class="pl-k">FROM</span> golang:1.20-bullseye as builder</pre> </div> <p> This is Go image I use as a build stage. This can be official Go image or custom image is required in some companies. </p> <div class="highlight highlight-source-dockerfile"> <pre><span class="pl-k">RUN</span> go install golang.org/dl/go1.20@latest \ && go1.20 download</pre> </div> <p> This is optional. In my case, my company is slow to update Go image so I use this trick to install latest Go version. </p> <div class="highlight highlight-source-dockerfile"> <pre><span class="pl-k">WORKDIR</span> /build <span class="pl-k">COPY</span> go.mod . <span class="pl-k">COPY</span> go.sum . <span class="pl-k">COPY</span> vendor . <span class="pl-k">COPY</span> . .</pre> </div> <p> I use <code>/build</code> to emphasize that I am building something in that directory. </p> <p> The 4 <code>COPY</code> lines are familiar if you use Go enough. First is <code>go.mod</code> and <code>go.sum</code> because it defines Go modules. 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. </p> <div class="highlight highlight-source-dockerfile"> <pre><span class="pl-k">RUN</span> CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GOAMD64=v3 go build -o ./app -tags timetzdata -trimpath .</pre> </div> <p>This is where I build Go program.</p> <p> <code>CGO_ENABLED=0</code> because I don't want to mess with C libraries. <code>GOOS=linux GOARCH=amd64</code> is easy to explain, Linux with x86-64. <code>GOAMD64=v3</code> is new since <a href="https://go.dev/doc/go1.18#amd64" rel="nofollow">Go 1.18</a>, I use v3 because I read about AMD64 version in <a href="https://gitlab.archlinux.org/archlinux/rfcs/-/blob/master/rfcs/0002-march.rst" rel="nofollow" >Arch Linux rfcs</a >. TLDR's newer computers are already x86-64-v3. </p> <p> <code>-tags timetzdata</code> to embed timezone database incase base image does not have. <code>-trimpath</code> to support reproduce build. </p> <div class="highlight highlight-source-dockerfile"> <pre><span class="pl-k">FROM</span> gcr.io/distroless/base-debian11 <span class="pl-k">COPY</span> --from=builder /build/app /app <span class="pl-k">ENTRYPOINT</span> [<span class="pl-s">"/app"</span>]</pre> </div> <p>Finally, I copy <code>app</code> to Distroless base image.</p> <div> Feel free to ask me via <a href="mailto:hauvipapro+posts@gmail.com">email</a> or <a rel="me" href="https://hachyderm.io/@haunguyen">Mastodon</a>. Source code is available on <a href="https://github.com/haunt98/posts-go">GitHub</a> </div> </body> </html>