Blog

Einrichtung von Jenkins auf einem Kubernetes-Cluster

22.07.2022
Lesezeit: 6 Minuten.
Zuletzt aktualisiert am: 08.01.2024

Inhaltsübersicht

Einführung in Jenkins in Kubernetes

Im Leben eines jeden DevOps-Abenteurers kommt irgendwann der Zeitpunkt, an dem er sich dem guten alten Jenkins in all seiner Pracht stellen muss. Mit all diesen Legacy-Pipelines begann die Jenkins VM mit den besten Absichten, endete aber in einer wahren Verkörperung des Jenkinstein-Memes. Und natürlich die längst vergessenen Jenkins-Instanzen, die zwar laufen, vor denen sich aber jeder fürchtet, weil man nicht weiß, wozu eine kleine Änderung in der Pipeline führen könnte.

Spaß beiseite, Jenkins ist ein großartiges Werkzeug. In den letzten Jahren sind viele andere CI/CD-Tools auf den Markt gekommen, aber ich habe noch keinen Fall erlebt, in dem Jenkins in Bezug auf die Funktionalität zu kurz gekommen wäre. Das liegt vor allem daran, dass Jenkins modular aufgebaut ist und ein großer Teil seiner Funktionalität aus ersten oder Drittanbieter-Plugins stammt. Ein solches Plugin, auf das wir uns in diesem Blog konzentrieren werden, ist das Jenkins-Kubernetes-Plugin.

PS: Wenn die Funktion eines Plugins mit ein paar Zeilen Code erreicht werden kann (z. B. API-Aufruf), ist es besser, sie selbst zu schreiben. Wenn Jenkins so schlank wie möglich ist, lässt es sich leichter aktualisieren und ist insgesamt weniger anfällig für Sicherheitslücken.

Das Jenkins-Kubernetes-Plugin

Mit diesem Plugin können Sie das normale Jenkins, das Sie kennen und lieben, nahtlos in Kubernetes integrieren. Der Zweck dieses Plugins besteht darin, Jenkins so in die Kubernetes-API zu integrieren, dass für jeden Auftrag neue Pods erzeugt werden können. Die Worker-Pods leben nur während der Ausführung der Pipeline. Dadurch können wir eine Menge Rechenressourcen für reguläre Jenkins-Agenten einsparen, die normalerweise ungenutzt bleiben, bis wir sie brauchen. Ein zusätzlicher Vorteil der dynamischen Worker ist, dass die Anzahl der Worker so hoch und niedrig wie nötig skaliert werden kann. Ich würde jedoch empfehlen, die Anzahl auf der Grundlage der verfügbaren Ressourcen im Cluster zu begrenzen.

Lassen Sie uns den Schwerpunkt in einige Hauptthemen aufteilen:

  • Jenkins Master-Installation;
  • Einrichten Ihrer Jenkins Worker-Pod-Vorlage;
  • Nützliche Pipeline-Tipps für die Ausführung in einem Kubernetes-Worker.

Jenkins auf Kubernetes-Cluster einrichten

1. Installieren Sie Jenkins auf Kubernetes: Jenkins Master-Installation

Es gibt vorgefertigte Docker-Images für Jenkins, die verwendet werden können. In der Vergangenheit haben wir Jenkins in k8s installiert, indem wir ein benutzerdefiniertes Helm-Diagramm mit benutzerdefinierten Ergänzungen zum Jenkins-Docker-Image erstellt haben, aber wir schreiben das Jahr 2022 und niemand hat die Zeit, das zu wiederholen, was bereits von vielen getan wurde. Es ist Open-Source, daher würde ich empfehlen, mit dem folgenden Helm-Diagramm zu beginnen.

Die Tabelle ist selbsterklärend, allerdings würde ich mich nicht auf die Backup-Funktion verlassen. Sie kopiert Dateien standardmäßig sequentiell. Wenn der Parameter Parallelität verwendet wird, geht ihr irgendwann der Speicher aus, und wenn Sie mehr als eine Million Dateien zu sichern haben, läuft sie sogar länger als 24 Stunden, was überhaupt nicht sinnvoll ist.

Wie bei anderen Java-Anwendungen müssen Sie die Xms- und Xmx-Werte entsprechend Ihrem Anwendungsfall und den dem Pod zur Verfügung stehenden Ressourcen einstellen.

Das Diagramm wird mit einem eingebauten Jenkins-Plugin "configuration-as-code" geliefert. Dadurch kann es mit einer Liste bestimmter Plugins initialisiert werden, ohne dass spezielle Groovy-Skripte geschrieben werden müssen.

Ich würde empfehlen, mit einigen Installationen herumzuspielen, aber schließlich die Variable "initializeOnce" auf true zu setzen. Dadurch wird sichergestellt, dass der Init-Container keine neuen Plugins überschreibt oder herunterlädt, was zu fehlerhaften Abhängigkeiten führen kann.

2. Einrichten Ihrer Jenkins Worker-Pod-Vorlage

Die Einrichtung des Worker Pods kann anfangs frustrierend sein, insbesondere wenn Sie versuchen, eine Datei für die YAML-Konfiguration des Pods zu verwenden. Ich empfehle die folgenden zwei Möglichkeiten:

Wenn Sie eine gemeinsam genutzte Pipeline-Bibliothek verwenden (empfohlen), können Sie den Pod Yaml wie folgt als libraryResource angeben:

  pipeline {
    agent {
      kubernetes {
          defaultContainer 'jnlp'
          yaml libraryResource("podTemplates/default.yaml")
      }
    }
    ...
  }

Dabei wird nach einer Datei in Ihrer Bibliothek gesucht, die sich unter resources/podTemplates/default.yaml befindet. Dies ist der bequemste Weg, der sogar parametrisiert werden kann.

Mehr über Jenkins-Pipeline-Bibliotheken, wenn ich das nächste Mal Zeit finde, einen Blog zu schreiben, spätestens in 5 Jahren.

-

Wenn Sie keine Pipeline-Bibliothek verwenden, können Sie die Pod-YAML als mehrzeiligen String in Ihrer Pipeline einrichten, etwa so:

  pipeline {
    agent {
      kubernetes {
          defaultContainer 'jnlp'
        yaml """
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: jenkins-worker
spec:
  containers:
  - name: jenkins-builder
  ...
  

  """
      }
    }
  }

Dies hat das gleiche Ergebnis, ist aber viel hässlicher und die YAML befindet sich in Ihrer Jenkins/Pipeline-Datei.

Bemerkenswerte Dinge über die Arbeiterkapsel:

  • In Ihrem Worker-Pod wird immer ein JNLP-Container laufen. Er muss nicht in der Pod-Vorlage angegeben werden und wird hauptsächlich für die Herstellung der Verbindung zum Jenkins-Master verwendet.
  • Die verschiedenen Container im Pod teilen sich standardmäßig ein Workspace-Volume, was sehr praktisch ist, da man keinen Artefakt-Build-Code zwischen den verschiedenen Schritten der Pipeline benötigt.

So richten Sie Docker in Docker in Ihrer Pod-Vorlage ein

Hier ist ein Beispiel für die Einrichtung von Docker in Docker in Ihrer Pod-Vorlage. Die Lösung ist sehr ähnlich wie bei anderen Pipeline-Tools. Ein "dind"-Container sollte neben den anderen laufen. Die Container, die ihn benötigen, sollten mit der Umgebungsvariablen DOCKER_HOST auf ihn verwiesen werden.

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: jenkins-worker
spec:
  containers:
  - name: python
    image: python:3.8.12-alpine3.13
    command:
    - cat
    tty: true
    env:
    - name: DOCKER_HOST
      value: tcp://localhost:2375

  - name: docker
    image: docker:19.03.0-dind
    tty: true
    env:
    - name: DOCKER_TLS_CERTDIR
      value: ""
    securityContext:
      privileged: true
    volumeMounts:
      - name: dind-cache
        mountPath: /var/lib/docker
  volumes:
  - name: dind-cache
    emptyDir: {}

3. Bereitstellen und Skalieren von Jenkins auf Kubernetes: Nützliche Pipeline-Tipps für die Ausführung in einem Kubernetes-Cluster

Wie bereits oben erwähnt, ist die wichtigste Konfiguration, die Sie benötigen, das podTemplate für den Worker, das in der Kubernetes-Agentenkonfiguration definiert ist.

Um verschiedene Programmiersprachen oder Versionen davon zu erstellen, können Sie mehrere Container innerhalb des Pods angeben und einfach auswählen, welcher je nach Anwendung erstellt werden soll. Zum Beispiel:

script {
if (env.APP_NAME == "a-node10-app") {
    container("node10") {
      funcs.codeBuild() // <-- This is a function from a shared library
    }
  } else if (env.APP_NAME == "another-node15-app"){
    container("node15") {
      funcs.codeBuild() // <-- This is a function from a shared library
    }
  }
}

Im obigen Beispiel wird davon ausgegangen, dass Sie Container mit den Namen node10 und node15 angegeben haben.

--

Lassen Sie uns ein Beispiel für einen voll funktionsfähigen Backup-Auftrag geben, der die Steuerkartenfunktionalität ersetzt

Wie ich bereits erwähnt habe, ist die Backup-Funktionalität, die das Diagramm bietet, nicht besonders gut, wenn sie überhaupt funktioniert. Daher werde ich ein Beispiel dafür geben, wie Sie Ihren Jenkins-Pod mit einem Jenkins-Job sichern können. Das Beispiel stammt aus einer Pipeline-Bibliothek.

Dies ist die Funktion, die wir in der Pipeline aufrufen.

src/org/itgix/Dienstprogramme.groovy

package org.itgix

def jenkinsBackup() {
  sh "aws eks update-kubeconfig --name {cluster_name}"
  sh """
  kubectl exec -it  -n jenkins jenkins-0 -- /bin/bash -c "cd /var/ && tar --exclude='*/backups' --exclude='*/workspace/*' --exclude='*/branches/*' --exclude='*/fingerprints' --exclude='*/org.jenkinsci.plugins.github_branch_source.GitHubSCMProbe.cache' -zcvf /var/jenkins_home/backups/jenkins_home_$(date +%F).tar.gz jenkins_home/"
  """
  sh "kubectl cp --retries=-1 jenkins/jenkins-0:/var/jenkins_home/backups/jenkins_home_$(date +%F).tar.gz ${WORKSPACE}/jenkins_home_$(date +%F).tar.gz"
  sh "ls -lh"
  sh "aws s3 cp ${WORKSPACE}/jenkins_home_$(date +%F).tar.gz s3://${JENKINS_BACKUPS_BUCKET}/backups/$(date +%F)/"
}

Hier ist die "Jenkinsfile" oder Pipeline-Konfiguration, die die Pipeline unten aufruft:

@Library('jenkins-shared-lib') _

JenkinsBackupJob {
    runnerTemplate = "default.yaml"
}

Hier ist die Pipeline dafür.

vars/JenkinsBackupJob.groovy

def call(body) {
  def pipelineParams= [:]
  body.resolveStrategy = Closure.DELEGATE_FIRST
  body.delegate = pipelineParams
  body()
  def funcs = new org.itgix.Utilities()

  pipeline {
    agent {
      kubernetes {
          defaultContainer 'jnlp'
          yaml libraryResource("podTemplates/${pipelineParams.runnerTemplate}")
      }
    }
    environment {
      AWS_ACCOUNT_ID = "123456789000"
      AWS_DEFAULT_REGION = "eu-central-1"
      JENKINS_BACKUPS_BUCKET = "an-s3-used-for-jenkins-backups"
    }
    stages {
      stage("Backup Jenkins Home") {
        steps {
          container('build') {
            script {
              // Credentials that need to have access to the cluster and S3
              withCredentials([usernamePassword(credentialsId: 'aws-jenkins-ci-user', 
                                                passwordVariable: 'AWS_SECRET_ACCESS_KEY', 
                                                usernameVariable: 'AWS_ACCESS_KEY_ID')]) {

                  funcs.jenkinsBackup()

              } // withCredentials
            } // script
          } // container
        } // steps
      } // stage
    } //stages
    post {
      failure {
        container('python') {
          script {
            env.BUILD_STAT = "FAILURE"
            env.JENKINS_ENV = "ITGix-Jenkins"
            env.MAIL_LIST = "person@itgix.com,person2@itgix.com"
            env.CHANGESET = funcs.changeLogHtmlTable(currentBuild.changeSets) // <-- That's another one of our self built functions utilizing a python script
            funcs.notifyEmail()
          } //script
        } // container
      } // failure
    } // post
  } // pipeline
} // body

Schlussfolgerung

Zusammenfassend lässt sich sagen, dass Jenkins auch dann praktikabel ist, wenn Sie einen Pipeline-fähigen Git-Anbieter wie Gitlab oder Github verwenden. Die Stärken von Jenkins zeigen sich am deutlichsten in einigen Grenzfällen, in denen Sie viele Umgehungen vornehmen müssten, um das zu erreichen, was der Entwicklungsprozess erfordert. Jenkins ist in der Lage, auch mit schwierigen Anforderungen umzugehen. Das liegt daran, dass die Pipeline-Logik mit der Programmiersprache Groovy geändert werden kann.

Ich würde empfehlen, sie in Grenzfällen oder nach dem Aufbau einer robusten Pipeline-Bibliothek, die für verschiedene Projekte wiederverwendet werden kann, einzusetzen.

Eine Antwort hinterlassen

Mehr Beiträge

Kubernetes ist eine der wichtigsten Technologien hinter modernen Cloud-nativen Plattformen. Da Unternehmen zunehmend Container und DevOps-Praktiken einsetzen, wird der Bedarf an zuverlässiger Orchestrierung, Skalierbarkeit und Automatisierung für ...
Lesen
Da Unternehmen in ganz Europa einem zunehmenden regulatorischen Druck in Bezug auf Datenhoheit, operative Kontrolle und digitale Souveränität ausgesetzt sind, geht es bei der Cloud-Strategie nicht mehr nur um Skalierbarkeit und Leistung. Es geht um Vertrauen, ...
Lesen
Kontakt aufnehmen
ITGix bietet Ihnen fachkundige Beratung und maßgeschneiderte DevOps-Services, um Ihr Unternehmenswachstum zu beschleunigen.
Newsletter für
Technik-Experten
Schließen Sie sich 12.000+ Geschäftsführern und Ingenieuren an, die Blogs, e-Books und Fallstudien Fallstudien über neue Technologie erhalten.