DEV Community

Alex Pliutau
Alex Pliutau

Posted on

Multi-stage Dockerfile for Golang application

multi-stage

A common workaround for building Golang application in Docker is to have 2 Dockerfiles - one to perform a build and another to ship the results of the first build without tooling in the first image. It called Builder Pattern.

Starting from Docker v17.0.5 it's be possible to do it via single Dockerfile using multi-stage builds.

Application

Let's start with "Hello world" application:

package main

import "fmt"

func main() {
    fmt.Println("Hello world!")
}
Enter fullscreen mode Exit fullscreen mode

Single Dockerfile

With multi-stage builds, a Dockerfile allows multiple FROM directives, and the image is created via the last FROM directive of the Dockerfile.

COPY –from=0 takes the file app from the previous stage and copies it to the WORKDIR. This basically copies the compiled go binary created from the previous stage.

The --from flag uses a zero-based index for the stage. You either reference stages by using offsets (like --from=0) or by using names. To name a stage use the syntax FROM [image] as [name].

FROM golang:1.8.1

WORKDIR /go/src/github.com/plutov/golang-multi-stage/

COPY main.go .

RUN GOOS=linux go build -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=0 /go/src/github.com/plutov/golang-multi-stage/app .

CMD ["./app"]
Enter fullscreen mode Exit fullscreen mode

Build and check size

docker build .
Enter fullscreen mode Exit fullscreen mode

Container size now is small, because it contains only binary file.

docker ps
REPOSITORY          TAG     IMAGE ID            CREATED           SIZE

golang-multi-stage  latest  bcbbf69a9b59        6 minutes ago     6.7MB
Enter fullscreen mode Exit fullscreen mode

Original article is posted on my blog

Top comments (8)

Collapse
 
lisitsky profile image
lisitsky

Can we use FROM scratch for the last step?

Collapse
 
sarbash profile image
Sergey Sarbash • Edited

Surely, I do this everyday when I have to rebuild my docker image for an app.
Just remember about a static build and You'll be alright.

CGO_ENABLED=0 GOOS=linux go build -a .

Have fun with Docker && Go! :)

Collapse
 
lisitsky profile image
lisitsky

Thank you!

Collapse
 
der_gopher profile image
Alex Pliutau

Right, it's much better!

Collapse
 
dorianamouroux profile image
Dorian Amouroux

You should use the same version of Alpine linux on both stage to make sure you don't run into issues later

Collapse
 
der_gopher profile image
Alex Pliutau

To be honest, I am not sure what issues I can meet there, because it will be one image for the last FROM directive.

Collapse
 
dorianamouroux profile image
Dorian Amouroux

But the binary is compiled on golang:1.8.1 which is Jessie I believe. I think it would be safer to have FROM golang:1.8.1-alpine for the first stage and FROM alpine:3.5 for the second one. (golang:1.8.1-alpine is from alpine:3.5)

Thread Thread
 
sarbash profile image
Sergey Sarbash

A static build can save you.