Task Overview
When you build an application image, it is often necessary to download temporary files or packages for build. In the results, the application image contains files that are not needed to run the application.
werf can import resources from images and artifacts. Thus you can isolate build process and tools in other images and then copy result files to reduce the image size. It is like a docker multi-stage builds which are supported starting with Docker 17.05, but has more advanced files importing options.
In this article, we will build an example GO application. Then we will optimize the build instructions to substantial reduce image size with using artifacts.
Requirements
-
Installed werf dependencies on the host system.
-
Installed multiwerf on the host system.
Select werf version
This command should be run prior running any werf command in your shell session:
. $(multiwerf use 1.1 stable --as-file)
Sample application
The example application is the Go Web App, written in Go.
Building
Create a gowebapp
directory and place the following werf.yaml
in the gowebapp
directory:
project: gowebapp
configVersion: 1
---
image: gowebapp
from: golang:1.14
docker:
WORKDIR: /app
ansible:
install:
- name: Getting packages
shell: go get github.com/josephspurrier/gowebapp
setup:
- file:
path: /app
state: directory
- name: Copying config
shell: |
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/config /app/config
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/static /app/static
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/template /app/template
cp $GOPATH/bin/gowebapp /app/
The config describes instructions to build one image — gowebapp
.
Build the application by executing the following command in the gowebapp
directory:
werf build --stages-storage :local
Running
Run the application by executing the following command in the gowebapp
directory:
werf run --stages-storage :local --docker-options="-d -p 9000:80 --name gowebapp" gowebapp -- /app/gowebapp
Check that container is running by executing the following command:
docker ps -f "name=gowebapp"
You should see a running container with the gowebapp
name, like this:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41d6f49798a8 werf-stages-storage/gowebapp:84d7...44992265 "/app/gowebapp" 2 minutes ago Up 2 minutes 0.0.0.0:9000->80/tcp gowebapp
Open in a web browser the following URL — http://localhost:9000.
The Go Web App
page should open. Click “Click here to login” to a login page where you can create a new account and login to the application.
Getting the image size
Determine the image size by executing:
docker images `docker ps -f "name=gowebapp" --format='{{.Image}}'`
The output will be something like this:
REPOSITORY TAG IMAGE ID CREATED SIZE
werf-stages-storage/gowebapp 84d7...44992265 07cdc430e1c8 10 minutes ago 857MB
Pay attention, that the image size of the application is above 800 MB.
Optimize sample application with artifacts
The config above can be optimized to improve the efficiency of the build process.
The only the files in the /app
folder are needed to run the application. So we don’t need Go itself and downloaded packages. The use of werf artifacts makes it possible to import only specified files into another image.
Building
Replace werf.yaml
with the following content:
project: gowebapp
configVersion: 1
---
artifact: gowebapp-build
from: golang:1.14
ansible:
install:
- name: Getting packages
shell: go get github.com/josephspurrier/gowebapp
setup:
- file:
path: /app
state: directory
- name: Copying config
shell: |
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/config /app/config
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/static /app/static
cp -r $GOPATH/src/github.com/josephspurrier/gowebapp/template /app/template
cp $GOPATH/bin/gowebapp /app/
---
image: gowebapp
docker:
WORKDIR: /app
from: ubuntu:18.04
import:
- artifact: gowebapp-build
add: /app
to: /app
after: install
In the optimized config, we build the application in the gowebapp-build
artifact and import the /app
directory into the gowebapp
image.
Pay attention, that gowebapp
image based on the ubuntu
image, but not on the golang
image.
Build the application with the modified config:
werf build --stages-storage :local
Running
Before running the modified application, you need to stop and remove running gowebapp
container we built. Otherwise, the new container can’t start or bind to 9000 port on localhost. E.g., execute the following command to stop and remove the gowebapp
container:
docker rm -f gowebapp
Run the modified application by executing the following command:
werf run --stages-storage :local --docker-options="-d -p 9000:80 --name gowebapp" gowebapp -- /app/gowebapp
Check that container is running by executing the following command:
docker ps -f "name=gowebapp"
You should see a running container with the gowebapp
name, like this:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41d6f49798a8 werf-stages-storage/gowebapp:84d7...44992265 "/app/gowebapp" 2 minutes ago Up 2 minutes 0.0.0.0:9000->80/tcp gowebapp
Open in a web browser the following URL — http://localhost:9000.
The Go Web App
page should open. Click “Click here to login” to a login page where you can create a new account and login to the application.
Getting images size
Determine the image size of optimized build, by executing:
docker images `docker ps -f "name=gowebapp" --format='{{.Image}}'`
The output will be something like this:
REPOSITORY TAG IMAGE ID CREATED SIZE
werf-stages-storage/gowebapp 84d7...44992265 07cdc430e1c8 10 minutes ago 79MB
Our example shows that with using artifacts, the image size smaller by more than 90% than the original image size!
Conclusions
The example shows us that using artifacts is a great way to exclude what shouldn’t be in the result image. Moreover, you can use artifacts in any image described in a werf.yaml
config. In some cases, it increases the speed of build.