DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Software Design and Architecture
  3. Cloud Architecture
  4. Multi-Stage Builds With Docker

Multi-Stage Builds With Docker

Two FROM directives in a Dockerfile? What sorcery is this? Well, Dockercon saw the announcement of multi-stage builds, so let's tackle a golang app to see it in action.

Harshal Shah user avatar by
Harshal Shah
·
Apr. 27, 17 · Tutorial
Like (3)
Save
Tweet
Share
8.64K Views

Join the DZone community and get the full member experience.

Join For Free

one of the key announcements at dockercon 2017 was the launch of multi-stage builds. it is available in docker version 17.05 onwards and is one of most exciting features coming out. in this post, we will do a quick demo of the feature and then discuss the details.

setup

one of the quickest ways to get our hands dirty with this feature is to sign up for a docker lab and add a new instance. this will spin up a new docker-in-docker container, which can be used for our experimentation. before going ahead, let's ensure the docker version is correct by running the command docker version .

image title

if the version of docker server is less than 17.05, perform the following steps:

  • to download the docker daemon 17.05-dev, run the following command:
curl -l https://master.dockerproject.org/linux/amd64/dockerd-17.05.0-dev -o dockerd


  • run the following command to overwrite the existing dockerd:
mv ./dockerd /usr/local/bin/dockerd && chmod +x /usr/local/bin/dockerd


  • kill the dockerd process by running ps -a and r un the following command to start the docker daemon again
nohup /usr/local/bin/dockerd &


  • verify the docker daemon version by running docker version

image title

building the image the old way

clone the git repo of this sample go-lang application and cd to the code directory. we will now build an image of the demo app via the default go-lang parent as a base image using the command docker build -t demo-go-app:1.0 . . one of the key things to notice here is the content of dockerfile, which is simply the golang image’s on-build tag:

from golang:1.6.3-onbuild


once the image is built, verify the image size using the command docker images . the image is about 756mb.

image title

the world of multi-stage builds

now check out the multi-stage branch of the same repo. one of the first things you will notice is a starkly different dockerfile:

from golang:1.6.3-alpine
run mkdir /app
add . /app/
workdir /app
run cgo_enabled=0 goos=linux go build -a -installsuffix cgo -o main .
from alpine:latest
workdir /root/
copy --from=0 /app/main .
cmd ["/root/main"]


there are two from directives in this file, which was not allowed before. with multi-stage builds, a dockerfile allows multiple from directives, and the image is created via the last from directive of the dockerfile.

the next interesting statement is copy –from=0 /app/main . . this takes the file /app/main from the previous stage and copies it to the workdir. this basically copies the compiled go binary created from the previous stage.

now let's run the docker build command docker build -t demo-go-app:2.0 . and verify the size by running docker images again:

image title

the image size is around 13.1 mb, which is a fraction of the older size (756mb).

a word of caution

if the build product is a compiled binary, a consistent platform has to be maintained. a binary compiled for linux will not work on alpine. for example, below are the file attributes of the above application when it is built via golang:1.6.3-onbuild:

root@44fee7671056:/go/src/app# file /go/bin/app
/go/bin/app: elf 64-bit lsb executable, x86-64, version 1 (sysv), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, not stripped


looking at the same application when compiled via golang:1.6.3-alpine:

$ docker exec -it c7a6afccf5509c2032444f14906cf17267994a3191e5d368fad9cff679797377 file /root/main
/root/main: elf 64-bit lsb executable, x86-64, version 1 (sysv), statically linked, not stripped


if the application compiled via golang:1.6.3-onubild gets copied to an alpine container, the application wont execute and will give an error, as below:

$ ./app
bash: ./app: cannot execute binary file: exec format error


this is why the dockerfile used in multi-stage build of the demo application is using golang:1.6.3-alpine as its base image, instead of golang:1.6.3-on-build.

why is the multi-stage image so small?

the main reason for the image being so small is that its parent image itself is very small. while the golang:1.6.3 image was about 750mb, the alpine image was a mere 3.99 mb. why alpine images so small, and their other advantages, are explained in detail here . the golang base image contains golang installation + python installation and many supporting modules, which are not really needed once the compiled application is ready.

consider another approach, where you would use alpine as a base image and create an application over it. this would mean adding every little dependency by hand to your dockerfile and adding more steps to image creation, which is certainly not desirable.

a third option is to use the alpine distribution of golang, such as golang:1.6.3-alpine. this also creates an image of around 300 mb, since the os is barebones but all golang dependencies and support modules are present.

multi-stage builds solve the above problems by giving the developer the flexibility to use bloated base images to create an executable/jar file. once the application is compiled, the remaining bloatware is not needed and the application itself can be easily injected into a minimal image, giving all the advantages of a small image size.

Docker (software) Build (game engine) application

Published at DZone with permission of Harshal Shah, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Type Variance in Java and Kotlin
  • Simulate Network Latency and Packet Drop In Linux
  • When AI Strengthens Good Old Chatbots: A Brief History of Conversational AI
  • Three SQL Keywords in QuestDB for Finding Missing Data

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: