Deployment über eine Bitbucket Pipeline
Pipeline – Warum sollte ich das verwenden?
Als wir mit dem Coden anfingen, haben wir unsere Änderungen entweder direkt auf dem Produktiv-Server gemacht oder per FTP hochgeladen. Beides hatte unmittelbar Auswirkungen auf die Projekte und verursachte öfter mal einen HTTP-Fehler oder Probleme am Layout. Gleichzeitig war ein Zurück auf die letzte Version unmöglich.
Mit der Einführung von Git/SVN konnten wir den Code zentral abspeichern und Änderungen exakt nachverfolgen. Code wurde mittels Git einfach direkt am Server gepulled. Kam es zum Bug, konnte man schnell die letzten Änderungen rückgängig machen. Doch auch hier gab es Probleme, vor allem als man noch nicht lokal entwickeln konnte oder Kleinigkeiten direkt am Server änderte. Wegen des Zeitdrucks wurde oftmals unsauber gearbeitet und die Überreste blieben auf dem Server liegen. Anschließend wusste keiner mehr, ob diese noch gebraucht werden.
All das Übel haben wir mit unserer Bitbucket Pipeline beseitigt. Es wird lokal gearbeitet, Änderungen werden überprüft und ins Git eingespielt. Die Pipeline erledigt den Rest. In der Bitbucket-Pipeline werden jetzt in den entsprechend konfigurierten Schritten vielfältige Aufgaben autonom erledigt. Dazu gehören:
- Der Build-Prozess, der die Quellcodes im Docker-Container kompiliert, um sicherzustellen, dass sie einwandfrei funktionieren.
- Testprozesse, die die Codequalität und die Funktionalität gründlich überprüfen, um höchste Qualitätsstandards zu gewährleisten.
- Das Deployment erfolgt reibungslos mit Hilfe von rsync auf das gewünschte Projekt, um die aktualisierten Dateien effizient zu übertragen.
Die Bitbucket-Pipeline übernimmt diese Aufgaben eigenständig und optimiert so den Entwicklungsprozess.
Welche Vorteile bringt die Pipeline mit sich?
- Codequalität: Keiner kann den Server vermüllt hinterlassen, da die Pipeline alle Dateien aus dem Repo mittels rsync überschreibt. Auch Testschritte können eingebaut werden. Wenn diese nicht erfüllt werden, schlägt die Pipeline fehl und der Code wird nicht auf dem Server aufgespielt.
- Sicherheit des Webservers: Der Webserver soll ausschließlich seine Hauptfunktion erfüllen: das Bereitstellen der Website. Durch Git und Composer erledigt der Webserver schon mehr als nur seine Hauptfunktion und zwar die Installation von Third-Party Extensions. Das Risiko darin besteht, dass in einem dieser Extensions Skripte ausgeführt werden, die die Systemgesundheit des Servers beeinträchtigen können. Dies unterbinden wir, indem die Installation der Pakete in einem kontrollierten Umfeld des Docker Containers in der Bitbucket Pipeline durchgeführt wird.
- Bereinigung von gehackten Dateien: Durch den rsync-Befehl wird das gesamte Projekt synchronisiert. Dateien, die nicht mehr identisch mit dem in der Pipeline gebauten Projekt sind, werden neu aufgespielt. Einzig jene, die mittels dem ignore-Parameter ausgenommen werden, bleiben für die manuelle Überprüfung übrig.
Diese Vorteile gelten übrigens für alle CI-Pipelines, also auch GitHub oder GitLab.
Konfiguration der Bitbucket Pipeline
Um eine CI-Pipeline in Bitbucket zu nutzen, müssen zwei Stellen beachtet werden: der Code in bitbucket-pipeline.yml und die Konfiguration im Repository auf bitbucket.org.
Auf Bitbucket musst du als erstes die Pipeline aktivieren:
Danach muss ein SSH Keypair generiert oder ein vorhandenes hinterlegt werden. Wie du einen Key erzeugst, haben wir im Artikel »So erstellst du einen SSH Key« erklärt. Über “Known hosts” muss der Fingerprint des Servers hinterlegt werden. Dafür hinterlegst du als “Host adress” deinen Server. Durch den Klick auf “Fetch” generierst du den Fingerprint.
In den Deployment-Einstellungen kannst du die Variablen der Pipeline für das jeweilige Environment anlegen. So kannst du für Production und Staging Server dieselbe Variable mit unterschiedlichen Werten verwenden. Dies wird später in der Konfiguration benötigt.
Nun erstellen wir im Root unseres Repositories die Datei bitbucket-pipelines.yml. Sobald diese existiert, werden die Befehle in der Pipeline abgearbeitet. Das File sieht folgendermaßen aus:
definitions:
caches:
composer: vendor/
steps:
- step: &composer
name: "Setup composer in pipeline"
caches:
- composer
image: composer:2.3
script:
- composer install --verbose --prefer-dist --no-progress --no-interaction --no-dev --optimize-autoloader
artifacts:
- public/**
- vendor/**
- step: &deploy
name: Sync project
script:
- pipe: atlassian/rsync-deploy:0.9.0
variables:
USER: ${SSH_USER}
SERVER: ${SSH_HOST}
REMOTE_PATH: ${SSH_PATH}
LOCAL_PATH: ${BITBUCKET_CLONE_DIR}/
EXTRA_ARGS_COUNT: 10
EXTRA_ARGS_0: "-a"
EXTRA_ARGS_1: "--exclude=.ddev/"
EXTRA_ARGS_2: "--exclude=var/"
EXTRA_ARGS_3: "--exclude=public/fileadmin/"
EXTRA_ARGS_4: "--exclude=public/uploads/"
EXTRA_ARGS_5: "--exclude=public/.well-known/"
EXTRA_ARGS_6: "--exclude=.bitbucket/"
EXTRA_ARGS_7: "--exclude=.git/"
EXTRA_ARGS_8: "--exclude=/.htaccess"
EXTRA_ARGS_9: "--exclude=/.htpasswd.mittwald"
DEBUG: "false"
DELETE_FLAG: "true"
- pipe: atlassian/ssh-run:0.5.0
variables:
SSH_USER: ${SSH_USER}
SERVER: ${SSH_HOST}
mode: 'script'
COMMAND: '${SSH_PATH}/script.sh'
pipelines:
branches:
master:
- step: *composer
- step:
<<: *deploy
deployment: Production
staging:
- step: *composer
- step:
<<: *deploy
deployment: Staging
Oben definieren wir unsere Steps, die wir dann je nach Branch wiederverwenden können.
Im step: &composer installieren wir dank eingecheckter composer.json und composer.lock im Docker Image composer:2.3 das Projekt. Würde beim Build-Prozess nun ein Fehler auftreten, weil ein Package nicht mehr existiert oder die PHP-Version nicht passt, kommt es zum Fehler und es gibt eine Benachrichtigung per Mail. Die Daten am Server bleiben bis dahin unverändert. Wenn der Build-Prozess abgeschlossen ist, laden wir die Dateien, die im public- und im vendor-Ordner liegen, in die Artifacts hoch. So können wir im nächsten Schritt auf diese zugreifen. Durch die Artifacts wird unser Repository um die Dateien erweitert, die nach dem composer install angelegt wurden. So können wir diese im Deploy Step ausspielen.
Der Deploy Step besteht aus 2 Schritten:
- rsync der Dateien des Repositories + Artifacts auf den Server. Durch das Delete Flag werden alle Dateien ersetzt. Ausnahme: Dateien, die im exclude angegeben wurden.
- Ausführung eines Scripts nach dem Deploy über ein Bash Script. Dies nutzen wir, um den Cache der Seite automatisch nach dem Deploy zu löschen.
pipelines:
branches:
master:
- step: *composer
- step:
<<: *deploy
deployment: Production
staging:
- step: *composer
- step:
<<: *deploy
deployment: Staging
In diesem Abschnitt werden die Schritte definiert, die bei einem Commit in den jeweiligen Branch durchgeführt werden. Hier sind die Branches master und staging definiert. Beide führen jeweils den Step composer und deploy aus. Der Unterschied ist jedoch der Parameter deployment. Dieser Parameter beeinflusst, welche Variablen aus dem Bitbucket Repository genommen werden. Sie müssen mit dem Environment Name aus den Deployment Variablen übereinstimmen. So können wir steuern, dass Commits in den Master direkt auf den Produktiv-Server ausgespielt werden und Commits im Staging Branch auf den Staging Server gehen.
Nach dem Commit der Datei kann im Repository nachverfolgt werden, wer die Pipeline angestoßen hat und ob das Deployment erfolgreich abgeschlossen wurde.
Tipp!
Alle Projekte mit einem Login verwalten und bei Bedarf Node.js & Redis fully managed draufpacken - das kannst du auf dem Space-Server. Teste den "next level vServer" jetzt kostenlos!
Kommentare
Und mit so nem klassischen WordPress-Projekt ist Bitbucket nicht das richtige, oder sehe ich das einfach nicht?
Viele Grüße
Gregor
Literatur zum Nachlesen haben wir garnicht genutzt. Auf den TYPO3 Days habe ich erstmals über dieses Thema erfahren und sind dadurch einfach tiefer in die Materie eingetaucht mit etwas Recherche aus dem Internet. Als Startpunkt haben wir die offizielle TYPO3.org Konfiguration mit Deployer genutzt,jedoch ist der Aufwand der Migration von Version 6 auf 7 so groß das wir uns entschieden haben, das Native über die Pipeline zu erledigen. Und im Prinzip ist das nicht so schwierig, denn die Pipeline nutzt Docker Images, dass heißt du suchst dir das passende Docker Image vom DockerHub und führst darin deine Befehle aus.
Für Wordpress haben wir eine eigene Pipeline entwickelt, da gibt es ja kein Composer. Aber hier kommt es echt darauf an wie ihr arbeitet. Habt ihr nur das child-theme commitet, oder geht ihr direkt vom Wordpress root weg. Je nachdem musst du Anpassungen an der bitbucket_pipelines.yml machen. Wenn nur das child_them eingecheckt ist, dann einfach den Deploy-Step mit RSYNC in den child_themes Ordner am Server syncen und fertig. Wenn es übers root geht, dann noch unbedingt die excludes anpassen im RSYNC oder das DELETE Flag auf false stellen, sonst kann es euch passieren, das plötzlich das ganze Wordpress fehlt. Denn RSYNC löscht alles was er nicht in den Sources findet.
Am besten du erstellst ein neues leeres Wordpress Projekt und probierst es einfach mal aus. Von meinem Beitrag brauchst du dann nur den deploy step in der bitbucket-pipelines.yml und einfach commiten und schaun was passiert. Aufwand sollten 1-3 Tage sein, wenn man sich das erste Mal in die Materie einliest. Wir haben ca. zwei Woche gebraucht um alle Informationen zu sammeln die wir benötigen und die lästigen Fehler auszumerzen. Und das ganze Wissen findest du in diesem Beitrag :-)
LG Markus
letztens bei einem Kunden gesehen, dass mittlerweile in Bitbucket zwischen der Pipeline und den Deployments unterschieden wird. Damit sollte man nun zB die Tests bei jedem Commit laufen lassen können und vielleicht auch schon das Projekt zusammenbauen können, aber erst auf Knopfdruck ein Release machen, welches dann auf den Server deployed wird. Könntest du dazu auch noch ein Tutorial machen?
Danke und beste Grüße,
Christoph