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.
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
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.
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,
The other new addition in our latest stacker file example is a
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.
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
/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