Building RPM packages using Docker

When you manage one or two packages, it’s a good idea to spin up a virtual machine as build environment just for that. Using some tricks it can be made possible to build them automatically.

Here I want to show fully automated approach using headless build host inside a Docker container.

A word on Docker

Docker is a containerization platform. In brief, it’s a lot like a headless virtual machine plus interactive1 shell. Or a chrooted environment… on steroids.

Docker consists of several parts like docker-engine, docker-compose, docker-machine and docker-swarm.

Docker-engine is the basic tool which purpose is to create and run images in containers, managing virtual networks.

Further in this post I assume the reader already knows docker basics.

RPM packaging

Creating of a package, when done manually, is fairly error prone. Such processes as version and release management, manual dependencies declarations or updating a repository require a lot of attention.

Also, to create an RPM package, put it into repository and update an index of that repo, one must do all that inside and rpm-based environment. The distro I used is Alt Linux, which is a bit odd. The thing is, it uses apt package manager, but the system itself is rpm-based. They used apt-rpm with it’s own repository structure, but putting that aside it looks a lot like ordinary apt. But it’s not my work environment, so at first Alt Linux lived inside a VirtualBox machine. It’s worth to give it a credit: virtual machine snapshots are awesome. But overall, spinning a virtual machine and building a dozen of packages by hand felt not right.

There are tools like Vargrant exist to make life a little easier for anyone who does a lot of boilerplate work with virtual machines. I think it’s awesome when one needs a development environment closely mimicking production environment or to have some environment at all, like using your linux setup under windows.

Docker to the rescue

Containers that serve a single purpose, seems like it. Actually, building a package is just a properly set rpmbuild call. Indexing a repository is a bit more complicated, but all in all it’s just 20 lines of Bash.

Luckily, there is an image of altlinux-p7 on the docker hub, which was just what I need. Here is a Dockerfile

FROM fotengauer/altlinux-p7

RUN apt-get update && apt-get -y install git rpm-build apt-repo-tools

ADD ./i386-alt-linux /usr/lib/rpm/i386-alt-linux
ADD ./i486-alt-linux /usr/lib/rpm/i486-alt-linux
ADD ./i586-alt-linux /usr/lib/rpm/i586-alt-linux
ADD ./i686-alt-linux /usr/lib/rpm/i686-alt-linux

RUN ln -s /usr/lib/rpm/i386-alt-linux /usr/lib/rpm/i386-linux
RUN ln -s /usr/lib/rpm/i486-alt-linux /usr/lib/rpm/i486-linux
RUN ln -s /usr/lib/rpm/i586-alt-linux /usr/lib/rpm/i586-linux
RUN ln -s /usr/lib/rpm/i686-alt-linux /usr/lib/rpm/i686-linux

RUN useradd -m builder
RUN useradd -M user
WORKDIR /home/builder
USER builder

ADD ./script /opt/

CMD /opt/script

These four files are macro definitions for build targets of the same name. This image is x86_64 and macro’s are necessary to build x86 binary rpms. A script looks like this:

#!/bin/bash

mkdir -p /tmp/pkgbuild
cd /tmp/pkgbuild
tar xvp </dev/stdin
ls -alh &>/dev/stderr

make

I’ll explain myself a little further. To build an image, use docker build -t rpmbuild . from the directory with script and Dockerfile.

Here comes the interesting part: the resulting image is meant to be dispossably used with a tar archive of project directory. Directory itself:

Makefile
PKGNAME
RELEASE
TARGET
package/
project.spec
project-file-1
...
project-file-n

All projects share the same Makefile which is as follows:

name=$(shell cat PKGNAME)
target=$(shell target)
version=$(shell git tag | tail -1)
src=$(shell git ls-files)

rpm: rpm-tarball
	rpmbuild \
	  --target $(target) \
	  --define "_topdir /tmp/"
	rm -f $(name)-$(version).tar.gz

rpm-tarball:
	tar czvpf $(name)-$(version).tar.gz $(src) --transform '/^./$(name)-$(version)/' --show-transformed

docker-rpm:
	tar cvp . -O | docker run -i -v ./package:/tmp/RPMS rpmbuild

docker-repo:
	docker run -i -v './package:/tmp/RPMS' -v '/myrepo:/tmp/repo' rpmrepo && echo $$$$((`cat RELEASE` + 1)) > RELEASE

Here rpmrepo image looks a lot like rpmbuild, except the script. It contains some setup and call to genbasedir2. docker-rpm target relies on piping tared current directory to stdout and piping it to rpmbuild container, which then decompresses it to /tmp3.

This post has shown how docker images can be used as a lightweight and highly automated alternative to virtual machines.

stdin and stdout. Volumes might be used here as well.


  1. or not, it depends on the setup and needs 

  2. genbasedir generates APT RPM repository control files 

  3. Actually, this is used just to show how Docker container can be used as an ordinary program with own 

SHOW COMMENTS (0)