Dockerizing applications with gentoo

By | 23. November 2014

Docker is a great tool to run applications in a standardized environment. But often, these containers are overblown. The usual way to build a docker container, is by taking an debian/ubuntu/cenots/whatever image from the Docker Hub and extend it with the applications and all dependencies. What remains in the container are things like a shell, systemd or other tools.

With the help of Gentoo and Paludis, an alternative package manager, we could easily build images, that are only containing the applications and its dependencies without the need of all the other stuff.

As we need to modify some paludis configurations, we should run your build environment within a container. To create a new gentoo image, we could simply import a stage3 tarball

curl \
    | bzcat | docker import - 'gentoo'

For a working build environment, we need to do some changes to the base image. We could do this with a Dockerfile. But it is less complicated if we use a interactive shell and commit the changes we made in the container to a new image. First launch the container:

docker run -ti gentoo /bin/bash

We need to sync the portage tree and install paludis

echo "sys-apps/paludis ~amd64" >> /etc/portage/package.keywords
emerge paludis

To generate a initial configuration we could use the portage2paludis script

bash portage2paludis.bash

Answer all questions with yes. Before we could use paludis, we have to fix the group memberships of the internal paludis user and regenerate the cache.

usermod -a -G root paludisbuild # /dev/tty is owned by root in docker containers
mkdir -p /usr/portage/.cache/names /var/db/pkg/.cache/names
cave fix-cache
chown paludisbuild: /var/tmp/paludis /usr/portage/distfiles
chmod g+rw /var/tmp/paludis /usr/portage/distfiles
cave sync

Now, we have to configure paludis. In the file /etc/paludis/bashrc we define the build parameters.

CFLAGS="-O3 -pipe"
LDFLAGS="-Wl,-O1 -Wl,--as-needed"
MAKEOPTS="-j5"  # how many parallel processes should be allow for make, use <num cpus>+1 

The build options could be set in /etc/paludis/use.conf

# disable docs, manpages and use the minimal version of a package if availible
*/* -bindist mmx sse sse2 -X minimal -man -doc -examples
# only install english language files
*/* LINGUAS: -* en
# we don't have any videocards
# and we have no input
# we only want one ruby target
*/* RUBY_TARGETS: -* ruby20
# don't build multilib versions
*/* ABI_X86: -32 64
# remove all debug symbols
*/* build_options: symbols=strip

Finally, we configure the repositories. We need to add a mirror to /etc/paludis/repositories/gentoo.conf

location = /usr/portage
sync = rsync://
profiles = /usr/portage/profiles/default/linux/amd64/13.0
distdir = /usr/portage/distfiles
format = e
names_cache = ${location}/.cache/names
write_cache = /var/cache/paludis/metadata

Now we create a new target repository for the new chroot target, we will create: /etc/paludis/repositories/chroot.conf

location = /build/db-pkg/
format = vdb
names_cache = ${location}/.cache/names
provides_cache = ${location}/.cache/provides
root = /build/root
name = chroot

Create the new directories for target and paludis:

mkdir -p /build/{root,db-pkg/.cache/{names,provides}}

The directory-structure under /build/root will be the base for our new docker images. It is missing some files and directories, that are not provided by any package we want to install in the chroot target.

mkdir /build/root/{dev,etc}
mknod -m 622 /build/root/dev/console c 5 1
mknod -m 666 /build/root/dev/null c 1 3
mknod -m 666 /build/root/dev/zero c 1 5
mknod -m 444 /build/root/dev/random c 1 8
mknod -m 444 /build/root/dev/urandom c 1 9
touch /build/root/etc/

To keep our resulting image as small as possible, we will create a hook under /etc/paludis/hooks/ebuild_install_post/clean_docs.bash:

source ${PALUDIS_ECHO_FUNCTIONS_DIR:-${PALUDIS_EBUILD_DIR}}/echo_functions.bash
if [ ${CAVE_PERFORM_CMDLINE_destination} = "chroot" ]; then
    shopt -s nullglob
    shopt -s dotglob
    einfo "installing to chroot, cleaning docs and man"
    rm -rf ${D}/usr/share/{doc,info,man}
    (( ${#chk_files[*]} )) || rmdir ${D}/usr/share/

Our target is ready to use. We now could install something into our chroot target, with the following command:

cave resolve -mc -/b --chroot-path /build/root/ -0 '*/*::installed' -x <somepackage>

As we don’t want to remember this bulk of options, we add some aliases to /etc/bash/bashrc

alias cave-i='cave resume --resume-file ~/.cave-resume'
alias cave-p='cave resolve --resume-file ~/.cave-resume'
alias cave-chroot="cave resolve --resume-file ~/.cave-resume -mc -/b --chroot-path /build/root/ -0 '*/*::installed'"

With this aliases we could install easily install packages to the system and the target chroot:

cave-p vim # build the dependency tree to install "vim" to the system and display it
cave-i # install the last dependency tree

cave-chroot glibc # like above but for the chroot
cave-i # install glibc to chroot

After we have installed all software we need into the chroot, we could create a tar file from the content and use it to create a new docker image to work with. To use dynamical linked software within the container, we always have to install glibc. But as we want a clean target for our images, we first commit clean up and commit our changes.

rm -rf /usr/portage/distfiles/*

Leave the container and commit the changes.

$ docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                       PORTS               NAMES
56dab46a7953        gentoo:latest       "/bin/bash"         2 hours ago         Exited (130) 4 seconds ago                       romantic_goldstine
$ docker commit 56dab46a7953 gentoo:latest

Now we have a Docker image, we could use to create lightweight images. You should read the paludis documentation and the Gentoo Ebuild Manuals, to learn, how you could create a own repository for your software and dependencies, to dockerize them.

2 thoughts on “Dockerizing applications with gentoo

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Kommentarlinks könnten nofollow frei sein.