„Wo programmiert wird, muss deployed werden.“ Diese alte Handwerker-Weisheit gilt auch für web-netz. Ein Deployment, das Ausrollen von Software auf Produktiv-Systeme, ist von jeher ein kitzeliger Moment, bei dem sowohl Geschwindigkeit als auch Sorgfalt wichtig sind – und jeder Fehler vor der Weltöffentlichkeit stattfindet.

Deployments sind wie ein Stapellauf für Software: Zum ersten Mal kommt ein über Tage, Wochen oder Monate vorbereitetes Stück Software mit dem Internet und der breiten Öffentlichkeit in Kontakt. Und genau wie bei einem Stapellauf darf nichts schief gehen – oder es sollte zumindest ein Plan bestehen, wie man im Falle eines Falles den Stapellauf stoppt – oder im schlimmsten Falle rückgängig macht.

web-netz hat inzwischen seinen ganz eigenen Weg gefunden, Deployments sicherer und schneller zu machen: mit automatischen Deployments.

Deployments… der alte Weg

Wie bei einem Stapellauf kommt es bei dem Deployment auf die richtigen Handgriffe in der richtigen Reihenfolge an. Wasser in das Trockendock zu lassen bevor man die Schleusentore öffnet, die Arbeiter aus dem Trockendock zu holen bevor man Wasser dort hineinpumpt… ein guter Plan sorgt dafür, dass niemand verletzt wird.

Der Plan in der Webentwicklung sah bei manuellen Deployments wie folgt aus:

  1. Der Entwickler wechselte per SSH auf den Zielhost
  2. Dort wechselte in das Verzeichnis, in dem das Projekt ausgecheckt war.
  3. Dann wurde mittels `git pull` (oder ähnlichen Kommandos) der letzte Zustand des Projekts heruntergeladen.
  4. Zuletzt wurden noch einige abschließende Handgriffe (wie das Leeren des Caches) durchgeführt.

Dieser Prozess war nicht nur zeitaufwändig, sondern bedurfte auch einer penibel eingehaltenen Reihenfolge bzw. Einhaltung aller notwendiger Schritte. Dabei dämmerte schnell die Erkenntniss: Die durchzuführenden Schritte sind eigentlich immer die selben – und das in jedem Projekt. Warum also nicht einfach ein automatisches Deployment einrichten?

Dabei wird der manuelle Ablaufplan in einen Ablaufplan übersetzt, der von einem Deployment-Tool interpretiert und umgesetzt werden kann – wie eine Checkliste oder ein Rezept. Das Deployment-Tool wird dann nur noch mit einem Auslöser verbunden, der bei Betätigung das eigentliche Deployment auslöst.

Der Vorteil von automatischen Deployments liegt dabei auf der Hand: Unabhängig vom Auslöser wird der Ablaufplan streng eingehalten und auch eventuell auftretende Komplikationen werden automatisch gelöst.

Durch die Automatisierung ist der Prozess auch deutlich schneller: Während früher für Deployments der Betrieb auf dem Zielserver angehalten werden musste, kann nun in der Regel ein Deployment so schnell durchgeführt werden, dass der Besucher der Internetseite gar nicht merkt, wie gerade im Hintergrund die Programmierung ausgetauscht wird.

Automatische Deployments mit Gitlab CI/CD

Für automatische Deployments benötigt man zuerst eine Software, die diese Deployments durchführt. web-netz setzt bereits seit geraumer Zeit auf „Gitlab CE“. Diese Software wird von Gitlab zur Verfügung gestellt, und kann entweder auf den Servern von Gitlab benutzt werden, oder aber lokal installiert werden. Bei der Online-Agentur web-netz wurde aus Sicherheitsgründen die lokale Variante gewählt – unsere Kundendaten sollen nach Möglichkeit nicht unsere eigenen Server verlassen.

In Gitlab wiederum ist mit „Gitlab CI/CD“ eine Deployment-Software integriert, die überraschend einfach aufsetzbar und leistungsfähig einsetzbar ist.

Schlichtes Logo von GitLab

Bildnachweis: Gitlab

Gitlab CI/CD reagiert dabei auf bestimmte Ereignisse in Git, wie z.B. Änderungen in bestimmten Branches oder das Setzen von Tags. Somit kann z.B. in Test-Branches jeder Commit ein Deployment auf einen Test-Server auslösen, während in Produktions-Branches ein Git-Tag als Auslöser benötigt wird, um versehentliche Deployments zu unterbinden.

Für jedes Projekt kann man dabei in einer Datei namens `.gitlab-ci.yml` ein genaues Rezept hinterlegen, auf welche Ereignisse das automatische Deployment reagieren soll.

Gitlab kennt dabei bei Deployments mehrere Stufen:

  1. Building
  2. Testing
  3. Deployment

Wenn eine dieser Stufen einen Fehler aufweist, wird die nächste Stufe schon nicht mehr ausgeführt. Gerade in Verbindung mit automatisierten Tests kann verhindert werden, dass Deployments durchgeführt werden, wenn die Tests einen Fehler bemerken.

Besonders praktisch: Wenn Gitlab CI/CD einen Fehler feststellt, wird nicht nur das Deployment abgebrochen, sondern auch der Verursacher mit einer E-Mail angeschrieben, in der wiederum der Fehlerbericht zu finden ist.

Der web-netz-Weg mit Gitlab CI/CD

Bei web-netz haben wir eine Vorlage für die `.gitlab-ci.yml`, unser Deployment-Rezept.

Die Grundlage ist dabei ein Docker-Image des britischen Government Digital Service, mit dem eine Umgebung erstellt wird, von der aus Deployments schnell und sicher möglich sind.

image: governmentpaas/git-ssh

Im nächsten Schritt fügen wir SSH-Einstellungen dazu, die dem Image erlauben, mit dem Kunden-Server Kontakt aufzunehmen.

before_script:
  - eval $(ssh-agent -s)
  - mkdir -p ~/.ssh
  - echo "$SSH_CONFIG" > ~/.ssh/config
  - echo "$SSH_KNOWNHOSTS" > ~/.ssh/known_hosts
  #- echo -e "\n\nHost *\n  StrictHostKeyChecking no\n\n" >> ~/.ssh/config
  - ssh-add <(echo "$SSH_PRIVATEKEY")

Dann können wir in der selben Datei beliebig viele Ereignisse definieren, auf die wir mit einem Deployment reagieren:

deploy_production:
  stage: deploy
  environment:
    name: production
    url: ${PRODUCTION_URL}
  variables:
    ENVIRONMENT_HOST: ${CI_ENVIRONMENT_NAME}
    ENVIRONMENT_DIR: ${PRODUCTION_DIR}
    ENVIRONMENT_PHP: ${PRODUCTION_PHP}
  only:
    - master
  script:
    - tools/ci/deploy.sh

In der Regel definieren wir diesen Block drei Mal, für jede Projekt-Instanz:

  • Preview: Hier zeigen wir den Kunden neue Features
  • Staging: Hier testen wir einen Release ein letztes Mal, bevor wir ihn auf das Produktiv-System loslassen
  • Production: Die eigentliche, im Internet verfügbare Instanz
Screenshot der Stage-Umgebung von GitLab

Staging: Hier wird getestet.  –  Bildnachweis: Screenshot GitLab

Dazu kommt im Projekt eine weitere Datei, in der wir das eigentliche Rezept für das Deployment hinterlegt haben: Ein Bash-Script, was wir unter `tools/ci/deploy.sh` abgelegt haben. Eine einfache, für fast jedes Projekt anwendbare Variante sieht z.B. so aus:

#!/bin/bash
set -e
ENVIRONMENT_PHP=${ ENVIRONMENT_PHP:-php}
if [[ ${CI_ENVIRONMENT_NAME} == *production ]]; then
 ssh ${ENVIRONMENT_HOST} "cd ${ENVIRONMENT_DIR} && echo -e '\e[94mStarting backup\e[39m' && time tools/get-dbdump.sh backup && gzip < tmp/dbdump.sql > tmp/dbdump-$(date "+%Y-%m-%d-%H-%M").sql.gz"
fi
ssh ${ENVIRONMENT_HOST} "cd ${ENVIRONMENT_DIR} && git fetch --tags && git checkout ${CI_COMMIT_SHA} . && git submodule update --init --recursive"

Nachdem die Projektdateien aktualisiert wurden, kann Composer oder NPM ausgeführt werden, oder auch Caches geleert bzw. neu aufgebaut werden. Bei Shopware könnte das wie folgt aussehen:

ssh ${ENVIRONMENT_HOST} "cd ${ENVIRONMENT_DIR} && ${ENVIRONMENT_PHP}
src/bin/console sw:cache:clear && ${ENVIRONMENT_PHP}
src/bin/console sw:theme:cache:generate"

Dieses Skript kann man auch um einen einfachen Test erweitern, ob nach dem Deployment das Ziel-System noch per http erreichbar ist:

STATUS_CODE=$(curl --write-out %{http_code} --silent --output /dev/null
${CI_ENVIRONMENT_URL})
echo "> Got status code ${STATUS_CODE}"

Das Deployment-Skript lässt sich natürlich um weitere Kommandos erweitern, die man auf dem Zielsystem ausführen möchte.

Variablen – für flexible Deployments

Das eigentliche Deployment-Skript erbt dabei den Inhalt von Variablen, die einerseits in der `.gitlab-ci.yml` definiert werden, andererseits aus einer in Gitlab selbst versteckten Konfigurationsmöglichkeit.

Diese in jedem Projekt pflegbaren, gitlab-internen Variablen verstecken sensible Daten vor dem Zugriff regulärer Programmierer – anderseits können Personen mit Zugriff auf Gitlab diese Einstellungen ändern, ohne Programmierer sein zu müssen.

Auf diesem Weg kann man sensible Daten wie SSH-Schlüssel und Zugangsdaten aus dem Git-Repository heraushalten, und in einem von Gitlab geschützten Bereich ablegen, wo nur noch wenige, authorisierte Personen herankommen.

Die Variablen werden pro Projekt in Gitlab unter „Project settings“ > „CI/CD“ > „Variables“ eingestellt.

Für uns haben wir die folgenden Variablen definiert, die in jedem Projekt neu gesetzt werden, und die folgenden Einstellungen definieren:

  • Die für das Deployment notwendigen SSH-Einstellungen
  • Die für die jeweilige Instanz notwendigen

 

Variable             Inhalt
`SSH_CONFIG` Hier muss eine SSH-Config für den Producktions-Host hinterlegt werden. Damit kann das Deployment die richtigen Nutzernamen, Ports und weitere SSH-Besonderheiten mitgeteilt bekommen.
`SSH_KNOWNHOSTS` Hier muss das Ergebnis von `ssh-keyscan [-p PORT] HOSTNAME` für jeden der Hosts in `SSH_CONFIG` hinterlegt werden. Damit kann die Authentizität des Hosts bei Deployments nachgewiesen werden.
`SSH_PRIVATEKEY` Hiermit wird der SSH-Key definiert, mit dem sich Gitlab zum jeweiligen System verbinden darf.
`PRODUCTION_URL` Die URL, unter der das Produktions-System erreichbar ist.
`PRODUCTION_DIR` Der Pfad, in dem die Produktions-Installation liegt.
`PRODUCTION_PHP` Das Kommando, mit dem auf dem Produktions-System PHP aufgerufen wird.

 

Die letzten Variablen können nochmals für `DEVELOP_` und `STAGING_` wiederholt, wenn es dazu passende Deployment-Schritte gibt.

In der offiziellen Gitlab-CI-Dokumentation gibt es weitere Erläuterungen, was in der `gitlab-ci.yml` alles eingestellt werden kann.

Fazit

Mit Gitlab-CI kann man Deployments nicht nur beschleunigen – ab sofort kann jedes Teammitglied vollkommen unabhängig von seinem Wissensstand ein fehlerfreies, sicheres Deployment hinlegen, ohne selber tätig werden zu müssen.

Darüber hinaus kann man nicht nur die Geschwindigkeit, sondern auch die Frequenz von Deployments erhöhen. Ein beliebter Workflow bei web-netz besteht darin, dass während heißer Entwicklungsphasen mit jedem Commit das Preview-System aktualisiert wird, so dass der Kunde jederzeit den aktuellen Entwicklungsstand sehen kann.

Nicht zuletzt sind automatisierte Tests und Backups eine gute Versicherung gegen spontan auftretende Server-Ausfälle aufgrund von verunglückten Deployments – ein Service, den alle Kunden von web-netz mit jedem Deployment automatisch erhalten.

Darüber hinaus wird das web-netz-Deployment beständig weiterentwickelt, so dass auch Alt-Kunden in den Genuss eines aktuellen, sicheren und zuverlässigen Deployments kommen.

Frank Boës

 

Fragen zu Deployments? web-netz hat die Antworten

 

Bildquelle Titelbild : © Brinki / www.flickr.com