Mit GitLab CI auf das Containerschiff

PHP-Applikationen containerisieren und auf Kubernetes deployen
Keine Kommentare

Sie möchten auf das Containerschiff MS Kubernetes deployen? Dann steigen Sie ein, halten Sie sich gut fest und lassen Sie die kommenden Themen über GitLab CI und automatisches Testen und Deployen für Softwareprojekte auf sich wirken.

Continuous, aus dem Englischen übersetzt, steht für kontinuierlich, stetig und weitere gleichwirkende Begrifflichkeiten. Dabei trifft kontinuierlich, nach der direkten Übersetzung und auch der Bedeutung in diesem Fall, am besten zu. Es geht darum, eine kontinuierliche Integration bzw. eine kontinuierliche Delivery zu haben. Auf ein Softwareprojekt übertragen meint Continuous Integration (CI), dass bei jeder Änderung am Code die Integration der unterschiedlichen Codeabschnitte (Interfaces, Klassen, Funktionen usw.) festgestellt werden kann. Dabei geht es nicht nur um die Integrität der Codefunktionalität im Projekt selbst, sondern auch um die Integrität mit externen Schnittstellen (zum Beispiel: Funktioniert die Wetterdienstanbindung richtig?). Es kann aber auch um einen Test gehen, ob der Coding-Style eingehalten wurde.

Delivery bezieht sich nicht auf Essenslieferungen, sondern auf das Liefern einer neuen Version in einem Softwareprojekt. Im Rahmen von Continuous Delivery (CD) stoßen wir auf Agile. Das sind kurz zusammengefasst Methoden, um schnell auf den Markt reagieren zu können. Bei Continuous Delivery kommt Agile im Rahmen der Time-to-Market-Strategie als einer der wichtigsten Punkte auf. Als Beispiel: Wenn bei klassischen Softwareprojekten jedes halbe Jahr eine neue Version veröffentlicht wird, würde mit Continuous Delivery und damit einem agilen Handeln der Zeitrahmen um einiges verkürzt. Dies hängt wiederum mit Continuous Integration zusammen. Wenn man entsprechend durch kontinuierliche Tests die Integrität des Projekts kennt, kann man anhand dieses Faktors kontinuierlich eine neue Version veröffentlichen. Neben einem Mindset, die Integrität des Projektes zu wahren, ist aber auch wichtig, dass man dort auch entsprechende agile Tools einsetzt, um die neue Version dann auszurollen.

Zusammengefasst kann Folgendes festgehalten werden: Wenn durch Tests die Integrität des Projekts geprüft und gewahrt wird, kann die Dauer bis Softwarefehler und/oder neue Features in eine Applikation eingebaut sind und den Nutzern zur Verfügung stehen (Time to Market) verkürzt werden.

GitLab CI: Was? Wie? Warum?

GitLab ist grundlegend ein Source-Code-Management-Server, der neben Source-Code-Management jedoch noch einiges mehr kann. GitLab hat ein Ticketsystem für Projekte an Bord, eine Auto-DevOps-Funktion, um Projekte automatisch anhand der genutzten Programmiersprache mit Continuous Integration zu versorgen. Codeänderungen können reviewt werden und es gibt viele andere nützliche Funktionen, um den Applikationsentwicklungsprozess einfacher und schneller zu gestalten. GitLab hat verschiedene Lizenzen, unter anderem eine gratis Community-Edition-Lizenz (mit eingeschränkter Funktionalität).

Aber nun zu GitLab CI. Es handelt sich um ein Continuous-Integration- und Delivery-System, das zu 100 Prozent in GitLab integriert ist und einen starken Fokus auf Container legt. Die starke Integration mit GitLab bedeutet, dass zum Beispiel bei Codeänderungen der Status des dadurch gestarteten Continuous-Integration-Laufs aus GitLab heraus verfolgt werden kann. Vom Grundprinzip her gibt es in jedem Projekt eine Datei, in der steht, welche Befehle für welche selbst definierbaren Stages ausgeführt werden sollen. Architekturseitig gibt es bei GitLab CI, wie bei den meisten anderen CI-Systemen auch, einen Runner. In jedem Projekt wird in einer eigenen Konfigurationsdatei im YAML-Format Folgendes festgelegt:

  • was für Stages es gibt,
  • welche Schritte pro Stage ausgeführt werden sollen,
  • welche CI Runner genutzt werden sollen
  • und weitere konfigurierbare Aspekte (zum Beispiel Limitierung von Schritten auf Basis von Git Branches).

Die Runner sind die exekutive Macht in GitLab CI. Sie starten, jeweils in einem neuen Container, die in der Konfiguration definierten Schritte pro ausgeführter Stage. Durch das immer neue Erstellen und Starten der Container ist sichergestellt, dass immer in einer neuen Umgebung getestet wird, und nicht noch Überreste von vorherigen Läufen zu Fehlern führen. Außerdem sorgen so mehrere CI-Läufe nicht für Probleme untereinander. Diese (Container-)Isolation bringt dabei nicht nur Sicherheit, sondern auch ein Level an Reproduzierbarkeit bei Fehlern, da man den Container lokal starten und dort die Befehle auch lokal auf seinem Laptop ausführen kann.

Kostenlos: IPC Agile Cosmos Cheat Sheet

Agile Cosmos Cheat Sheet-small-220x311In diesem Cheat-Sheet von unserem Experten René Schröder bekommen Sie einen Überblick über den Agile Cosmos und die organisatorische Struktur. Mit diesem Mind-Map haben Sie die perfekte Voraussetzung, um entweder Ihr eigenes agiles Team aufzubauen, Ihre aktuelle Organisation zu verbessern oder einen eigenen Stil von Agil zu kreieren.


Warum sollte man GitLab und damit GitLab CI nutzen? GitLab wird rein durch seinen Umfang an Funktionalitäten in den meisten Punkten der Kategorie Source-Code-Management-Server häufig genutzt und unter anderem deswegen von vielen Firmen eingesetzt. Für GitLab CI spricht dabei die Integration mit GitLab, die einfache Bedienung sowie der starke Fokus auf zuverlässiges Bauen und Testen mit Containern.

Continuous Integration der Applikation

Beginnen wir mit CI und CD. Mit Hilfe einer „Hello World!“-PHP-Applikation demonstriert, werden wir nun folgende Punkte absatzweise durchgearbeitet:

  • Continuous Integration für Applikation
  • Applikation als Container verpacken
  • Container auf Kubernetes deployen

Wenn man über die Punkte nachdenkt und versucht, diese kurz und bündig zusammenzufassen, kann man an Abbildung 1 orientieren.

Abb 1: Test-Container-Deploy-Workflow

Abb 1: Test-Container-Deploy-Workflow

Man kann diesen Ablauf über sogenannte Stages nachstellen; Stages werden in gleichbleibender Reihenfolge abgearbeitet. Das bedeutet: Bevor ein Container bei einer Codeänderung gebaut wird, müssen die Tests erfolgreich durchgelaufen sein.
Somit beginnen wir die GitLab-CI-Konfigurationsdatei .gitlab-ci.yml wie folgt:

stages:
- test
- container
- deploy

Im Fall der „Hello World!“-Applikation soll sichergestellt werden, dass die Applikation auch immer „Hello World!“ zurückgibt. Lebenswichtig für die Applikation, denn so wurde ein simpler PHPUnit-Test geschrieben, der das überprüft.

Damit das bei jeder Codeänderung sichergestellt ist, fügen wir der test-Stage einen Schritt hinzu, der den PHPUnit-Test ausführt:

phpunit:
  image: phpunit/phpunit
  stage: test
  script:
    - phpunit --verbose HelloWorldTest

Dieser hinzugefügte Schritt wird einen Container mit dem Image phpunit/phpunit starten, in dem der Befehl phpunit ––verbose HelloWorldTest ausgeführt wird. Wenn der Test erfolgreich ist, wird der Schritt grün und weitere Stages werden ausgeführt. Sollte der Test jedoch fehlschlagen, wird der Schritt rot und es werden keine weiteren Stages ausgeführt.

Nachdem wir nun wissen, dass die Applikation funktionieren sollte (nach den Tests, die definiert/geschrieben wurden), muss noch ein Container(-Image) gebaut werden.

CD fängt beim Containerbau an

Wer schon mal ein Container-Image gebaut hat, wird von Dockerfiles gehört haben. Ein Dockerfile beschreibt ein Container-Image unter anderem in folgenden Aspekten:

  • Welches Container-Image als Basis genutzt wird (FROM)
  • Welche Befehle in den einzelnen Schritten beim Container-Image-Bau ausgeführt werden (RUN)
  • Welche Dateien in das Container-Image kopiert werden (COPY, ADD)
  • Und weitere Aspekte, um das perfekte Container-Image zu bauen

Folgende Zeilen zeigen einen Ausschnitt aus einem Dockerfile, das ein Basis-Image mit Apache Webserver und PHP in Version 7.3 nutzt, und einen Ordner in das Container-Image hineinkopiert:

FROM php:7.3.3-apache-stretch
[...]
# 'src' is the directory with the PHP files and our libraries
COPY src/ /var/www/src
# 'vendor' contains dependent libraries
COPY vendor/ /var/www/vendor

Dieses Dockerfile wird dann in einem GitLab-CI-Schritt mit docker build -t IMAGE_NAME . zusammengebaut und in die Container-Image-Registry der GitLab-Instanz per docker push hochgeladen. Der Schritt sollte im Rahmen der container-Stage durchgeführt werden und muss entsprechend ähnlich dem oberen Dateiabschnitt aus .gitlab-ci.yml definiert werden.

Das Container-Image wird in einem sogenannten Docker-in-Docker-(DinD-)Container zusammengebaut. Jedoch werde ich in diesem Artikel nicht näher auf dieses Thema eingehen.

Nun haben wir also ein Dockerfile, das automatisch zu einem Container-Image gebaut wird; dann braucht es jetzt nur noch einen Schritt, um das Ganze auf Kubernetes zu deployen.

GitLab-Kubernetes-Cluster-Funktion

Um den Prozess des Deployments auf Kubernetes zu vereinfachen, gibt es in GitLab die Kubernetes-Cluster-Funktion. Diese erlaubt es, die entsprechenden Zugangsdaten für einen Kubernetes-Cluster in GitLab zu hinterlegen und sogar auch einen neuen Kubernetes-Cluster zu erstellen, zum Beispiel auf der Google Cloud Platform.

Das Praktische dabei ist, dass die Kubernetes-Cluster-Zugangsdaten entsprechend an die GitLab-CI-Läufe weitergegeben werden. Somit kann sofort auf einen Kubernetes-Cluster zugegriffen und deployt werden.

Die Optionen dafür findet man im GitLab innerhalb eines Projekts auf der linken Seite unter CI/CD. Dort gibt es die Möglichkeit, einen Kubernetes-Cluster zum Projekt hinzuzufügen. Der praktischen Seite davon werden wir uns im nächsten Abschnitt widmen.

Deployen der Applikation auf Kubernetes

Für das Deployen der Applikation auf Kubernetes werden entsprechende Kubernetes-Objekt- Manifeste gebraucht. Eine einfache Webapplikation benötigt in den meisten Fällen mindestens folgende Kubernetes-Objekte:

  • Deployment: „Über“-Objekt, das dafür sorgt, dass Container (Pods) der Applikation nach der entsprechenden Konfiguration im Cluster laufen
  • Service: Objekt, um alle Container (Pods) der deployten Applikation hinter eine Cluster IP mit einem internen DNS-Eintrag verfügbar zu machen
  • Ingress: Bei Benutzung eines Ingress Controllers erlaubt dieses Objekt, einen Service auf HTTP-Ebene nach außen zu exposen. Dies kann auf Ebene von Hostname, URL-Pfad und anderen Faktoren getan werden

Zusammenfassend lässt sich sagen, dass das Objekt Deployment dafür sorgt, dass der Applikationscontainer im Cluster läuft und die Objekte Service und Ingress dazu dienen, dass die Applikation im Cluster und auch extern erreichbar ist.

Im Fall der Beispieldateien wird das Templating mit einfachen Search-and-Replace-Platzhaltern in den Manifesten betrieben. Dabei werden Informationen wie die Codeänderungs-IDs (Git Commit IDs) an die Kubernetes-Objekte angehängt, womit man schnell herausfinden kann, welche Version gerade läuft.

Templating ist ein wichtiger Aspekt für die Objekte, denn dabei geht es speziell darum, so wenig Duplikationen für die Manifeste der Objekte wie möglich zu haben. Es ist wichtig, für seinen Einsatzfall das passende Tool zu finden.
Je nach Templating-Tool wird dann entweder direkt mit dem Kubernetes-Cluster-API gesprochen oder per Kubernetes-Client kubectl.

Im Fall der Beispieldateien wird zuerst das Templating durchgeführt und dann die Manifeste mit kubectl auf das Kubernetes-Cluster angewendet. Abbildung 2 zeigt eine exemplarische Ausgabe eines GitLab-CI-Schritts, der eine Applikation auf Kubernetes deployt hat.

Abb. 2: Exemplarische Ausgabe eines GitLab-CI-Schritts

Abb. 2: Exemplarische Ausgabe eines GitLab-CI-Schritts

In der gezeigten Ausgabe wird effektiv Folgendes getan:

  • Mit kubectl wird die Version des Clients und des Clusters angezeigt.
  • Die Objektmanifeste, die im YAML gespeichert sind, werden per sed (Search and Replace) templatet.
  • Die getemplateten Manifeste werden per kubectl in mehreren Schritten auf das Cluster angewendet.
  • Nach dem Anwenden der Manifeste wird über den kubectl rollout-Befehl der Status des Deployments überwacht. Dabei wird der Befehl erst dann enden, wenn die gewünschte Anzahl an Containern (Pods) erfolgreich läuft.
  • Als Letztes werden noch die momentan existierenden Objekte der Applikation angezeigt.

Diese Schritte führen zum letzten Puzzleteil, nämlich dem automatischen Deployen des gebauten Container-Images auf den Kubernetes-Cluster.

Ausblick: Merge Request

Die hier gezeigten Dateiabschnitte können in ihrer Ganzheit auf GitHub gefunden werden.

Um einen Ausblick zu geben, was unter anderem noch mit GitLab CI möglich ist, werde ich kurz auf Merge Requests eingehen. Merge Requests sind ein Mittel, um Codereviews für Codeänderungen zu haben, jedoch beschränken sich diese nur auf den Code. Wäre es nicht praktisch, beispielsweise bei einer Farbänderung im Design einer Webseite, dies direkt als Demo sehen zu können? Das ist mit GitLab CI möglich. Kubernetes ist dafür perfekt, da man mit einem Kubernetes-Ingress-Objekt (und Wildcard DNS) die Demoanwendung exposen kann. GitLab und GitLab CI bieten entsprechende Wege, die Adresse der Demoapplikation automatisch im Merge Request anzuzeigen. Was aber auch wichtig ist, ist dass die Demoapplikation bei Schließung oder Mergen des Merge Requests auch wieder automatisch weggeräumt wird. Eine Demonstration der Merge-Request-Review-Funktion kann im GitHub Repository gefunden werden. Diese und weitere Funktionen sind mit GitLab und GitLab CI möglich.

PHP Magazin

Entwickler MagazinDieser Artikel ist im PHP Magazin erschienen. Das PHP Magazin deckt ein breites Spektrum an Themen ab, die für die erfolgreiche Webentwicklung unerlässlich sind.

Natürlich können Sie das PHP Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -