Every time I need to compile some code which I downloaded on the Internet, I have to spend a lot of time installing tools and libraries. Sometimes I need to cross-compile code or patch a library, and I need to configure some local environment which occupies space on my disk, but I can't throw away because I might need it again later.
What if I could easily and reliabily create the environment when I need?
Docker represents a versatile tool which can help to simplify and eliminate tedious operations such as preparing an environment for compiling code. This principle can be extended to any build process, and actually there are already companies such as CircleCI, which have adopted Docker for their CI/CD solution.
Personally I find Docker extremely helpful for creating a build environment. I don't need to install
tools and libraries on my local computer or on a virtual machine. I can build an environment for each
library or application I want to build, I can throw it away and then recreate it everytime I need.
I can have multiple versions of the same tools, but in different containers, so I don't need
to worry about conflicts. I can commit my Dockerfile into some git repository, I can change
computer, checkout my code and I am still able to build, because I just need Docker and the Dockerfile.
No more "it works for me" excuses :)
Pull base image
There are many images which we can use to create our build environment. We can choose among Ubuntu, Debian,
Fedora, Centos, and others which are available on Docker Hub.
Let's assume we choose Ubuntu, we can pull any version we want just passing a tag:
docker pull ubuntu:latest
docker pull ubuntu:16.04
Create Dockerfile
We want to create our build environment using Ubuntu as operating system.
We can do that creating a Dockerfile where we declare the commands required
to install all the tools we need, for example the GNU C compiler:
mkdir build-c-image
cat <<EOF >build-c-image/Dockerfile
FROM ubuntu:16.04
RUN apt-get update -y && apt-get install -y gcc
EOF
Build custom image
We can now create a new image using our Dockerfile:
docker build -t build-c build-c-image
Compile and execute code
We can easily compile and execute a simple C program:
mkdir -p project/src project/bin
cat <<EOF >project/src/main.c
#include <stdio.h>
int main(int argv, char ** argc) {
printf("Hello Docker!\n");
}
EOF
docker run -it -v $(pwd)/project:/project build-c gcc -o /project/bin/main /project/src/main.c
docker run -it -v $(pwd)/project:/project build-c /project/bin/main
Cross-compile code
We can install cross compilation tools like Mingw-w64 and compile code for Windows:
mkdir build-mingw-w64-image
cat <<EOF >build-mingw-w64-image/Dockerfile
FROM ubuntu:16.04
RUN apt-get update -y && apt-get -y install mingw-w64
EOF
docker build -t build-mingw-w64 build-mingw-w64-image
mkdir -p project/src project/bin
cat <<EOF >project/src/main.c
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MessageBox(NULL, "Hello World!", "My app", MB_OK);
}
EOF
docker run -it -v $(pwd)/project:/project build-mingw-w64 /usr/bin/i686-w64-mingw32-gcc -o /project/bin/main.exe -luser32 /project/src/main.c
Customize a library
We can compile a customized version of a library like ffmpeg:
mkdir build-autotools-image
cat <<EOF >build-autotools-image/Dockerfile
FROM ubuntu:16.04
RUN apt-get update -y && apt-get -y install git make gcc autoconf nasm yasm pkg-config
EOF
docker build -t build-autotools build-autotools-image
mkdir output
docker run -it --rm -v $(pwd)/output:/output build-autotools git clone https://github.com/FFmpeg/FFmpeg.git /output/ffmpeg
docker run -it --rm -v $(pwd)/output:/output build-autotools bash -c "cd /output/ffmpeg && ./configure --enable-gpl --disable-ffserver && make"
Execute isolated builds
One common issue with CI servers is managing multiple versions of the tools required for building code.
Managing multiple versions of different tools means that we need to install and configure several plugins
in our CI server and we need to configure our builds to use the correct version of those tools.
A simplier approach, which is available in Jenkins
and other CI/CD solutions such as CircleCI,
would be to use Docker to create the environment with the required tools for executing our builds.
Using this approach we relay on Docker for creating isolated build processes which can easily run in parallel.