Blog
Hochskalieren: Bringen Sie Ihr Azure Devops CI/CD Setup auf die nächste Stufe

Einführung
Azure DevOps-Pipelines sind eine großartige Möglichkeit, Ihren CI/CD-Prozess zu automatisieren. Meistens werden sie auf Projektbasis konfiguriert. Das funktioniert gut, wenn Sie nur wenige Projekte haben. Aber was ist, wenn Sie viele Projekte haben? In diesem Blogbeitrag zeigen wir Ihnen, wie Sie Ihre Azure DevOps CI/CD-Einrichtung skalieren können, um sie wiederzuverwenden und einfach zu pflegen.
Ihre typische DevOps-Pipeline
Eine typische DevOps-Pipeline befindet sich innerhalb des Projekt-Repositorys. Betrachten wir eine Pipeline für ein Python-Projekt. Sie umfasst die folgenden Schritte:
- Qualitätsprüfungen wie Code-Formatierung und Linting
- ein Paket wie ein Python-Rad erstellen
- Freigabe eines Pakets für die Python-Paketregistrierung (wie Azure Artifacts oder PyPi)
Mit einer Azure DevOps-Pipeline können wir dies wie folgt erreichen:
trigger: - main steps: # Python setup & dependencies - task: UsePythonVersion@0 inputs: versionSpec: 3.10 - script: | pip install .[dev,build,release] displayName: 'Install dependencies' # Code Quality - script: | black --check . displayName: 'Formatting' - script: | flake8 . displayName: 'Linting' - script: | pytest . displayName: 'Testing' # Build - script: | echo $(Build.BuildNumber) > version.txt displayName: 'Set version number' - script: | pip wheel --no-deps --wheel-dir dist/ . displayName: 'Build wheel' # Publish - task: TwineAuthenticate@1 inputs: artifactFeed: 'better-devops-pipelines-blogpost/devops-pipelines-blogpost' displayName: 'Authenticate pip with twine' - script: | twine upload --config-file $(PYPIRC_PATH) --repository devops-pipelines-blogpost dist/*.whl displayName: 'Publish wheel with twine'
Nun, das ist doch großartig, oder? Wir haben alle Ziele erreicht, die wir uns gewünscht haben:
- Code-Qualitätsprüfungen mit black, flake8 und pytest.
- Erstellen und verpacken Sie das Projekt als Python-Rad.
- Veröffentlichen Sie das Paket in einer Registry Ihrer Wahl, in diesem Fall Azure Artifacts.
Wachsende Schmerzen
Eine DevOps-Pipeline wie die obige funktioniert gut für ein einzelnes Projekt. Aber ... was ist, wenn wir unser Projekt erweitern wollen? Angenommen, unser Unternehmen wächst, wir erstellen mehr Repositorys und es müssen mehr Projekte verpackt und freigegeben werden. Kopieren wir dann einfach diese Pipeline und fügen sie in ein neues Repository ein? Können wir angesichts der wachsenden Größe unseres Unternehmens effizienter sein, als diese Pipeline von Anfang bis Ende auszuführen?
Die Antwort ist nein - wir müssen nicht alle diese Pipelines in ein neues Repository kopieren und einfügen, und die Antwort ist ja - wir können diese Pipelines effizienter ausführen. Lassen Sie uns sehen, wie.
Richtig hochskalieren
Lassen Sie uns sehen, wie wir skalierbare DevOps-Pipelines erstellen können. Zunächst werden wir DevOps-Pipeline-Vorlagen einführen. Dabei handelt es sich um modulare Teile von Pipelines, die wir in verschiedenen Pipelines und auch in verschiedenen Projekten, die sich in unterschiedlichen Repositories befinden, wiederverwenden können.
Lassen Sie uns sehen, wie wir Pipeline-Vorlagen zu unserem Vorteil nutzen können.
1. DevOps-Vorlage einrichten
Lassen Sie uns Teile unserer Pipeline in DevOps-Pipeline-Vorlagen umschreiben. Wichtig dabei ist, dass Sie Vorlagen entweder für stages, jobs oder steps schreiben können. Die Hierarchie ist wie folgt:
stages:
- stage: Stage1
jobs:
- job: Job1
steps:
- step: Step1
Dies kann in einem Bild wie folgt veranschaulicht werden:
Wir können dann eine Vorlage in einer Datei erstellen, zum Beispiel für steps:
templates/code-quality.yml
steps:
- script: |
echo "Hello world!"
.. und sie in unserer früheren Pipeline wiederverwenden:
stages:
- stage: Stage1
jobs:
- job: Job1
steps:
- template: templates/code-quality.yml
... oder für diejenigen, die eine visuellere Art der Darstellung bevorzugen:
So einfach ist es, DevOps-Pipeline-Vorlagen zu verwenden! Wenden wir sie nun auf unseren eigenen Anwendungsfall an.
Vorlage für Code-Qualitätsprüfungen
Lassen Sie uns zunächst die Pipeline für die Codequalitätsprüfungen in eine Vorlage einfügen. Wir machen die Pipeline auch umfangreicher, damit sie Testergebnisse und Abdeckungsberichte ausgibt. Denken Sie daran, dass wir diese Vorlage nur einmal definieren und sie dann an anderen Stellen wiederverwenden.
templates/code-quality.yml
steps:
# Code Quality
- script: |
black --check .
displayName: 'Formatting'
- script: |
flake8 .
displayName: 'Linting'
- script: |
pytest
--junitxml=junit/test-results.xml
--cov=.
--cov-report=xml:coverage.xml
.
displayName: 'Testing'
# Publish test results + coverage
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFiles: '**/test-*.xml'
testRunTitle: 'Publish test results'
failTaskOnFailedTests: true
displayName: 'Publish test results'
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: 'Cobertura'
summaryFileLocation: '**/coverage.xml'
displayName: 'Publish test coverage'
... die wir wie folgt verwenden:
steps:
- template: templates/code-quality.yml
Ganz einfach! Beachten Sie auch, dass wir zwei zusätzliche Aufgaben hinzugefügt haben: eine zur Veröffentlichung der Testergebnisse und eine weitere zur Veröffentlichung der Codeabdeckungsberichte. Diese Informationen sind sehr nützlich für die Anzeige in DevOps. Zum Glück bietet DevOps dafür Unterstützung:
... ein Klick auf die Testergebnisse bringt uns zur Ansicht Tests, in der wir genau sehen können, welcher Test fehlgeschlagen ist (wenn überhaupt):
Und schließlich gibt es auch eine Ansicht, die erklärt, welche Codezeilen Sie mit Tests abgedeckt haben und welche nicht:
Diese sind sehr nützlich, wenn Sie Ihren Code testen wollen!
Jetzt haben wir das alles in DevOps-Vorlagen definiert. Das verschafft uns eine komfortablere Position, um aufwändigere Pipelineschritte zu definieren, denn wir importieren diese Vorlagen, anstatt sie zu kopieren und einzufügen.
Wir können die Vorteile der Verwendung von DevOps-Vorlagen folgendermaßen zusammenfassen:
- Einmal definieren, überall wiederverwenden Wir können diese Pipeline für Codequalitätsprüfungen sowohl im selben Projekt als auch aus anderen Repositories mehrfach verwenden. Wenn Sie aus einem anderen Repository importieren, lesen Sie bitte den Abschnitt'Andere Repositorys verwenden' für die Einrichtung.
- Machen Sie sie ausfallsicher Sie können in eine sehr gute Vorlage investieren, anstatt mehrere schlechte Versionen in Ihrem Unternehmen herumliegen zu haben.
- Reduzieren Sie die Komplexität Die Abstrahierung von häufig genutztem Code kann sich positiv auf die Lesbarkeit Ihrer Pipeline auswirken. So können Neulinge die verschiedenen Teile Ihrer CI/CD-Einrichtung mit DevOps-Pipelines leicht verstehen.
2. Weitergabe von Daten zwischen Vorlagen
Gehen wir noch einen Schritt weiter und abstrahieren auch die Build- und Release-Schritte in Vorlagen. Wir werden die folgende Vorlage für die Erstellung eines Python-Rades verwenden:
steps:
# Build wheel
- script: |
echo $(Build.BuildNumber) > version.txt
displayName: 'Set version number'
- script: |
pip wheel
--no-deps
--wheel-dir dist/
.
displayName: 'Build wheel'
# Upload wheel as artifact
- task: CopyFiles@2
inputs:
contents: dist/**
targetFolder: $(Build.ArtifactStagingDirectory)
displayName: 'Copy wheel to artifacts directory'
- publish: '$(Build.ArtifactStagingDirectory)/dist'
artifact: wheelFiles
displayName: 'Upload wheel as artifact'
Diese Definition unterscheidet sich geringfügig von der, die wir zuvor in der ersten Pipeline definiert haben. Diese Pipeline verwendet Artefakte . Diese ermöglichen es uns, Daten zwischen Aufträgen oder Phasen zu übergeben. Dies ist nützlich, wenn wir unsere Pipeline in kleinere Teile aufteilen möchten. Durch die Aufteilung des Prozesses in kleinere Segmente erhalten wir mehr Transparenz und Kontrolle über den Prozess. Ein weiterer Vorteil ist, dass wir durch die Aufteilung des Python-Rad-Build- und Freigabeprozesses die Möglichkeit haben, an mehrere Anbieter gleichzeitig zu liefern.
Wenn diese Pipeline ausgeführt wird, können wir sehen, dass ein Artefakt (eine Wheel-Datei) hinzugefügt wurde:
... mit der eigentlichen Raddatei darin:
Dies ist auch nützlich, damit wir überprüfen können, was die Build-Pipeline produziert hat. Jetzt können wir diese wheel-Datei wieder aus den Artefakten
vorlage/veroeffentlichen-rad.yml
parameters:
- name: artifactFeed
type: string
- name: repositoryName
type: string
steps:
# Retrieve wheel
- download: current
artifact: wheelFiles
displayName: 'Download artifacts'
# Publish wheel
- task: TwineAuthenticate@1
inputs:
artifactFeed: ${{ parameters.artifactFeed }}
displayName: 'Authenticate pip with twine'
- script: |
twine upload
--config-file $(PYPIRC_PATH)
--repository ${{ parameters.repositoryName }}
$(Pipeline.Workspace)/wheelFiles/*.whl
displayName: 'Publish wheel with twine'
... können sowohl die Build- als auch die Release-Pipeline wie folgt verwendet werden:
- stage: Build
jobs:
- job: BuildWheel
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: 3.10
- script: |
pip install .[build]
displayName: 'Install dependencies'
- template: templates/build-wheel.yml
- stage: Publish
jobs:
- job: PublishWheel
steps:
- script: |
pip install twine==4.0.2
displayName: 'Install twine'
- template: templates/publish-wheel.yml
parameters:
artifactFeed: 'better-devops-pipelines-blogpost/devops-pipelines-blogpost'
repositoryName: 'devops-pipelines-blogpost'
Und hier kommt eine weitere neue Funktion ins Spiel. Stages. Diese ermöglichen uns die Ausführung von Pipelines, die voneinander abhängen. Wir haben unsere Pipeline jetzt in 2 Phasen aufgeteilt:
- Bauabschnitt
- Phase veröffentlichen
Die Verwendung von Phasen macht es einfach zu sehen, was vor sich geht. Sie sorgen für Transparenz und ermöglichen es Ihnen, den Fortschritt der Pipeline leicht zu verfolgen. Sie können die Phasen auch separat starten und die vorherigen Phasen überspringen, sofern die erforderlichen Abhängigkeiten vorhanden sind. Zu den Abhängigkeiten können zum Beispiel Artefakte gehören, die in der vorherigen Phase erzeugt wurden.
Verbesserung des Freigabeprozesses
Was ist also ein weiterer Vorteil dieser Einrichtung? Nehmen wir an, Sie geben Ihr Paket für zwei Pip-Registrierungen frei. Das ist mit diesem Setup ganz einfach, indem Sie in der Veröffentlichungsphase zwei Aufträge erstellen:
- stage: Publish
jobs:
- job: PublishToRegistryOne
steps:
- script: |
pip install twine==4.0.2
displayName: 'Install twine'
- template: templates/publish-wheel.yml
parameters:
artifactFeed: 'better-devops-pipelines-blogpost/registry-1'
repositoryName: 'devops-pipelines-blogpost'
- job: PublishToRegistryTwo
steps:
- script: |
pip install twine==4.0.2
displayName: 'Install twine'
- template: templates/publish-wheel.yml
parameters:
artifactFeed: 'better-devops-pipelines-blogpost/registry-2'
repositoryName: 'devops-pipelines-blogpost'
Wie Sie sehen, können wir die definierten Vorlagen verwenden, um unsere Pipelines zu skalieren. Das Wichtigste dabei ist, dass wir dank der Artefakte unser Rad einmal erstellen und dasselbe Rad mehrmals verwenden können.
Außerdem werden die Veröffentlichungsaufträge standardmäßig parallel gestartet (es sei denn, es wurden explizit Abhängigkeiten definiert). Dies beschleunigt Ihren Veröffentlichungsprozess.
3. Automatisieren Sie mithilfe einer Strategiematrix
Kehren wir noch einmal kurz zur Phase der Codequalität zurück. In der Phase der Codequalität installieren wir zunächst eine bestimmte Python-Version und führen dann alle Qualitätsprüfungen durch. Es kann jedoch sein, dass wir Garantien benötigen, dass unser Code für mehrere Python-Versionen funktioniert. Das ist zum Beispiel bei der Veröffentlichung eines Pakets häufig der Fall. Wie können wir die Ausführung unserer Codequalitäts-Pipeline mit Hilfe unserer Pipeline-Vorlagen ganz einfach automatisieren? Eine Möglichkeit besteht darin, ein paar Aufträge manuell zu definieren und in jedem Auftrag die richtige Python-Version zu installieren. Eine andere Möglichkeit ist die Verwendung einer Strategie-Matrix . Damit können wir eine Matrix von Variablen definieren, die wir in unserer Pipeline verwenden können.
So können wir unseren CodeQualityChecks Job verbessern:
jobs:
- job: CodeQualityChecks
strategy:
matrix:
Python38:
python.version: '3.8'
Python39:
python.version: '3.9'
Python310:
python.version: '3.10'
Python311:
python.version: '3.11'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: $(python.version)
- script: |
pip install .[dev]
displayName: 'Install dependencies'
- template: templates/code-quality.yml
Fantastisch! Die Pipeline führt nun die gesamte Codequalitäts-Pipeline für jede Python-Version aus. Wenn wir uns ansehen, wie unsere Pipeline jetzt läuft, können wir mehrere Aufträge sehen, einen für jede Python-Version:
.. wie Sie sehen können, werden 4 Aufträge gestartet. Wenn keine Job-Abhängigkeiten explizit festgelegt sind, laufen die Jobs innerhalb einer Stufe parallel! Dadurch wird die Pipeline erheblich beschleunigt und Sie können schneller iterieren! Das ist definitiv ein Gewinn.
Endgültiges Ergebnis
Bringen wir es zu Ende! Unsere gesamte Pipeline, die Vorlagen verwendet:
trigger:
- main
stages:
- stage: CodeQuality
jobs:
- job: CodeQualityChecks
strategy:
matrix:
Python38:
python.version: '3.8'
Python39:
python.version: '3.9'
Python310:
python.version: '3.10'
Python311:
python.version: '3.11'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: $(python.version)
- script: |
pip install .[dev]
displayName: 'Install dependencies'
- template: templates/code-quality.yml
- stage: Build
jobs:
- job: BuildWheel
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: 3.10
- script: |
pip install .[build]
displayName: 'Install dependencies'
- template: templates/build-wheel.yml
- stage: Publish
jobs:
- job: PublishWheel
steps:
- script: |
pip install twine==4.0.2
displayName: 'Install twine'
- template: templates/publish-wheel.yml
parameters:
artifactFeed: 'better-devops-pipelines-blogpost/devops-pipelines-blogpost'
repositoryName: 'devops-pipelines-blogpost'
... die diese Vorlagen verwendet:
... für den gesamten Quellcode siehe: better-devops-pipelines-blogpost. Das Repository enthält Pipelines, die die oben beschriebenen Prinzipien anwenden. Die Pipelines ermöglichen das Testen, Erstellen und Freigeben eines Python-Projekts ✓.
Fazit
Wir haben gezeigt, wie Sie Ihr Azure DevOps CI/CD-Setup skalieren und es wiederverwendbar, wartbar und modular machen können. Dies hilft Ihnen, eine gute CI/CD-Einrichtung beizubehalten, wenn Ihr Unternehmen wächst.
Kurz gesagt, wir haben Folgendes erreicht:
- Erstellen modularer DevOps-Pipelines mithilfe von VorlagenErleichtertdie Wiederverwendung von Pipelines über Projekte und Repositories hinweg
- Weitergabe von Daten zwischen DevOps-Pipeline-Jobs mithilfe von ArtefaktenErmöglichtuns die Aufteilung unserer Pipeline in kleinere Teile, die Artefakte aus früheren Jobs verwenden können.
- Teilen Sie Ihre Pipeline in Phasen auf, um mehr Transparenz und Kontrolle über Ihr CI/CD zu erhalten.
Ein Beispiel-Repository mit bewährten Pipelines finden Sie unter:
better-devops-pipelines-blogpost - Repos
Prost
Verfasst von

Jeroen Overschie
Machine Learning Engineer
Jeroen is a Machine Learning Engineer at Xebia.
Unsere Ideen
Weitere Blogs
Contact



