Eigenes Apache2/Perl-Image erzeugen

In diesem HowTo geht es darum ein Docker Image mit einem Apache2 Server mit Perl-Umgebung zu erstellen.

Dieses Image setze ich mittlerweile in meinem Tommatic Projekt ein.

Das "docker-apache-perl" Image basiert auf meinem eigenen "raspbian-buster" Basis-Image welche ich im HowTo "Eigenes Raspian Basis-Image erzeugen" vorgestellt habe.

Die Veröffentlichung dieses Howtos erfolgt in der Hoffnung, dass es dem Ein oder Anderen von Nutzen sein wird, aber OHNE IRGENDEINE GARANTIE, sogar ohne die implizite Garantie der MARKTREIFE oder der VERWENDBARKEIT FÜR EINEN BESTIMMTEN ZWECK.

Bekannte Probleme - die jetzt nicht mehr existieren

In der ersten Version dieses Howto konnte ich den Container nicht über die Docker Befehle starten, stoppen und neu starten. Hierbei kam es zu Problemen mit dem ausführen von CGI/Perl-Skripten und der Fehlermeldung "Apache: 503 Service Unavailable".

In der ersten Version hatte ich schon erkannt, dass im Container im Verzeichnis "/var/run/apache2" ein PID-File und Socket-Dateien übrigblieben, welche das starten des cgid verhinderten.

Die Lösung ist dabei recht einfach: vor dem Start des Apache2 müssen eventuelle Dateien im Verzeichnis "/var/run/apache2" gelöscht werden! Dann gehören die Probleme der Vergangenheit an.

Ich habe dieses Howto nun dahingehend abgewandelt, dass das Starten des Apache 2 über ein mini Skript innerhalb des Containers geschieht, welches vor dem Start die Dateien im Verzeichnis "/var/run/apache2" löscht.

Schnittstellen des Apache2/Perl-Images

Damit der Apache-Server von außen konfiguriert und überwacht werden kann hat er folgende Volumes:

  1. /var/www/html
  2. /usr/lib/cgi-bin
  3. /etc/apache2/sites-available
  4. /var/log/apache2
  5. /etc/letsencrypt

Diese Volumes werden beim Erzeugen eines Containers auf lokale Verzeichnisse außerhalb des Containers gemappt. In den Lokalen Verzeichnissen werden dann die Konfigurationen abgelegt und die Logfiles von Apache2 können dort eingesehen werden.

Erzeugen des Apache2/Perl-Image

Als erstes wird ein Verzeichnis angelegt, in dem alles bearbeitet wird:

mkdir apache2-perl-image
cd apache2-perl-image 

In diesem Verzeichnis legen wir nun das Skript an, welches den Apache2 innerhalb des Containers sicher startet:

touch startapache2.sh"
chmod 755 startapache2.sh
vi startapache2.sh

Das Skript "startapache2.sh" hat den folgenden Inhalt:

#!/bin/bash"
rm -rf /var/run/apache2/*
/usr/sbin/apache2ctl -D FOREGROUND

In selben Verzeichnis wird eine Datei mit dem Namen "Dockerfile" angelegt. Diese hat den folgenden Inhalt:

FROM "raspbian-buster"
COPY startapache2.sh /usr/sbin/startapache2.sh
RUN \
    DEBIAN_FRONTEND=noninteractive && \
    apt-get update && \
    apt-get install -y \
        apache2 \
        libwww-perl \
        libxml-simple-perl \
        libgd-gd2-perl \
        libdate-calc-perl \
        libdata-uniqid-perl \
        liburi-encode-perl \
        libxml-libxml-perl \
        libxml-twig-perl \
        libxml-xpathengine-perl \
        libcgi-pm-perl \
        python-libxml2 \
        python-lxml \
        libgraphics-colorobject-perl \
        libmath-round-perl \
        make \
        gcc \
        vim && \
    cpan JSON::Parse && \
    a2enmod cgid && \
    a2enmod rewrite && \
    a2enmod cgi && \
    a2enmod ssl && \
    a2ensite  default-ssl.conf && \
    apt-get update -y && \
    apt-get upgrade -y && \
    apt-get -y clean && \
    mkdir /etc/letsencrypt && \
    echo "ServerName localhost" >>/etc/apache2/apache2.conf

VOLUME ["/var/www/html", "/usr/lib/cgi-bin", "/etc/apache2/sites-available", "/var/log/apache2", "/etc/letsencrypt"]

ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data

EXPOSE 80 443

Zeile 02

Mit dem Befehl COPY wird das Startskript aus dem lokalen Verzeichnis in das Image kopiert.

Zeilen 05 - 24

Hier werden alle notwendigen Raspian-Pakete installiert. Die Liste kann den eigenen Bedürfnissen angepasst werden.

Hinter dem letzen Paketnamen muss zwingend ein "&&" stehen!

Zeile 25

In dieser Zeile wird ein CPAN-Modul heruntergeladen und installiert.

Zeilen 26 - 30

Hier werden zusätzliche Apache2 Module und die "default-ssl.conf" aktiviert.

Zeilen 31 - 33

Aufräumen von "apt-get".

Zeile 34

In dieser Zeile wird ein Verzeichnis innerhalb des Docker Containers angelegt.

Zeile 35

Damit es wegen eines fehlenden Servername zu keinen Fehlermeldungen kommt wird an die Datei "apache2.conf" die Zeile "ServerName localhost" angehängt.

Zeile 37

Hier werden die Volumes als Schnittstelle zur Außenwelt erzeugt .

Zeile 42

Der HTTP und der HTTPS Port wird nach außen geöffnet.


Um das eigentliche Image zu erzeugen wird der folgende Befehl ausgeführt. Dabei ist zu beachten, dass der Befehl im Verzeichnis der Datei "Dockerfile" ausgeführt wird.

docker build -t apache-perl .

Erzeugen und Starten des Apache2/Perl-Containers

Bevor wir den eigentlichen Container erzeugen und starten müssen einige Verzeichnisse angelegt und teilweise auch gefüllt werden.

/root/docker/apache/html

Das Basisverzeichnis für HTML-Seiten des Apache2. Hier werden die Webinhalte abgelegt.
Da meine Webanwendungen samt und sonders Perl-Anteile haben verwend ich dieses Verzeichnis nicht und es bleibt bei mir immer leer.

mkdir /root/docker/apache/html

/root/docker/apache/cgi-bin

Das Basisverzeichnis für CGI-Skripte. In meinem Fall liegen hier die HTML-Seiten, Perl-Skripte, und, und, und.

mkdir /root/docker/apache/cgi-bin

/root/docker/apache/log

In diesem Verzeichnis werden die Logdateien des Apache2 angelegt und können hier eingesehen werden.

mkdir /root/docker/apache/log 

/root/docker/apache/sites-available

Hier werden die Konfigurationsdateien für HTTP (000-default.conf) und HTTPS (default-ssl.conf) abgelegt .
Beispiele habe ich weiter unten aufgeführt.

mkdir /root/docker/apache/sites-available 

/root/docker/apache/letsencrypt

In diesem Verzeichnis lege ich meine Zertifikate für die HTTPS-Verschlüsselung ab.

mkdir /root/docker/apache/letsencrypt


Los geht's

Nun wird es Zeit, aus dem gerade erzeugten Image einen Container zu erstellen. Dies kann aus der Kommandozeile oder aber auch durch ein Skript erfolgen.

 docker run -d \
   --name="apache2" \
   --restart=always \
   -v /root/docker/apache/html:/var/www/html \
   -v /root/docker/apache/cgi-bin:/usr/lib/cgi-bin \
   -v /root/docker/apache/log:/var/log/apache2 \
   -v /root/docker/apache/sites-available:/etc/apache2/sites-available \
   -v /root/docker/apache/letsencrypt:/etc/letsencrypt \
   -p 80:80 \
   -p 443:443 \
   -d apache-perl \
   /usr/sbin/startapache2.sh

Zeile 02

Hier wird der zukünftige Name des Docker Containers angegeben.

Zeile 03

In dieser Zeile wird das Restartverhalten angegeben. Ich stelle es so ein, dass der Container immer automatisch neu gestartet wird.

Zeilen 04 - 08

Mappen der Volumes zu lokalen Verzeichnissen.

Zeilen 09 - 10

Mappen der externen zu den internen TCP-Ports.

Zeile 11

Das Image das als Grundlage für diesen Container verwendet wird.

Zeile 12

Hier steht der eigentliche Startbefehl - das Startskript

Beispiel für "000-default.conf"

<VirtualHost *:80>

  ServerAdmin webmaster@localhost

  DocumentRoot /usr/lib/cgi-bin/

  <Directory />
    Options FollowSymLinks
    AllowOverride None
  </Directory>

  <Directory "/usr/lib/cgi-bin">
    Options +Indexes +ExecCGI -MultiViews +FollowSymLinks +Includes -SymLinksIfOwnerMatch
    AddHandler default-handler .xml .tar .rom .d64 .prg .txt .png .js .gif .jpg .htm .html .jar .css
    AddHandler cgi-script .cgi .pl .sh
    AllowOverride None
    Order allow,deny
    allow from 192.168.0.0/255.255.255.0
  </Directory>

  #ErrorLog /var/log/apache2/error.log
  ErrorLog /dev/null

  #CustomLog /var/log/apache2/access.log combined
  CustomLog /dev/null combined

  ServerName localhost

</VirtualHost>

Beispiel für "default-ssl.conf"

<VirtualHost *:443> 

  ServerAdmin webmaster@localhost

  DocumentRoot /usr/lib/cgi-bin/

  # SSL
  SSLEngine On
  SSLCipherSuite HIGH:MEDIUM

  <Directory /> 
    Options FollowSymLinks
    AllowOverride None
  </Directory> 

  <Directory "/usr/lib/cgi-bin"> 
    Options +Indexes +ExecCGI -MultiViews +FollowSymLinks +Includes -SymLinksIfOwnerMatch
    AddHandler default-handler .xml .tar .rom .d64 .prg .txt .png .js .gif .jpg .htm .html .jar .css
    AddHandler cgi-script .cgi .pl
    AllowOverride None
    Satisfy Any
    Order allow,deny
    allow from 192.168.0.0/255.255.255.0
    AuthType Basic
    AuthName "Geschuetzter Bereich"
    AuthUserFile /usr/lib/cgi-bin/htpasswd
    Require valid-user
  </Directory> 

  #ErrorLog /var/log/apache2/error_ssl.log
  ErrorLog /dev/null

  #CustomLog /var/log/apache2/access_ssl.log combined
  CustomLog /dev/null combined

  ServerName localhost
  SSLCertificateFile /etc/letsencrypt/fullchain.pem
  SSLCertificateKeyFile /etc/letsencrypt/privkey.pem
  Include /etc/letsencrypt/options-ssl-apache.conf
  
</VirtualHost>