Simples deployment via git

Das Problem

Egal ob Homepage, Konfigurationsdateien oder Programmcode: Für praktisch alle Projekte verwendet man heute git.

Möchte man jedoch Versionen automatisch ausrollen (z.B. statische HTML Dateien einer Webseite), benötigt man einige Skripte oder einen Build-Dienst wie z.B. Gitlab-CI, Jenkins oder CircleCI.

Oftmals reicht es jedoch die Daten auf das Zielsystem zu kopieren. Aber auch das geht mit git:

Verteilen von Paketen

Ausgangssituation

Wie haben einen Server (für die Homepage), einen Laptop (für die Entwicklungsarbeit), sowie ein Git-Projekt (z.B. bei Github).
Auf Github befindet sich unser Projekt.
Auf Laptop wie auch auf dem Server haben wir uns mit git clone das Projekt eingerichtet.

Vorgehen

Das Projekt auf dem Server soll beim Empfangen neuer Dateien diese auch in das "Working Directory" schreiben. Dies aktivieren wir mit :

# auf Server, im Projektverzeichnis
git config receive.denyCurrentBranch updateInstead

Nun müssen wir unserem Projekt auf dem Laptop das neue Ziel beibringen:

# auf Laptop, im Projektverzeichnis
git remote add deploy benutzer@servername:/pfad/zu/projektordner/auf/server
# jetzt noch sagen: schiebe den lokalen branch _master_ auf den server (nur 1 mal notwendig)
git push --set-upstream deploy master

Unsere Änderungen können wir weiterhin zu Github schieben:

git push origin

Aber nun auch neu: Auf dem Server direkt ausrollen:

git push deploy

Es ist sogar möglich mittels push-url mehrere Ziele auf einmal zu definieren.

Fazit

Natürlich ersetzt unser kleiner Workflow keinen Build-Server oder ausgetüftelte Deployment Prozesse. Aber für Kleinst-Projekte kann das vorgehen mit updateInstead sehr praktisch sein.

Aber Achtung: Verwenden wir diese Möglichkeit um Webseiten zu versionieren: Nicht vergessen, den .git Ordner nicht auszuliefern.

Geräte unter eigenem Namen: udev rules

Das Problem

Unter Linux wird jedem verbundenen Gerät ein Pfad zugeordnet.
So erscheint z.B. ein verbundener Esp8266 Microcontroller als (serielle) Schnittstelle /dev/ttyUSB0.
Sind noch mehr Geräte verbunden, so erscheinen diese als /dev/ttyUSB1, /dev/ttyUSB2 usw.

Welches Gerät ist aber nun welche Schnittstelle? Und sind diese nach dem Reboot noch gleich?
Rules

Die Lösung: udev rules

Um dies sicher zu stellen erstellt man eine Regel mit udev:

Zunächst identifizieren wir, welches Gerät wir haben wollen. Als Beispiel nutzen wir hier einen Microcontroller, der unter /dev/ttyUSB0 eingehängt ist.

udevadm info --name=/dev/ttyUSB0 --attribute-walk

Aus der Ausgabe von udevadm suchen wir uns einige Werte heraus: Die von ATTRS{idVendor}, ATTRS{idProduct} und ATTRS{serial}.
Dies sollte zum eindeutigen Identifizieren des Gerätes ausreichen.

Nun legen wir uns eine Regel-Datei an:

Eine neu erstellte /etc/udev/rules.d/20-mydevice.rules füllen wir nun mit den gesammelten Informationen. Natürlich müssen hier echte Werte eingesetzt werden:

SUBSYSTEM=="tty", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="abcd", ATTRS{serial}=="01010101010101010101010101010", SYMLINK+="ttyUSB_mydevice"

Nun noch die neuen Regeln anlegen:

sudo udevadm trigger

und den udev Dienst neu starten:

sudo systemctl restart udev

Nun sollte unsere Regel angewendet werden und ein neuer Eintrag /dev/ttyUSB_mydevice angelegt sein. Dieser ist ein Link auf das "echte" Gerät. Somit können wir uns immer auf den selbstgewählten Namen verlassen - auch wenn sich der automatisch Vergebene Name nach einem Neustart ändern kann.

Dateien finden mit fd

Das Tool find ist ein praktisches Programm und Dateien und Ordner zu finden.
Find kann aber auch komplexere Aktionen wie z.B. mehrere Dateien konvertieren. Leider ist es jedoch nicht allzu einsteigerfreundlich.

Hier kommt fd ins Spiel:

Das Open-Source Programm erledigt nahezu alle Aufgaben von find, ist aber einfacher zu bedienen.

LPs

Beispiel1

Finde alle Dateien mit der Zeichenfolge schuh im Namen:

find:

find . -iname '*schuh*'

fd:

fd schuh

Beispiel 2

Finde alle .jpg Dateien:

find:

find . -iname '*.jpg'

fd:

fd -e jpg

Beispiel 3

Finde alle .png Dateien und konvertiere diese in .jpg Dateien:

find:

find ./ -name '*.png' -exec bash -c 'convert $0 ${0/png/jpg}' {} \;
# Konvertiert eine nach der anderen Datei

fd:

fd -e png -x convert {} {.}.jpg
# Konvertiert parallel mehrere Dateien auf einmal

Fazit:

Wer find in- und auswendig beherrscht hat keinen Zwang zu wechseln. Der bequeme Syntax von fd macht das Leben jedoch leichter. Die Möglichkeit parallel mehrere Dateien zu verarbeiten ist ungemein praktisch. Daher ist fd für jeden Kommandozeilen-Fan absolut empfehlenswert.

Docker services via docker-compose und systemd template

Docker ist das derzeit omnipresente Werkzeug um Dienste in Containern auszuführen.

Wie kann man jedoch einen logischen Verbund an Diensten mit dem System zusammen starten?

Container

Mit der Hilfe von Docker können Anforderungen eines Dienstes (z.B. Gitlab) an die Distribution innerhalb eines Containers befriedigt werden. Das ausführende System aussenherum bleibt davon unberührt und kann auch eine inkompatible Distribution sein.

In der Praxis werden jedoch oft mehrere Dienste in einem Verbund benötigt. Diese können mit dem Tool docker-compose beschrieben und gestartet werden.

Will man nun Dienste via docker-compose mit dem System zusammen starten, bietet sich folgende Vorgehensweise an:

Als erstes definieren wir ein Dienst-Template in einer neuen Datei /etc/systemd/system/dc@.service

[Unit]
Description=%i service with docker compose
Requires=docker.service
After=docker.service

[Service]
Restart=always
TimeoutStartSec=1200

WorkingDirectory=/opt/dockerfiles/%i

# Remove old containers, images and volumes and update it
ExecStartPre=/usr/local/bin/docker-compose down -v
ExecStartPre=/usr/local/bin/docker-compose rm -fv
ExecStartPre=/usr/local/bin/docker-compose pull

# Compose up
ExecStart=/usr/local/bin/docker-compose up

# Compose down, remove containers and volumes
ExecStop=/usr/local/bin/docker-compose down -v

[Install]
WantedBy=multi-user.target
ExecStartPre=/usr/local/bin/docker-compose pull

# Compose up
ExecStart=/usr/local/bin/docker-compose up

# Compose down, remove containers and volumes
ExecStop=/usr/local/bin/docker-compose down -v

[Install]
WantedBy=multi-user.target

Wir gehen dabei davon aus, dass alle docker-compose Konfigurationsdateien in /opt/dockerfile/DIENSTNAME liegen und sich docker-compose im Verzeichnis /usr/local/bin liegt.

Nun legen wir unsere docker-compose.yml Datei z.B. in /opt/dockerfile/gitlab/docker-compose.yml ab.

Nun weisen wir systemd an, das Template zu instanziieren:

sudo systemctl enable dc@gitlab

Ab sofort ist der Dienst vorhanden und kann auch direkt gestartet werden:

sudo systemctl start dc@gitlab

Beim starten des Dienstes wird auch das Image aktualisiert - ist dies nicht gewünscht, muss lediglich die entsprechende Zeile im Template entfernt werden.

Viel Spaß!

SD Karte beschreiben mit „flash“

Um einen Raspberry Pi in Betrieb zu nehmen, ist es notwendig eine SD Karte mit einem Betriebssystem zu beschreiben.
Dabei ist es in der Regel nicht mit einem einfachen Datei kopieren getan.

Unter Windows bietet sich Win32 Disk Imager an,
unter macOS das Tool ApplePi Baker und unter Linux nutzt man einfach dd.

SD Speicherkarte

Nun wünscht man sich bei häufiger Nutzung ein einfaches Kommandozeilentool, dass diese Aufgabe komfortabel übernimmt.

Genau das erledigt das Tool flash.
Das in bash geschrieben Werkzeug ist schnell installiert:

curl -O https://raw.githubusercontent.com/hypriot/flash/master/$(uname -s)/flash
chmod +x flash
sudo mv flash /usr/local/bin/flash

Wie in der Installationsanleitung auf Github zu sehen, sind noch optionale Abhängigkeiten auf einige Tools zu installieren (u.a. curl, pv, unzip).
Nach erfolgreicher Installation durch den Paketmanager des Vertrauens, können Raspberry Images geschrieben werden:

flash jessie-light.zip

Dabei übernimmt flash das entpacken (bzw. Download) der Datei und fordert den Benutzer anschließend auf, die SD Karte einzulegen. Nach Identifizieren des Speichermediums braucht man lediglich selbiges zu bestätigen und der Schreibeprozess beginnt.

Zum Abschluss des Ganzen werden auch alle Dateisystem ausgehängt und die Speicherkarte kann in den Raspberry Pi wandern.

Für mich ist flash das Werkzeug zum Schreiben von SD Karten geworden.
Minimalistisch, komfortabel und einfach zu handhaben erleichtert mit das kleine Helferlein den Alltag.

Systemdateien editieren mit sudoedit

Praktisch jeder Entwickler und jeder Administrator hat seine eigene Editor-Konfiguration.
Ob Farbschema, Plugins, eigene Kürzel oder Optionen - kaum ein Werkzeug wird
so intensiv den eigenen Vorstellungen, Wünschen und Vorlieben angepasst wie ein
Editor.

Muss jedoch eine Datei editiert werden, die nicht dem Benutzer "gehört" (z.B.
Konfigurationsdateien eines Webservers), funktioniert der "eigene" Editor nicht
mehr.

Read more "Systemdateien editieren mit sudoedit"

Kommandozeile mit FZF

Wer viel auf der Kommandozeile/Shell arbeitet, lernt im Lauf der Zeit einige nützliche Tools und Tastaturkürzel kennen.
So stöbert man in bash bzw. zshmit Ctrl+R in der Eingabe-History, sucht mit find Dateien in einem gegeben Pfad - und so weiter und so fort.

Ein nützliches Helferlein, welches die beiden Aktionen (und mehr) beschleunigt
ist fzf:

Read more "Kommandozeile mit FZF"

Wget wiederaufnehmen

Beim Download großer Dateien (z.B. Iso-Images, Podcasts) ist es oft hilfreich einen bereits begonnenen Download weiterführen zu können.

Mit dem Download-Werkzeug wget ist dies einfach mit der Kommandozeilenoption -r möglich.

Damit man jedoch nicht immer daran denken muss, lässt sie dies auch als Standardverhalten einstellen.

Hierzu tragen wir die die Datei ~/.wgetrc folgendes ein:

continue = on

Ab sofort nimmt wget Downloads automatisch wieder auf.

Linux Pakete mit fpm bauen

Software installieren

Unter den meisten Linux Distributionen wird Software zumeist via Paketmanager
installiert.
Gerade in der Software-Entwicklung kommt es aber immer wieder dazu, dass
man selbstkompilierte oder selbstentwickelte Software installieren muss.

Der übliche "Installationsmechanismus" via

./configure && make && make install

führt jedoch dazu, dass die Software unversioniert installiert wird und eine
Deinstallation nur in mühseliger Handarbeit möglich ist.
Auch die Installation auf mehreren Rechner erfordert immer wieder den selben
Aufwand und eine Updatemöglichkeit gibt es ebenfalls nicht.

Pakete

Die optimale Lösung für unser Problem ist natürlich die Erstellung eines
eigenen Pakets:
Das Paket wird ein einziges Mal gebaut, kann jederzeit deinstalliert werden
und die Version des Pakets ist ebenfalls protokolliert, so dass später Updates
möglich sind.

Das Problem daran ist, dass das Entwickeln von Paketen recht Aufwändig ist.
Eine Abhilfe schafft hierbei das
Werkzeug fpm.

Installation

Zunächst stellen wir sicher, dass auf unserem Rechner das Paket ruby-dev
installiert ist:

Bei auf Debian basierenden Systemen (z.B. Ubuntu):

sudo apt-get install install ruby-dev

Bei auf RedHat basierenden Systemen (z.B. CentOS):

sudo yum install fpm

Nun können wir mit dem Ruby Paketinstaller gem das Programm installieren:

gem install fpm

Nun sollte das Programm fpm auf der Kommandozeile zur Verfügung stehen.

Paket bauen

Wir haben nun unsere Software wie zuvor und bauen diese - jedoch mit einem
Präfix, z.B.:

mkdir /tmp/place_to_install
./configure && make && make install DESTDIR=/tmp/place_to_install

Wenn wir nun an den Ort /tmp/place_to_install schauen, sehen wir dort die
installierten Daten. Diese sollen nun in unser Paket kommen.
Diese bauen wir mit:

fpm -s dir -t rpm -n myprogram -v 0.1.2 -C /tmp/place_to_install bin lib

Hierbei stehen die Parameter für:

  • -s dir: Das Paket wird aus einem Verzeichnis gebaut (andere Möglichkeiten u.a. Python Module, rpm Dateien usw.)
  • -t rpm: Es soll ein rpm-Paket gebaut werden. Auch deb-Pakete sind möglich
  • -n myprogram: Der Name des Pakets
  • -v 0.1.2: Die Version des Pakets
  • -C /tmp/place_to_install: Das Verzeichnis, in dem sich die Dateien befinden
  • bin lib: Die Dateien/Verzeichnisse in dem Verzeichnis, welche auch wirklich im Zielsystem installiert werden sollen

Nach kurzer Wartezeit purzelt aus dem Werkzeugs ein installierbares Paket heraus.

Fazit

Mit fpm lassen sich bequem, schnell und einfach Pakete bauen.
Natürlich berücksichtigt das hier erstellte Paket keinerlei Abhängigkeiten und
es werden auch nicht alle Möglichkeiten und Funktionen des Paketmanagements genutzt, aber
in der Praxis erleichtert dies die Installation von Software - gerade in Teams
ungemein.

Jetzt nur noch ein Repository-Server aufgesetzt - und schon hat man im Team
eine komfortablen Weg Software bereit zu stellen.