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!")
}
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"]
Build and check size
docker build .
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
Top comments (8)
Can we use
FROM scratch
for the last step?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! :)
Thank you!
Right, it's much better!
You should use the same version of Alpine linux on both stage to make sure you don't run into issues later
To be honest, I am not sure what issues I can meet there, because it will be one image for the last
FROM
directive.But the binary is compiled on
golang:1.8.1
which is Jessie I believe. I think it would be safer to haveFROM golang:1.8.1-alpine
for the first stage andFROM alpine:3.5
for the second one. (golang:1.8.1-alpine
is fromalpine:3.5
)A static build can save you.