CI/CD-Pipeline als Code vs. als Konfigurationsdatei

  • Veröffentlicht

Um als Unternehmen wettbewerbsfähig zu bleiben, ist es von entscheidender Bedeutung, Software schnell und zuverlässig bereitzustellen. Dabei kann Continuous Integration und Continuous Delivery CI/CD helfen. Es ermöglicht Entwickler_innen, Änderungen kontinuierlich zu integrieren und Tests durchzuführen. Dazu ist eine Pipeline notwendig, die oft über eine Konfigurationsdatei definiert oder per grafischem Interface zusammengeklickt wird. Müssen komplexe Projekte bearbeitet werden, führen Konfigurationsdateien zu hohem Aufwand. CI/CD als Code ist eine Alternative, um diese Herausforderung zu meistern. Wie das funktioniert und wir das bei Cloudomation umgesetzt haben, zeigen wir in diesem Blogpost.

Die CI/CD-Konfigurationsdatei

Der Vollständigkeit halber ein Überblick, was eine Konfigurationsdatei im CI/CD-Kontext ist:

Eine CI/CD-Konfigurationsdatei ist eine Datei, die in der Regel von einem CI/CD-Tool verwendet wird, um eine Pipeline für kontinuierliche Integration (CI) und kontinuierliche Bereitstellung (CD) von Software zu konfigurieren. Diese Datei enthält normalerweise Anweisungen für das CI/CD-Tool, um den Build-, Test-, Deployment- und Freigabeprozess für die Software zu automatisieren.

Die CI/CD-Konfigurationsdatei kann in verschiedenen Formaten vorliegen, je nach verwendetem CI/CD-Tool. Einige Beispiele für CI/CD-Tools sind Jenkins, Travis CI, CircleCI, GitLab CI/CD und GitHub Actions. Diese Tools haben jeweils ihre eigenen Formate für die Konfigurationsdatei.

Die Konfigurationsdatei enthält Anweisungen, z.B.

  • welche Build-Tools verwendet werden sollen,
  • welche Tests ausgeführt werden sollen und welche Umgebungen für das Deployment verwendet werden sollen.

Eine solche Datei könnte so aussehen (GitLab CI/CD (.gitlab-ci.yml)):

stages:
  - build
  - test

build-code-job:
  stage: build
  script:
    - echo "Check the ruby version, then build some Ruby project files:"
    - ruby -v
    - rakestages:
  - build
  - test

build-code-job:
  stage: build
  script:
    - echo "Check the ruby version, then build some Ruby project files:"
    - ruby -v
    - rake

test-code-job1:
  stage: test
  script:
    - echo "If the files are built successfully, test some files with one command:"
    - rake test1

test-code-job2:
  stage: test
  script:
    - echo "If the files are built successfully, test other files with a different command:"
    - rake test2

Neben der Konfiguration per Konfigurationsfile ist es auch möglich, Pipelines über eine grafische Benutzeroberfläche (GUI) einzurichten. Dies bringt lt. Gitlab mehrere Herausforderungen mit sich:

  • Die Entwickler können nicht zusammenarbeiten
  • Die Behebung von Problemen ist schwierig
  • Es ist schwierig, Änderungen an der letzten bekannten Konfiguration rückgängig zu machen.
  • Pipelines sind anfällig für Störungen

Konfigurationsdateien sind hier schon flexibler. Aber machen wir noch einmal einen Schritt zurück.

Warum CI/CD-Konfigurationsdateien für komplexe Prozesse schwierig sind

CI bezieht sich auf den Build einer Komponente. Üblich ist, dass die Konfiguration als Konfigurationsdatei im Repository der Komponente vorliegt.

CD steht u. a. für Continuous Delivery. Das kann heißen, dass ein Artefakt gespeichert wird. CD steht aber auch für Continuous Deployment. Hier geht es um die Installation einer Komponente. Und an diesem Punkt wird es kompliziert. In einer Konfigurationsdatei lässt sich oft die Komplexität und Logik nicht abbilden, die benötigt wird. Beispielsweise, wenn mehrere Komponenten installiert werden müssen. Folgende Fragen stellen sich:

  • Sind die Komponenten miteinander kompatibel?
  • Wird eine Konfiguration benötigt?
  • Wie sollen Fehler behandelt werden?
  • Wie funktionieren Rollbacks etc.

Kommen mehrere Softwareversionen hinzu, die wiederum aus verschiedenen Komponenten bestehen, erhöht sich die Komplexität weiter.

Wie lässt sich das besser lösen? Hier kommt die Konfiguration als Code ins Spiel.

Was bedeutet CI/CD als Code?

Die Definition von Gitlab:

Pipeline als Code ist eine Methode, um Deployment Pipelines mit der Hílfe von Source Code zu definieren.

Kurz und knapp: „As Code / als Code“ bedeutet, dass für die Konfiguration eine Datei angelegt wird, die per Quellcode definiert wird.

Was ist der Unterschied und der Vorteil, wenn die Pipeline so und nicht per Konfigurationsfile definiert wird?

Unterschied als Code vs. als Konfigurationsfile

Wie bereits beschrieben ist eine CI/CD-Konfigurationsdatei eine Datei, die die Schritte für die Build-, Test- und Bereitstellungsprozesse einer Software definiert und normalerweise von einem CI/CD-Tool verwendet wird, um den automatisierten Build- und Deploy-Prozess zu steuern.
Diese Konfigurationsdateien unterliegen einem fixen, vom Hersteller vordefinierten Schema. Im obigen Beispiel (.gitlab-ci.yml) enthält jeder Block einen optionalen „stage“ key und einen „script“ key. Die maximale Komplexität, die so abgebildet werden kann, ist limitiert durch das Schema. Pipelines, welche Schleifen, Bedingungen, oder Fehlerbehandlungen beinhalten, können oft nur mühsam oder gar nicht sinnvoll mit dem Konfigurationsdatei-Schema abgebildet werden

CI/CD als Code hingegen hat kein fixes, vordefiniertes Schema. Durch die flexible Syntax einer Programmiersprache lässt sich auch hohe Komplexität elegant abbilden.

Hier ein Beispiel unserer internen Pipeline. Das Script wird ausgeführt, nachdem der Build für eine Komponente erfolgreich war. Das Ziel ist, Cloudomation-Installationen zu finden, die so konfiguriert sind, dass sie automatisch mit neuen Builds der Komponente aus einem spezifischen Branch aktualisiert werden.

"""
Create deploy records for all auto-update installations which can be updated with the provided build
"""

""" # Cloudomation metadata:
project_id_project:
   name: CI/CD
"""

import flow_api

def handler(system: flow_api.System, this: flow_api.Execution):
   inputs = this.get('input_value')
   push_info = inputs['push_info']
   build_id = inputs['build_id']
   is_nightly_build = inputs.get('is_nightly_build', False)
   repository_name = push_info['repository_name']
   
   if repository_name.endswith('-content'):
       installations = this.connect(
           'masterdata',
           name='find updateable installations',
           fetch=(
               '''
               SELECT installation.id
               FROM content
               JOIN workspace
               ON content.workspace_id = workspace.id
               JOIN installation
               ON workspace.installation_id = installation.id
               WHERE installation.is_auto_update IS TRUE
               AND content.branch_name = $1
               AND content.repository_name = $2
               '''
           ),
           params=[
               push_info['branch_name'],
               push_info['repository_name'],
           ],
       ).get('output_value')['result']
   else:
       installations = this.connect(
           'masterdata',
           name='find updateable installations',
           fetch=(
               f'''
               SELECT installation.id
               FROM installation
               WHERE installation.is_auto_update IS TRUE
               AND "{repository_name}_branch" = $1
               '''
           ),
           params=[
               push_info['branch_name'],
           ],
       ).get('output_value')['result']

   deploy_executions = []
   for installation in installations:
       # ensure the installation is not powersaved
       try:
           this.flow(
               'powersave-installation',
               installation_id=installation['id'],
               scale=1,
           )
       except flow_api.DependencyFailedError:
           # we want to be able to deploy to an installation which is not running (eg broken)
           pass
       deploy_executions.append(this.flow(
           'create-deploy',
           build_id=build_id,
           installation_id=installation['id'],
           is_nightly_build=is_nightly_build,
           wait=False,
       ))
   this.wait_for(*deploy_executions)
   return this.success('all done')

Zusammenfassend:

  • Eine CI/CD-Konfigurationsdatei ist limitiert durch das vorgegebene Schema und kann keine -oder nur sehr eingeschränkt- zusätzliche Logik abbilden.
  • CI/CD-as-Code verwendet die Syntax einer Programmiersprache und kann beliebige zusätzliche Logik abbilden.

Vorteile von CI/CD als Code

  • Die CI/CD-Pipeline kann als weitere Komponente der Software angesehen werden.
  • Die Pipeline hat Versionen/Branches. Alle Änderungen können nachvollzogen werden. Ein Roll Back ist möglich und alle anderen Vorteile eines Versionskontrollsystems sind vorhanden.
  • Am wichtigsten: Der Code kann benutzerdefinierte Logik beinhalten.
  • Für verschiedene Prozesse müssen oftmals mehrere Konfigurationsdateien angelegt werden, wohingegen bei einer Nutzung als Code nur ein zusätzliches “if-Statement” reicht

Cloudomation und CI/CD als Code

Wir haben für jede Major-Version eine Pipeline. Wird Version 1 released, wird auch die Pipeline intern veröffentlicht. Jetzt beginnt die Arbeit für Version 2 (Software + Pipeline). Wird jetzt eine Änderung an Version 2 vorgenommen, muss nur die jeweilige Pipeline geändert werden.

Die Pipeline ist als Code angelegt und wird über Git synchronisiert. Der Content kann von Git jederzeit in einen Cloudomation-Workspace geladen werden. Damit steht sofort die gewünschte Pipeline zur Verfügung.

Cloudomation kann auch Ihnen als CI/CD-Tool dienen. Hier finden Sie z.B. einen Vergleich zwischen Cloudomations als Alternative zu Jenkins.

Zusammenfassung

  • Eine CI/CD-Konfigurationsdatei ist eine Datei, die in der Regel von einem CI/CD-Tool verwendet wird, um eine Pipeline zu konfigurieren. Diese Datei enthält normalerweise Anweisungen für das CI/CD-Tool, um den Build-, Test-, Deployment- und Freigabeprozess für die Software zu automatisieren. Diese Konfigurationsdateien unterliegen einem fixen, vom Hersteller vordefinierten Schema. Die maximale Komplexität, die so abgebildet werden kann, ist limitiert durch das Schema.
  • „Als Code“ bedeutet, dass für die Konfiguration eine Datei angelegt wird, die per Quellcode definiert wird. CI/CD als Code hat kein fixes, vordefiniertes Schema. Durch die flexible Syntax einer Programmiersprache lässt sich auch hohe Komplexität elegant abbilden.
  • Wir bei Cloudomation arbeiten mit Pipelines, die per Code definiert sind. Suchen Sie eine geeignete CI/CD-Software? Melden Sie sich bei uns – wir zeigen Ihnen, wie Sie Cloudomation bei Ihnen im Unternehmen einsetzen können.

Jetzt den Cloudomation-Newsletter abonnieren

Werden Sie Cloudomation-Insider. Immer am Ende des Monats neue News zum Thema „Remote Development Environments“ und „DevOps“ erhalten. 




    Johannes Ebner

    Marketing Manager