LXC, LXD und “Error: not found”

Beim Lernen wie LXC und LXD funktionieren ist mir eine Meldung recht häufig aufgetreten. Das war die Fehlermeldung

Error: not found

Wie es sich herausgestellt hat, habe ich ein LXD Kommando ausgeführt und wollte das auf einen Container anwenden, den ich aber mit LXC erstellt hatte. Das führt aber zur Frage: Was ist der Unterschied zwischen LXC und LXD.

LXC ist eine Abkürzung für “Linux Container”. Sie funktioniert wie eine normale VM, jedoch mit weniger Overhead als andere Hypervisor.

LDX ist hingegen eine Erweiterung von LXC. LXD ist ein Systemdaemon und dieser verbindet sich mit libxlc, der Bibliothek, auf der LXC aufgebaut ist. Um mit dem Daemon zu kommunizieren, benutzt man eine REST API. LXD ist hierbei der neue Weg, Container zu erstellen und zu verwalten und ist eine Alternative zu den LXC Werkzeugen. Es wird abgerate, LXC und LXD gleichzeitig laufen zu lassen.

Den Unterschied zwischen LXC und LXD bemerkt man auch an den Kommandos, die man aufruft. Kommandos, die ein Bindestrich enthalten wie z.B. lxc-ls oder lxc-create sind LXC Kommandos. Kommandos ohne ein Bindestrich wie z.B. lxc info kommen von LXD.

Quellen

Kleiner Einblick in Snapshots mit LXC

Mit LXC lassen sich Snapshots von Containers erstellen. Auf diese Weise wird ein gewissen Zustand von dem System abgespeichert. Diesen kann man später nutzen, um damit einen neuen Container zu schaffen, ohne die mühselige Arbeit von Anfang an. In diesem Post will ich zeigen, wie man das mit wenigen LXC-Kommandos erreicht.

In diesem Beispiel habe ich schon einen Container namens container1 erstellt und werde ihn nun auch weiterhin verwenden. D.h. überall, wo bei mir container1 steht, solltet ihr euren Containernamen hinschreiben.

Um herauszufinden, ob ein gewählter Container schon Snapshots besitzt, findet man durch das Kommando

sudo lxc-snapshot -n container1 --list

heraus. Falls noch keine Snapshots existieren, wird hier die Meldung No snapshots auftauchen.

Bevor wir ein Snapshot erstellen, müssen wir sichergehen, dass der Container gestoppt ist, was wir mittels folgendem Kommando sehen:

sudo lxc-ls --fancy

Nur noch mal kurz zur Erinnerung: Einen LXC Container stoppt man mittels

sudo lxc-stop -n container1

Nun erstellen wir ein Snapshot von dem gewünschten Container:

sudo lxc-snapshot -n container1

Wenn alles erfolgreich gelaufen ist, müssten wir nun einen Snapshot sehen, wenn wir das Kommando

sudo lxc-snapshot -n container1 --list

aufrufen.

Snapshots des Containers container1 aufgelistet.

Um jetzt einen neuen Container aus einem bestehenden Snapshot zu schaffen, muss man folgendes Kommando aufrufen (in meinem Fall war es der Snapshot mit dem Namen snap0):

sudo lxc-snapshot -n container1 --restore snap0 --newname container2

Nun müsstet ihr einen weiteren Container sehen wenn ihr wieder alle Container auflistet. Mehr Informationen zum Thema Snapshots von LXC Containern findet man in der Dokumentation.

Erste Schritte mit LXC

In diesem Post möchte ich kurz zeigen, wie man mit LXC recht schnell einen ersten Container starten kann. Ich habe hierfür Ubuntu 18.04.2 als Hostsystem gehabt, aber auf anderen System müssten die meisten Kommandos genauso funktionieren.

Als erstes müssen wir die entsprechenden Pakete installieren:

sudo apt install lxc lxc-templates bridge-utils

Nachdem diese Pakete installiert sind, muss geprüft werden, ob alle Einstellungen OK sind. Soweit mir bekannt, kann der folgende Befehl ohne sudo ausgeführt werden.

lxc-checkconfig

Man müsste nun eine Ausgabe sehen, die in etwa so aussieht

Wenn alles gut aussieht, kann man anfangen, LXC Container zu erstellen und diese dann starten. Beim Erstellen eines Containers muss man in der Regel ein Template dazu angeben. Eine Liste der möglichen Templates findet man in dem Ordner /usr/share/lxc/templates. Die Templates werden über das apt-Paket lxc-templates installiert, was ich weiter oben aber schon getan hab.

In meinem ersten Versuch habe ich das Ubuntu-Template genommen. Um ein LXC Container zu erstellen, muss man nun diese Befehl ausführen:

sudo lxc-create -n <containername> -t <template>

In meinem Fall sah der Befehl so aus:

sudo lxc-create -n container1 -t ubuntu

Nun braucht LXC eine Weile, bis alles eingerichtet ist. Am Ende müsste man jedoch diesen Text zu sehen bekommen:

#
The default user is 'ubuntu' with password 'ubuntu'!
Use the 'sudo' command to run tasks as root in the container.
#

Bis jetzt haben wir nur einen Container erstellt. Nun müssen wir ihn zum Laufen bekommen:

sudo lxc-start -n container1

Um zu sehen, ob ein Container läuft oder nicht, sieht man mit diesem Befehl:

sudo lxc-ls --fancy

Wenn ein Container läuft, können wir uns nun mit ihm verbinden:

sudo lxc-console -n container1

Hier kommt der typische Bash-Login:

Hier bitte nicht vom Containernamen verwirren lassen 🙂

Für den Login gibt man als Benutzer ubuntu und als Passwort ebenfalls ubuntu ein (wie in der Ausgabe weiter oben beschrieben).

Hier noch eine wichtige Info: Wenn man nach dem Login irgendwann exit ausführt, wird man gleich wieder nach einem Benutzer und Passwort gefragt. Um aus der Containerumgebung wieder in die Hostumgebung zu kommen, muss man die Tastenkombination STRG+A und Q drücken. Das heißt, erst STRG+A gleichzeitig drücken und direkt danach nur Q.

lxd auf Ubuntu 18.04.2 LTS installieren

In diesem Post will ich mit ein paar Stichpunkten beschreiben, wie man lxd auf Ubuntu 18.04.2 LTS installiert. Da es sich bei mir um die Server Version handelt, musste ich am Ende nicht so viel manuell machen.

Für die, die nicht wissen was lxd ist: lxd ist eine Software um Container zu verwalten. In diesem Fall um Linux Container.

Der erste Schritt besteht also darin, lxd zu installieren:

$ sudo apt install lxd

Anschließend wird ZFS installiert. Dies ist sinnvoll, weil die Container mit ZFS die Copy-on-Write-Funktionalität nutzen und dadurch schneller werden:

$ sudo apt install zfsutils-linux

Nachdem die Pakete lxd und zfsutils-linux installiert worden sind, müsste man in der neu angelegten Gruppe lxd sein. Damit diese Änderung aber aktiv wird, muss man sich neu einloggen. Anschließend muss man nur noch das Kommando

$ sudo lxd init

aufrufen. Nun wird man einige Dinge gefragt. In meinem Fall habe ich die Standardwerte so gelassen wie sie sind, nur bei dem Namen für den Storagepool habe ich einen anderen genommen:

$ sudo lxd init
Would you like to use LXD clustering? (yes/no) [default=no]:
Do you want to configure a new storage pool? (yes/no) [default=yes]: yes
Name of the new storage pool [default=default]: lxd-pool
Name of the storage backend to use (btrfs, dir, lvm, zfs) [default=zfs]:
Create a new ZFS pool? (yes/no) [default=yes]:
Would you like to use an existing block device? (yes/no) [default=no]:
Size in GB of the new loop device (1GB minimum) [default=15GB]:
Would you like to connect to a MAAS server? (yes/no) [default=no]:
Would you like to create a new local network bridge? (yes/no) [default=yes]:
What should the new bridge be called? [default=lxdbr0]:
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
Would you like LXD to be available over the network? (yes/no) [default=no]:
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:

Das sollte es eigentlich gewesen sein.

findstr: Die Zeile darüber ausgeben

Manchmal hat man Probleme ähnlich wie dieses hier: Man möchte nicht die gesuchte Zeile in einer Datei ausgeben, sondern die Zeile darüber. Und das Ganze soll mit einem Batch-Skript ausgeführt werden. Für dieses Problem habe ich mir einer Testdatei erstellt, die in etwas so aussieht:

123
abc
234
qwe
345
qwe
456
abc

Alle Zeilen, die über dem gesuchten String abc stehen, sollen ausgegeben werden, also

123
456

Das habe ich anschließend in einem Batch-Skript realisiert. Ich hoffe, die Kommentare hierfür sind ausreichend:

@echo off
SetLocal EnableDelayedExpansion

rem set initial values for the variables
set index=0
set "previous_line="
set "current_line="

rem Iterate over file, line by line
For /F "tokens=*" %%L in (array_data2.txt) do (
set "previous_line=!current_line!"
set "current_line=%%L"

rem check if current line has searched
string echo %%L| findstr /C:"abc" > nul

rem if it has searched string, print previous
rem and current line
if not errorlevel 1 (
echo !index!: !previous_line!
set /a "curr_index=index+1"
echo !curr_index!: !current_line!
echo.
)

rem add 1 to the index
set /a "index=index+1"
)

Quellen:

UCS-2 Kodierung und findstr

Dateien können verschiedene Kodierungen haben. Das Kommandozeilen-Werkzeug findstr kann in so einem Fall einen gesuchten String in einer Datei nicht finden. Ich hatte eine Datei, die hatte eine UCS-2 Kodierung und beim Aufruf von

findstr "abc" utc2.txt

kam nichts dabei heraus, obwohl die Textdatei diesen String enthielt. Um den gesuchten String trotzdem zu finden, kann man in so einem Fall auf das SysInternal Tools strings.exe zurückgreifen:

strings -nobanner utc2.txt | findstr "abc"


This computer doesn’t have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory

Ich wollte meine ersten Schritte mit minikube auf einer Linux Maschine machen. Diese Maschine war eine virtuelle Maschine, die unter Azure lief. Beim Aufruf von

minikube start

bekam ich jedoch die Meldung:

This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory

Nach einer Weile kam ich auf eine Lösung. Meine virtuelle Maschine hatte die Größe D2_v2. Laut Dokumentation muss es jedoch eine Maschine der Größe v3 sein:

Zur schnellen Information: Alle virtuellen v3-Computer unterstützen geschachtelte Virtualisierung. Eine vollständige Liste der VM-Größen, die Schachtelung unterstützen, finden Sie im Artikel zur Azure-Compute-Einheit.

https://docs.microsoft.com/de-de/azure/virtual-machines/windows/nested-virtualization

Nachdem ich also meine Maschine auf die Größe D2_v3 geändert hatte, ist das minikube-Kommando problemlos durch gelaufen.

CreateProcess failed: The system cannot find the file specified.

Beim Bau der Bibliothek skia unter Windows bekam ich diese Fehlermeldung:

[419/2871] copy ../../third_party/externals/icu/common/icudtl.dat icudtl.dat
FAILED: icudtl.dat
python .../skia/build/skia/gn/cp.py ../../third_party/externals/icu/common/icudtl.dat icudtl.dat
CreateProcess failed: The system cannot find the file specified.
ninja: fatal: ReadFile: The handle is invalid.

Wie es sich herausgestellt hat, lag das Problem darin, dass ninja versucht hat, Python aufzurufen, Python aber nicht gefunden hat. Genauer gesagt, python.exe.

Um die python.exe aus den depot_tools aufrufen zu können, muss man vorher erstmal das Skript update_depot_tools.bat aufrufen (wenn man das vorher nicht schon getan hat). Danach sollte sich eine python.exe in dem Ordner <some\path>\depot_tools\win_tools-2_7_6_bin\python\bin befinden. Der Pfad kann sich in der Zukunft vermutlich ändern. Jedenfalls war das bei mir der Fall. Diesen Pfad muss man noch in der Umgebungsvariable PATH hinzufügen, z.b. in einem Batch-Skript mit

set "PATH=%PATH%;<some\path>\depot_tools\win_tools-2_7_6_bin\python\bin"

Danach sollte der Fehler verschwunden sein.

Anmerkungen zu if errorlevel 0

Ab und an sieht man im Internet Batch-Code, bei denen es um die Abfrage geht, ob der Errorlevel 0 oder ungleich 0 ist. Dabei sieht man Code-Zeilen, die in etwa so aussehen:

if errorlevel 0 (
...
) else (
...
)

Diese Abfrage ist aber in vielen Fällen falsch. Im Grunde bedeutet sie: Ist der Errorlevel 0 oder größer. D.h. diese Abfrage kommt fast gar nicht in den else-Teil, weil der Errorlevel meist 0 oder größer ist. Außer man hat ein Programm, welches negative Errorlevel zurückgibt.

Wenn man nun aber wissen möchte, ob der Errorlevel 1 oder größer ist, dann muss die Abfrage anders gestaltet werden:

if errorlevel 1 (
...
) else (
...
)