Erstellen einer Jenkins Pipeline zum Bauen und Pushen von Docker Images

Mai 7, 2020

ci/cd, continuous integration, devops, docker, jenkins, pipeline, spring boot

1. Überblick

In diesem Artikel erstellen wir eine Continuous Integration Pipeline mit Jenkins. Wir lassen den Jenkins lokal laufen und bauen die wesentlichen Schritte für Continuous Integration ein: Spring Boot Anwendung aus GitHub auschecken, kompilieren, testen, Docker Image bauen, Docker Image ins DockerHub pushen.

2. Jenkins im Docker Container starten

Das Starten des Jenkins in einem Docker Container ist sehr einfach, denn hierfür stellt Jenkins ein offizielles Docker Image im Docker Hub bereit: https://hub.docker.com/r/jenkins/jenkins

Um den Jenkins im Container zu starten, führen wir folgenden Befehl aus:

docker run -p 8080:8080 -p 50000:50000 --privileged -u root  -v jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -v /usr/local/bin/docker:/usr/local/bin/docker jenkins/jenkins:lts

Damit ist der Jenkins lokal unter der URL http://localhost:8080/ zu erreichen. Unter dem Ordner /var/jenkins_home speichert Jenkins seine Daten ab, unter anderem die installierten Plugins und Konfigurationen. Hierfür geben wir dem Container ein eigenes Volume damit unsere Installation auch ein Neustart überlebt.

Wenn der Container erfolgreich gestartet ist, was beim ersten Mal etwas Dauern kann, sehen wir zum Ende des Logs ein Passwort. Dieses Passwort benötigen wir bei der initialen Installation, um den Jenkins zu entsperren.

Das Passwort befindet sich unter /var/jenkins_home/secrets/initialAdminPassword.  

Anschließend werden wir von Jenkins durch die Installation geführt, wir installieren die Default Plugins und legen zum Schluss den Benutzer an.

3. Konfiguration der Hilfsprogramme: Maven und Docker

In unserer Pipeline möchten wir Maven und Docker benutzen um unsere Anwendung zu bauen. Um diese Hilfsprogramme im Jenkins zu konfigurieren, gehen wir auf Jenkins > Jenkins verwalten > Konfiguration Hilfsprogramme. Unter den Abschnitten Maven und Docker können wir eine automatische Installation der Tools einstellen, siehe nächster Screenshot.

4. JENKINS Pipeline erstellen

Jenkins ist nun lokal installiert und konfiguriert, jetzt können wir beginnen unsere Pipeline deklarativ aufzubauen. 

Im ersten Schritt soll unsere Jenkins Pipeline folgendes können:

  1. Code auschecken
  2. Code kompilieren
  3. Tests ausführen

Wir bauen die Pipeline für die bestehende Beispielanwendung https://github.com/softwarehandwerk/sample-docker-java-app, hierbei erweitern wir das Projekt um das folgende Jenkinsfile: 

Jenkinsfile

pipeline {
   agent any

   environment {
      dockerHome = tool 'JenkinsDocker'
      mavenHome = tool 'JenkinsMaven'
      PATH = "$dockerHome/bin:$mavenHome/bin:$PATH"
   }

   stages{

      stage('Compile') {
         steps{
            sh "mvn clean compile"
         }
      }

      stage('Test') {
         steps{
            sh "mvn test"
         }
      }

      stage('Integration Test') {
         steps{
            sh "mvn failsafe:integration-test failsafe:verify"
         }
      }

      stage('Package') {
         steps{
            script{
               sh "mvn package -DskipTests"
            }
         }
      }
   }
}

In der Direktive „environment“ konfigurieren wir unsere installierten Versionen von Maven und Docker. Unter „stages“ fügen wir drei Stages hinzu, in denen wir das Projekt mit Maven kompilieren, die Tests ausführen und anschließend paketieren. Das Auschecken des Source Codes muss nicht explizit in einer Stage deklariert werden, dies erfolgt automatisch durch die Pipeline Direktive.

Als nächstes erstellen wir im Jenkins ein neues Job Element (Pipeline) und nennen es „sample-docker-java-app-pipeline“. Nach dem Anlegen gelangen wir auf die Konfigurationsseite des Jobs, hier konfigurieren wir wie der Jenkins-Job an unsere Jenkinsfile kommt, siehe Screenshot.

Das Repository mit unserer Jenkinsfile ist öffentlich auf GitHub https://github.com/softwarehandwerk/sample-docker-java-app.git , daher benötigen wir auch keine Credentials zu konfigurieren um darauf zuzugreifen. Damit wäre auch die Konfiguration des Jobs an dieser Stelle fertig und wir können den Job starten.

5. Docker Image bauen und pushen

Unsere Anwendung ist nun gebaut, getestet und liegt bereit zum Paketieren unter dem target Ordner. Das Dockerfile welches direkt neben der pom.xml liegt, basiert auf einem schmalen Alpine Image. Im „COPY“ Befehl wird unsere Anwendung auf die oberste Ebene kopiert damit die Anwendung nach dem Start des Container mit dem CMD Befehl gestartet werden kann.

Dockerfile

FROM openjdk:8-jre-alpine
COPY target/*.jar /app.jar
EXPOSE 8080
CMD ["/usr/bin/java", "-jar", "-Dspring.profiles.active=default", "/app.jar"]

In der Pipeline greifen wir auf unser bereits deklariertes Tool Docker zu und rufen die build Methode mit dem Repositorynamen und dem Buildtag auf. Nach dem Ausführen von dieser Stage wurde das Docker Image gebaut und in das lokale Repository abgelegt. 

stage('Build Docker Image') {
    steps{
        script{
            dockerImage = docker.build("softwarehandwerk/sample-docker-java-app:${env.BUILD_TAG}")
        }
    }
}

Bevor wir im letzten Schritt unser Docker Image nach Docker Hub pushen, müssen wir in Jenkins die Zugangsberechtigungen für Docker Hub hinterlegen. Unter Jenkins > Zugangsdaten > System > Globale Zugangsdaten legen wir die Zugangsdaten ab.

Sobald die Zugangsdaten für Docker Hub in Jenkins hinterlegt sind, können wir die letzte Stage „Push Docker Image“ in unsere Pipeline einbauen. Das Docker Pipeline Plugin bietet mit der Methode withRegistry die Möglichkeit sich mit dem Registry vom Docker Hub zu verbinden, als Parameter bekommt es die Jenkins Credentials ID. 

stage('Push Docker Image') {
    steps{
        script{
            docker.withRegistry('',’dockerhub-credentials-id'){
                dockerImage.push()
                dockerImage.push('latest')
            }
        }
    }
}

Wenn das Pushen aus der Pipeline erfolgreich war, dann ist das Docker Image im Docker Hub sichtbar.

6. FAZIT

In diesem Artikel haben wir eine Jenkins Pipeline mit den wesentlichen Schritten für Continuous Integration gebaut. Dabei haben wir gesehen wie einfach es sein kann mit Hilfe von Docker den Jenkins lokal zu starten und die wichtigsten Pipelineschritte einzubauen. Wir können nun in einem lokalen Jenkins ein Projekt aus GitHub auschecken, dieses bauen und testen, anschließend ein Docker Image bauen und es in DockerHub einpflegen.

Der nächste Schritt wäre „Delivery“.

https://github.com/softwarehandwerk/sample-docker-java-app/tree/pipeline

Source Code auf GitHub

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>