Tarballs, the ultimate container image format
A year ago we introduced guix pack
,
a tool that allows you to create “application bundles” from a set of Guix
package definitions. On your Guix machine, you run:
guix pack -S /opt/gnu/bin=bin guile gnutls guile-json
and you get a tarball containing your favorite programming language
implementation and a couple of libraries, where /opt/gnu/bin
is a
symlink to the bin
directory containing, in this case, the guile
command. Add -f docker
and, instead of a tarball, you get an image in
the Docker format that you can pass to docker load
on any machine
where Docker is installed. Overall that’s a relatively easy way to
share software stacks with machines that do not run Guix.
The tarball format is plain and simple, it’s the one we know and love,
and it’s been there “forever” as its name
suggests.
The tarball that guix pack
produces can be readily extracted on
another machine, one that doesn’t run Guix, and you’re done. The
problem though, is that you’ll need to either unpack the tarball in the
root file system or to play tricks with the unshare
command, as we saw
in the previous
post.
Why can’t we just extract such a tarball in our home directory and
directly run ./opt/gnu/bin/guile
for instance?
Relocatable packages
The main issue is that, except in the uncommon case where developers
went to great lengths to make it possible (as with
GUB, see the *-reloc*.patch
files), packages
built for GNU/Linux are not relocatable. ELF files embed things like
the absolute file name of the dynamic linker, directories where
libraries are to be search for (they can be relative file names with
$ORIGIN
but usually aren’t), and so on; furthermore, it’s very common
to embed things like the name of the directory that contains locale data
or other application-specific data. For Guix-built software, all these
are absolute file names under /gnu/store
so Guix-built binaries won’t
run unless those /gnu/store
files exist.
On machines where support for “user
namespaces”
is enabled, we can easily “map” the directory where users unpacked the
tarball that guix pack
produced to /gnu/store
, as shown in the
previous post:
$ tar xf /path/to/pack.tar.gz
$ unshare -mrf chroot . /opt/gnu/bin/guile --version
guile (GNU Guile) 2.2.0
It does the job but remains quite tedious. Can’t we automate that?
guix pack --relocatable
The --relocatable
(or -R
) option of guix pack
, which landed a few
days ago, produces tarballs with
automatically relocatable binaries. Back to our earlier example, let’s
say you produce a tarball with this new option:
guix pack --relocatable -S /bin=bin -S /etc=etc guile gnutls guile-json
You can send the resulting tarball to any machine that runs the kernel Linux (it doesn’t even have to be GNU/Linux) with user namespace support—which, unfortunately, is disabled by default on some distros. There, as a regular user, you can run:
$ tar xf /path/to/pack.tar.gz
$ source ./etc/profile # define ’GUILE_LOAD_PATH’, etc.
$ ./bin/guile
guile: warning: failed to install locale
GNU Guile 2.2.3
Copyright (C) 1995-2017 Free Software Foundation, Inc.
Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.
Enter `,help' for help.
scheme@(guile-user)> ,use(json)
scheme@(guile-user)> ,use(gnutls)
We were able to run Guile and to use our Guile libraries since sourcing
./etc/profile
augmented the GUILE_LOAD_PATH
environment variable
that tells Guile where to look for libraries. Indeed we can see it by
inspecting the value of %load-path
at the Guile prompt:
scheme@(guile-user)> %load-path
$1 = ("/gnu/store/w9xd291967cvmdp3m0s7739icjzgs8ns-profile/share/guile/site/2.2" "/gnu/store/b90y3swxlx3vw2yyacs8cz59b8cbpbw5-guile-2.2.3/share/guile/2.2" "/gnu/store/b90y3swxlx3vw2yyacs8cz59b8cbpbw5-guile-2.2.3/share/guile/site/2.2" "/gnu/store/b90y3swxlx3vw2yyacs8cz59b8cbpbw5-guile-2.2.3/share/guile/site" "/gnu/store/b90y3swxlx3vw2yyacs8cz59b8cbpbw5-guile-2.2.3/share/guile")
Wait, it’s all /gnu/store
! As it turns out, guix pack --relocatable
created a wrapper around guile
that populates /gnu/store
in the
mount namespace of the process. Even though /gnu/store
does not exist
on that machine, our guile
process “sees” our packages under
/gnu/store
:
scheme@(guile-user)> ,use(ice-9 ftw)
scheme@(guile-user)> (scandir "/gnu/store")
$2 = ("." ".." "0249nw8c7z626fw1fayacm160fpd543k-guile-json-0.6.0R" "05dvazr5wfh7lxx4zi54zfqnx6ha8vxr-bash-static-4.4.12" "0jawbsyafm93nxf4rcmkf1rsk7z03qfa-libltdl-2.4.6" "0z1r7ai6syi2qnf5z8w8n25b1yv8gdr4-info-dir" "1n59wjm6dbvc38b320iiwrxra3dg7yv8-libunistring-0.9.8" "2fg01r58vv9w41kw6drl1wnvqg7rkv9d-libtasn1-4.12" "2ifmksc425qcysl5rkxkbv6yrgc1w9cs-gcc-5.5.0-lib" "2vxvd3vls7c8i9ngs881dy1p5brc7p85-gmp-6.1.2" "4sqaib7c2dfjv62ivrg9b8wa7bh226la-glibc-2.26.105-g0890d5379c" "5kih0kxmipzjw10c53hhckfzkcs7c8mm-gnutls-3.5.13" "8hxm8am4ll05sa8wlwgdq2lj4ddag464-zlib-1.2.11" "90vz0r78bww7dxhpa7vsiynr1rcqhyh4-nettle-3.4" "b90y3swxlx3vw2yyacs8cz59b8cbpbw5-guile-2.2.3" "c4jrwbv7qckvnqa7f3h7bd1hh8rbg72y-libgc-7.6.0" "f5lw5w4nxs6p5gq0c2nb3jsrxc6mmxbi-libgc-7.6.0" "hjxic0k4as384vn2qp0l964isfkb0blb-guile-json-0.6.0" "ksyja5lbwy0mpskvn4rfi5klc00c092d-libidn2-2.0.4" "l15mx9lrwdflyvmb4a05va05v5yqizg5-libffi-3.2.1" "mm0zclrzj3y7rj74hzyd0f224xly04fh-bash-minimal-4.4.12" "vgmln3b639r68vvy75xhcbi7d2w31mx1-pkg-config-0.29.2" "vz3zfmphvv4w4y7nffwr4jkk7k4s0rfs-guile-2.2.3" "w9xd291967cvmdp3m0s7739icjzgs8ns-profile" "x0jf9ckd30k3nhs6bbhkrxsjmqz8phqd-nettle-3.4" "x8z6cr7jggs8vbyh0xzfmxbid63z6y83-guile-2.2.3R" "xbkl3nx0fqgpw2ba8jsjy0bk3nw4q3i4-gnutls-3.5.13R" "xh4k91vl0i8nlyrmvsh01x0mz629w5a9-gmp-6.1.2" "yx12x8v4ny9f6fipk8285jgfzqavii83-manual-database" "zksh1n0p9x903kqbvswgwy2vsk2b7255-libatomic-ops-7.4.8")
The wrapper is a small statically-linked C
program.
(Scheme would be nice and would allow us to reuse
call-with-container
,
but it would also take up more space.) All it does is create a child
process with separate mount and user namespaces, which in turn mounts
the tarball’s /gnu/store
to /gnu/store
, bind-mounts other entries
from the host root file system, and chroot
s into that. The result is
a binary that sees everything a “normal” program sees on the host, but
with the addition of /gnu/store
, with minimal startup overhead.
In a way, it’s a bit of a hack: for example, what gets bind-mounted in the mount namespace of the wrapped program is hard-coded, which is OK, but some flexibility would be welcome (things like Flatpak’s sandbox permissions, for instance). Still, that it Just Works is a pretty cool feature.
Tarballs vs. Snap, Flatpak, Docker, & co.
Come to think of it: if you’re a developer, guix pack
is probably
one of the easiest ways to create an “application bundle” to share with
your users; and as a user, these relocatable tarballs are about the
simplest thing you can deal with since you don’t need anything but
tar
—well, and user namespace support. Plus, since they are
bit-reproducible,
anyone can rebuild them to ensure they do not contain
malware or to
check the provenance and licensing of its
contents.
Application bundles cannot replace full-blown package management, which allows users to upgrade, get security updates, use storage and memory efficiently, and so on. For the purposes of quickly sharing packages with users or with Guix-less machines, though, you might find Guix packs to be more convenient than Snap, Flatplak, or Docker. Give it a spin and let us know!
About GNU Guix
GNU Guix is a transactional package manager for the GNU system. The Guix System Distribution or GuixSD is an advanced distribution of the GNU system that relies on GNU Guix and respects the user's freedom.
In addition to standard package management features, Guix supports transactional upgrades and roll-backs, unprivileged package management, per-user profiles, and garbage collection. Guix uses low-level mechanisms from the Nix package manager, except that packages are defined as native Guile modules, using extensions to the Scheme language. GuixSD offers a declarative approach to operating system configuration management, and is highly customizable and hackable.
GuixSD can be used on an i686, x86_64 and armv7 machines. It is also possible to use Guix on top of an already installed GNU/Linux system, including on mips64el and aarch64.
Unless otherwise stated, blog posts on this site are copyrighted by their respective authors and published under the terms of the CC-BY-SA 4.0 license and those of the GNU Free Documentation License (version 1.3 or later, with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts).