Perl on Docker - Offizielle Perl Docker Images als Grundlage eigener Images

Veröffentlicht von Thomas Fahle am (Permalink)

Camel head under blue sky

 

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 $$"
7

Jetzt 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

 

Source-Code der Beispiele im Github Repo perl-howto-code.

 

Bildnachweis

Resized Photo by Kristian Egelund on Unsplash

 

Weitere Posts