Perl on Docker - Dancer2 und Redis docker-compose Beispiel

Veröffentlicht von Thomas Fahle am (Permalink)

Two kissing camels

 

Einführung

Das Einrichten von einzelnen Docker-Containern mit Dockerfiles ist eigentlich unkompliziert. Oft benötigt man aber Setups aus mehreren Containern, z.B. einen Front- und einen Backendserver in einem eigenem Netzwerk.

Docker Compose ermöglicht das Bereitstellen einer Gruppe mit mehreren Containern aus einer Konfigurationsdatei (docker-compose.yml oder docker-compose.yaml) im YAML-Format.

In diesem Beitrag wird eine Dancer2-Webapplikation (Frontend) in einem eigenen Container erstellt. Diese Webapp verwendet einen Redis-Server in einem weiterem Container als Backend im selben Netzwerk. Beide Container werden per Docker Compose gebaut, verbunden und gemeinsam gestartet.

 

Beispiel: Dancer2-Webapp mit Redis-Backend

Alle nachfolgend aufgeführten Dateien befinden sich in einem eigenen Verzeichnis.

Der Source-Code der Beispiele befindet sich auch Github Repo perl-howto-code.

Dancer2-Webapp

Zunächst die Dancer2-Webapp – Datei redis-counter.pl – ein einfaches Counter-Programm, welches den Zählerstand in einer Redis-Datenbank vorhält.

#!/usr/bin/env perl
use strict;
use warnings;

use Dancer2;
use Redis;

set 'logger'       => 'console';
set 'log'          => 'debug';
set 'show_errors'  => 1;
set 'startup_info' => 1;

my $redis_host = 'myredis';
my $redis_port =  6379;

my $redis = Redis->new( server => "$redis_host:$redis_port" );

get '/' => sub {
    my $counter = $redis->incr('counter');
    debug "Counter: $counter";
    return "$counter\n";
};

dance;

Redishost und -port (myredis, 6379) müssen mit den Angaben in der Konfigurationsdatei docker-compose.yaml exakt übereinstimmen. Dazu später mehr.

Die Ausgabe der eingestreuten debug-logging-Anweisungen lässt sich auch per docker logs am laufenden Container einsehen.

 

Dockercontainer der Dancer2-Webapp

Das nachfolgende Dockerfile nutzt Official Docker Perl Images, die bereits in Perl on Docker - Offizielle Perl Docker Images als Grundlage eigener Images beschrieben wurden, als Basis (FROM perl:latest).

Zunächst werden alle installierten Pakete mit apt-get auf den neuesten Stand gebracht. Die Umgebungsvariable DEBIAN_FRONTEND wird per ARG temporär – also nur während des Builds – gesetzt.

Im nächsten Schritt werden die CPAN-Module Dancer2 und Redis mit cpanm installiert.

Um das Image so klein wie möglich zu halten, werden nun noch alle temporären Dateien und apt-repos gelöscht.

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 redis-counter.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.)

 

Dockerfile

Datei Dockerfile.

FROM perl:latest

LABEL maintainer="Thomas Fahle <perlhowto.github.io@gmail.com>"
LABEL version="0.10"
LABEL description="Perl-on-Docker perl:latest buildpacks-deps Image running Dancer2 Redis Example"


# apt-get
# Upgrade already installed packages
# Autoremove packages unused by other packages
# Clean local repos
#
# cpanm
# Install Dancer2
# Install Redis
#
# 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 autoremove && \
     apt-get clean ; \
    /usr/local/bin/cpanm \
      Dancer2 \
      Redis \
      ; \
    rm -rf /var/lib/apt/lists/* ; \
    rm -rf /tmp/* ; \
    rm -rf /root/.cpanm/

WORKDIR /app

COPY ./redis-counter.pl /app/

EXPOSE 3000/tcp

ENTRYPOINT ["/usr/local/bin/perl", "/app/redis-counter.pl"]

Das Image kann jetzt gebaut

sudo DOCKER_BUILDKIT=1 docker build -t perl-redis-client-image .

und zumindestens – der Redis-Container läuft ja noch nicht – inspiziert werden.

# Debug
sudo docker run -it --rm --entrypoint='' --name perl-redis-client perl-redis-client-image /bin/bash

Es ist nicht zwingend erforderlich, das Image bereits jetzt zu bauen. Das kann docker-compose ebenfalls erledigen. Es vereinfacht aber das Debugging erheblich.

 

Dockercontainer des Redis-Servers

Für den Redis-Container wird ein vorgefertigtes offizielles Redis Image vom Docker Hub mit dem besonders schlanken Alpine Linux verwendet.

Redis-Daten werden im Docker VOLUME /data persistent gespeichert, falls das Volume vorhanden ist.

 

Container mit docker-compose verbinden

Die nachfolgende Konfiguration erzeugt zwei Services bzw. Container, dancer und myredis, in einem eigenem Netzwerk.

Die Servicekennzeichungen werden von docker-compose auch als Hostnamen für die Container verwendet. Der Hostname des Redis-Containers (myredis) wurde auch in der Datei redis-counter.pl hartkodiert.

Da kein Netzwerk explizit konfiguriert wurde, legt docker-compose automatisch ein Netzwerk an. Der Name des Netzwerks wird in diesem Fall aus dem Namen des Ordners, hier redis, erzeugt.

Der Webapp-Container dancer wird ggf. neu gebaut, öffnet den Dancer2-Standard-Port 3000, mappt den Port 3000 des Dockerhosts auf den Port 3000 des Containers und startet mit dem im Dockerfile konfiguriertem Kommando.

Da der Webapp-Container dancer ohne den zugehörigen Redis-Container myredis nicht lauffähig ist, wird dancer erst nach myredis gestartet

Der Redis-Container myredis nutzt, wie bereits beschrieben, ein vorgefertigtes offizielles Redis Image vom Docker Hub, öffnet den Redis-Standard-Port 6379 im Netzwerk dieser Containergruppe, mountet das lokale Verzeichnis ./redis-data als Volume /data und startet den redis-server.

docker-compose.yml

Datei docker-compose.yml

version: '3'
services:
  dancer:
    build: .
    expose:
     - "3000"
    ports:
     - "3000:3000"
    depends_on:
     - "myredis"
  myredis:
    image: redis:alpine
    expose:
     - "6379"
    command: redis-server
    volumes:
      - ./redis-data:/data

Container bauen

Container in einem eigenen Arbeitsschritt bauen.

sudo COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose build

Container starten, testen und stoppen

Containergruppe im Vordergrund starten

sudo docker-compose up

Das sieht dann ungefähr so aus:

Creating network "redis_default" with the default driver
Starting redis_myredis_1 ... done
Starting redis_dancer_1  ... done
Attaching to redis_myredis_1, redis_dancer_1
myredis_1  | 1:C 16 Sep 2021 10:39:03.467 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
myredis_1  | 1:C 16 Sep 2021 10:39:03.467 # Redis version=6.2.5, bits=64, commit=00000000, modified=0, pid=1, just started
myredis_1  | 1:C 16 Sep 2021 10:39:03.467 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
myredis_1  | 1:M 16 Sep 2021 10:39:03.468 * monotonic clock: POSIX clock_gettime
myredis_1  | 1:M 16 Sep 2021 10:39:03.469 * Running mode=standalone, port=6379.
myredis_1  | 1:M 16 Sep 2021 10:39:03.469 # Server initialized
myredis_1  | 1:M 16 Sep 2021 10:39:03.469 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
myredis_1  | 1:M 16 Sep 2021 10:39:03.469 * Ready to accept connections
dancer_1   | >> Dancer2 v0.301004 server 1 listening on http://0.0.0.0:3000

Testen im Browser http://127.0.0.1:3000/ bzw. http://0.0.0.0:3000/ oder in einer weiteren Shell.

curl 0.0.0.0:3000

Containergruppe im Vordergrund mit der Tastenkombination Crtl-C stoppen.

Die Containergruppe kann natürlich auch im detached-Modus gestartet

sudo docker-compose -d up

und gestoppt werden.

sudo docker-compose down

Persistenz der Redis-Daten

Beim erneuten Start der Containergruppe liest Redis die Datenbank aus dem VOLUME /data ein:

...
myredis_1  | 1:M 16 Sep 2021 10:42:28.509 * Loading RDB produced by version 6.2.5
myredis_1  | 1:M 16 Sep 2021 10:42:28.509 * RDB age 25 seconds
myredis_1  | 1:M 16 Sep 2021 10:42:28.509 * RDB memory usage when created 0.77 Mb
myredis_1  | 1:M 16 Sep 2021 10:42:28.509 * DB loaded from disk: 0.000 seconds

Aufräumarbeiten

Sobald die Containergruppe nicht mehr benötigt wird, kann weitestgehend aufgeräumt werden.

sudo docker-compose down -v --rmi all --remove-orphans

Removing redis_dancer_1  ... done
Removing redis_myredis_1 ... done
Removing network redis_default
Removing image redis:alpine
Removing image redis_dancer

 

Siehe auch

 

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

 

Bildnachweis

Photo by Lesly Juarez on Unsplash

 

Weitere Posts