Door Pieter Vogelaar 30 Juli 2018
Het gebruik van een
Jenkinsfile om de
build job voor broncode te configureren is geweldig. Jenkins beschikt over een uitstekende
Docker Pipeline plugin
die het mogelijk maakt om Docker commando's op een elegante manier uit te voeren tijdens de build.Let op: Vergeet niet de update van 16 augustus 2018 onderaan deze pagina te lezen.
Veel voorbeelden op https://jenkins.io/doc/book/pipeline/docker/ houden het heel eenvoudig. Ze starten en stoppen
binnen één enkele pipeline stage, met methoden zoals docker.inside of docker.withRun. Denk bijvoorbeeld aan het
bouwen van een container, deze draaien, commando’s uitvoeren en de container weer verwijderen; allemaal binnen één stage. Voor
sommige use cases is dit prima, maar bij het bouwen van een Docker container voor een applicatie is het veel
overzichtelijker om dit over meerdere stages te verdelen.
In het meer geavanceerde voorbeeld op deze pagina gebruikten we een globale variabele pipelineContext van het type
LinkedHashMap. Omdat de programmeertaal van een Jenkinsfile Groovy is, komt dit in de praktijk overeen met een
JavaScript-object. Deze variabele maakt het mogelijk om data of objecten te delen tussen verschillende stages.
Laten we kijken naar de “Run” stage.
stage('Run') {
steps {
echo "Run docker image"
script {
pipelineContext.dockerContainer = pipelineContext.dockerImage.run()
}
}
}
Wanneer je een Docker-container start op basis van een image, wordt er een container instantie teruggegeven. Om de container te kunnen stoppen en verwijderen als de build mislukt of klaar is, heb je dit container object nodig.
post {
always {
echo "Stop Docker image"
script {
if (pipelineContext && pipelineContext.dockerContainer) {
pipelineContext.dockerContainer.stop()
}
}
}
}
Hieronder staat een compleet voorbeeld van een Declarative Pipeline dat verschillende Docker commando’s verspreidt over meerdere stages.
// Initialize a LinkedHashMap / object to share between stages
def pipelineContext = [:]
pipeline {
agent any
environment {
DOCKER_IMAGE_TAG = "my-app:build-${env.BUILD_ID}"
}
stages {
stage('Configure') {
steps {
echo 'Create parameters file'
}
}
stage('Build') {
steps {
echo "Build docker image"
script {
dockerImage = docker.build("${env.DOCKER_IMAGE_TAG}", '-f ./Dockerfile .')
pipelineContext.dockerImage = dockerImage
}
}
}
stage('Run') {
steps {
echo "Run docker image"
script {
pipelineContext.dockerContainer = pipelineContext.dockerImage.run()
}
}
}
stage('Test') {
steps {
echo "Testing the app"
}
}
stage('Push') {
steps {
echo "Pushing the Docker image to the registry"
}
}
stage('Deploy') {
steps {
echo "Deploying the Docker image"
}
}
stage('Verify') {
parallel {
stage('Verify home') {
agent any
steps {
echo "HTTP request to verify home"
}
}
stage('Verify health check') {
agent any
steps {
echo "HTTP request to verify application health check"
}
}
stage('Verify regression tests') {
agent any
steps {
echo "Running regression test suite"
}
}
}
}
}
post {
always {
echo "Stop Docker image"
script {
if (pipelineContext && pipelineContext.dockerContainer) {
pipelineContext.dockerContainer.stop()
}
}
}
}
}
UPDATE 16 augustus 2018: pipelineContext is niet (meer?) nodig
Ik weet niet zeker waarom we problemen ondervonden tijdens het schrijven van dit bericht, maar het gebruik van een globale pipelineContext variabele lijkt niet (meer) strikt noodzakelijk. Variabelen kunnen direct over verschillende stages heen worden aangeroepen.
pipeline {
agent any
stages {
stage('Build') {
steps {
script {
docker_image = docker.build("${env.DOCKER_IMAGE_TAG}", '-f ./Dockerfile .')
}
}
}
stage('Test') {
parallel {
stage('Unit tests') {
agent any
steps {
script {
docker_image.inside("--entrypoint='/start.sh'") {
sh 'cd /var/www/app && vendor/bin/phpunit --testsuite=Unittest'
}
}
}
}
stage('Health check') {
agent any
steps {
script {
docker_image.inside("--entrypoint='/start.sh'") {
timeout(time: 1, unit: 'MINUTES') {
retry(5) {
sleep 5
sh "curl -sS http://localhost/info | grep 'My API'"
}
}
}
}
}
}
}
}
}
}

Vogelaar Solutions helpt organisaties met DevOps, platform engineering en web development. Neem contact op voor een vrijblijvend gesprek.