Sie kennen das: Sie setzen auf allen Maschinen eine auf musl basierte Linux-Distribution wie Alpine Linux ein. Jetzt kommt nun einer ihrer Kollegen an und möchte unbedingt sein Python-Programm, das tensorflow, keras und OpenCV benötigt.
Na, Gute Nacht, Marie.
Falls Sie jetzt auf die Idee kommen sollten alle Abhängigkeiten für musl zu bauen: Vergessen Sie es. Stattdessen: containers: Lightweight containers using Linux user namespaces -- Für alle, denen docker zu 2013 ist und systemd-nspawn zu viel systemd. Hier auch mal wieder Dank an Markus, der containers vorgeschlagen hat. containers macht also nichts anderes als den Inhalt eines Verzeichnisses als eigenständiges Betriebssystem zu behandeln, welches auch keinen Zugriff auf die Prozesse des Hostsystems hat.
Unter Archlinux ist containers im AUR zu finden, bei voidlinux scheint es eine im offiziellen Repository zu geben. Debian, Ubuntu und auch Alpine selber gehen leer aus, muss also selber gebaut werden.
Ist die Installation erfolgt, sollte der Befehl contain
ausgeben:
[sxw@archeus] [~] > contain
Usage: contain [OPTIONS] DIR [CMD [ARG]...]
Options:
-c disable console emulation in the container
-g MAP set the container-to-host GID map
-i CMD run a helper child inside the new namespaces
-n share the host network unprivileged in the container
-o CMD run a helper child outside the new namespaces
-u MAP set the container-to-host UID map
GID and UID maps are specified as START:LOWER:COUNT[,START:LOWER:COUNT]...
[sxw@archeus] [~] >
Die Usage-Line sagt uns hier ganz klar, dass wir ein Verzeichnis mit dem System benötigen.
Die Kollegen von Alpinelinux haben das Potential ihrer Distribution als prädestinierten Kandidaten für Container-System erkannt und bieten uns ein Mini Root Filesystem im Download-Bereich an. Dort laden wir das tar.gz für x86_64 in meinem Fall herunter und entpacken es.
Dafür habe ich mir mal ein Verzeichnis erstellt, nach Entpacken sollte das Ganze dann so aussehen:
[sxw@archeus] [~/my-container] > ls
bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var
[sxw@archeus] [~/my-container] >
Jetzt können wir den container starten indem wir eingeben:
[sxw@archeus] [~/my-container] > contain -n . /bin/sh
Wir sehen einen neuen root-Prompt und die Eingabe von ps ax
zeigt
lediglich unsere Prozesse, die nun in einem anderen Namespace laufen:
/ # ps ax
PID USER TIME COMMAND
1 root 0:00 /bin/sh
2 root 0:00 ps ax
/ #
Der aufmerksame Leser wird die Verwendung von -n
bemerkt haben. Diese
Option aktiviert lediglich die Netzwerkunterstützung und das System im
Container sieht somit die gleichen Network Interfaces wie der Host.
Damit auf einem Alpine-System glibc-Programm wie tensorflow laufen, muss zunächst die glibc installiert werden. Außerhalb des containers auf dem Host-System das apk herunterladen und ins root-Verzeichnis des containers abspeichern:
[sxw@archeus] [~/my-container/root] > wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.32-r0/glibc-2.32-r0.apk
[sxw@archeus] [~/my-container/root] > wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.32-r0/glibc-bin-2.32-r0.apk
und die URLs aus dem Repository auskommentieren. Die werden nicht benötigt:
[sxw@archeus] [~/my-container] > sed -ie 's/^/#/g' etc/apk/repositories
Wieder den Container aktivieren und das APK installieren:
[sxw@archeus] [~/my-container] > contain -n . /bin/sh
/ # cd root
~ # apk --allow-untrusted add glibc-2.32-r0.apk
~ # apk --allow-untrusted add glibc-bin-2.32-r0.apk
Und wir sollten sehen, dass der Linker installiert wurde:
~ # apk info -L glibc | grep ld
etc/ld.so.cache
lib/ld-linux-x86-64.so.2
lib64/ld-linux-x86-64.so.2
usr/glibc-compat/etc/ld.so.conf
usr/glibc-compat/etc/ld.so.cache
usr/glibc-compat/lib/ld-2.32.so
usr/glibc-compat/lib/ld-linux-x86-64.so.2
usr/glibc-compat/lib64/ld-linux-x86-64.so.2
~ #
Den Rest erledigen wir über Miniconda, einem Python-Installer, der auch jegliche Binaries, eigene Python-Installation fuer x86_64 mitbringt und ziemlich problemlos in meinen Tests funktioniert hat.
Damit Miniconda auch die Hostnamen auflösen kann sollten wir einen
Nameserver in die /etc/resolv.conf
des Containers eintragen:
~ # echo 'nameserver 141.1.1.1' > /etc/resolv.conf
~ # wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
Connecting to repo.anaconda.com (104.16.131.3:443)
saving to 'Miniconda3-latest-Linux-x86_64.sh'
Miniconda3-latest-Li 1% | |
...
'Miniconda3-latest-Linux-x86_64.sh' saved
~ # ls
Miniconda3-latest-Linux-x86_64.sh glibc-2.32-r0.apk
~ # chmod 755 Miniconda3-latest-Linux-x86_64.sh
~ # ./Miniconda3-latest-Linux-x86_64.sh
Welcome to Miniconda3 py38_4.8.3
In order to continue the installation process, please review the license
agreement.
Please, press ENTER to continue
>>>
[... LICENSE ...]
Do you accept the license terms? [yes|no]
[no] >>> yes
Miniconda3 will now be installed into this location:
/miniconda3
- Press ENTER to confirm the location
- Press CTRL-C to abort the installation
- Or specify a different location below
[/miniconda3] >>>
PREFIX=/miniconda3
Unpacking payload ...
...
installation finished.
Do you wish the installer to initialize Miniconda3
by running conda init? [yes|no]
[no] >>> yes
Nun sollten wir eine komplette Python-Umgebung in /miniconda3
bekommen
haben, dessen Funktionalität wir durch Ausführen von
/miniconda3/bin/python
bestätigen können:
/ # /miniconda3/bin/python
Python 3.8.3 (default, May 19 2020, 18:47:26)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Jetzt nur noch die restlichen Abhängigkeiten installieren:
/ # /miniconda3/bin/conda install pandas
/ # /miniconda3/bin/conda install -c conda-forge opencv
/ # /miniconda3/bin/conda install scikit-image
/ # /miniconda3/bin/conda create -n tf tensorflow
/ # /miniconda3/bin/conda install -c conda-forge keras
/ # /miniconda3/bin/conda install -c conda-forge imutils
/ # /miniconda3/bin/conda install -c anaconda psycopg2
/ # /miniconda3/bin/conda install -c anaconda redis