img
Standalone, daemon-less, unprivileged Dockerfile and OCI compatible
container image builder.
img
is more cache-efficient than Docker and can also execute multiple build stages concurrently,
as it internally uses BuildKit's DAG solver.
The commands/UX are the same as docker {build,tag,push,pull,login,logout,save}
so all you
have to do is replace docker
with img
in your scripts, command line, and/or life.
Table of Contents
- Goals
- Upstream Patches
- Benchmarks - Installation
- Usage
- How It Works
- Contributing
- Acknowledgements
Goals
This a glorified cli tool built on top of
buildkit. The goal of this project is to be
able to build container images as an unprivileged user.
Running unprivileged allows companies who use LDAP and other login mechanisms
to use img
without needing root. This is very important in HPC environments
and academia as well.
Currently this works out of the box on a Linux machine if you install via
the directions covered in installing from binaries. This
installation will ensure you have the correct version of img
and also runc
.
Upstream Patches
The ultimate goal is to also have this work inside a container. There are
patches being made to container runtimes and Kubernetes to make this possible.
For the on-going work toward getting patches into container runtimes and
Kubernetes, see:
- moby/moby#36644 merged
- docker/cli#1347
- kubernetes/community#1934 merged
- kubernetes/kubernetes#64283 merged
The patches for runc has been merged into the upstream since ecd55a4135e0a26de884ce436442914f945b1e76
(May 30, 2018).
The upstream BuildKit can also run in rootless mode since 65b526438b86a17cf35042011051ce15c8bfb92a
(June 1, 2018).
You might also be interested in reading:
Benchmarks
If you are curious about benchmarks comparing various container builders, check
out @AkihiroSuda's buildbench
results.
Installation
You need to have newuidmap
installed. On Ubuntu, newuidmap
is provided by the uidmap
package.
You also need to have seccomp
installed. On Ubuntu, seccomp
is provided by the libseccomp-dev
package.
runc
will be installed on start from an embedded binary if it is not already
available locally. If you would like to disable the embedded runc you can use BUILDTAGS="seccomp
noembed"
while building from source with make
. Or the environment variable
IMG_DISABLE_EMBEDDED_RUNC=1
on execution of the img
binary.
NOTE: These steps work only for Linux. Compile and run in a container
(explained below) if you're on Windows or MacOS.
Binaries
For installation instructions from binaries please visit the Releases Page.
From Source
$ mkdir -p $GOPATH/src/github.com/genuinetools
$ git clone https://github.com/genuinetools/img $GOPATH/src/github.com/genuinetools/img
$ cd !$
$ make
$ sudo make install
# For packagers if you would like to disable the embedded `runc`, please use:
$ make BUILDTAGS="seccomp noembed"
Alpine Linux
There is an APKBUILD.
$ apk add img --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing
Arch Linux
There is an AUR build.
# Use whichever AUR helper you prefer
$ yay -S img
# Or build from the source PKGBUILD
$ git clone https://aur.archlinux.org/packages/img.git
$ cd img
$ makepkg -si
Gentoo
There is an ebuild.
$ sudo emerge -a app-emulation/img
Running with Docker
Docker image r.j3ss.co/img
is configured to be executed as an unprivileged user with UID 1000 and it does not need --privileged
since img
v0.5.7.
$ docker run --rm -it \
--name img \
--volume $(pwd):/home/user/src:ro \ # for the build context and dockerfile, can be read-only since we won't modify it
--workdir /home/user/src \ # set the builder working directory
--volume "${HOME}/.docker:/root/.docker:ro" \ # for credentials to push to docker hub or a registry
--security-opt seccomp=unconfined --security-opt apparmor=unconfined \ # required by runc
r.j3ss.co/img build -t user/myimage .
To enable PID namespace isolation (which disallows build containers to kill(2)
the img
process), you need to specify
--privileged
so that build containers can mount /proc
with unshared PID namespaces.
Note that even with --privileged
, img
works as an unprivileged user with UID 1000.
See docker/cli patch for how to allow mounting /proc
without --privileged
.
Running with Kubernetes
Since img
v0.5.7, you don't need to specify any securityContext
for running img
as a Kubernetes container.
However the following security annotations are needed:
container.apparmor.security.beta.kubernetes.io/img: unconfined
container.seccomp.security.alpha.kubernetes.io/img: unconfined
To enable PID namespace isolation, you need to set securityContext.procMount
to Unmasked
(or simply set
securityContext.privileged
to true
).
securityContext.procMount
is available since Kubernetes 1.12 with Docker 18.06/containerd 1.2/CRI-O 1.12.
Usage
Make sure you have user namespace support enabled. On some distros (Debian and
Arch Linux) this requires running echo 1 > /proc/sys/kernel/unprivileged_userns_clone
.
$ img -h
img - Standalone, daemon-less, unprivileged Dockerfile and OCI compatible container image builder
Usage: img [OPTIONS] COMMAND [ARG...]
Flags:
-b, --backend string backend for snapshots ([auto native overlayfs]) (default "auto")
-d, --debug enable debug logging
-h, --help help for img
-s, --state string directory to hold the global state (default "/home/user/.local/share/img")
-v, --version Print version information and quit
Commands:
build Build an image from a Dockerfile
du Show image disk usage.
help Help about any command
login Log in to a Docker registry.
logout Log out from a Docker registry.
ls List images and digests.
prune Prune and clean up the build cache.
pull Pull an image or a repository from a registry.
push Push an image or a repository to a registry.
rm Remove one or more images.
save Save an image to a tar archive (streamed to STDOUT by default).
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE.
unpack Unpack an image to a rootfs directory.
version Show the version information.
Use "img [command] --help" for more information about a command.
Build an Image
$ img build -h
build - Build an image from a Dockerfile
Usage: img build [OPTIONS] PATH
Flags:
--build-arg list Set build-time variables
-f, --file string Name of the Dockerfile (Default is 'PATH/Dockerfile')
-h, --help help for build
--label list Set metadata for an image
--no-cache Do not use cache when building the image
--no-console Use non-console progress UI
-o, --output string BuildKit output specification (e.g. type=tar,dest=build.tar)
--platform list Set platforms for which the image should be built
-t, --tag list Name and optionally a tag in the 'name:tag' format
--target string Set the target build stage to build
Global Flags:
-b, --backend string backend for snapshots ([auto native overlayfs]) (default "auto")
-d, --debug enable debug logging
-s, --state string directory to hold the global state (default "/home/user/.local/share/img")
Use just like you would docker build
.
$ img build -t r.j3ss.co/img .
Building r.j3ss.co/img:latest
Setting up the rootfs... this may take a bit.
[+] Building 44.7s (16/16) FINISHED
=> local://dockerfile (Dockerfile) 0.0s
=> => transferring dockerfile: 1.15kB 0.0s
=> local://context (.dockerignore) 0.0s
=> => transferring context: 02B 0.0s
=> CACHED docker-image://docker.io/tonistiigi/copy:v0.1.1@sha256:854cee92ccab4c6d63 0.0s
=> => resolve docker.io/tonistiigi/copy:v0.1.1@sha256:854cee92ccab4c6d63183d147389e 0.0s
=> CACHED docker-image://docker.io/library/alpine@sha256:e1871801d30885a610511c867d 0.0s
=> => resolve docker.io/library/alpine@sha256:e1871801d30885a610511c867de0d6baca7ed 0.0s
=> docker-image://docker.io/library/golang:1.10-alpine@sha256:98c1f3458b21f50ac2e58 5.5s
=> => resolve docker.io/library/golang:1.10-alpine@sha256:98c1f3458b21f50ac2e5896d1 0.0s
=> => sha256:866414f805391b58973d4e3d76e5d32ae51baecb1c93762c9751b9d6c5 126B / 126B 0.0s
=> => sha256:ae8dbf6f23bf1c326de78fc780c6a870bf11eb86b45a7dc567 308.02kB / 308.02kB 0.0s
=> => sha256:44ccce322b34208317d748e998212cd677c16f1a58c2ff5e59578c 3.86kB / 3.86kB 0.0s
=> => sha256:0d01df27c53e651ecfa5c689dafb8c63c759761a757cc37e30eccc5e3a 153B / 153B 0.0s
=> => sha256:ff3a5c916c92643ff77519ffa742d3ec61b7f591b6b7504599d95a 2.07MB / 2.07MB 0.0s
=> => sha256:4be696a8d726150ed9636ea7156edcaa9ba8293df1aae49f9e 113.26MB / 113.26MB 0.0s
=> => sha256:98c1f3458b21f50ac2e5896d14a644eadb3adcae5afdceac0cc9c2 2.04kB / 2.04kB 0.0s
=> => sha256:bb31085d5c5db578edf3d4e5541cfb949b713bb7018bbac4dfd407 1.36kB / 1.36kB 0.0s
=> => unpacking docker.io/library/golang:1.10-alpine@sha256:98c1f3458b21f50ac2e5896 5.4s
=> local://context 0.8s
=> => transferring context: 116.83MB 0.8s
=> /bin/sh -c apk add --no-cache bash build-base gcc git libseccomp-dev linux 3.8s
=> copy /src-0 go/src/github.com/genuinetools/img/ 1.5s
=> /bin/sh -c go get -u github.com/jteeuwen/go-bindata/... 7.3s
=> /bin/sh -c make static && mv img /usr/bin/img 15.2s
=> /bin/sh -c git clone https://github.com/opencontainers/runc.git "$GOPATH/src/git 7.6s
=> /bin/sh -c apk add --no-cache bash git shadow shadow-uidmap strace 2.3s
=> copy /src-0/img usr/bin/img 0.5s
=> copy /src-0/runc usr/bin/runc 0.4s
=> /bin/sh -c useradd --create-home --home-dir $HOME user && chown -R user:user $H 0.4s
=> exporting to image 1.5s
=> => exporting layers 1.4s
=> => exporting manifest sha256:03e034afb839fe6399a271efc972da823b1b6297ea792ec94fa 0.0s
=> => exporting config sha256:92d033f9575176046db41f4f1feacc0602c8f2811f59d59f8e7b6 0.0s
=> => naming to r.j3ss.co/img:latest 0.0s
Successfully built r.j3ss.co/img:latest
Cross Platform
img
and the underlying buildkit
library support building containers for arbitrary platforms (OS and architecture combinations). In img
this can be achieved using the --platform
option, but note that
using the RUN
command during a build requires installing support for the desired platform, and any FROM
images used must exist for the target platform as well.
Some common platforms include:
- linux/amd64
- linux/arm64
- linux/arm/v7
- linux/arm/v6
- linux/s390x
- linux/ppc64le
- darwin/amd64
- windows/amd64
If you use multiple --platform
options for the same build, they will be included into a manifest and should work for the different platforms built for.
The most common way to get RUN
working in cross-platform builds is to install an emulator such as QEMU on the host system (static bindings are recommended to avoid shared library loading issues). To properly use the emulator inside the build environment, the kernel binfmt_misc parameters must be set with the following flags: OCF
.
You can check the settings in /proc
to ensure they are set correctly.
$ cat /proc/sys/fs/binfmt_misc/qemu-arm