[REX] Migrez efficacement vos Jobs CI Maven en Pipeline Docker avec Jenkins 2
About me, eXo, me@eXo
Maxime Gréau - @mgreau
- Senior Software Engineer
- @ eXo since 2015
- 2010-2015 - Architect DGFIP
- Before 2010 - Software Developer
- Book Author
- 2011: Apache Maven 3
- Open Source
- Asciidoctor
- Speaker
- Devoxx, DevNation, DevFest, JUG…

eXo at a glance

What is eXo Platform?
- Open Source
- Enterprise class
- Digital Collaboration
- Platform

Software Factory Overview
What do we have to build?
Platform Components & Add-ons

- More than 20 components to build
- Platform projects
- GateIn projects
- Juzu projects
- Lots of Add-ons
- 15+ supported Add-ons
- 100+ community Add-ons
- Native Mobile projects
- Android Application
- iOS Application
Note
|
1 component = 1 multi-module Maven project |
eXo Component Example - Social Maven Project

Platform versions and Clients
- Since 2003, several Platform versions
- eXo Platform 3.x (3.0 / 3.5)
- eXo Platform 4.x (4.0, 4.1, 4.2, 4.3, 4.4)
- eXo Platform 5.x
- Several versions = multiple stacks (run & build)
- Java 6 / Java 7 / Java 8…
- Maven 3.0 / Maven 3.2 / Maven 3.3
- Native Mobile Applications
- Android / Gradle 2.14
- iOS / XCode 7.3
- Clients don’t update their instances each year
Git Workflow

- develop
- stable:
- stable/1.x stable/4.3.x stable/4.2.x
- feature
- feature/<feature-name>
- fix
- fix/<issue-id>
- integration
- …
Jobs type
- 450+ CI jobs (<project>-<branch>-ci)
- for all major branches (develop, stable, features)
- for some important fix
- 50+ Sonar jobs (<project>-<branch>-sonar)
- for all develop branches
- 30+ Maven Site jobs
- for all Platform project
- Translation jobs
- …
Remembered how to create a Maven Jenkins Job?

Note
|
Yes, this is only ONE job! |
Me when I had to use the Jenkins UI

If you start to feel like your are a toaster…AUTOMATE!!
Jenkins DSL and Pipeline jobs with Docker to the rescue!
Overview
- 1- Create your own CI Docker Images
- 2- Use Jenkins Pipeline & Pipeline Docker plugins
- 3- Generate all your Pipeline jobs with DSL jobs
1- Create your own CI Docker Images

Build stacks

- What we need?
- Git
- Maven / Gradle / Android SDK
- eXo CI Images
- exoplatform/ci:jdk6-maven30
- exoplatform/ci:jdk7-maven30
- exoplatform/ci:jdk7-maven32
- exoplatform/ci:jdk8-maven30
- exoplatform/ci:jdk8-maven32
- exoplatform/ci:jdk8-maven33
- exoplatform/ci:jdk8-gradle2
- exoplatform/ci:gradle2-android
Docker Image for Jenkins (1/4)
Define the locale
# Set the locale
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
Docker Image for Jenkins (2/4)
Configure the user
- Create the same user in the image and in the server
# eXo CI User
ARG EXO_CI_USER_UID=13000
ENV EXO_CI_USER ciagent
ENV EXO_GROUP ciagent
...
# Create user and group with specific ids
RUN useradd --create-home --user-group -u ${EXO_CI_USER_UID} --shell /bin/bash ${EXO_CI_USER}
- Because Jenkins starts containers like this (in eXo context):
$ docker run -t -d -u 13000:13000
-v m2-cache-ecms-develop-ci:/home/ciagent/.m2/repository
-v /srv/ciagent/workspace/ecms-develop-ci:/srv/ciagent/workspace/ecms-develop-ci:rw
-v /srv/ciagent/workspace/ecms-develop-ci@tmp:/srv/ciagent/workspace/ecms-develop-ci@tmp:rw
... -e ******** --entrypoint cat exoplatform/ci:jdk8-maven32
Docker Image for Jenkins (3/4)
entrypoint and cat command
- Example Jenkins job running docker container
$ docker run ... -e ******** exoplatform/ci:jdk8-maven32 cat
$ docker run ... -e ******** --entrypoint cat exoplatform/ci:jdk8-maven32
COPY docker-entrypoint.sh /usr/bin/docker-entrypoint
# Workaround to be able to execute others command than "mvn" as entrypoint
ENTRYPOINT ["/usr/bin/docker-entrypoint"]
CMD ["mvn", "--help"]
- docker-entrypoint.sh
# Hack for Jenkins Pipeline: authorize cat without absolute path
if [[ "$1" == "/"* ]] || [[ "$1" == "cat" ]]; then
exec "$@"
fi
exec mvn "$@"
Docker Image for Jenkins (4/4)

2- Use Jenkins Pipeline & Docker plugins
Pipelines are a series of steps that allow you to orchestrate the work required to build, test and deploy applications.
Plugins to use
- Pipeline/Docker plugins
- Pipeline: Shared Groovy Libraries
- Others useful plugins
Note
|
Take a look at my Jenkins2 Dockerfile/plugins.txt samples https://github.com/mgreau/dockerfiles/tree/master/jenkins2 |
eXo Pipeline example
Maven CI job in Docker
node('ci-docker'){
exoCI{
dockerImage = 'exoplatform/ci:jdk8-maven32'
gitUrl = 'git@github.com:exodev/platform-ui.git'
gitBranch = 'develop'
}
}
Pipeline Shared Groovy Libraries
- Create your Groovy scripts
- src/org/exoplatform/swf/ExoSWFCommands.groovy
- vars/exoCI.groovy
- vars/exoCI.txt (doc)
- vars/exoSonar.groovy
- vars/exoSonar.txt (doc)
- vars/mailNotification.groovy
- Manage as a code
- external github repository for now

Pipeline Shared Groovy Libraries
vars/exoCI.groovy (1/3)
def exoSWF = new org.exoplatform.swf.ExoSWFCommands()
def utils = new org.exoplatform.swf.Utils()
//init values
def M2_REPO_IN_CONTAINER = utils.getValue('m2RepositoryPath', '/home/ciagent/.m2/repository', config, env)
def DOCKER_IMAGE = utils.getValue('dockerImage', '', config, env)
def eXoJDKMaven = docker.image(DOCKER_IMAGE);
def pipelineError = null
stage(DOCKER_IMAGE){
eXoJDKMaven.pull()
}
stage('Checkout ' + GIT_BRANCH){
// checkout scm with credentials
git branch: GIT_BRANCH, credentialsId: GIT_CREDENTIALS_ID, url: GIT_URL
}
Pipeline Shared Groovy Libraries
vars/exoCI.groovy (2/3)
stage('Build'){
// Create m2 cache (use docker volume)
def m2Cache = exoSWF.createMavenCacheVolume(JOB_NAME)
// Use custom settings.xml file on project workspace directory
configFileProvider(
[configFile(fileId: "${MAVEN_SETTINGS_FILE_ID}", targetLocation: 'settings.xml')]) {
try {
eXoJDKMaven.inside("${DOCKER_RUN_PARAMS} -v ${m2Cache}:${M2_REPO_IN_CONTAINER}") {
sh "mvn ${MAVEN_GOALS} -P${MAVEN_PROFILES} -DdeployAtEnd=${DEPLOY_AT_END} -s settings.xml"
}
} catch (error) {
currentBuild.result = 'FAILURE'
pipelineError = error
} finally {
// Delete temporary settings file
sh 'rm -f settings.xml'
}
}
}
Pipeline Shared Groovy Libraries
vars/exoCI.groovy (3/3)
stage('Publish Reports'){
junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
}
stage('Send Notifications'){
// Send notification to inform about Build status
mailNotification(env,currentBuild, mailTo)
// Add comment to JIRA
jiraNotification(env,currentBuild)
// Clean up the workspace at the end (except in failure, and unstable cases)
step([$class: 'WsCleanup', cleanWhenSuccess: true, cleanWhenFailure: false, cleanWhenUnstable: false])
}
Note
|
See: Directions for Pipeline by Jesse Glick at Jenkins World 2016 |
DEMO
Pipeline Docker for CI Builds / Gradle-Android Build / Sonar Analysis

3- Generate your Pipeline jobs with DSL jobs
triggers {
scm("${scmValue}")
cron("${cronValue}")
}
definition {
cps {
script('''#!groovy
node('docker'){
exoSonar{}
}
''')
}
}
}
}
Pipeline, DSL, Shared Groovy libs
DEMO
Generate all pipeline jobs with DSL jobs

Now how to create new Jobs?
$ git commit -am "Create CI/Sonar jobs..."
$ git push

7 seed jobs to generate 350+ jobs
then you feel more like…

Notes about Pipeline
- Not all plugins are compatible with pipeline yet
- example: Maven Plugin (deployed at the end)
- Be careful with Jenkinsfile
- Still verbose syntax
- IN BETA: "Declarative Pipeline in beta"
postBuild {
always {
sh 'echo "This will always run"'
}
success {
sh 'echo "This will run only if successful"'
}
...
}
What about local builds?
use Docker for Mac /Docker for Windows

Use your CI Images on your laptop
.bash_profile
mvn(){
mvnInContainer "exoplatform/ci:jdk8-maven32" "$@"
}
jdk7mvn30(){
mvnInContainer "exoplatform/ci:jdk7-maven30" "$@"
}
# Run Maven commands in a container
mvnInContainer(){
local dockerImage=$1
docker run --rm \
-v $(pwd):/srv/ciagent/workspace \
-v ~/.gnupg/gpg.conf:/home/ciagent/.gnupg/gpg.conf:ro \
-v ~/.gnupg/pubring.gpg:/home/ciagent/.gnupg/pubring.gpg:ro \
-v ~/.gnupg/secring.gpg:/home/ciagent/.gnupg/secring.gpg:ro \
-v ~/.gnupg/trustdb.gpg:/home/ciagent/.gnupg/trustdb.gpg:ro \
-v ~/.gnupg:/home/ciagent/.gnupg \
-v ~/.m2/repository:/home/ciagent/.m2/repository \
-v ~/.m2/settings.xml:/home/ciagent/.m2/settings.xml \
--workdir /srv/ciagent/workspace \
${dockerImage} "${@:2}"
}
Use your CI Images on your laptop
$ mvn -v
Apache Maven 3.2.5 (12a6b3acb947671f09b81f49094c53f426d8cea1; 2014-12-14T17:29:23+00:00)
Maven home: /usr/share/maven
Java version: 1.8.0_92, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.8.0_92-oracle-x64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.4.22-moby", arch: "amd64", family: "unix"
$ jdk7mvn30 -v
Apache Maven 3.0.5 (r01de14724cdef164cd33c7c8c2fe155faf9602da; 2013-02-19 13:51:28+0000)
Maven home: /usr/share/maven
Java version: 1.7.0_79, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-1.7.0_79-oracle-x64/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.4.22-moby", arch: "amd64", family: "unix"
Tip: Rebuild the image locally with your user ID if necessary
$ docker build --build-arg EXO_CI_USER_UID="1100" \
-t exoplatform/ci:base https://github.com/exo-docker/exo-ci.git#master:base
To sum up
- Upgrade to latest Jenkins 1.651.3 then Jenkins 2 will be easy
- Create your CI Docker images for Jenkins
- use the same images on your laptop and the CI
- Pipeline as code
- create your custom pipeline scripts with Pipeline: Shared Groovy Libraries
- Execute builds into containers
- Generate Pipeline jobs with DSL jobs
- manage your DSL scripts as code
- Be careful with security & Jenkinsfile
- Create agent with Docker only
- Remove executor on master
<Thank You!>
- Slides online
- Slides in AsciiDoc, feel free to send a PR ;)
- Demo files
twitter: @mgreau - github: @mgreau - mgreau.com
