Lerne dein Deployment zu lieben − durch Automatisierung

|
Im Oktober war ich auf der T3CON eingeladen, einen Talk zum Thema "Deployment & Infrastruktur" zu halten. In diesem Talk bin ich auf die Herausforderungen eingegangen, die in Web-Projekten beim Deployment auf moderne Infrastrukturen auftreten können, welche Best Practices etabliert sind, und auf welche Weise diese Best Practices auch auf Infrastruktur-Komponenten angewandt werden können. Nun möchte ich auch den Blog-Lesern erklären, wie sie es lernen, ihr Deployment zu lieben.

Was ist Deployment?

Aller Anfang ist schwer: Stellt euch vor, ihr habt einen ersten Release-fähigen Stand eures Web-Projekts, in eurem lokalen DDEV-Setup läuft alles, und ihr möchtet das Projekt jetzt beim Infrastruktur-Anbieter eures Vertrauens (ähem, *cough* mittwald *cough*) an den Start bekommen. Und genau das bezeichnet der Begriff „Deployment“: Das Problem, eure Software in der designierten Betriebsumgebung in der gewünschten Version ans Laufen zu bekommen.

Seien es eine kontinuierliche Weiterentwicklung im Rahmen eines agilen Projekts, Änderungswünsche des Kunden oder einfache Bugfixes: In den seltensten Fällen bleibt es bei einem einzelnen Deployment. Schnell ergibt sich die Herausforderung, öfter oder regelmäßig neue Versionen eines Softwareprojekts releasen zu wollen.

Ebenso schnell merkt man, dass es dabei mit einem einfachen SFTP-Upload oder rsync nicht getan ist. So braucht ein SFTP-Upload auch bei hinreichender Bandbreite immer ein bisschen Zeit, während der eurer Projekt quasi in einem Mischstand aus zwei Versionen läuft und sich unvorhersehbar verhält. Außerdem sind womöglich neben dem reinen Bereitstellen der Projekt-Dateien weitere Deployment-Schritte nötig, wie beispielsweise ein Update des Datenbank-Schemas, oder das Leeren bestimmter Caches.

Dadurch, dass Deployment-Prozesse oft wiederholt werden müssen, und gegebenenfalls aus mehreren verschiedenen Schritten bestehen, ergeben sich einige typische Anforderungen an derartige Prozesse:

  • Der Prozess sollte einfach wiederholbar sein, und (zumindest potentiell) automatisiert ausgeführt werden können. In der Regel sollte ein Deployment also geskripted oder über ein spezialisiertes Tool ausgeführt werden und (abgesehen von maximal einem Knopfdruck oder einem einfachen Befehl auf der Befehlszeile) keine weitere menschliche Interaktion erfordern.
  • Der Prozess sollte keine Downtime des Projekts verursachen („Zero-Downtime“), und die ausgelieferte Version eurer Software idealerweise innerhalb eines Augenblicks umstellen (also „atomar“ sein).
  • Im Fall des Falles sollte ein Deployment einfach umkehrbar sein („Rollback“).

Aufgrund dieser speziellen Anforderungen ist es in vielen Fällen sinnvoll, ein spezialisiertes Deployment-Tool einzusetzen – von denen ich gerne im folgenden ein paar vorstellen möchte.

Und wie deploye ich jetzt meinen Code?

Deployment-Tools gibt es viele: Eines der ersten seiner Art war Capistrano; im PHP-Umfeld haben sich mittlerweile vor allem Deployer, und speziell im TYPO3-Umfeld auch TYPO3 Surf durchgesetzt.

Um eine eigene, Composer-basierte Anwendung in einen mittwald Space zu deployen, müsst ihr zunächst in einem Projekt eine eigene PHP-Anwendung erstellen. Mit der mittwald CLI geht das mit einer Zeile:

$ mw app create php --wait --document-root=/current/public

Dieser Befehl gibt die ID und den Installationspfad der erstellten App aus, die ihr im Folgenden für eure Deployer-Konfiguration benötigt.

Für ein Deployment mit Deployer könnt ihr euch Deployer als Dev-Dependency in euer Composer-Projekt installieren:

$ composer require --dev deployer/deployer

Der eigentliche Deployment-Prozess wird im Anschluss über eine PHP- oder YAML-Datei im Projektverzeichnis beschrieben. Für ein Deployment in einen mittwald Space könnte eine solche deploy.php-Datei beispielsweise so aussehen:

<?php
namespace Deployer;

require 'recipe/composer.php';

// Config

set('mittwald_project_shortid', 'p-YYYYYY');
set('mittwald_app_shortid', 'a-XXXXXX');
set('mittwald_username', 'maxmustermann.example');

set('repository', 'https://github.com/...');
set('http_user', '{{mittwald_project_shortid}}');
set('writable_mode', 'chmod');

// Hosts

host('ssh.ZZZZZZ.project.host')
    ->set('remote_user', '{{mittwald_username}}@{{mittwald_app_shortid}}')
    ->set('deploy_path', '/home/{{mittwald_project_shortid}}/html/app-install-path')
    ->set('public_path', '/public');

Deployer bringt einige vorkonfigurierte „Rezepte“ für die häufigsten Anwendungen mit (beispielsweise für TYPO3, WordPress, Shopware, Contao, Symfony und Laravel), die über ein require einfach eingebunden werden können. In der deploy.php selbst können dann entsprechende Konfigurationseinstellungen vorgenommen und die vom Rezept definierten Standard-Prozesse an das eigene Projekt angepasst werden. Für eine ausführliche Liste der verfügbaren Rezepte, deren Benutzung und deren Konfigurationsparameter sei auf die offizielle Dokumentation verwiesen.

Um Konfigurationswerte nicht allzu oft wiederholen zu müssen, könnt ihr außerdem per set('variable_name', 'value') eigene Werte definieren, und dann in anderen Konfigurationswerten wiederverwenden. Im Anschluss kann das Deployment über einen Einzeiler auf der Kommandozeile gestartet werden:

$ vendor/bin/dep deploy 

Diesen Befehl könnt ihr natürlich auch ebenso gut in einer CI-Pipeline (beispielsweise Github Actions, Gitlab CI oder einen der anderen üblichen Verdächtigen) ausführen, um das Deployment noch weiter zu automatisieren – beispielsweise jedes Mal, wenn ein neuer Git-Tag erstellt wird.

Auf dem Server hinterlässt jede Ausführung dieses Befehls einen eigenen Release in einem releases/-Verzeichnis, und einen current-Symlink, der auf den jeweils letzten Release zeigt:

current -> releases/3
releases/
   1/
   2/
   3/

Diese Symlink-Konstruktion ermöglicht einerseits ein atomares Update, da dieser Symlink sehr schnell von einem auf ein anderes Release umgebogen werden kann, und andererseits auch ein Rollback, indem der Symlink einfach wieder auf ein vorheriges Release zurückgesetzt wird:

$ vendor/bin/dep rollback

Um euch die Konfiguration etwas einfacher zu machen, könnt ihr auch das mittwald Rezept in eure deploy.php einbinden. Dieses könnt ihr per Composer nachinstallieren:

$ composer require --dev mittwald/deployer-recipes

Im Anschluss könnt ihr euch die gesamte Host-spezifische Konfiguration in der deploy.php-Datei sparen, und müsst nur noch die ID der Ziel-App angeben:

<?php 
namespace Deployer;

require 'recipe/composer.php';
require __DIR__ . '/vendor/autoload.php';
require __DIR__ . '/vendor/mittwald/deployer-recipes/recipes/deploy.php';

set('repository', 'https://github.com/...');

mittwald_app('<app-id>')
    ->set('public_path', '/public');

Mehr über den genauen Umgang mit Deployer und dem mittwald Rezept könnt ihr in unserem Developer Portal nachlesen.

Und was ist mit meiner Infrastruktur?

Mit Tools wie Deployer, Surf & co. könnt ihr also das Deployment eurer Software wunderbar in Code definieren und dann automatisieren. Aber was ist eigentlich mit der Infrastruktur, auf die ihr eure Software deployt? Denn nicht selten kommt es vor, dass sich über den Verlauf eines Softwareprojekts auch die Anforderungen an die Infrastruktur verändern. So wird beispielsweise nach einem Update der Applikation eine neue PHP-Version benötigt, oder für ein neues Feature wird auf einmal eine Redis- oder OpenSearch-Datenbank gebraucht.

Um bösen Überraschungen vorzubeugen, ist es hier – insbesondere bei langlebigen Projekten – ratsam, auch die Konfiguration eurer Infrastruktur in euren Deployment-Prozess zu integrieren. An das Deployment der Infrastruktur sollten dabei dieselben Anforderungen (Wiederholbarkeit, Automatisierbarkeit, Verwaltung als Code, ...) gestellt werden, wie an euer Software-Deployment.

Insbesondere im Cloud-Umfeld hat sich hier Terraform (bzw. dessen jüngster Open Source-Fork OpenTofu) als De-Facto-Standard etabliert.
Tools wie Terraform folgen dabei streng dem „Infrastructure as Code“-Prinzip: Hier definiert ihr den gewünschten Zustand eurer Infrastruktur in einer menschen- und maschinenlesbaren Quelltext-Datei. So eine Definition kann beispielsweise wie folgt aussehen:

resource "mittwald_project" "demo" {
  server_id = vars.server_id
  description = "Beispiel-Projekt"
}

Terraform unterstützt dabei verschiedene Provider, um dort über die jeweilige API Infrastrukturkomponenten zu verwalten. Das obige Code-Beispiel zeigt den mittwald Terraform-Provider.

Nachdem ein solches Terraform-Manifest definiert wurde, kann mit dem Befehl terraform apply ein Infrastruktur-Deployment angestoßen werden – hierbei verbindet sich Terraform zur API des jeweiligen Anbieters (hier also zur mittwald API) um den aktuellen Ist-Zustand der jeweiligen Ressourcen (im Beispiel also ein Projekt auf einem Space Server) zu ermitteln, und dann die nötigen Änderungen vorzunehmen, um die Infrastruktur in den gewünschten Soll-Zustand zu überführen, der in der Manifest-Datei definiert wurde.

Eine Stärke von Terraform liegt auch darin, dass viele verschiedene Infrastruktur-Anbieter unterstützt werden und in einem gemeinsamen Deployment angesprochen werden können. Habt ihr beispielsweise ein Projekt in einem mittwald Space erstellt, und verwaltet euer DNS über Cloudflare, so könnt ihr beide Infrastruktur-Komponenten in einem einzigen Terraform-Deployment bedienen:

resource "mittwald_project" "demo" {
  server_id = vars.server_id
  description = "Beispiel-Projekt"
}

resource "cloudflare_record" "demo" {
  zone_id = vars.zone_id
  name = "demo"
  type = "A"
  value = mittwald_project.demo.default_ips[0]
}

Über den mittwald Terraform-Provider könnt ihr auch das (oben per mw-CLI gezeigte) Erstellen der PHP-App erledigen. Der Vorteil bei Terraform liegt darin, dass ihr diese Definition auch im Nachhinein bearbeiten könnt, und Terraform sich automatisch darum kümmert, die Konfiguration anzugleichen:

data "mittwald_systemsoftware" "php" {
  name = "php"
  selector = "^8.2"
}

data "mittwald_systemsoftware" "composer" {
  name = "composer"
  recommended = true
}

resource "mittwald_app" "demo" {
  project_id = mittwald_project.demo.id
  app = "php"
  version = "1.0.0"
  
  description = "Demo-App"
  document_root = "/current/public"
  update_policy = "none"
  
  dependencies = {
    (data.mittwald_systemsoftware.php.name) = {
      version = data.mittwald_systemsoftware.php.version
      update_policy = "patchLevel"
    }
    (data.mittwald_systemsoftware.composer.name) = {
      version = data.mittwald_systemsoftware.composer.version
      update_policy = "patchLevel"
    }
  }
}

Der eigentliche Rollout dieser Terraform-Deployments kann natürlich ebenfalls über die üblichen CI/CD-Tools automatisiert werden. Gitlab beispielsweise bietet zahlreiche Integrationsmöglichkeiten, die auch gut dokumentiert sind.

Infrastruktur vs. Container

Zu guter Letzt besteht natürlich auch noch die Möglichkeit, eure Anwendung über Container-Images, wie beispielsweise Docker, bereitzustellen. Container-Images haben den Vorteil, dass die Abhängigkeiten eurer Software (also beispielsweise PHP) unmittelbarer Teil eures Deployment-Artefakts sind, und ihr eure Software somit direkt zusammen mit allen benötigten Abhängigkeiten testen und ausliefern könnt. Auf diese Weise seid ihr nicht darauf angewiesen, dass ein Infrastruktur-Anbieter die nötigen Systemkomponenten für den Betrieb eures Projekts bereitstellt, sondern liefert diese benötigten Komponenten einfach selbst mit aus. Im Gegenzug liegt dafür auch die Verantwortung für das Bereitstellen von Updates und die allgemeine Instandhaltung dieser Umgebung bei euch.

Das Erstellen eines Container-Images erfolgt üblicherweise über ein Dockerfile, welches eine wiederholbare und einfach automatisierbare “Bauanleitung” für euer Image enthält — auch hier idealerweise über einen CI-Job, der das Image automatisch baut und in eine Registry hochlädt:

FROM php:8.2-apache

COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY src/ /var/www/html

WORKDIR /var/www/html
RUN composer install --no-dev

Wer Container-Images baut, und seine Software darüber deployen möchte, muss diese Container dann natürlich auch irgendwo starten. Der Betrieb von Containern ist einen eigenen Blog-Artikel wert, denn die Möglichkeiten sind von Docker Compose bis hin zu Kubernetes mannigfaltig. Auch mittwald hat bereits sein eigenes Produkt für den Betrieb von Containern in der Entwicklung.

Fazit

Insbesondere in komplexen und langlebigen Projekten sollten das Deployment von Software und dafür benötigter Infrastruktur immer ganzheitlich gedacht werden. Die hierfür etablierten Methoden und Technologien (Infrastructure as Code und das Container-Ökosystem) helfen euch, eure Entwicklungs- und Betriebsprozesse näher aneinander zu führen (um das Buzzword-Bingo zu vervollständigen: DevOps!) und damit sicherzustellen, dass ihr auch in komplexen und anspruchsvollen Projekten über lange Zeit in der Lage bleibt, in schnellen Iterationen und mit hoher Zuversicht Software an eure Kunden ausliefern zu können.

Ähnliche Artikel:

Webentwicklung

Lieber mit Agentur: TYPO3 Upgrade oder Relaunch?

Wer TYPO3 nicht regelmäßig aktualisiert, tut sich keinen Gefallen. Erfahrt hier, warum Agenturen bei großen Versionssprüngen unterstützen sollten.

"PHP in WordPress-Projekt aktualisieren" Schriftzug vor einem PHP-Logo
Webentwicklung

So aktualisierst du die PHP-Version deiner WordPress Website

Schritt-für-Schritt-Anleitung für die Aktualisierung der PHP-Version deines WordPress Projekts.

Webentwicklung

WordPress und KI (1): Content Creation

Um Zeit zu sparen, greifen immer mehr Content-Ersteller auf KI zurück. Erfahre wie du AI-Plugins und -Funktionen gut in WordPress nutzen kannst.

Webentwicklung

WordPress und KI: Revolutionäre Partnerschaft oder sinnlose Spielerei?

Innovative KI-Plugins helfen, WordPress-Seiten zu optimieren. Beginnt eine neue Ära der Webentwicklung? Finden wir es heraus.

Webentwicklung

Time To First Byte optimieren

Eine schnelle Auslieferung deiner Seite gibt's nur mit geringer TTFB. Lies hier, wie du den Wert u.a. über Caching und Datenbank-Queries optimierst.

Kommentare

Šukri Jusuf am
Hallo Martin,

vielen Dank für diesen aufschlussreichen Artikel zum Thema "Deployment & Infrastruktur" und die Präsentation von Best Practices, die auch auf Infrastruktur-Komponenten anwendbar sind. Als Online Marketing Agentur, die sich mit der Entwicklung und dem Hosting von Webprojekten befasst, wissen wir, wie entscheidend ein effizientes und problemloses Deployment für den Erfolg eines jeden Projekts ist.

Deine Ausführungen zu den Herausforderungen und Lösungen im Deployment-Prozess sind äußerst wertvoll. Es ist beeindruckend, wie du die Notwendigkeit eines einfach wiederholbaren und automatisierten Prozesses betonst. Deine Erklärungen zum Thema „Zero-Downtime“ und die Bedeutung von Rollbacks in Notfällen sind präzise und informativ.

Wir schätzen besonders deine detaillierte Darstellung verschiedener Deployment-Tools und -Methoden, insbesondere im PHP- und TYPO3-Umfeld. Deine Beispiele und Code-Snippets geben einen praktischen Einblick in die Welt des Deployments und machen deutlich, wie wichtig es ist, die richtigen Tools und Prozesse zu wählen, um ein reibungsloses Deployment zu gewährleisten.

Die Integration von Infrastruktur-Management in den Deployment-Prozess, wie du es mit Terraform beschreibst, ist ebenfalls ein wichtiger Punkt. Deine Erläuterungen dazu, wie Terraform und andere Tools dazu beitragen können, die Infrastruktur effektiv zu verwalten und Anpassungen im Laufe der Zeit vorzunehmen, sind sehr aufschlussreich.

Wir stimmen dir vollkommen zu, dass in komplexen Projekten die ganzheitliche Betrachtung von Software und Infrastruktur entscheidend ist. Die von dir beschriebenen Methoden und Technologien sind ein klarer Wegweiser für die Entwicklung und das Management von Webprojekten.

Wir freuen uns darauf, einige der von dir vorgestellten Konzepte und Tools in unseren eigenen Prozessen anzuwenden und zu sehen, wie sie unsere Effizienz und Effektivität im Umgang mit Kundenprojekten verbessern können. Nochmals vielen Dank für die hervorragenden Einblicke und das Teilen deines Wissens!

Beste Grüße aus Neuss,

Šukri Jusuf
Antworten
Lu am
Lieber Martin,

ich habe deinen ausführlichen Beitrag mit Interesse gelesen und habe mich sehr gut informiert gefühlt. Du hast das Thema rundum erläutert und dem Artikel eine sehr klare Struktur gegeben.
Danke für diese Wissenserweiterung. :)

Liebe Grüße
Lu
Antworten
Matteo  am
Endlich jemand, der das Deployment so charmant erklärt! Wer hätte gedacht, dass es möglich ist, sich in sein Deployment zu verlieben? Aber jetzt kann ich es kaum erwarten, meine Website mit einer extra Portion Liebe zu deployen. Danke für die hilfreichen Tipps!

Liebe Grüße,
Matteo
Antworten
worktime am

Die Kombination von Terraform und Deployer für die Software- und Infrastrukturbereitstellung ist sinnvoll. Mir gefällt der praktische Ansatz, die Art und Weise, wie man Probleme löst und gleichzeitig Lösungen anbietet. Ein weiterer Pluspunkt für die Automatisierung ist die Integration von CI/CD-Tools. Vielen Dank für die Informationen und nützlichen Tipps!

Antworten
Jorg Stromsdorfer am

Guten Morgen Martin. Bei allen Vorteilen sollte man aber auch bedenken, dass während die Betonung auf automatisierten und effizienten Deployment-Prozessen liegt, dürfen wir die Bedeutung einer gründlichen Vorab-Prüfung und Qualitätskontrolle nicht unterschätzen. Automatisierung und Tools zur Vereinfachung des Deployments sind zweifellos vorteilhaft, aber sie ersetzen nicht die Notwendigkeit einer sorgfältigen Überprüfung und Validierung der Software vor dem eigentlichen Deployment. Es besteht das Risiko, dass Fehler, die in der Entwicklungsumgebung nicht sichtbar waren, in der Produktionsumgebung zu schwerwiegenden Problemen führen können. Daher sollte neben der Entwicklung robuster Deployment-Verfahren auch ein starker Fokus auf regelmäßigen und gründlichen Testverfahren liegen, um sicherzustellen, dass die Software in der Produktionsumgebung wie vorgesehen funktioniert. Dies gilt besonders für komplexe Projekte, bei denen Fehler in der Produktion nicht nur kostspielig sind, sondern auch den Ruf des Unternehmens schädigen können. Grüße aus Köln

Antworten

Kommentar hinzufügen