How to make Jenkins build NodeJS, Ruby, and Maven on Docker

TL;DR see the final GitHub repo

I thought I'd share with you how we are increasing our productivity here at Quantum Mob. When you are a small team you have to do more with less. Two of the greatest weapons we use to slice overhead to confetti are Jenkins and Docker. Jenkins can speed up repetitive tasks that robots are much better performing and Docker simplifies spinning up VM's for your application in a very simple and repeatable way. What I'm going to cover here is how to setup your Jenkins server in a Docker container and how to fix some of the limits of the Jenkins official image. Let's get you up and building faster. Don't worry, before I get into the thick of it I'll provide a little background about Docker & Jenkins for those that haven't had much chance to play with them.

Docker

Docker is the most popular containerization platform. Containerization helps by describing everything need to get an app running. OS binaries, all the software dependencies you need to install, environment variables... absolutely everything. Think a lighter virtual machine on steroids with your application pre-installed. No more head scratching wondering why it runs perfectly on your machine but not in DEV. No more hours of debugging only to discover the version of node or java are different locally. You build it and it runs the same everytime, everywhere.

Here is how Docker describes itself:

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.

You can wrap just about anything in a container, describing the container via a Dockerfile. And docker has a massive marketplace for open source containers called Docker Hub. Some of the most popular containers are Nginx, Redis, MySQL, Mongo and Elasticsearch, but the one that caught my eye was Jenkins.

Jenkins

Jenkins is a continuous integration server. It is very flexible and can perform quite a few tricks right out of the box. It offers webhooks that github, bitbucket, and others can call when you push your code which saves a lot of time. It ensures your unit tests are executed everytime and can refresh your server with the compiled code if it builds successfully.

While it is nice of Jenkins to offer an official docker image, you'll quickly run into a few issues if you are doing anything even slightly other than compiling plain Java. For instance, Maven, the popular dependency management tool for Java, is not included in the Docker container. If you want to work with Ruby or NodeJS you'll notice those are missing as well. Let's make our Jenkins build a little more robust.

Jenkins on Docker

Right near the beginning of the official Dockerfile you'll see this line:

RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*  

Because we work with Java, JavaScript, and NodeJS at Quantum Mob, here is my modified file:

ENV BASE_APKS="sudo openssl openssh-client zip ttf-dejavu maven ruby" \  
    BUILD_APKS=" make gcc clang g++ paxctl binutils-gold autoconf bison"

RUN apt-get install -y $BASE_APKS $BUILD_APKS \  
      && rm -rf /var/lib/apt/lists/*

This makes sudo access available in our shell scripts incase we need it. It also adds Maven, Ruby and a couple other tools that will become useful. If you are using something else like Python or Go, this would be where you would add it.

Now we're going to add NodeJS to our container, based on node.docker.

ENV NODE_PREFIX=/usr/local \  
    NODE_VERSION=6.4.0 \
    NPM_VERSION=latest \
    NODE_SOURCE=/usr/src/node

RUN [ "${NODE_VERSION}" == "latest" ] && { \  
        DOWNLOAD_PATH=https://nodejs.org/dist/node-latest.tar.gz; \
    } || { \
        DOWNLOAD_PATH=https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}.tar.gz; \
    }; \
    mkdir -p $NODE_SOURCE && \
    wget -O - $DOWNLOAD_PATH -nv | tar -xz --strip-components=1 -C $NODE_SOURCE && \
    cd $NODE_SOURCE && \
    export GYP_DEFINES="linux_use_gold_flags=0" && \
    ./configure --prefix=$NODE_PREFIX $NODE_CONFIG_FLAGS && \
    make -j$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \
    make install;

RUN paxctl -cm ${NODE_PREFIX}/bin/node && \  
    cd / && \
    if [ -x /usr/bin/npm ]; then \
      npm install -g npm@${NPM_VERSION} && \
      find /usr/lib/node_modules/npm -name test -o -name .bin -type d | xargs rm -rf; \
    fi && \
    rm -rf \
        ${NODE_SOURCE} \
        ${NODE_PREFIX}/include \
        ${NODE_PREFIX}/share/man \
        /tmp/* \
        /var/cache/apk/* \
        /root/.npm \
        /root/.node-gyp \
        /root/.gnupg \
        ${NODE_PREFIX}/lib/node_modules/npm/man \
        ${NODE_PREFIX}/lib/node_modules/npm/doc \
        ${NODE_PREFIX}/lib/node_modules/npm/html \
    && \
    mkdir -p /app && \
    exit 0 || exit 1;

Setting up NodeJS was certainly more verbose, but it handles downloading the tar as well as ensuring all the correct configurations have been made. Now we're all set with NodeJS and NPM.

Lastly, I add yarn, gulp, grunt, bower, and sass to our build. This will allow the most common NodeJS libraries to be present when we are building our projects.

RUN npm install -g yarn \  
  && yarn global add gulp grunt node-sass bower

Conclusion

You've learned to create a Jenkins Docker-container that's ready to run jobs for Java, Ruby and NodeJS projects. Next time I'll cover how we automated all projects to use this version of Jenkins, which allows us to set up new projects across environments and hop on development in minutes.

Source code

Star

Erich Reich

Erich is a Co-Founder and Director of Technology with Quantum Mob

Toronto, Canada https://qmo.io

Need help with docker?

Let's work together.

Subscribe to Quantum Thoughts

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!