Stacker Tutorial¶
Stacker is a tool that allows you to build OCI images in a reproducible manner, completely unprivileged. In this tutorial, we'll introduce the stacker configuration file and perform a few simple builds.
For this tutorial, we assume you have followed the installation instructions and your environment satisfies all runtime dependecies.
Your first stacker.yaml file¶
The basic input to stacker is the stacker file, such as stacker.yaml. This YAML-format file uses key:value pairs to describe what the base for your OCI image should be, and what steps must be performed to construct the image.
Let's start with one of the smallest stacker files:
In this example, the key named first represents the name of the layer.
This key can have any name except
config, which has a special usage in the stacker file. See the full documentation for stacker yaml.
Using the small stacker file, named first.yaml, we can perform a basic stacker build:
$ stacker build -f first.yaml
building image first...
importing files...
Getting image source signatures
Copying blob sha256:5e35d10a3ebadf9d6ab606ce72e1e77f8646b2e2ff8dd3a60d4401c3e3a76f31
69.60 MB / 69.60 MB [=====================================================] 16s
Copying config sha256:44a17ce607dadfb71de41d82c75d756c2bca4db677bba99969f28de726e4411e
862 B / 862 B [============================================================] 0s
Writing manifest to image destination
Storing signatures
unpacking to /home/ubuntu/tutorial/roots/_working
running commands...
generating layer...
filesystem first built successfully
During the build, stacker downloaded the centos:latest tag from the docker hub and generated it as an OCI image with the additional first tag. We can verify the result by using the umoci ls command, which lists the tags in an image:
$ umoci ls --layout oci
centos-latest
first
The centos-latest tag is the OCI tag for the base image, and first is the name of the image we generated.
If we execute a rebuild at this point, fewer steps are needed:
$ stacker build -f first.yaml
building image first...
importing files...
found cached layer first
The rebuild is shorter because stacker caches all of the inputs to a build, and only rebuilds the parts that change. The cache (and all of stacker's metadata) reside in the /stacker directory, from which you run stacker.
Stacker's metadata can be cleaned with
stacker clean, which also removes its entire cache.
Adding import and run directives¶
At this point in our example, the only input is a base image, but what if we want to import a script to run or a config file? Consider the next stacker file example:
first:
from:
type: docker
url: docker://centos:latest
import:
- config.json
- install.sh
run: |
mkdir -p /etc/myapp
cp /stacker/config.json /etc/myapp/
/stacker/install.sh
If the content of install.sh is "echo hello world," the stacker build output will look like this:
$ stacker build -f first.yaml
building image first...
importing files...
copying config.json
copying install.sh
Getting image source signatures
Skipping fetch of repeat blob sha256:5e35d10a3ebadf9d6ab606ce72e1e77f8646b2e2ff8dd3a60d4401c3e3a76f31
Copying config sha256:44a17ce607dadfb71de41d82c75d756c2bca4db677bba99969f28de726e4411e
862 B / 862 B [============================================================] 0s
Writing manifest to image destination
Storing signatures
unpacking to /home/ubuntu/tutorial/roots/_working
running commands...
running commands for first
+ mkdir -p /etc/myapp
+ cp /stacker/config.json /etc/myapp
+ /stacker/install.sh
hello world
generating layer...
filesystem first built successfully
In this latest stacker file, we've added an import section, with two new directives :
This new section imports two files into the /stacker directory inside the image. This directory will not be present in the final image, so you must copy any needed files from this directory into their final place in the image. Also, importing files from the web (via URLs like http://example.com/foo.tar.gz) is supported, and these files will also be cached on disk.
If a file is already cached, stacker will not access the URL again. If the file at the URL changes, you must run
stacker buildwith the--no-cacheargument or you can delete the file from its cached location (in this case,/stacker/imports/$target_name/foo.tar.gz).
The other new addition in our latest stacker file example is a run section:
This section lists the commands that will be run in order to install and configure the image.
Note that the build in the latest example used a cached version of the base layer again, but then rebuilt the part where you asked for commands to be run, since that is new.
dev/build containers¶
Finally, stacker offers "build only" containers, which are built but not emitted in the final OCI image. Consider this stacker file example:
build:
from:
type: docker
url: docker://ubuntu:latest
run: |
apt update
apt install -y software-properties-common git
apt-add-repository -y ppa:gophers/archive
apt update
apt install -y golang-1.9
export PATH=$PATH:/usr/lib/go-1.9/bin
export GOPATH=~/go
mkdir -p $GOPATH/src/github.com/openSUSE
cd $GOPATH/src/github.com/openSUSE
git clone https://github.com/opencontainers/umoci
cd umoci
make umoci.static
cp umoci.static /
build_only: true
umoci:
from:
type: docker
url: docker://centos:latest
import: stacker://build/umoci.static
run: cp /stacker/umoci.static /usr/bin/umoci
This file builds a static version of umoci in an ubuntu container, but the final image will contain only an umoci tag with a statically linked version of umoci at /usr/bin/umoci. There are a few new directives to support this result:
This line indicates that the container shouldn't be emitted in the final image, because we're only going to import something from it and we don't need the rest of it.
This line performs the actual import. The line calls for this action: "From a previously built stacker image called build, import /umoci.static."