
Habr has many articles about Jenkins, but few describe examples of working with Jenkins and Docker agents. All popular project build tools like , , , and others can build everything in containers. But what about Jenkins?
Today, there is a solution to this problem: Jenkins 2 works wonderfully with . In this article, I want to share my experience and show you how you can do this yourself.
Why did I tackle this problem?
Since we at use many different technologies, our build machine needs to hold various versions of Node.JS, Gradle, Ruby, JDK, and others. But version conflicts are often unavoidable. Yes, you would be right to say that there are various version managers like nvm, rvm, but it’s not all that smooth with them, and these solutions have their issues:
- a large volume of runtimes that developers forget to clean;
- there are conflicts between different versions of the same runtimes;
- each developer needs a different set of components.
There are other problems as well, but let me tell you about the solution instead.
Jenkins in Docker
Since Docker is now well entrenched in development, almost everything can be launched using Docker. My solution is that Jenkins runs in Docker and can launch other Docker containers. This question was raised back in 2013 in the article “«.
In short, it is necessary to install Docker itself in the working container and mount the file. /var/run/docker.sock.
Here’s an example Dockerfile that was created for Jenkins.
FROM jenkins/jenkins:lts
USER root
RUN apt-get update &&
apt-get -y install apt-transport-https
ca-certificates
curl
gnupg2
git
software-properties-common &&
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey &&
add-apt-repository
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")
$(lsb_release -cs)
stable" &&
apt-get update &&
apt-get -y install docker-ce &&
usermod -aG docker jenkins
RUN curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose
RUN apt-get clean autoclean && apt-get autoremove —yes && rm -rf /var/lib/{apt,dpkg,cache,log}/
USER jenkinsThis way, we got a Docker container that can execute Docker commands on the host machine.
Build Configuration
Not long ago, Jenkins gained the ability to describe its rules using syntax, which allows for fairly simple changes to the build script and storing it in a repository.
So let's place a special Dockerfile directly in the repository, which will contain all the necessary libraries for building. This way, the developer can prepare a reproducible environment and won't need to ask OPS to install a specific version of Node.JS on the host.
FROM node:12.10.0-alpine
RUN npm install yarn -gSuch a build image is suitable for most Node.JS applications. But what if you need an image for a JVM project with an integrated Sonar scanner? You are free to choose the necessary components for the build.
FROM adoptopenjdk/openjdk12:latest
RUN apt update
&& apt install -y
bash unzip wget
RUN mkdir -p /usr/local/sonarscanner
&& cd /usr/local/sonarscanner
&& wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.3.0.1492-linux.zip
&& unzip sonar-scanner-cli-3.3.0.1492-linux.zip
&& mv sonar-scanner-3.3.0.1492-linux/* ./
&& rm sonar-scanner-cli-3.3.0.1492-linux.zip
&& rm -rf sonar-scanner-3.3.0.1492-linux
&& ln -s /usr/local/sonarscanner/bin/sonar-scanner /usr/local/bin/sonar-scanner
ENV PATH $PATH:/usr/local/sonarscanner/bin/
ENV SONAR_RUNNER_HOME /usr/local/sonarscanner/bin/
We have described the build environment, but what does Jenkins have to do with this? Jenkins agents can work with such Docker images and perform builds inside.
stage("Build project") {
agent {
docker {
image "project-build:${DOCKER_IMAGE_BRANCH}"
args "-v ${PWD}:/usr/src/app -w /usr/src/app"
reuseNode true
label "build-image"
}
}
steps {
sh "yarn"
sh "yarn build"
}
}Directive agent uses the property docker, where you can specify:
- the name of the build container according to your naming policy;
- the arguments necessary to run the build container, where in our case we mount the current directory as a directory inside the container.
And in the build steps, we specify which commands to execute inside the build Docker agent. It can be anything; this way, I also deploy applications using Ansible.
Below, I want to show a general Jenkinsfile that can build a simple Node.JS application.
def DOCKER_IMAGE_BRANCH = ""
def GIT_COMMIT_HASH = ""
pipeline {
options {
buildDiscarder(
logRotator(
artifactDaysToKeepStr: "",
artifactNumToKeepStr: "",
daysToKeepStr: "",
numToKeepStr: "10"
)
)
disableConcurrentBuilds()
}
agent any
stages {
stage("Prepare build image") {
steps {
sh "docker build -f Dockerfile.build . -t project-build:${DOCKER_IMAGE_BRANCH}"
}
}
stage("Build project") {
agent {
docker {
image "project-build:${DOCKER_IMAGE_BRANCH}"
args "-v ${PWD}:\/usr\/src\/app -w \/usr\/src\/app"
reuseNode true
label "build-image"
}
}
steps {
sh "yarn"
sh "yarn build"
}
}
post {
always {
step([$class: "WsCleanup"])
cleanWs()
}
}
}What was the result?
Thanks to this approach, we solved the following issues:
- the build environment configuration time is reduced to 10-15 minutes per project;
- a fully reproducible application build environment, as it can be built on a local computer;
- no issues with conflicts of different versions of build tools;
- always a clean workspace that does not get cluttered.
The solution itself is simple and obvious and allows us to gain numerous advantages. Yes, the entry threshold has slightly increased compared to simple build commands, but now there is a guarantee that it will always build, and the developer can choose everything they need for their build process.
You can also use the image I built . All the source codes are open and available at .
During the writing of the article, there was a discussion about using agents on remote servers to avoid loading the master node using the plugin . But I will talk about this in the future.
Source: habr.com
