Build Software with Docker

How to build Software inside a Docker container

It appears that you are using AdBlocking software. The cost of running this website is covered by advertisements. If you like it please feel free to a small amount of money to secure the future of this website.

Introduction

Two of the major requirements for a Continuous Integration/Delivery system (CI/CD) are:

  • Isolation: the ability to run a well isolated environment from other builds executed on the same infrastructure;
  • Reproducibility : the ability to reproduce the build environment from a pre-defined setup.

These requirements also apply to local development environments, ensuring developers the ability of consistently reproduce building and testing processes.

A great way to bootstrap reproducible and isolated environments is using Docker containers.

“Docker containers wrap a piece of software in a complete filesystem that contains everything needed to run: code, runtime, system tools, system libraries – anything that can be installed on a server. This guarantees that the software will always run the same, regardless of its environment.”

Compared to a Virtual Machine (VM), Docker it’s faster to launch and lighter to use. The Docker image can be defined both as a binary image pulled from a repository, or as a plain text Dockerfile that can be stored along side the project source, so that the source and environment are always in sync and recorded.

Docker Builds

Following the Software Structure and Software Automation guides, we can easily add a Docker-based building system to a project in such a way that can be used both manually and automatically via CI/CD:

1. Dockerfile “resources/DockerDev/Dockerfile

This file defines the base Docker development environment. It can be based on a minimum standard image and define all the required dependencies, or it can inherit from a custom Docker image that can be shared across multiple software projects. In any case we can add here any extra dependency and configuration.

Projects like tecnickcom/alldev can be created to generate custom Docker images to be reused across various projects. Each new image is tagged with the version number and the “latest” tag.

Example

FROM phusion/baseimage
MAINTAINER name.surname@example.com

ENV DEBIAN_FRONTEND noninteractive
ENV TERM linux
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
ENV HOME /root
ENV DISPLAY :0

ENV GOPATH=/root
ENV PATH=/usr/local/go/bin:$GOPATH/bin:$PATH

# Install extra packages
RUN apt-get install -y build-essential

# Install and configure GO
RUN wget https://storage.googleapis.com/golang/go1.7.3.linux-amd64.tar.gz && \
tar xvf go1.7.3.linux-amd64.tar.gz && \
mv go /usr/local && \
mkdir -p /root/bin && \
mkdir -p /root/pkg && \
mkdir -p /root/src && \
echo 'export GOPATH=/root' >> /root/.profile && \
echo 'export PATH=/usr/local/go/bin:$GOPATH/bin:$PATH' >> /root/.profile && \
go version

2. Building Script “dockerbuild.sh

This script is used to generate a temporary Docker container to build and test the software and copy the results back in the project target folder.

#!/bin/sh
#
# dockerbuild.sh
#
# Build the software inside a Docker container
#
# @author      Nicola Asuni <info@tecnick.com>
# @copyright   2015-2016 Nicola Asuni - Tecnick.com LTD
# ------------------------------------------------------------------------------

# NOTES:
# This script requires Docker

# EXAMPLE USAGE:
# CVSPATH=project VENDOR=vendorname PROJECT=projectname MAKETARGET=buildall ./dockerbuild.sh

# Get vendor and project name
: ${CVSPATH:=project}
: ${VENDOR:=vendor}
: ${PROJECT:=project}

# make target to execute
: ${MAKETARGET:=buildall}

# Name of the base development Docker image
DOCKERDEV=${VENDOR}/dev_${PROJECT}

# Build the base environment and keep it cached locally
docker build -t ${DOCKERDEV} ./resources/DockerDev/

# Define the project root path
PRJPATH=/root/src/${CVSPATH}/${PROJECT}

# Generate a temporary Dockerfile to build and test the project
# NOTE: The exit status of the RUN command is stored to be returned later,
#       so in case of error we can continue without interrupting this script.
cat > Dockerfile <<- EOM
FROM ${DOCKERDEV}
RUN mkdir -p ${PRJPATH}
ADD ./ ${PRJPATH}
WORKDIR ${PRJPATH}
RUN make ${MAKETARGET} || (echo \$? > target/make.exit)
EOM

# Define the temporary Docker image name
DOCKER_IMAGE_NAME=${VENDOR}/build_${PROJECT}

# Build the Docker image
docker build --no-cache -t ${DOCKER_IMAGE_NAME} .

# Start a container using the newly created Docker image
CONTAINER_ID=$(docker run -d ${DOCKER_IMAGE_NAME})

# Copy all build/test artifacts back to the host
docker cp ${CONTAINER_ID}:"${PRJPATH}/target" ./

# Remove the temporary container and image
docker rm -f ${CONTAINER_ID} || true
docker rmi -f ${DOCKER_IMAGE_NAME} || true

3. Makefile Target “dbuild

Following the model proposed in the Software Automation guide, we can use the “make dbuild” command to execute the building script both manually or via CI/CD task.

# Full build and test sequence
buildall: deps build qa

# Build everything inside a Docker container
dbuild:
        @mkdir -p target
        @rm -rf target/*
        @echo 0 > target/make.exit
        CVSPATH=$(CVSPATH) VENDOR=$(VENDOR) PROJECT=$(PROJECT) MAKETARGET='$(MAKETARGET)' ./dockerbuild.sh
        @exit `cat target/make.exit`

In this example we are returning the exit code of the “make buildall” command.

An arbitrary make target can be executed inside a Docker container by specifying the MAKETARGET parameter:

MAKETARGET='qa' make dbuild

The list of make targets can be obtained by typing make.

Useful Docker Commands

To manually create a container you can execute:

docker build --tag="example/devenv" ./resources/DockerDev/

where:

  • example/devenv is name of the Docker image
  • ./resources/DockerDev/ is the path to the directory containing the Dockerfile

To log into the newly created container:

docker run -t -i example/devenv /bin/bash

To get the container ID:

CONTAINER_ID=`docker ps -a | grep example/devenv | cut -c1-12`

To delete the newly created docker container:

docker rm -f $CONTAINER_ID

To delete the docker image:

docker rmi -f example/devenv

To delete all containers

docker rm $(docker ps -a -q)

To delete all images

docker rmi $(docker images -q)

Examples

 

© 1998-2023 – Nicola Asuni - Tecnick.com - All rights reserved.
about - disclaimer - privacy