Perl on Docker - Offizielle Perl Docker Images als Grundlage eigener Images
Veröffentlicht von Thomas Fahle am (Permalink)
Einführung
Official Docker Perl Images sollen das Erstellen eigener Docker-Images für Perl-Anwendungen erleichtern.
Die zahlreichen Varianten beruhen aktuell (Anfang September 2021) auf den Debian Docker Images für Stretch (9) bzw. Buster (10) und bieten
- Perlversionen von 5.8 bis aktuell 5.34
- Threaded und non-threaded Perls in allen o.g. Perlversionen
- Unterstützung für zahlreiche Architekturen
- Leichtgewichtige slim Varianten, falls minimaler Speicherplatz oder passgenaue eigene Installation erforderlich
- Entwicklerfreundliche buildpacks-deps Varianten, bei denen zahlreiche Entwicklungsbibliotheken, Header, Compiler usw. bereits vorinstalliert sind
- cpanm passend vorinstalliert
- Einsehbare, nachvollziehbare und wiederverwendbare Dockerfiles im offiziellen docker-perl Git-Repo.
Dieser Beitrag beschäfigt sich mit Official Docker Perl Images als Basis für eigene Perl-Docker-Images und setzt grundlegende Dockerkenntnisse voraus.
Offizielle Perl Images im Dockerhub
Offizielle Perl Images im Dockerhub finden
Ein docker search nach den offiziellen Perl-Images liefert
sudo docker search --filter=is-official=true perl NAME DESCRIPTION STARS OFFICIAL AUTOMATED perl Perl is a high-level, general-purpose, inter… 398 [OK]
Die Tags finden sich auf der Official Docker Perl Images Website oder lassen sich indirekt ermitteln
curl -s https://registry.hub.docker.com/v1/repositories/perl/tags | jq '.[].name'
Das liefert folgende, hier deutlich gekürzte, Liste:
latest" "5" "5-buster" "5-slim" "5-slim-buster" "5-slim-stretch" "5-slim-threaded" "5-slim-threaded-buster" ... "5.8.9-threaded-buster" "5.8.9-threaded-stretch" ...
In den sprechenden Tags steht slim für die slim-Variante, threaded für ein Perl mit Threadunterstützung. Perls ohne Threads – der Default – sowie buildpacks-deps werden nicht gesondert bezeichnet. buster und stretch kennzeichnen die jeweilige Debianbasis.
Offizielle Perl Images von Dockerhub laden
Images können wie gewohnt mittels docker pull heruntergeladen
sudo docker pull perl:5.34-buster sudo docker pull perl:5.34-slim-buster sudo docker pull perl:5.34-threaded-buster sudo docker pull perl:5.34-slim-threaded-buster
und per docker images angezeigt werden.
sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE perl 5.34-threaded-buster 6e420f9d6dd2 2 weeks ago 860MB perl 5.34-buster 4307319f1e3e 2 weeks ago 860MB perl 5.34-slim-threaded-buster 6e91d128664e 2 weeks ago 128MB perl 5.34-slim-buster 49cc294090d8 2 weeks ago 128MB
Hier fällt sofort die deutlich unterschiedliche Größe der slim und buildpacks-deps Varianten auf.
Offizielle Perl Images im Detail
Installierte Pakete der Offiziellen Perl Images
Zwecks Inspektion werden per docker run je ein buildpacks-deps und ein slim Wegwerf-Container erzeugt, in die man sich einloggen kann.
Nun wird einfach die Anzahl der vorinstallierten Pakete und der Entwickler-Pakete via dpkg -l und wc gezählt.
Zunächst ein perl:5.34-buster Image.
sudo docker run -it --rm perl:5.34-buster /bin/bash root@1c9e7e9c7016:/# dpkg -l <snip> ii apt 1.8.2.3 amd64 commandline package manager ii autoconf 2.69-11 all automatic configure script builder ii automake 1:1.16.1-4 all Tool for generating GNU Standards-compliant Makefiles ii autotools-dev 20180224.1 all Update infrastructure for config.{guess,sub} files <snip> root@1c9e7e9c7016:/# dpkg -l | wc -l 418 root@1c9e7e9c7016:/# dpkg -l | grep dev | wc -l 95
Dann ein perl:5.34-slim-buster Image.
sudo docker run -it --rm perl:5.34-slim-buster /bin/bash root@bbf8ebc06c9b:/# dpkg -l | wc -l 91 root@bbf8ebc06c9b:/# dpkg -l | grep dev | wc -l 4
Es fällt auf, das in der slim-Variante wesentlich weniger Pakete (91) als in der buildpacks-deps-Variante (418) vorinstalliert sind. In der buildpacks-deps-Variante sind mit 95 Paketen auch wesentlich mehr Entwicklungsbibliotheken (*-dev Pakete) als in der slim-Variante (4) vorinstalliert.
Threaded vs. Non-Threaded
Die jeweiligen threaded und non-threaded aka Standard Varianten unterscheiden sich nur einer Stelle - der Compileroption -Dusethreads.
Das lässt sich an Hand der Dockerfiles aus dem offiziellem docker-perl Git-Repo leicht prüfen.
git clone https://github.com/Perl/docker-perl.git cd docker-perl diff 5.034.000-slim-buster/Dockerfile 5.034.000-slim,threaded-buster/Dockerfile 36c36 < && ./Configure -Darchname="$gnuArch" "$archFlag" -Duseshrplib -Dvendorprefix=/usr/local -des \ --- > && ./Configure -Darchname="$gnuArch" "$archFlag" -Dusethreads -Duseshrplib -Dvendorprefix=/usr/local -des \
Perl läuft mit PID 1 im Container: IPC, Signals, exec(), fork()
In einem Dockercontainer laufen Programme, hier Perl, mit der PID 1. Dies hat Auswirkungen auf die Signalbehandlung, insbesondere für SIGTERM und SIGINT. Das ist auch deutlich dokumentiert.
sudo docker run -it --rm perl:5.34-buster /bin/bash root@e23b33ace873:/# ps -fA UID PID PPID C STIME TTY TIME CMD root 1 0 0 06:32 pts/0 00:00:00 /bin/bash root 7 1 0 06:32 pts/0 00:00:00 ps -fA root@e23b33ace873:/# perl -E "say $$" 1
Vor der Containerisierung von Perlprogrammen, die fork() oder Signale nutzen, sollte man z.B. folgende Hinweise und Tipps berücksichtigen: Signals in perlipc, Perl/docker-perl#44, Perl as PID 1 under Docker, The Curious Case of Pid Namespaces, dumb-init und tini.
Bei aktuellen Dockerversionen (>1.13) ist es möglich tini - A tiny but valid init for containers als Init-Prozess zu verwenden.
sudo docker run --init -it --rm perl:5.34-buster /bin/bash root@69f9d951cc50:/# ps -fA UID PID PPID C STIME TTY TIME CMD root 1 0 0 06:38 pts/0 00:00:00 /sbin/docker-init -- /bin/bash root 7 1 0 06:38 pts/0 00:00:00 /bin/bash root 8 7 0 06:38 pts/0 00:00:00 ps -fA root@69f9d951cc50:/# perl -E "say $$" 7Jetzt läuft tini mit PID 1 und stellt sicher, dass die Standardsignalhandler im Docker-Image wie gewohnt funktionieren. So beendet beispielsweise SIGTERM Prozesse auch dann ordnungsgemäß, wenn kein expliziter Signalhandler programmiert/installiert wurde.
Zwei Perl-Binaries in den Offiziellen Perl Images
Da Perl für die Debian-Paketverwaltung via apt-get zwingend benötigt wird, sind zwei verschiedene Perl-Binaries in den Offiziellen Perl Images installiert:
/usr/bin/perl, das über die Paketverwaltung installiert wurde (oft als System-Perl bezeichnet)
sudo docker run -it --rm perl:5.34-buster /bin/bash root@4bbb7adc8926:/# /usr/bin/perl -v This is perl 5, version 28, subversion 1 (v5.28.1) built for x86_64-linux-gnu-thread-multi (with 65 registered patches, see perl -V for more detail) Copyright 1987-2018, Larry Wall root@4bbb7adc8926: /usr/bin/perl -E "foreach $inc ( @INC ) { say $inc }" /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.28.1 /usr/local/share/perl/5.28.1 /usr/lib/x86_64-linux-gnu/perl5/5.28 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.28 /usr/share/perl/5.28 /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base
und /usr/local/bin/perl, das in das Dockerimage installierte Perl
root@4bbb7adc8926:/# /usr/local/bin/perl -v This is perl 5, version 34, subversion 0 (v5.34.0) built for x86_64-linux-gnu Copyright 1987-2021, Larry Wall root@4bbb7adc8926:/# /usr/local/bin/perl -E "foreach $inc ( @INC ) { say $inc }" /usr/local/lib/perl5/site_perl/5.34.0/x86_64-linux-gnu /usr/local/lib/perl5/site_perl/5.34.0 /usr/local/lib/perl5/vendor_perl/5.34.0/x86_64-linux-gnu /usr/local/lib/perl5/vendor_perl/5.34.0 /usr/local/lib/perl5/5.34.0/x86_64-linux-gnu /usr/local/lib/perl5/5.34.0
Die Umgebungsvariable PATH ist in den Offiziellen Perl Docker Images so gesetzt, das /usr/local/bin/ Vorrang vor /usr/bin/ hat.
root@4bbb7adc8926:/# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin root@4bbb7adc8926:/# which perl /usr/local/bin/perl
Daher sollte die Shebang-Zeile für Perlprogramme im Dockercontainer wie folgt gesetzt werden:
#!/usr/bin/env perl
oder auch
#!/usr/local/bin/perl
Und eben nicht auf /usr/bin/perl verweisen, das wäre das System-Perl.
Alternativ kann das Perlprogramm auch direkt aufgerufen (als Parameter an /usr/local/bin/perl übergeben) werden, was üblicherweise für CMD bzw. ENTRYPOINT in Dockerfiles genutzt wird.
Siehe dazu auch Official Docker Perl - Coexisting with Debian's /usr/bin/perl, Why do official perl docker images have two version of perl? und Perl/docker-perl#26.
cpan und cpanm als Installer für CPAN-Module
cpan
Da zwei verschiedene Perl-Binaries in den Offiziellen Perl Images installiert sind, wurde auch der mit Perl ausgelieferte CPAN-Installer zweimal installiert – /usr/bin/cpan und /usr/local/bin/cpan.
/usr/bin/cpan nutzt das Systemperl in /usr/bin/perl, /usr/local/bin/cpan nutzt das zusätzlich installierte Perl in /usr/local/bin/perl.
Wie oben bereits gezeigt verwenden beide installierte Perls jeweils einen eigenen @INC, so daß sie sich nicht gegenseitig in die Quere kommen.
/usr/local/bin/cpan
root@4bbb7adc8926:/# /usr/local/bin/cpan Dancer2 Appending installation info to /usr/local/lib/perl5/5.34.0/x86_64-linux-gnu/perllocal.pod CROMEDOME/Dancer2-0.301004.tar.gz /usr/bin/make install -- OK
vs. /usr/bin/cpan
root@4bbb7adc8926:/# /usr/bin/cpan Dancer2 Appending installation info to /usr/local/lib/x86_64-linux-gnu/perl/5.28.1/perllocal.pod CROMEDOME/Dancer2-0.301004.tar.gz /usr/bin/make install -- OK
Auch hier sorgt die Umgebungsvariable PATH dafür, das Binaries in /usr/local/bin/ Vorrang vor /usr/bin/ haben.
root@4bbb7adc8926:/# which cpan /usr/local/bin/cpan
Notwendige Aufräumarbeiten nach der Installation im Dockerfile
RUN rm -rf /root/.cpan/
cpanm
cpanm wird nur ein Mal mit dem zusätzlich installierten Perl als /usr/local/bin/cpanm installiert. Das Debian-Paket cpanminus, welches /usr/bin/cpanm enthält, wird nicht installiert.
root@4bbb7adc8926:/# which cpanm /usr/local/bin/cpanm
cpanm installiert CPAN-Module in die korrekten Ordner (@INC) des zusätzlich installierten Perls.
root@4bbb7adc8926:/# cpanm Moose root@4bbb7adc8926:/# tail -n 4 /root/.cpanm/build.log Successfully installed Moose-2.2015 Installing /usr/local/lib/perl5/site_perl/5.34.0/x86_64-linux-gnu/.meta/Moose-2.2015/MYMETA.json Installing /usr/local/lib/perl5/site_perl/5.34.0/x86_64-linux-gnu/.meta/Moose-2.2015/install.json 20 distributions installed
Notwendige Aufräumarbeiten nach der Installation im Dockerfile
RUN rm -rf /root/.cpanm/
Umgebungsvariablen für cpan, cpanm, local:lib, Module::Build
Bei einigen Installationsproblemen kann man evtl. versuchen, die – standardmäßig nicht gesetzten – Umgebungsvariablen PERL_MB_OPT, PERL_MM_OPT, PERL_LOCAL_LIB_ROOT und PERL5LIB für Module::Build, local:lib, cpanm oder cpan temporär im Dockerfile zu setzen. Also etwas wie
RUN PERL_MB_OPT=--install_base "/home/feelsafe/perl5" \ PERL_MM_OPT=INSTALL_BASE=/home/feelsafe/perl5 \ PERL_LOCAL_LIB_ROOT=/home/feelsafe/perl5 \ PERL5LIB=/home/feelsafe/perl5/lib/perl5 \ /usr/local/bin/cpanm ....
Your mileage may vary.
Dancer2 Hello World Beispiel
Im vorherigen Beitrag Perl on Docker - Dancer2 Hello World Beispiel wurde bereits die Containerisierung/Dockerisierung einer sehr einfachen Dancer2 Webapplikation für Dockereinsteiger mit minimalen Dockerkenntnissen beschrieben. Das dort verwendete Beispiel wird auch in diesem Beitrag nochmals verwendet.
In zwei eigenen Verzeichnissen – buildpacks-deps- und slim-Variante – werden jeweils die Datei der Webapplikation hello-world.pl und ein passendes Dockerfile erstellt.
Hello World App
Datei hello-world.pl.
# Shamelessly stolen from https://metacpan.org/pod/Dancer2 use Dancer2; get '/' => sub { "Hello World\n" }; dance;
Dockerfile für buildpacks-deps Dancer2 Hello World Beispiel
Datei Dockerfile.
FROM perl:5.34.0 # https://github.com/perl/docker-perl/blob/311f05366d91427d289740dd15fb9401dc4347ef/5.034.000-main-buster/Dockerfile # https://hub.docker.com/_/buildpack-deps/ # pulls buildpack-deps:buster # https://github.com/docker-library/buildpack-deps/tree/65d69325ad741cea6dee20781c1faaab2e003d87/debian/buster # pulls buildpack-deps:buster-scm # https://github.com/docker-library/buildpack-deps/tree/65d69325ad741cea6dee20781c1faaab2e003d87/debian/buster/scm # pulls buildpack-deps:buster-curl # https://github.com/docker-library/buildpack-deps/tree/65d69325ad741cea6dee20781c1faaab2e003d87/debian/buster/curl # pulls debian:buster # https://hub.docker.com/_/debian/ # Debian Buster aka Debian 10 LABEL maintainer="Your Name <YourMail@example.com>" LABEL version="0.10" LABEL description="Just Another Docker perl:5.34.0 buildpacks-deps Image running Dancer2 hello-world using build-in server" # apt-get # Upgrade already installed packages # Install SQLite3 database # Autoremove unused packages # Clean local repos # # cpanm # Install Dancer2 # Install File::Slurper # Install Template-Toolkit # Install DBD::SQLite # # Free disk space to decrease image size ARG DEBIAN_FRONTEND=noninteractive RUN set -ex; \ apt-get update && \ apt-get dist-upgrade -y && \ apt-get -y -qq --no-install-recommends install sqlite3 && \ apt-get -y autoremove && \ apt-get clean ; \ /usr/local/bin/cpanm --notest \ Dancer2 \ Template \ File::Slurper \ DBD::SQLite \ ; \ rm -rf /var/lib/apt/lists/* ; \ rm -rf /tmp/* ; \ rm -rf /root/.cpanm/ WORKDIR /app COPY ./hello-world.pl /app/ EXPOSE 3000/tcp ENTRYPOINT ["/usr/local/bin/perl", "/app/hello-world.pl"]
Das neue Container-Image basiert auf dem offiziellem Perl Buster Docker Image in der buildpacks-deps Variante ohne Threads (FROM perl:5.34.0).
Per LABEL werden diverse Metadaten gesetzt, welche die Verwaltung der Images erleichtern sollen.
Da im buildpacks-deps-Image schon sehr viele Werkzeuge und Bibliotheken installiert sind, werden hier nur mittels apt-get zunächst alle installierten Pakete auf den neuesten Stand gebracht und die Datenbank SQLite installiert. Die Umgebungsvariable DEBIAN_FRONTEND wird per ARG temporär – also nur während des Builds – gesetzt. (Bei der Verwendung von ENV werden Umgebungsvariablen dauerhaft (unveränderlich) im Image gesetzt.)
Anschließend werden per cpanm neben Dancer2 auch die – im Dancer2::Tutorial erwähnten – Module Template-Toolkit, File::Slurper und DBD::SQLite installiert, auch wenn sie für das Beispiel nicht benötigt werden.
Danach werden noch alle temporären Dateien und apt-repos gelöscht, um das Image so klein wie möglich zu halten.
Die WORKDIR-Anweisung erzeugt den Ordner /app im Containerimage und setzt diesen als Startpunkt für nachfolgende Befehle, z.B. COPY.
Anschließend wird die Datei hello-world.pl im aktuellen Verzeichis per COPY in das Image kopiert.
Der in Dancer2 eingebaute Miniwebserver nutzt den TCP-Port 3000, der per EXPOSE von aussen zugänglich gemacht wird.
Im letzten Schritt wird mittels ENTRYPOINT die Anwendung gesetzt, die mit dem Start des Containers gestartet werden soll. (Hier könnte man auch CMD verwenden.)
Containerimage bauen
sudo DOCKER_BUILDKIT=1 docker build -t big-perl-dancer:v10 .
Dockerfile für Slim Dancer2 Hello World Beispiel
Datei Dockerfile.
FROM perl:5.34.0-slim # https://github.com/perl/docker-perl/blob/311f05366d91427d289740dd15fb9401dc4347ef/5.034.000-slim-buster/Dockerfile # pulls debian:buster-slim # https://github.com/debuerreotype/docker-debian-artifacts/blob/3714465332cd80e3d37ef7a611ad558424ecc03d/buster/slim/Dockerfile LABEL maintainer="Your Name <YourMail@example.com>" LABEL version="0.10" LABEL description="Just Another Docker perl:5.34.0 slim Image running Dancer2 hello-world using build-in server" # apt-get # Upgrade already installed packages # Install build-essential (gcc, cc, make, ...) # Install SQLite3 database and devel # # cpanm # Install Dancer2 # Install File::Slurper # Install Template-Toolkit # Install DBD::SQLite # # apt-get # Remove/Purge build-essential # Autoremove unused packages # Clean local repos # # Free disk space to decrease image size ARG DEBIAN_FRONTEND=noninteractive RUN set -ex; \ apt-get update && \ apt-get dist-upgrade -y && \ apt-get -y -qq --no-install-recommends install \ build-essential \ libsqlite3-0 \ sqlite3 \ ; \ /usr/local/bin/cpanm --notest \ Dancer2 \ DBD::SQLite \ File::Slurper \ Template \ ; \ apt-get -y purge build-essential && \ apt-get -y autoremove && \ apt-get clean \ ; \ rm -rf /var/lib/apt/lists/* ; \ rm -rf /tmp/* ; \ rm -rf /root/.cpanm/ WORKDIR /app COPY ./hello-world.pl /app/ EXPOSE 3000/tcp ENTRYPOINT ["/usr/local/bin/perl", "/app/hello-world.pl"]
Das neue Container-Image basiert auf dem offiziellem Perl Buster Docker Image in der slim-Variante ohne Threads (FROM perl:5.34.0-slim).
Der Aufbau des Dockerfiles für die slim-Variante entspricht weitestgehend dem der buildpacks-deps-Variante (s.o.).
Bei der slim Variante müssen zuerst die Entwicklerwerkzeuge (gcc, cc, make, ...), passende Header und Bibliotheken installiert werden, bevor die gewünschten CPAN-Module installiert werden können.
Um das Containerimage möglichst klein zu halten, werden die nicht mehr benötigten Pakete (insbs. Entwicklerwerkzeuge) anschließend in dem selben RUN wieder entfernt.
Containerimage bauen
sudo DOCKER_BUILDKIT=1 docker build -t slim-perl-dancer:v10 .
Größenvergleich der beiden Images
Zunächst ein Blick auf die Größe der neu gebauten Images im Vergleich zu den Ausgangsimages.
sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE slim-perl-dancer v10 9bd73448bf0d 10 minutes ago 152MB big-perl-dancer v10 ffa53a69084f 13 hours ago 884MB perl 5.34-buster 4307319f1e3e 2 weeks ago 860MB perl 5.34-slim-buster 49cc294090d8 2 weeks ago 128MB
Erwartungsgemäß ist das Image der buildpacks-deps-Variante deutlich größer als das der slim-Variante – fast 6 mal so groß (884/152 = 5,8157).
In absoluten Zahlen sind beide Varianten gegenüber dem Ausgangsimage genau gleich um jeweils 24 MB gewachsen:
- slim-Variante (152 - 128 = 24)
- buildpacks-deps-Variante (884 - 860 = 24)
Beispiel Container starten und testen
Jetzt können die neuen Container gestartet und getestet werden.
buildpacks-deps Container
buildpacks-deps Container starten
sudo docker run -d -p 3000:3000 --name big-container big-perl-dancer:v10
buildpacks-deps Container testen
curl -s 0.0.0.0:3000 Hello World
buildpacks-deps Container stoppen
sudo docker stop big-container
buildpacks-deps Container ggf. löschen
sudo docker rm big-container
Slim Container
Slim Container starten
sudo docker run -d -p 3000:3000 --name slim-container slim-perl-dancer:v10
Slim Container testen
curl -s 0.0.0.0:3000 Hello World
Slim Container stoppen
sudo docker stop slim-container
Slim Container ggf. löschen
sudo docker rm slim-container
Zusammenfassung
Wie bereits in der Einführung erwähnt, sollen Official Docker Perl Images das Erstellen eigener Docker-Images für Perl-Anwendungen erleichtern.
Diese Aufgabe lösen die Official Docker Perl Images mit Bravour – egal ob kleine (slim) oder entwicklerfreundliche (buildpacks-deps) Container, legacy Anwendungen mit alten Perls oder Modern Perl mit coolen neuen Features und vieles andere mehr.
Siehe auch
- Official Docker Perl
- Github docker-perl
- Official Docker buildpack-deps A collection of common build dependencies used for installing various modules, e.g., gems.
- Docker, Perl and GitHub
- docker-perl-tester
- Perl on Docker - Dancer2 Hello World Beispiel
- Perl on Docker - Multi-stage Builds zur Verkürzung von Build-Laufzeiten
- Perl on Docker - Dancer2 und Redis docker-compose Beispiel
- Perl on Docker - Perl commandline Programm als Docker Container erstellen und nutzen
- Docker
- Docker Docs
- Docker Official Images
- Github Docker Official Images
Source-Code der Beispiele im Github Repo perl-howto-code.
Bildnachweis
Resized Photo by Kristian Egelund on Unsplash