Website-Deploy über CI-Pipelines

|
Vor ein paar Wochen habe ich im Blog erläutert, warum ihr Git-Repositories nicht direkt auf den Server klonen solltet. Als Alternative habe ich euch Möglichkeiten vorgestellt, wie ihr eure Website stattdessen auf den Server bekommt. Dabei ging es um möglichst einfache Methoden. Eine weitere sind Pipelines. Diese könnt ihr nutzen, wenn ihr euer Git-Repository online bei Github, GitLab, Bitbucket oder einem anderen Anbieter verwaltet.

Was ist eine Pipeline?

Im Kontext des Continuous Integration/Continuous Deployment sind Pipelines eine Abfolge vordefinierter Aktionen, die bei bestimmten Events ausgelöst werden. So kann zum Beispiel bei neu hinzugefügten Commits zuerst eine Testaktion erfolgen, die den neuen Code auf Fehler überprüft. Ist diese erfolgreich, wird eine weitere Aktion ausgeführt, die den Code in einem speziellen Staging-Bereich ausrollt. In diesem lässt sich das Ergebnis dann begutachten. Eine finale Aktion übernimmt daraufhin das Ausrollen in die Live-Umgebung.

In einer Pipeline werden also mehrere Aktionen gesammelt. Das Anlegen und Ausführen von Pipelines wird inzwischen von den meisten großen Anbietern wie GitHub, GitLab und Bitbucket nativ unterstützt. Nutzt ihr eine Software ohne Unterstützung für Pipelines, könnt ihr zum Beispiel auf die Software Jenkins oder den Anbieter TravisCI zurückgreifen.

Github Action FTP Deploy

In GitHub tragen Pipelines den Namen „Actions“. Private Repositories haben aktuell ein monatliches Kontingent an 2.000 Freiminuten, jede weitere kostet je nach Betriebssystem zwischen 0,0008$ und 0,016$. Durch die Community stehen bereits eine Menge an vordefinierten Actions zur Verfügung. Diese könnt ihr in euer eigenes Projekt einbinden und durch Parameter anpassen.

Zum Hochladen eures Projektes auf einen Server per FTP oder SFTP lässt sich zum Beispiel die Action FTP Deploy einsetzen. Diese nutzt im Hintergrund git-ftp, das wir bereits im letzten Beitrag zu dem Thema vorgestellt haben.

Um FTP Deploy einzurichten, klickt ihr auf den „Actions“-Reiter und daraufhin auf „set up a workflow yourself“. In der darauffolgenden Oberfläche könnt ihr den Workflow einrichten. Bindet FTP Deploy folgendermaßen ein:

name: main
on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: FTP Deploy
        uses: SamKirkland/FTP-Deploy-Action@3.1.1
        with:
          ftp-server: sftp://p123XX.mittwaldserver.info/html/
          ftp-username: example
          ftp-password: ${{ secrets.ftp_password }}

Zunächst definiert ihr, wann die Aktion ausgeführt werden soll. In diesem Beispiel habe ich mich für jeden Push auf den Branch `main` entschieden. Es ist aber auch möglich, die Action beim Erstellen von Tags zu starten. Schaut dafür am besten direkt in die Dokumentation von GitHub. Danach definiert ihr einen Job mit zwei Schritten. Erstens: Auschecken des Repositories. Der zweite Schritt ist der Deploy mit der Action FTP Deploy.

Hier solltet ihr die Werte für `ftp-server` und ftp-username ändern. Achtet darauf, dass das ftp-password nicht im Klartext hinterlegt wird. Denn so landet es in der Git-History – und da haben Zugangsdaten nichts zu suchen. ;-) Da Github die Verwendung von Secrets unterstützt, wird das Passwort nur beim Ausführen der Action übergeben und kann von Außenstehenden nicht eingesehen werden. Um den Workflow zu erstellen, klickt auf „Start Commit“ und die Datei wird angelegt.

Bevor der Workflow jedoch endgültig funktioniert, müsst ihr das FTP Passwort als Secret hinterlegen. Dies geschieht über die Projekteinstellungen: Settings › Secrets › New secret. Als Name tragt ihr ftp_password ein – also genau das, was wir oben in der Konfiguration der Action auch angegeben haben. Beim Wert tragt ihr das zu verwendende Passwort ein. Auch die anderen Variablen ftp.server und ftp-username können bei Bedarf als Secret angelegt werden, sodass ihr bei einer Änderung nur das Secret und nicht die Action selbst bearbeiten müsst.

Nun wird bei jedem neuen Commit, der auf dem main Branch landet, die Action gestartet und die Projektdaten auf den Server hochgeladen. Auch für diese Action könnt ihr alle Konfigurationsmöglichkeiten von Git-ftp nutzen.

Pipelines bei GitLab & Bitbucket

Bei Gitlab und Bitbucket ist ein bisschen mehr Handarbeit nötig.

GitLab

In GitLab legt ihr zunächst die Datei .gitlab-ci im Root-Verzeichnis des Git-Projekts an. Bei mir hat die Datei folgenden Inhalt:

stages:
  - build
  - test
  - deploy

# ...
# other jobs

.install_dependencies:
  - apt update && apt -y install curl git
  - curl https://raw.githubusercontent.com/git-ftp/git-ftp/master/git-ftp > /bin/git-ftp
  - chmod 755 /bin/git-ftp
  - mkdir ~/.ssh

deploy:dev:
  stage: deploy
  environment:
    name: staging
    url: https://staging.project.com
  image: ubuntu
  before_script:
    - *install_dependencies
    - ssh-keyscan -H $FTP_HOST_DEV >> ~/.ssh/known_hosts
  script: git-ftp push -vv --auto-init -u $FTP_USER_DEV -p $FTP_PASSWORD_DEV --remote-root $REMOTE_ROUTE_DEV $DEV_PROTOCOL://$FTP_HOST_DEV
  only:
    - master
    - tags

Nach den Stages definiert ihr ein Set an Skripten, das ihr wiederverwenden möchtet: die Installation der benötigten Abhängigkeiten. Im Anschluss definiert ihr den Job deploy:dev für die Stage deploy. Die Angabe des Environments ist optional und sorgt dafür, dass ihr sehen könnt, welche Version in welcher Umgebung läuft.

Das Wichtigste sind dann die Skripte, die ausgeführt werden sollen. Bevor es losgeht, ruft ihr das vorbereitete Set der Installationsskripte auf und macht den Host bekannt. Unter script nutzt ihr git-ftp zum Hochladen der Dateien. Wichtig ist hier der Flag --auto-init, der ermöglicht, dass das Push-Command auch beim ersten Deploy auf die Umgebung funktioniert.

Anschließend setzt ihr bei GitLab die Variablen. Zu vergleichen sind diese mit den Secrets bei GitHub. Die CI-Variablen sind bei Gitlab unter Einstellungen › CI/CD › Variablen versteckt. Ich habe Folgende genutzt:

  • DEV_PROTOCOL : sftp
  • FTP_HOST_DEV : p123XX.mittwaldserver.info
  • REMOTE_ROUTE_DEV : /home/www/p123XX/html/project (es empfiehlt sich hier den absoluten Pfad zu verwenden)
  • FTP_USER_DEV : p123XX
  • FTP_PASSWORD_DEV : super_s3cret

Beim FTP_PASSWORD solltet ihr das Häckchen für *maskiert* setzen.

Zusätzlich habe ich für meine Projekte noch einen Job deploy:tag:prod angelegt, der sich abgesehen von den Variablen-Namen und dem Environment nur durch das only unterscheidet. Während ich ins Staging auch bei jedem master-Commit deployen möchte, soll das bei der Produktiv-Umgebung nur bei Tags passieren.

only:
  - tags

Durch die Nutzung von Regeln statt only könnt ihr auch nur bestimmte Tags deployen (z. B. nur Tags, die nicht beta oder alpha sind). Und wenn ihr noch mehr aus der Pipeline herausholen wollt, lohnt sich ein Blick in die Dokumentation von GitLab.

Bitbucket

Hier sieht es recht ähnlich aus wie bei GitLab. Lediglich die Syntax der Konfigurationsdatei unterscheidet sich. Daher gehe ich hier jetzt nicht mehr auf die Details ein. Bei Bitbucket heißt die Datei bitbucket-pipelines.yml und sieht wie folgt aus:

image: ubuntu

pipelines:
  default:
    - step:
      name: Deploy to FTP.
      image: samueldebruyn/debian-git
      script:
          - apt update && apt -y install curl git
          - curl https://raw.githubusercontent.com/git-ftp/git-ftp/master/git-ftp > /bin/git-ftp
          - chmod 755 /bin/git-ftp
          - mkdir ~/.ssh
          - ssh-keyscan -H $FTP_HOST_DEV >> ~/.ssh/known_hosts
          - git-ftp push -vv --auto-init -u $FTP_USER_DEV -p $FTP_PASSWORD_DEV --remote-root $REMOTE_ROUTE_DEV $DEV_PROTOCOL://$FTP_HOST_DEV

Die Variablen lassen sich bei Bitbucket im Projekt unter Einstellungen › Pipelines › Environment-Variablen einrichten. Für genauere Informationen gibt es die Pipeline-Dokumentation von Bitbucket.

Stolperfallen

Auf ein paar wichtige Stolperfallen möchte ich hier noch kurz eingehen.

Git-ftp lädt alle Dateien auf den Server hoch, die im Repository verwaltet werden. Möchtet ihr Dateien ausschließen, bietet sich eine .git-ftp-ignore-Datei im Repo an. Bei mir hat diese mindestens zwei Einträge: .gitlab-ci.yml und .git-ftp-ignore.

Wenn ihr generierte Dateien habt, die nicht im Repo verwaltet werden, müssen diese in der Pipeline auch generiert werden, bevor sie hochgeladen werden können. Beispiele sind hier minifizierte Skripte oder CSS-Dateien, die durch SASS oder LESS entstehen, aber auch Dependencies von Tools wie composer. Hierfür könnt ihr weitere Jobs oder Steps anlegen, die dann Artefakte generieren. Da das den Rahmen des Beitrags endgültig sprengen würde, empfehle ich einen Blick in die verlinkten Dokumentationen.

Über die Datei .git-ftp-include könnt ihr die generierten Dateien in den Deploy inkludieren. Ihr solltet für diese generierten Dateien in der Pipeline keinen Comitt erstellen, da dieser nach dem Abschließen der Pipeline verloren geht und beim nächsten Deploy nicht wiedergefunden werden kann.

Gitlab und Bitbucket führen in der Pipeline standardmäßig einen sog. „shallow clone“ durch. Das heißt, dass nicht das gesamte Repo geklont wird, sondern nur die letzten 50 Änderungen. Das kann dazu führen, dass der zuletzt deployte Commit in der Pipeline nicht vorhanden ist und daher nicht gefunden wird. Bei GitLab könnt ihr das unter Einstellungen › CI/CD › Generell umstellen. Bei Bitbucket könnt ihr die clone-depth erhöhen oder auf full setzen.

Fazit

Mit CI-Pipelines lässt sich der Deploy-Prozess hervorragend automatisieren. Mit git-ftp ist es relativ leicht, einen schnellen Deploy zu ermöglich, ohne manuell Dateien hochladen zu müssen. Natürlich können auch die anderen Tools, die ich im letzten Beitrag erläutert habe, über eine CI-Pipeline zum Deploy genutzt werden. Habt ihr euch einmal an den Workflow gewöhnt, lernt ihr die Vorteile schnell zu schätzen.

Nutzt ihr bereits eine CI-Pipeline zum Deploy? Habt ihr andere Methoden oder Tools, mit denen ihr diese Aufgabe erledigt? Ich freue mich, von euch in den Kommentaren zu lesen. 

Ähnliche Artikel:

Webentwicklung

Warum Git-Repositories auf dem Server keine gute Idee sind

Git-Repositories auf dem Server können zur Sicherheitslücke werden. Wie du das umgehen kannst, erklärt dir Lukas im Blog.

Webentwicklung

Automatisierte Tests dank Gitlab CI

Testen ist bei der Entwicklung das A und O. Umso besser, dass es mit Gitlab CI eine Möglichkeit des automatisierten Testens gibt. Details erfährst du im Blog.

Webentwicklung

Tool-Tipp: Git-Oberfläche Fork

Ich war lange auf der Suche nach der richtigen GUI für Git. In diesem Artikel geht es darum, was mich an der GUI Fork so begeistert und warum es für mich die beste Git-GUI ist.

Webentwicklung

Aus unserer Kubernetes Werkzeugkiste: der Secret Generator

Der Secret Generator – einer unserer eigens entwickelten Kubernetes Operatoren. Damit kannst du Passwort-Erstellung automatisieren. Wie? Lies es im Blog.

Webentwicklung

AppImage, Flatpak und Snap: Was können die alternativen Paketformate?

Entwickler Fabian nimmt im Blog die alternativen Paketformate AppImage, Flatpak und Snap unter die Lupe. Welche Vor- und Nachteile sie haben, liest du hier.