Prof. Dr.-Ing. Oliver Radfelder
Informatik / Wirtschaftsinformatik
Hochschule Bremerhaven

VM und darin Docker installieren und verwalten

In der Informatik-Infrastruktur haben wir eine Docker-basierte Umgebung, die Ihr vom ersten Semester an benutzt, um dort in einer isolierten Umgebung Web-Anwendungen zu entwickeln (docker).

Im dritten Semester untersuchen wir in dem Modul Vernetzte Systeme mit welchen Techniken und Werkzeugen skalierbare, redundante und separierte Systeme aufgebaut werden. Das wird üblicherweise in virtuellen Maschinen und Containern auf Servern in Rechenzentren - in der Cloud - durchgeführt.

Hier wollen wir Euch zeigen, wie Ihr auf Eurem Laptop eine kleine Umgebung mit docker aufbauen könnt, die derjenigen in der Infrastruktur ähnelt und mit der Ihr Vernetzung und Verteilung üben könnt und die Ihr mit in Euer Berufsleben nehmen könnt.

Dazu müsst Ihr die Umgebung so aufbauen, wie es unter setup.html, wezterm.html und hopper.html beschrieben ist.

Zudem muss nun unter Windows noch die WSL2 mit Ubuntu installiert sein und unter MacOS/ARM utm und darin Ubuntu/Server installiert sein.

Alternativ für MacOS und Linux probieren wir gerade lima (Linux Machine) aus (siehe unten).

Sorgt in jedem Fall dafür, dass Ihr eine aktuelle Ubuntu (ab 24.04) oder Debian-Distribution laufen habt und dass Ihr dort mit ssh hineinkommt.

Unter Windows:

Folgt der Anleitung unter Microsoft/wsl/install. Sorgt zuvor dafür, dass Eure Windows-Version auf dem aktuellen Stand ist.

Nach der Installation solltet Ihr noch dafür sorgen, dass Ihr schnell mit ssh ubuntu innerhalb des Terminals in Eure Ubuntu-Installation kommt. Startet dazu die wsl aus der Powershell oder git-bash heraus, installiert erst einmal die minimale Software (openssh-server, vim, curl) und sorgt dafür, dass der ssh-Server automatisch gestartet wird.

Nach dem update legt den Port des ssh-Servers innerhalb der WSL auf 2222 und startet mit systemctl den ssh-Server neu, damit die Änderung wirksam wird.

Seit einiger Zeit übernimmt offensichtlich systemd diesen Teil und wir müssen in /etc/systemd/system/sockets.target.wants/ssh.socket auch noch den ListenStream von 22 auf 2222 setzen.

Wenn Ihr Probleme habt - z.B. systemctl meldet, dass systemd nicht der Prozess 1 sei, dann könnte es sein, dass Eure WSL selbst nicht aktuell ist.

Bringt sie dann mit wsl --update auf den aktuellen Stand. Weitere hilfreiche Kommandos für die wsl gibt es unter basic-commands

wsl
sudo apt update
sudo apt install -y openssh-server vim curl

sudo vim /etc/ssh/sshd_config
# passe dort den Eintrag an:
Port 2222
# und verlasse den vim ...
sudo vim /etc/systemd/system/sockets.target.wants/ssh.socket 
# passe dort den Eintrag an
ListenStream=2222
# und verlasse den vim
sudo systemctl daemon-reload
sudo systemctl enable ssh
sudo systemctl restart ssh
      

Da wir nicht nur mal eben in die Linux-Umgebung hineinwollen, sondern sie eine ständig verfügbare Arbeitsumgebung sein soll, müssen wir noch dafür sorgen, dass sie sich nicht wieder abschaltet, wenn wir mal nicht im Terminal drin sind. Innerhalb der WSL gebt dafür an:

dbus-launch true

Und wenn sie automatisch beim Windowsstart mit hochgefahren werden soll, legt eine Datei runwsl.bat im Autostart-Ordner unter Windows an mit dem Inhalt:

wsl --exec dbus-launch true

Die Konfiguration für das passwortlose Einloggen in die VM mit ssh ubuntu löst Ihr analog zu der Installation aus dem ersten Semester.

Hier die Kurzfassung:

Editiert die Datei ~/.ssh/config in der git-bash - also unter Windows - so, dass dort der Alias ubuntu auf die wsl zeigt.

host ubuntu
      hostname localhost
      port 2222
      user EUER-LINUX-USER

Auch hier könnt Ihr mit der richtigen Kombination aus ssh-keygen und ssh-keyscan das erstmalige Nachfragen beim Fingerprint verhindern.

ssh-keygen -R [localhost]:2222; ssh-keyscan -p 2222 -4 localhost >> .ssh/known_hosts

Unter MacOS/utm

Unter MacOS empfehlen wir im Moment lima statt utm. Die Installation ist einfacher und ähnelt derjenigen unter Linux. Siehe Abschnitt Lima Linux Machine

Für Mac-Systeme installiert utm als Virtualisieungssoftware wie unter https://mac.getutm.app/ beschrieben.

Nach der Installation von utm solltet Ihr darin Ubuntu 24.04 installieren (guides/ubuntu). Ich empfehle die Server-Variante. Achtet darauf, genügend Speicher zur Verfügung zu stellen (wenn möglich 32GB).

Unter Linux

Unter Linux könnt Ihr docker direkt installieren. Unter Windows und MacOS muss docker in einer Linux-VM installiert sein. Ihr könnt natürlich, wenn Ihr Euch herausfordern wollt, auch unter Linux eine Linux-VM mit kvm installieren und dort dann docker installieren.

Für den Moment empfehle ich hier ebenfalls lima.

Lima (Linux Machine)

lima wirkt ausgesprochen leichtgewichtig und ist schnell mal eben installiert. Zudem gibt es lima für Linux und MacOS.

Sowohl unter MacOS als auch unter Linux (debian/ubuntu) ging es bei mir nach der Anleitung unter https://lima-vm.io/docs/installation/ unter dem Abschnitt Binary.

Für MacOS mit brew installiert es so wie es da steht.

Für Linux statt brew install jq dann einfach sudo apt install jq (was vermutlich schon passiert ist). Die beiden anderen Zeilen mit curl funktionieren dann auch. Lediglich vor dem tar muss ebenfalls sudo stehen. Danach dann limactl start und dann lima und man ist drin. Selbst ssh ist dann vorbereit (der Port wird ausgegeben (60022) und es müssten dann nur noch Einträge in der .ssh/config nachgezogen werden).

Notfalls lässt sich mit

limactl shell default bash -c 'cat >>~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub

der Public-Key nachträglich injizieren und Ihr könnt Euch einloggen.

ssh -p 60022 localhost

Falls angemerkt wird, dass noch ein Paket fehlt, ist es vermutlich qemu

sudo apt install qemu-system qemu-utils

Etwas ungewöhnlich ist, dass man mit dem Kommando lima in dem Verzeichnis auch innerhalb der VM ist, in dem das Kommando aufgerufen wurde. Mit cd kommt man dann wie gewöhnlich in das VM-spezifische Homeverzeichnis. Wenn Ihr hingegen mit ssh in die VM geht, ist das aktuelle Verzeichnis direkt das VM-Homeverzeichnis.

Bei meinem Lenovo X260 Notebook mit debian musste ich noch dafür sorgen, dass die Virtualisierung insgesamt aktiviert wird. Das stellt man im BIOS ein (bei mir: F1 beim Booten drücken und dann in den Menüs Virtualisierung suchen und aktivieren).

Mit:

grep --color vmx /proc/cpuinfo # intel
grep --color svm /proc/cpuinfo # amd
seht Ihr dann, ob Hardware-Virtualisierung aktiviert ist. (Quelle: cyberciti.biz)

Auf einem meiner Systeme (debian) musste ich mich noch zur Gruppe kvm hinzufügen (sudo usermod -aG kvm ...) und beim Start die Konfiguration so anpassen, dass unter firmware der Parameter legacyBIOS auf true steht (kvm-permission-denied).

Bei lima werden beim Starten (also auch beim automatischen Starten nach dem Booten des Laptops) die Host-Keys normalerweise neu erzeugt. Beim erstmaligen Verbinden über ssh wird geprüft, ob der Host - bzw. dessen Key in der .ssh/known_hosts bekannt ist. Falls nicht, wird der fingerprint angezeigt und wir sollen sie prüfen und gegebenenfalls mit yes bestätigen.

Inhaltlich ist das eher ein Thema für die Security-Veranstaltungen - also: was kann eigentlich passieren, wenn man all zu blindlings jeweils mit yes bestätigt? Auf jeden Fall ein sehr spannendes Thema und wer Lust und Interesse hat, kann sich damit gerne beschäftigen - z.B. mit Understanding SSH Host Keys .

Aus einem Host-Key, der sich mit ssh-keyscan erfragen lässt, wird mit ssh-keygen der Fingerprint berechnen:

ssh-keyscan -p 60022  localhost | ssh-keygen -lf -

Für hopper gelten folgende Fingerprints:

ssh-keyscan -p 8080  hopper.hs-bremerhaven.de | ssh-keygen -lf -
# hopper.hs-bremerhaven.de:8080 SSH-2.0-OpenSSH_9.7p1 Debian-4
# hopper.hs-bremerhaven.de:8080 SSH-2.0-OpenSSH_9.7p1 Debian-4
# hopper.hs-bremerhaven.de:8080 SSH-2.0-OpenSSH_9.7p1 Debian-4
# hopper.hs-bremerhaven.de:8080 SSH-2.0-OpenSSH_9.7p1 Debian-4
# hopper.hs-bremerhaven.de:8080 SSH-2.0-OpenSSH_9.7p1 Debian-4
2048 SHA256:hfbmouXTPHIVX/ar4i7QlDrMkX+ygPeCTPWMjPcVwwc [hopper.hs-bremerhaven.de]:8080 (RSA)
256 SHA256:tqmc7laeYMeRFZwgIFheEBPPr3dCpqAfvBI9/tx6O+M [hopper.hs-bremerhaven.de]:8080 (ECDSA)
256 SHA256:uSmqfeC1Eij31faRWjOgI2GtGblQxjhYFvYlMHO7XrU [hopper.hs-bremerhaven.de]:8080 (ED25519)

Jedenfalls wird bei lima cloud-init genutzt. Das ist ein mittlerweile ausgesprochen weit verbreitetes Verfahren um bei Cloud-Providern wie Hetzer oder Amazon virtuelle Maschinen bei der Initialisierung konfigurieren zu lassen.

Hier nur so viel: Bei cloud-init wird mit einer Text-Datei (im yaml-Format) beschrieben, wie die zu erzeugende VM aussehen soll (welche Software nachinstalliert werden soll, welche User angelegt werden sollen, etc).

Wir müssen nun bei der Initialisierung der lima-default-VM dafür sorgen, dass an einer bestimmten Stelle explizit angegeben wird, dass wir beim Starten die Host-Keys nicht neu erzeugen. Ich habe für lima (MacOS und Linux) eine kleine Konfigurationsdatei vorbereitet, die Ihr statt der Default-Konfiguration von lima nutzen solltet.

Wie bei mir üblich, holt Ihr sie Euch mit curl aus meinem Web-Verzeichnis:
curl -O https://informatik.hs-bremerhaven.de/oradfelder/lima.yaml

Danach könnt Ihr sie nutzen, um eine VM zu erzeugen und dann zu starten:

cat lima.yaml | limactl create --name=default -
limactl start

Ab dann solltet Ihr die VM stoppen und starten können, ohne dass Ihr den Key neu bestätigen müsst. Beim erstmaligen ssh -p 60022 localhost müsst Ihr das natürlich trotzdem bestätigen - es ist ja jetzt eine neue VM und damit hat sie auch neue Host-Keys ...) Nach dem Stoppen und Starten jedoch werdet Ihr nicht mehr gefragt...

Schaut ruhig in die yaml-Datei hinein - das ist die Default-Config von lima, bei der ich lediglich zwei Dinge angepasst habe:

Umkonfigurieren der cloud-init Struktur

provision:
- mode: system
  script: |
    #!/bin/sh
    mkdir -p /etc/cloud/cloud.cfg.d
    echo "ssh_deletekeys: false" > /etc/cloud/cloud.cfg.d/lima-local.cfg

Den Port localhost:8000 in die VM als Port 80 forwarden (auf Port 80 lauscht später der apache2-Webserver)

portForwards:
 - guestPort: 80
   hostPort: 8000 # overrides the default value 80

Für die Automatisierung des Erzeugens von lokalen VMs ist es hilfreich, die bisherigen Keys mit ssh-keygen -R zu entfernen und die neuen mit ssh-keyscan anzuhängen:

ssh-keygen -R [localhost]:60022   # entferne die Keys aus .ssh/known_hosts
ssh-keyscan -p 60022  localhost >> .ssh/known_hosts    # scannen und anfügen

Für alle

Sobald Ihr mit ssh in Eure Linux-Umgebung kommt, sorgt zunächst dafür, dass sie auf dem aktuellen Stand ist:

sudo apt update && sudo apt -y upgrade

Dann solltet Ihr dringend mit:

sudo update-alternatives --set editor /usr/bin/vim.basic
dafür sorgen, dass Ihr nicht im falschen Moment im nano landet und dann vielleicht nicht wisst, wie Ihr dort wieder herauskommt ...

Ich tendiere dazu, unter allen Systemen als Standard en_US.UTF-8 einzutragen, da ansonsten einige Programme (date, bc, ...) sich aufgrund der Lokalisierung anders verhalten.

sudo sed  -i "s/^# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/g" /etc/locale.gen && \
sudo locale-gen en_US.UTF-8 && \
sudo update-locale LANG=en_US.UTF-8

Wenn Ihr genug Platz habt in der VM solltet Ihr Euch auch dort wieder unser Standard-Setup installieren - bzw. eine Variante ohne die grafischen Anwendungen - wir wollen ja im Server arbeiten. Dieses Mal nur als Liste für eine hopper-ähnliche Umgebung - jeweils einzeln oder alle zusammen könnt Ihr die Software mit sudo apt -y install selbständig hinzufügen:

a2ps apache2 apache2-utils bash-builtins bash-completion
bat bc bsdmainutils build-essential coreutils csvkit curl direnv dos2unix
ed emacs-nox ffmpeg fonts-liberation git gnuplot-nox
gpg graphviz html2text imagemagick inkscape
inotify-tools iperf3 iproute2 iputils-ping
jq jid libxml2-utils maven moreutils mutt ncat
net-tools nftables nmap openjdk-17-jdk openjdk-17-doc openjdk-17-source openssh-server
pdf2svg pdftk plantuml poppler-utils procps pv pwgen python-is-python3 python3-pygments python3-venv
qrencode redis-server restic shellcheck
shfmt socat sqlite3 sshfs tcpdump
tidy tree unzip valgrind vim w3m wrk websocketd
wget wrk xml2 xmlstarlet xxd xsltproc
zbar-tools

Solch eine Sammlung von Paketen lässt sich gut in eine Textdatei kopieren und dann in einer Schleife installieren. Allerdings wird man gegebenenfalls nach jedem installierten Paket gefragt, welche Dienste neu zu starten sind. Das lässt sich durch entsprechende Umgebungsvariablen verhindern:

while read -r line; do sudo bash -c "export DEBIAN_FRONTEND=noninteractive;export NEEDRESTART_MODE=a; apt install -y $line"; done < packs.txt
Insgesamt ist dann mit den noch zu installierenden docker und nodejs in der VM etwa 10 GB für das System verbraucht. Wenn es bei Euch eng ist, sprecht mit uns, was Ihr weglassen könnt.

Denkt unter Ubuntu und Debian daran, dass Ihr auch hier einen Softlink für bat setzt:

sudo ln -s /usr/bin/batcat /usr/local/bin/bat

Nur im dritten Semester, wenn wir mit Docker arbeiten: Da wir in den docker-Containern auch einen Webserver auf Port 80 installieren, solltet Ihr baldmöglich den installierten apache2 deaktivieren.

sudo systemctl stop apache2
sudo systemctl disable apache2

Für diejenigen, die Linux direkt installiert haben (und nicht in einer VM): Ihr braucht nicht per ssh auf Euren Rechner zu kommen und solltet das auch vorerst abschalten:

sudo systemctl stop ssh
sudo systemctl disable ssh

Zudem ist vermutlich nftables bei Euch als Firewall eingerichtet. Die solltet Ihr auch gleich so einrichten, dass erst einmal Euer Rechner nach außen hin abgesichert ist. Prüft zuerst, ob nftables installiert und aktiviert ist:

sudo systemctl status nftables

Falls dort inactive angzeigt wird, sollte es mit

sudo systemctl enable nftables
sudo systemctl start nftables

aktiviert und gestartet werden. Gegebenenfalls muss dann auch der docker-Daemon einmal mit

sudo systemctl restart docker
neu gestartet werden, damit er seine Regeln dort wieder hineinschreiben kann. Nach dem nächsten Systemstart ist das nicht mehr notwendig.

Die unterschiedlichen Distributionen benutzen nun unterschiedliche Frontends für nftables oder gar andere Firewall-Systeme wie ufw. Mein Vorschlag wäre, direkt auf nftables zu setzen.

Bisweilen ist es notwendig, die lokale Zeitzone einzustellen:

sudo timedatectl set-timezone "Europe/Berlin"

Danach installiert docker nach der Anleitung auf Install Docker Engine on Debian bzw. Install Docker Engine on Ubuntu jeweils unter dem Abschnitt Install using the apt repository.

Sorgt dann dafür, dass Euer Standarduser, mit dem Ihr in die WSL oder VM kommt, Mitglied der Gruppe docker ist, damit Ihr nicht jedesmal sudo vor die docker-Befehle schreiben müsst.

sudo usermod -aG docker $USER

Damit Ihr dann für die aktuelle Sitzung auch in der Gruppe seid:

newgrp docker

Nach dem nächsten Einloggen seid Ihr dann automatisch in der Gruppe.

Auf keinen Fall installiert Euch irgendeine docker for ...-Variante.

Es mag einfacher erscheinen, mit dem grafischen Werkzeug zu arbeiten, bei dem die konkrete Arbeit wegabstrahiert wird. Hier geht es aber nur zum Teil um docker als solches, sondern vornehmlich um docker als Werkzeug, um auf Euren Notebooks eine Netzwerkumgebung einzurichten, wie sie dann in realen Umgebungen aus vernetzten Rechnern besteht.

Und natürlich könnt Ihr, wenn erst einmal VM und docker soweit eingerichtet sind, auch alle 'klassischen' docker-Dinge tun.

Upgrade auf Ubuntu 24.04

Sowohl unter Windows/WSL2 als auch in UTM/lima/Ubuntu wird zunächst die derzeitige LTS (Long Term Support) Version 24.04 installiert. Falls Ihr noch aus dem vorigen Semester 22.04 oder eine Zwischenversion habt, lässt sie sich in einem Schritt upgraden:

sudo apt update
sudo apt upgrade -y
sudo apt dist-upgrade -y
sudo apt install update-manager-core -y
sudo sed -i 's/normal/lts/g' /etc/update-manager/release-upgrades
sudo sed -E -i -'s/jammy|lunar|kinetic|mantic/noble/g' /etc/apt/sources.list
sudo apt update
sudo apt upgrade -y
sudo apt dist-upgrade -y
sudo reboot 
# bzw. unter Windows in der git-bash: 
#   wsl shutdown 
#   wsl bash -i -c "nohup sleep 3600 &>/tmp/sleep.log &"
      

Dementsprechend kann nun auch das jdk-17 deinstalliert und jdk-21 installiert werden, falls das noch nicht der Fall ist.

sudo apt purge openjdk-17-jdk openjdk-17-doc openjdk-17-source
sudo apt install -y openjdk-21-jdk openjdk-21-doc openjdk-21-source
sudo apt autoremove -y

Erst im nächsten Schritt lässt sich dann auf 23.10 upgraden, was sicher auch getan werden sollte, da im Januar 2024 die Version 23.04 ihr End of Life erfuhr:

sudo do-release-upgrade 

node.js in der VM installieren

Es gibt sehr unterschiedliche Arbeitsweisen mit docker und je nachdem, wie es praktiziert wird und in welcher Entwicklungswelt wir unterwegs sind, wird auch unterschiedliches in der VM selbst benötigt. Wir machen unterwegs einen kleinen Ausflug in die typische Entwicklung mit Javascript/Node und dort ist es üblich, dass eine aktuelle node-Umgebung zum Arbeiten in der VM bzw. dem umgebenden Linux installiert ist und dort entwickelt wird. Daher ist es wichtig, hier eine passende node-Version zu installieren. Macht das bitte unbedingt nach dem aktuell empfohlen Verfahren, wie es im unten stehenden Link beschrieben ist, und folgt nicht irgendwelchen veralteten Webseiten!

Auf der Seite https://github.com/nodesource/distributions findet Ihr den Weg - ähnlich wie bei docker - ein externes Repository in die eigene Paketverwaltung mit aufzunehmen. Das sind immer die drei Schritte:

Arbeiten mit node/npm unter Linux

Ihr werdet recht oft aufgefordert werden, mit dem Paketmanager irgendetwas global zu installieren und benötigt dann dafür sudo-Rechte. Das ist aber nicht immer vernünftig. Auch wenn es keine perfekte Lösung ist, scheint das Umbiegen des globalen Installationsverzeichnisses auf ein HOME-lokales der bessere Weg zu sein.

mkdir "${HOME}/.npm-packages"
npm config set prefix "${HOME}/.npm-packages"

Dann sollte die .bashrc mit den folgenden Zeilen ergänzt werden:

NPM_PACKAGES="${HOME}/.npm-packages"
export PATH="$PATH:$NPM_PACKAGES/bin"
export MANPATH="${MANPATH-$(manpath)}:$NPM_PACKAGES/share/man"

Damit wird dafür gesorgt, dass dann auch vermeintlich global installierte Pakete im Pfad gefunden werden und Binaries ebenfalls. Mit npm config list kann dann ab dem nächsten Einloggen auf die aktuelle Konfiguration zugegriffen werden und mit npm list -g die installierten globalen Pakete aufgelistet werden. Dependency-Management in der Javascript-Welt ist nicht so richtig rund gelöst. Zu npm kommen noch npx, nvm und yarn und manches andere dazu.

Pakete und die Programme, die nun installiert sind

a2ps
a2ps ascii to postscript
apache2
apache2ctl controls apache2 (Module: apache2ctl -M); a2enmod/a2dismod enables/disables Modules (a2enmod cgi)
apache2-utils
Performanceanalyse für apache2 und Verwaltung von digest-Usernab, htpasswd, htdigest
bash-builtins
installiert effiziente Builtins (enable -f ...)
bash-completion
erweitert Tab-Completions
bat
bat Quellcode-Formatierung im Terminal
bc
bc basic calculator
build-essential
gcc, g++, make
coreutils
sleep, sort, nohup, factor, install, csplit, comm, ...
csvkit
csvcut, csvformat, csvgrep, csvsql, in2scv sql2csv - Programme zur besseren Verarbeitung von csv-Dateien
curl
curl
direnv
direnv vereinfacht die Verwaltung von dot-Files
dos2unix
dos2unix, unix2dos, mac2unix, unix2mac
ed
ed - der Ur-Editor
emacs-nox
emacs Kommandozeilen-Emacs
ffmpeg
ffmpeg, ffplay, ffprobe Videos konvertieren und Informationen anzeigen (ffmpeg)
fonts-liberation
fonts, die überall installiert sein sollten (LiberationSans, LiberationSerif, LiberationMono)
git
git Versionsverwaltung (git)
gnuplot-nox
gnuplot Visualisierungen
gpg
gpg Open PGP Verschlüsselung und Signaturen
graphviz
dot, nop, circo, ...Graphenvisualisierung (graphviz)
html2text
html2text einfache Darstellung von HTML im Terminal
imagemagick
convert, mogrify, montage, composite konvertiert und bearbeitet Bildformate (imagemagick)
inkscape
inkscape - GUI- und Kommandozeilenprogramm für svg-Grafiken
inotify-tools
inotifywatch, inotifywait beobachten des Filesystemes

Weiterführendes