Du brauchst ein Jenkins Tutorial?
Hier ist die komplette Einführung in CI / CD.
Starten wir!
Was ist Jenkins?
Jenkins ist eine Continuous Integration / Continuous Delivery (CI /CD) Software, welche viele Arbeitsschritte bei der Entwicklung und Veröffentlichungen einer Software automatisiert. Continuous Integration bedeutet, dass alle Fortschritte von Entwicklern, welche am Tag entstehen, automatisch zu einem fertigen gebauten Produkt integriert werden. Der Code führt die Software zusammen und baut eine oder mehrere verschiedene Versionsstände. Das System ist stark automatisiert, um theoretisch mehrere Releases am Tag durchzuführen. In der Realität werden nicht alle Releases veröffentlicht, aber den Zwischenstand ist ausführbar (zum Testen).
Pipelines arbeiten mit super frischen Code (Bleading Edge), deshalb kann die Pipeline wegen Fehlern, Tests und sonstigen Bedingungen scheitern oder zu der fertigen Software durchlaufen (Erfolg).
Warum soll ich eine Pipeline verwenden?
Schnell, Schneller, Pipeline
Ein Entwickler kann jetzt noch eine Änderung an einem Produkt vornehmen und in wenigen Minuten eine fertige Software an den Kunden senden (theoretisch). Dabei handelt es sich nicht um eine unbrauchbare Code-Leiche, sondern um eine funktionierende Software.
Bessere Qualität durch automatisches Testen
In einem guten Softwareprojekt sind immer Unit-, Regressions- oder Integrations-Tests eingebunden. Diese testen die Software auf Herz und Nieren und die Interaktion mit anderer Software. Die Pipeline prüft verpflichten bei jedem Release, ob die Tests funktionieren. Solange keine komplexen neuen Features eingearbeitet wurden, ist die Software direkt vom Endkunden verwendbar.
Der Überblick – Zusammenführen der Einzelteile
Software Projekte können sehr schnell sehr unübersichtlich werden. 1000 von Entwickler arbeiten an einem Produkt. Eine Pipeline führt den Code zu einem Produkt zusammen. Das schafft mehr Übersicht.
IT ist Handarbeit – Reduzierung manueller Aufgaben
Die IT automatisiert nicht nur die reale Welt, sondern automatisiert sich selbst. Pipelines führen anderen Programme aus, während Pipelines selbst eine Art Programm (Meta-Programm) bzw. Skript sind. Wenn ein fauler Entwickler eine Aufgabe 10 Mal am Tag macht, dann automatisiert dieser die Aufgabe.
Multicore – Parallelisierung von Arbeiten
Die Welt der Endgeräte ist so divers, dass eine Pipeline notwendig ist. Code muss für verschiedene Endgeräte, z. B. Prozessorarten vorbereitet (kompiliert) werden. Diese Arbeit muss kein Mensch machen, sondern dafür soll eine Pipeline schuften.
Was sind Pipelines?
Pipelines sind Scripte mit Abfolgen von Automatisierungsschritten. Eine Pipeline kann so einfach oder komplex wie das Projekt sein. Die folgenden Bausteine beinhalten viele Pipelines. Für mich sind die folgenden Bausteine Pflicht:
Code zusammenstellen: Die Pipeline kopiert den Code von allen (bzw. einem) Repo in den Arbeitsordner.
Testen: Alle Projekte beinhalten Tests, mit der die Funktion des Codes zu einem großen Teil abgedeckt sind. Hat der Entwickler eine Abhängigkeit zu einer anderen Funktion, Repo oder Software vergessen (Interdependenz)?
Automatischer Detektiv – Statischer Sicherheitstest
Statischen Tools soll die Software auf mögliche Ausnutzungen (Exploits) testen. Diese Software ist kein Garant für Schwachstellen-freie Software, sondern ein erster voll automatisierbarer Check, um die groben Klatscher zu entdecken. Wir müssen nicht unnötig viel manuelles Pentesting durchführen, wenn die Software eine erste Fehlersuche ermöglicht.
Einfach mal bauen
Jede Pipeline soll ein nutzbares Endprodukt haben. Bisher hat die Pipeline nur die Güte des Codes analysiert. Jetzt soll die Pipeline möglichst für alle Endgeräte, CPU-Architekturen, Installationsformate oder Betriebssystem eine passende ausführbare Datei erstellen. Ein nettes Format ist Docker, weil diese eine abgestimmte Laufzeitumgebung mit sich bringt. Diese ist wichtig für den nächsten Schritt.
Direkt lauffähig machen – Installieren
Wenn wir ein fertiges Docker-Image haben, kann die Pipeline das Image mit einem Kommando installieren. Die Vorbereitung tätigst Du nur beim Aufsetzen des Docker-Hosts. Jede zukünftige Installation basiert auf dem Docker-Image, welches alles beinhaltet, damit die Software läuft.
Continuous Delivery (CD) sieht vor, dass das System produktiv (nach alle Testschritten) installiert ist. Die klassische Softwareentwicklung sieht eine Testphase und Abnahme vor. Bei CD kann das Unternehmen einen Teil der Fangemeinde dafür missbrauchen 🙂 (Beta-Versionen, Insider-Preview). Das Unternehmen führt ein Rollback durch, wenn die Fans unzufrieden sind.
Optionale Schritte (aus meiner Sicht)
Code Style: Soll die Klammer unter oder hinter der if
-Anweisung sein? Brauchen wir ein let
oder int
bei Variablen? Diese Regeln kannst Du nur subjektiv beantworten. Wichtig ist nur, dass die Entwickler sich auf ein Schema einigen. Der Code Style Checker überprüft die Änderungen.
Verzweigungen: Je nach Build oder Ergebnis muss die Pipeline Verzweigungen durchlaufen. Pipelines können unendlich komplex sein. Behalte die Option im Kopf auf verschiedene Pipelines zu bauen.
Menschliche Tests: Software, welche im Anschluss direkt Endkunden unter die Nase bekommen, sollte wenigstens 1 Mensch prüfen. Menschliche Tester können Aspekte z. B. grafische Nutzeroberflächen prüfen, welche automatische Tests nur schwierig abbilden. Die Tester geben die Version frei und die Pipeline läuft weiter.
Obfuscation: Viele Unternehmen möchten ihren hart erarbeiteten Code nicht weitergeben. Deshalb nutzen diese Verschleierungstechniken, die aus Clean-Code einen menschen-unfreundlichen umständlichen Code machen. Variablennamen bestehen aus zufälligen Folgen von „I“ und „L“, sodass der Code möglichst unappetitlich wirkt.
Branding: Jeder Kunde möchte eine eigene App mit ihrem eigenen Logo und den Unternehmensfarben haben. Dies kann eine Pipeline am Ende des Build-Prozesses integrieren.
Jenkins Tutorial – Das Installieren
Jenkins kannst Du am einfachsten mit Docker installieren. Wenn Du keine Ahnung von Docker hast, dann schaue Dir hier das Tutorial an. Docker ist auch die Schlüsseltechnologie, um CI / CD nach heutigen Standards umzusetzen. Jenkins selbst könntest Du alternativ nativ installieren (Java App) und Dir spezielle native Deployment Scripts überlegen.
Die Docker Compose für den Start sieht so aus:
docker-compose.yml
version: "3.9"
services:
jenkins:
image: jenkins/jenkins:latest
deploy:
restart_policy:
condition: on-failure
delay: 3s
max_attempts: 5
window: 60s
user: root
ports:
- 8080:80
- 50000:50000
container_name: jenkins
networks:
- mysqlnetwork
volumes:
- jenkinsstorage:/var/jenkins_home
- /run/user/1000/docker.sock:/var/run/docker.sock
- /home/user/bin/docker:/usr/local/bin/docker
volumes:
jenkinsstorage:
networks:
mysqlnetwork:
external: true
name: mysqlnetwork
Führe dann das Kommando aus docker compose up
Grundlage schaffen
Die einfachste Methode ist das manuelle Anstoßen einer Pipeline. Du drückst auf den Knopf „Starten“ und die Pipeline rennt. Die elegante Lösung ist, dass bei jedem Commit (oder Merge) auf einer Git-Verzweigung (Branch) die Pipeline losrennt – mit Webhooks.
Jenkins erkläre ich anhand von dem App-Framework Ionic. Du kannst jede anderen Code / Framework verwenden.
Lege eine Ionic Projekt in einem öffentlichen Git-Repo an.
Erstelle eine Jenkins-Datei. Die Syntax Jenkins musst Du Dir schrittweise aneignen. Für jedes Repo sieht dieses anders aus, wenn Du nicht exakt gleich arbeitest. Prinzipiell kannst Du mit „sh“ viele Shell Kommandos zum Bauen, Testen oder Konfigurieren ausführen. Variablen unterstützt Jenkins auch.
pipeline {
environment {
registry = 's20'
registryCredential = '977ae189-bf38-4b7c-a7c4-ccc5f6111858'
dockerImage = ''
}
agent {
docker {
image 'busybox'
}
}
stage('Frontend') {
agent {
docker {
image 'satantime/puppeteer-node:19-buster-slim'
}
}
stages {
stage('Install NPM Dep') {
steps {
dir('frontend-ionic') {
sh 'npm install -f'
sh 'npm install -g @angular/cli'
sh 'npm i -D puppeteer && node node_modules/puppeteer/install.js'
}
}
}
stage('Run Tests') {
steps {
dir('frontend-ionic') {
sh 'ng test'
}
}
}
stage('Modify Env') {
steps {
dir('frontend-ionic') {
sh 'cp src/app/config.prod.ts src/app/config.ts'
}
}
}
stage('Build Ionic Docker') {
steps {
dir('frontend-ionic') {
script {
dockerImage = docker.build 's20:nightly'
}
}
}
}
stage('Push Ionic Docker') {
steps {
script {
dir('frontend-ionic') {
docker.withRegistry('https://register.lan', registryCredential) {
dockerImage.push()
}
}
}
}
}
}
}
}
}
Halte die Reihenfolge Stage > Steps > Scripts (Optional: > Subfolder) > Kommando / Aktion immer ein. Benenne die Stages eindeutig. Die sh Zeilen beinhalten die Kommandos, die die Aktionen ausführen. Der Agent ist die Grundlage, in der die Pipeline sich ausführt.