in DevOps

Creating RPMS with fpm and docker

Now and again, you need to create RPMS of third-party tools, such as Python libraries or Ruby gems.  The most effective (and best!) way to do this is with fpm. Historically, I have built RPMS using a few dedicated virtual machines for CentOS5 and CentOS6-specific builds. These virtual machines have gotten crufty with all the various libraries installed. Wouldn’t it be nice to have a clean environment every time you ran fpm? Docker allows me to have a quickly instantiated, clean environment for building these RPMS without having to worry about previous builds causing problems or conflicts.

Install Docker

These instructions assume you’re running Docker on CentOS7. Adjust accordingly for your OS.

First, install Docker onto your system.

$ sudo tee /etc/yum.repos.d/docker.repo <<-'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF
 
$ sudo yum install -y docker-engine docker-engine-selinux
$ sudo service docker start

Setup your Dockerfile

A Dockerfile is like a Makefile. It provides a concrete, repeatable set of steps to accomplish your build, while allowing you to iterate quickly as you add new steps to the build process.

This is the Dockerfile I created.

$ mkdir ~/tmp/fpm
$ cd ~/tmp/fpm

$ cat < Dockerfile
FROM centos:6
RUN yum update -y
RUN yum -y install rubygems ruby-devel ruby-json gcc gcc-c++ python-setuptools rpm-build openssh-clients
RUN gem install cabin -v 0.7.2 && gem install fpm
RUN mkdir /tmp/fpmbuild
VOLUME ["/tmp/fpmbuild"]
WORKDIR /tmp/fpmbuild
RUN bash
EOF

The build process for a Docker image takes the instructions from the Dockerfile and generates several layers, each overlaying the previous one. In my Dockerfile, I begin with a base OS image using CentOS6 which creates the first layer, building new layers with each RUN command.  These layers offer a lightweight mechanism for making changes to the image.

Build your Docker image

Next, I build the image so I can use it for running fpm builds.

$ docker build -t fpm-centos6 .
.
.
[stuff deleted]
.
.
Successfully built 
 
$ docker images
REPOSITORY          TAG           IMAGE ID            CREATED             VIRTUAL SIZE
fpm-centos6         latest        6317eaa241a6        23 minutes ago      593.7 MB

Run your docker image

This will give you a working and isolated fpm build environment.  Running this Docker image will drop you into a bash shell.

$ mkdir /tmp/fpmbuild
$ docker run -ti -v /tmp/fpmbuild:/tmp/fpmbuild fpm-centos6
[root@243861237bad fpmbuild]#

This Docker command ties the VOLUME command in my Dockerfile with the directory living outside of my container.  This creates a pass through between the container and container host using a bind mount. You will see below why this is important.

Install any third-party dependencies

If your build requires any other development libraries or third-party dependencies, you would install them at this stage.

NOTE: These dependencies go away when you exit the running fpm container shell, allowing you to always return to a clean environment.

[root@243861237bad fpmbuild]# yum -y install foo bar baz

Run your fpm command

Now that the build environment is up and running and all of my build dependencies are satisfied, I can fire off the fpm command.  In this case, I’m building a python library into an RPM.

[root@243861237bad fpmbuild]#  fpm -s python -t rpm thirdpartypkg
Created package {:path=>"python-thirdpartypkg-2.7.2-1.noarch.rpm"}
 
[root@243861237bad fpmbuild]# ls
python-thirdpartypackage-2.7.2-1.noarch.rpm

[root@243861237bad fpmbuild]# rpm -qpi *.rpm
Name        : python-thirdpartypkg         Relocations: /
Version     : 2.7.2                             Vendor: none
Release     : 1                             Build Date: Fri 08 Jan 2016 12:59:24 AM UTC
Install Date: (not installed)               Build Host: 243861237bad
Group       : default                       Source RPM: python-thirdpartypkg-2.7.2-1.src.rpm
Size        : 873582                           License: Apache License 2.0
Signature   : (none)
Packager    : <@243861237bad>
URL         : http://github.com/someone/thirdpartypackage
Summary     : A third party package
Description :
A third party package

Exit your fpm Docker container

Through the magic of Docker VOLUME mounts, the RPMS you have created inside the container are saved outside of the container in the container host.  This makes it easy to copy your RPMs to a Yum repo.

[root@243861237bad fpmbuild]# exit
exit
$ cd /tmp/fpmbuild

$ ls 
python-thirdpartypkg-2.7.2-1.noarch.rpm

Going forward

This was my first non-lab experience with Docker. I’m quite thrilled at how quick it was to set up and get the environment working. Iterating at each step was easy, allowing me to play around with different ideas for how I wanted the Dockerfile to work.

If you’d like to follow along, I’ve created a dockerfiles repo on my Github account where I’ll be sticking useful images in the future.  You can find my fpm-cent6 Docker image there, too.

Travis Campbell
Staff Systems Engineer at ghostar
Travis Campbell is a seasoned Linux Systems Engineer with nearly two decades of experience, ranging from dozens to tens of thousands of systems in the semiconductor industry, higher education, and high volume sites on the web. His current focus is on High Performance Computing, Big Data environments, and large scale web architectures.