21 July 2018 ancho

gitlab logo

Eine kleine Beschreibung wie wir den deploy unserer Webseite mit gitlab automatisieren.

Die Vergangenheit

Bisher haben wir unsere Webseite immer manuell deployed. Dafür haben wir ein kleines bash Skript das uns die von jbake generierte Seite per rsync auf unseren Server bei uberspace per ssh kopiert.

#!/bin/bash
#
# rsync upload von output/ via ssh.
#
# Benutzt eine alternative Konfiguration in .ssh/config
#

CONFIG=.ssh/config

function deploy() {
  rsync $DRY -r -u -v --filter="P .htaccess" --delete -e "ssh -F $CONFIG" output/ vlughess:~/html/
}

function help() {
  echo "usage: deploy [--dry] [--help]"
}

case "$1" in
  --dry)
    DRY=-n
    ;;
  --help)
    help
    exit
    ;;
esac

deploy

Jeder der einen Zugang, bzw. einen public key in unserer authorized_keys eingetragen hat, kann das tun. Das liegt vor allem daran, dass wir ein und denselben user benutzen. Eine kleine Einschränkung mit der man leben muss auf einem uberspace.

Die Vision

Mit gitlab haben wir auch gleichzeitig die Möglichkeit eine deployment pipeline einzurichten. Wir hatten bereits den Wunsch den deploy zu automatisieren in einem issue festgehalten.

Im Wesentlichen ist das Ziel, dass durch einen push eines git tags, die Seite gebacken und die generierte Webseite veröffentlicht wird.

Dafür wollen wir einen SSH key verwenden, der eben nur dem Zweck dient einen Deploy durchzuführen und sonst nichts.

Kein Login, keine weiteren Befehle ausführen, keine Portweiterleitung.

Die Umsetzung

Allerdings muss man dann diesen Key als Variable im repository auf gitlab hinterlegen. Deswegen ist es auch wichtig, dass dieser key nichts anderes kann als einen build zu triggern. Im Falle einer Kompromittierung des keys, kann dann schon mal nicht ganz so viel kaputt gehen.

Hätten wir die Möglichkeit einen weiteren user anzulegen könnten wir auch einfach die Berechtigungen soweit einschränken, dass da nicht viel schief gehen kann. Das hätte auch den Vorteil, dass wir die eigentliche Arbeit des Bauens der Seite auf gitlab auslagern könnten und in einer zweiten Stufe dann die Dateien auf den Server kopieren.

Aber das können wir mit unserem uberspace, wie oben bereits erwähnt, nicht so umsetzen.

public key einschränken

Deswegen schränken wir die Berechtigung des keys mithilfe der authorized_keys Datei ein. Dafür haben wir ein kleines Skript geschrieben, dass wir mit der command Option an den öffentlichen Schlüssel binden. Zusätzlich haben wir für den Schlüssel dann noch das portforwarding eingeschränkt mit der Option no-port-forwarding.

Die Zeile sieht jetzt folgendermaßen aus:

no-port-forwarding,command="/home/vlughess/bin/gitdeploy.sh" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFxzSTWhjPxfPZBcJ1d51+HwHwipv/nRi6e8jBlqF6lv devlug_deploy

Wenn man nun versucht sich per ssh mit dem entsprechenden key zu verbinden, wird nur das in command definierte Skript ausgeführt und am Ende der Ausführung wird die Verbindung unterbrochen.

build skript

Das build Skript ist ziemlich straightforward.

header von gitdeploy.sh
#!/usr/bin/env bash
#
# gitdeploy
#
# Kleines Helferskript um die Homepage auf einem Server zu bauen und im webroot zu deployen.
#
#
# Die Aufgaben sind recht simpel:
#
# * erstelle einen klon des Projekts, falls nicht schon geschehen.
# * wechsel in einen entsprechenden branch, tag oder commit
# * baue die Seite mit jbake
# * kopiere die Daten in das webroot
# * räume auf
# * sende eine Nachricht in den IRC channel bei Erfolg oder Fehlschlag
#

Wen es im Detail interssiert kann sich das Skript auf gitlab anschauen.

Es hat allerdings ein paar Abhängigkeiten.

  • java

  • jbake

  • git

  • rsync

  • ansible (optional, wird für irc Nachrichten verwendet)

Java und Jbake haben wir auf dem uberspace mit sdkman installiert. Der Rest war schon vorhanden.

gitlab-ci Konfiguration

Die gitlab-ci Konfiguration haben wir uns aus einem example repository geklaut und ein wenig angepasst.

image: ubuntu

before_script:
  ##
  ## Install ssh-agent if not already installed, it is required by Docker.
  ## (change apt-get to yum if you use an RPM-based image)
  ##
  - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client git -y )'

  ##
  ## Run ssh-agent (inside the build environment)
  ##
  - eval $(ssh-agent -s)

  ##
  ## Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
  ## We're using tr to fix line endings which makes ed25519 keys work
  ## without extra base64 encoding.
  ## https://gitlab.com/gitlab-examples/ssh-private-key/issues/1#note_48526556
  ##
  - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null

  ##
  ## Create the SSH directory and give it the right permissions
  ##
  - mkdir -p ~/.ssh
  - chmod 700 ~/.ssh

  ##
  ## Use ssh-keyscan to scan the keys of your private server. Replace gitlab.com
  ## with your own domain name. You can copy and repeat that command if you have
  ## more than one server to connect to.
  ##
  - ssh-keyscan aldebaran.uberspace.de >> ~/.ssh/known_hosts
  - chmod 644 ~/.ssh/known_hosts

produktiv_deploy:
  stage: deploy
  environment: Produktiv
  only:
    - tags (1)
  script:
    - ssh vlughess@aldebaran.uberspace.de (2)
  1. Der build wird nur für Tags ausgeführt.

  2. Wir triggern den command über eine Verbindung mit dem privaten Key, den wir in gitlab hinterlegt haben.

Release

Ein release der Webseite sieht jetzt folgendermaßen aus.

git checkout master
git tag -a -s -m 'Release der Webseite' 2018-07-21T1731
git push --tags

Durch den push wird der tag auf gitlab übertragen. Der neue Tag triggert den produktiv_deploy job auf gitlab.

In der Ausgabe kann man schön nachvollziehen was vorsich geht.