Wer schon unter grösserer Langweile gelitten hat, dürfte mit dem “Spiel” fortune(6) bekannt sein:
Code: Alles auswählen
# apt install fortunes
$ fortune
Debug is human, de-fix divine.
$ fortune
Abraham Lincoln didn't die in vain. He died in Washington, D.C.
$ fortune
You work very hard. Don't try to think as well.
Sollte man daraus nicht eine Web-Anwendung machen, die dann mit systemd gestartet wird?Excuse me, wir haben 2023!
Ein Go-Server
Zunächst benötigen wir einmal Go, damit wir damit eine Anwendung schreiben können:
Code: Alles auswählen
# apt install golang
Code: Alles auswählen
package main
import (
"math/rand"
"net/http"
)
var quotes = []string{
"Kommet mer am beschta glei nochem Mittagessa, no sendr zom Veschpara wieder drhoim.",
"Sieba Johr hen se mer verseggelt, aber i hans glei gmerkt.",
"Der putzt da Arsch vor er gschissa hot – no ka-ner s' Babieer zwoamol braucha.",
"A bissle domm isch jedr, abbr so domm wia manchr isch koinr.",
"A guads Gwissa kommd bloß vom a schlechda Gedächdnis.",
"A Schwob wird nedd reich durch viel vrdiena, sondern durch wenig ausgäba!",
"Drei Bier senn au a Veschbr, ond no hasch erschd no nix gessa.",
"Wo Geld isch, isch au dr Deifl, wo kois isch, do isch er zwoimol.",
"Schlof naggich, no vrsoichsch koi Hemmad.",
"Mr ko au ogschaffd veschbara, abbr ohne Veschbr ko mr nedd schaffa!",
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
i := rand.Intn(len(quotes))
w.Write([]byte(quotes[i] + "\n"))
})
http.ListenAndServe("0.0.0.0:2023", nil)
}
Code: Alles auswählen
$ go build fortune1.go
$ ./fortune1
Code: Alles auswählen
# apt install curl
$ curl localhost:2023
A guads Gwissa kommd bloß vom a schlechda Gedächdnis.
$ curl localhost:2023
A bissle domm isch jedr, abbr so domm wia manchr isch koinr.
$ curl localhost:2023
Mr ko au ogschaffd veschbara, abbr ohne Veschbr ko mr nedd schaffa!
Damit uns die Weisheit immer gleich beim Systemstart zur Verfügung steht, soll der Server von einer systemd-Service-Unit gestartet werden.
Zunächst soll das ausführbare Programm an einen Ort verschoben werden, wo es alle Benutzer des Systems verwenden können:
Code: Alles auswählen
# mv fortune1 /usr/local/bin/
Code: Alles auswählen
[Unit]
Name=fortune1: Weisheiten aus dem Schwarzwald
Documentation=https://wiki.debianforum.de/Adventskalender_2023
After=network.target
[Service]
ExecStart=/usr/local/bin/fortune1
Type=simple
Restart=always
[Install]
WantedBy=multi-user.target
- [Unit]: Allgemeine Informationen über die Unit
- Name: Ein sprechender Name für die Unit
- Documentation: Ein Verweis auf eine Manpage oder Online-Dokumentation
- After: Abhängigkeiten; für unser Beispiel sollte das Netzwerk verfügbar sein
- [Service]: Spezifische Direktiven für Service-Unit (systemd.service(5))
- ExecStart: Der Befehl, mit dem der Service gestartet wird
- Type: Wie der Service gestartet werden soll (Rückmeldung über Starterfolg)
- Restart: Ob und wie (oft) der Service im Fehlerfall neugestartet werden soll
- [Install]: Direktiven für die Installation der Unit
- WantedBy: Welche Target-Unit den Service aufstarten soll
Code: Alles auswählen
# mv fortune1.service /etc/systemd/system/
Code: Alles auswählen
# systemctl daemon-reload
Code: Alles auswählen
# systemctl start fortune1.service
Code: Alles auswählen
$ systemctl is-active fortune1.service
active
Code: Alles auswählen
$ curl localhost:2023
A Schwob wird nedd reich durch viel vrdiena, sondern durch wenig ausgäba!
Code: Alles auswählen
# systemctl enable fortune1.service
Einige Verbesserungen
Die Anwendung und ihr Deployment weisen noch einige Schwächen auf:
- Die Anwendung läuft mit root-Rechten. Das sollte zwar bei der aktuellen Implementierung kein Problem darstellen, ist aber trotzdem unnötig.
- Die Weisheiten sind hartkodiert und können nicht so einfach erweitert werden.
- Der Systemadministrator erfährt nicht, wie oft und von wem der Service verwendet wird.
Verbesserter Go-Server
Der Server soll die Weisheiten aus einer Textdatei lesen, welche über ein Kommandozielenargument mitgegeben werden soll. Ausserdem soll er die Aufrufe loggen. Diese Änderungen wurden hier vorgenommen (fortune2.go):
Code: Alles auswählen
package main
import (
"fmt"
"math/rand"
"net/http"
"os"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "usage: %s [file]\n", os.Args[0])
os.Exit(1)
}
data, err := os.ReadFile(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "reading file %s: %s\n", os.Args[1], err)
os.Exit(1)
}
var quotes []string
lines := strings.Split(string(data), "\n")
for _, line := range lines {
quote := strings.TrimSpace(line)
if len(quote) > 0 {
quotes = append(quotes, quote)
}
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(os.Stderr, "wisdom requested from %s\n", r.RemoteAddr)
i := rand.Intn(len(quotes))
w.Write([]byte(quotes[i] + "\n"))
})
http.ListenAndServe("0.0.0.0:2023", nil)
}
Damit der neue Server getestet werden kann, muss erst einmal der alte beendet (und beim Systemstart nicht wieder aufgestartet) werden:
Code: Alles auswählen
# systemctl disable --now fortune1.service
Code: Alles auswählen
Kommet mer am beschta glei nochem Mittagessa, no sendr zom Veschpara wieder drhoim.
Sieba Johr hen se mer verseggelt, aber i hans glei gmerkt.
Der putzt da Arsch vor er gschissa hot – no ka-ner s' Babieer zwoamol braucha.
A bissle domm isch jedr, abbr so domm wia manchr isch koinr.
A guads Gwissa kommd bloß vom a schlechda Gedächdnis.
A Schwob wird nedd reich durch viel vrdiena, sondern durch wenig ausgäba!
Drei Bier senn au a Veschbr, ond no hasch erschd no nix gessa.
Wo Geld isch, isch au dr Deifl, wo kois isch, do isch er zwoimol.
Schlof naggich, no vrsoichsch koi Hemmad.
Mr ko au ogschaffd veschbara, abbr ohne Veschbr ko mr nedd schaffa!
Code: Alles auswählen
$ go build fortune2.go
$ ./fortune2 schwaebische-weisheiten.txt
Code: Alles auswählen
$ curl localhost:2023
Wo Geld isch, isch au dr Deifl, wo kois isch, do isch er zwoimol.
Code: Alles auswählen
wisdom requested from 127.0.0.1:39948
Code: Alles auswählen
# mv fortune2 /usr/local/bin/
Zunächst erstellen wir einen neuen Benutzer mit $HOME-Verzeichnis, eigener Benutzergruppe und der Bash als Shell:
Code: Alles auswählen
# useradd -m -d /home/fortune -U -s /usr/bin/bash fortune
Code: Alles auswählen
# mv schwaebische-weisheiten.txt /home/fortune/
# chown fortune:fortune /home/fortune/schwaebische-weisheiten.txt
Code: Alles auswählen
# cp /etc/systemd/system/fortune1.service /etc/systemd/system/fortune2.service
Code: Alles auswählen
[Unit]
Name=fortune2: Weisheiten aus aller Welt
Documentation=https://wiki.debianforum.de/Adventskalender_2023
After=network.target
[Service]
ExecStart=/usr/local/bin/fortune2 schwaebische-weisheiten.txt
Type=simple
Restart=always
User=fortune
Group=fortune
WorkingDirectory=/home/fortune
[Install]
WantedBy=multi-user.target
- Name: Die Unit heisst nun fortune2 und nicht mehr fortune1.
- ExecStart: Das neue Programm fortune2 wird mit einem Argument gestartet.
- User und Group: Der Service wird mit dem neuen Benutzer gestartet.
- WorkingDirectory: Das Arbeitsverzeichnis ist das $HOME-Verzeichnis des fortune-Benutzers, welches auch die Datei mit den Weisheiten enthält.
Code: Alles auswählen
# systemctl daemon-reload
Code: Alles auswählen
# systemctl start fortune2.service
Code: Alles auswählen
$ systemctl is-active fortune2.service
active
Code: Alles auswählen
# journalctl -fu fortune2.service
Code: Alles auswählen
$ curl localhost:2023
Kommet mer am beschta glei nochem Mittagessa, no sendr zom Veschpara wieder drhoim.
Code: Alles auswählen
Nov 30 10:23:48 bookworm fortune2[3216]: wisdom requested from 127.0.0.1:40670
Code: Alles auswählen
# systemctl enable fortune2.service
Es wurde zunächst eine einfache Go-Serveranwendung entwickelt, welche zufällige Zitate aus einer hartkodierten Liste via HTTP zurückliefert. Die Serveranwendung wurde als systemd-Service-Unit konfiguriert und ausgeführt.
Anschliessend wurde die Serveranwendung verbessert, sodass sie Weisheiten aus einer Datei anbieten kann und die Aufrufe als Logmeldungen ausgibt. Die Service-Konfiguration wurde dahingehend erweitert, dass der Server von einem Benutzer mit eingeschränkten Rechten ausgeführt wird. Die Logmeldungen der Anwendung können im Journal eingesehen werden.
Übungen
Wer etwas tiefer in Go, systemd oder andere Quellen der Weisheit eintauchen möchte, der kann sich an den folgenden Übungen versuchen:
- Der Service horcht auf die Adresse 0.0.0.0:2023, d.h. akzeptiert Aufrufe von überall her auf Port 2023. Diese beiden Angaben könnte man per Kommandozeilen-Flag (Go-Package flag) oder mithilfe von Umgebungsvariablen (FORTUNE_ADDR, FORTUNE_PORT) konfigurierbar machen. Hierzu muss sicher das Go-Programm, aber je nach Lösungsweg auch die Unit-Konfiguration angepasst werden.
- Da die Datei mit den Weisheiten nun einfach ersetzbar ist, könnten ein paar weitere Beispiele aus anderen Regionen für Abwechslung sorgen.