Ich habe kürzlich angefangen, mich mit Continuous Integration (CI) zu beschäftigen. Damit sind unterschiedliche automatische Aktionen nach einem Push in ein Versionsverwaltungs-Repo möglich, wie etwa das Testen des Codes oder auch das Deployment auf einen Server. Hier zeige ich euch, wie ein Deployment-Prozess von Gitlab.com auf einen Staging- und Produktiv-Server aussehen kann.
Im Gegensatz zu GitHub hat GitLab die Möglichkeit zur CI direkt integriert, die sogar im kostenlosen Plan genutzt werden kann. Das Theme meiner Website wird mit Git versioniert. Es gibt eine lokale Version, eine auf einem Staging-Server und die hier auf dem Produktiv-Server. Über GitLab CI kann ich das Theme durch Pushen eines Commits automatisch auf den Staging- oder Produktiv-Server übertragen – je nachdem, ob der Push an den staging
- oder master
-Branch geht. So muss ich keine Dateien per SFTP auf unterschiedliche Server laden, sondern kann alles über Git regeln.
Um in einem kostenlosen GitLab-Account die CI zu nutzen, müsst ihr eigentlich nur eine .gitlab-ci.yml
erstellen und auf die oberste Ebene des Git-Repos legen (bei einer eigenen GitLab-Instanz müsst ihr vielleicht erst Runner installieren). Darin steht dann, was GitLab CI machen soll, wenn ein Push ankommt. Wir möchten folgende Dinge umsetzen:
Bei einem Push im staging
-Branch
- Verbinde dich mit dem Staging-Server.
- Erstelle einen temporären Theme-Ordner.
- Kopiere die Theme-Dateien aus dem Git-Repo in den temporären Ordner auf dem Staging-System.
- Verschiebe das bis dahin aktuelle Theme in einen weiteren temporären Ordner.
- Verschiebe den neuen aktuellen Stand in den Theme-Ordner.
- Lösche den temporären Ordner mit den alten Theme-Dateien.
Dieser Umweg über temporäre Ordner ist aus der GitLab-Dokumentation entnommen und sorgt dafür, dass immer ein funktionierendes Theme auf dem Server ist. Andernfalls wäre für eine kurze Zeit der Theme-Ordner nicht vorhanden.
Bei einem Push im master
-Branch
Hier sieht der Vorgang gleich aus wie bei dem Staging-Server, nur dass sich mit dem Produktiv-Server verbunden wird.
Die .gitlab-ci.yml
für das Deployment erstellen
In der .gitlab-ci.yml
stehen wie bereits erwähnt die Schritte, die von der CI ausgeführt werden. Die Aufgaben können in unterschiedliche Jobs aufgeteilt werden, die in unterschiedlichen Stages laufen können. Wir haben nur eine deploy
-Stage und die beiden Jobs deploy_staging
und deploy_production
. Der Anfang der Datei sieht so aus:
stages:
- deploy
deploy_staging:
stage: deploy
image: tetraweb/php:7.1
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- mkdir -p ~/.ssh
- eval $(ssh-agent -s)
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ssh-add <(echo "$STAGING_PRIVATE_KEY")
- apt-get install rsync
script:
- ssh -p22 user@staging.example.com "mkdir -p /html/wp-content/themes/_tmp"
- rsync -rav -e ssh --exclude='.git/' --exclude='.gitlab-ci.yml' --delete-excluded ./ user@staging.example.com:/html/wp-content/themes/_tmp
- ssh -p22 user@staging.example.com "mv /html/wp-content/themes/fbn /html/wp-content/themes/_old && mv /html/wp-content/themes/_tmp /html/wp-content/themes/fbn"
- ssh -p22 user@staging.example.com "rm -rf /html/wp-content/themes/_old"
only:
- staging
Code-Sprache: YAML (yaml)
Zunächst wird die deploy
-Stage definiert und anschließend der Job deploy_staging
, der sich um das Deployment auf den Staging-Server kümmert. Dieser Job gehört zu deploy
und wir nutzen zum Ausführen das Docker-Image tetraweb/php:7.1
. Über before_script
können wir Aufgaben vor Durchführung des Skripts ausführen – in unserem Fall die Vorbereitung für eine SSH-Verbindung und die Installation des rsync
-Pakets. Die Schritte für SSH sind wieder aus der GitLab-Doku. Damit es funktioniert, müsst ihr in GitLab eine Variable STAGING_PRIVATE_KEY
anlegen, die den privaten Key des SSH-Schlüssels enthält.
Im script
-Teil des Jobs verbinden wir uns per SSH mit dem Staging-Server und erstellen den Ordner _tmp
im Theme-Verzeichnis, falls noch nicht vorhanden. Danach übertragen wir per rsync
die Daten aus dem Git-Repo in diesen Ordner. Im Anschluss wird das Verschieben der Ordner vorgenommen. Über only
können wir einen Branch angeben, für den dieser Job gelten soll – andernfalls würde der Job bei jedem Push ausgeführt, da die Datei ja auch im master
-Branch liegt.
Letztlich sieht der Job für das Deployment zum Produktiv-Server fast genauso aus (der Code wird einfach unter dem anderen Job eingefügt):
deploy_production:
stage: deploy
image: tetraweb/php:7.1
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- mkdir -p ~/.ssh
- eval $(ssh-agent -s)
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ssh-add <(echo "$STAGING_PRIVATE_KEY")
- apt-get install rsync
script:
- ssh -p22 user@example.com "mkdir -p /html/wp-content/themes/_tmp"
- rsync -rav -e ssh --exclude='.git/' --exclude='.gitlab-ci.yml' --delete-excluded ./ user@example.com:/html/wp-content/themes/_tmp
- ssh -p22 user@example.com "mv /html/wp-content/themes/fbn /html/wp-content/themes/_old && mv /html/wp-content/themes/_tmp /html/wp-content/themes/fbn"
- ssh -p22 user@example.com "rm -rf /html/wp-content/themes/_old"
only:
- master
Code-Sprache: YAML (yaml)
Da der before_script
-Teil in diesem Beispiel für beide Jobs genau gleich ist, könnte er auch aus den Jobs rausgezogen und global eingefügt werden (auf dieselbe Ebene wie die Angabe von stages
).
Und damit wären wir auch schon am Ende und können in Zukunft das Deployment mit Git-Aktionen vornehmen.
Schöne Anleitung. Ich hätte nur einen Verbesserungsvorschlag und einen Hinweis. Statt bei jedem Deployment den Container immer neu zu bauen, würde ich einen vorbereiten, bei dem rsync schon installiert ist. Da GitLab ja auch eine eigene Docker-Registry hat, kannst du diese auch direkt für die Speicherung dieses Containers nutzen.
Und noch effektiver als ein Deployment mit rsync ist eines per Git, sofern auf dem Server vorhanden. Meine
.gitlab-ci.yml
sieht in etwa wie folgt aus:Das Deployment passiert hier aus Staging immer bei einem Push auf
master
, der immer stabil ist. Ein Push auf Production wird manuell über GitLab ausgeführt und ist nur auf Tags zugelassen. Auch die Rollbacks sind hierdurch super einfach möglich.Der Image
gitlab-ci-git-deploy
beinhaltet schon alles, was notwendig ist. Lediglich der Public-Key muss dann noch auf dem Zielsystem für den Nutzer in die Datei~/.ssh/authorized_keys
eingetragen werden.Aber wenn es mal kein Git auf dem Zielsystem gibt, ist deine Anleitung wirklich sehr hilfreich. Werde ich mal ausprobieren, denn mit Git-FTP kommt es genau zu dem beschriebenen Problem, dass nicht immer alle Dateien auf einmal übertragen werden können.
Danke für den Beitrag. Ich hab gerade mit deinem Post meine erste Pipeline auf Gitlab CI erstellt. Auch wenn ich Bernhards Methode bevorzuge, git zu verwenden, war deine Anleitung erstmal einfacher zu verstehen und nach zu bauen. Als nächstes werde ich mich mal mit Bernhards Lösung beschäftigen und das ganze dann auf einen git-deploy modus umstellen.
Kann man nicht einfach den Gitlab-Runner auf dem Produktiv-Server laufen lassen? Dann braucht man ja kein ssh mehr, um zu deployen sondern kann lokal arbeiten
Hey,
danke für Deinen Kommentar und den guten Tipp! Ich wusste bis eben zwar nicht, dass man den Runner so abgekoppelt von einer GitLab-Instanz laufen lassen kann, aber ja, das wäre noch eine weitere Möglichkeit 🙂
Viele Grüße
Florian
Das hätte nur den Nachteil, dass die Performance & Security des Produktiv-Servers drunter leidet, wenn da ein Runner drauf läuft.
Vll. ist ssh doch besser.