DevOps Tutorial Teil-3: Webserver aufsetzen
| DevOps TYPO3
Dieses Thema hat mich am längsten beschäftigt, da es keine Anleitung im Netz gibt die einem im Gesamtpaket erklärt wie man den reverse Proxy mit seinen Projekten entspannt über SSL zum Laufen bringt und da ich das alles in meiner Freizeit recherchiere ist die Motivation auch nicht immer da… Anyways dies wird ein längerer Beitrag, weil hier gibt es verdammt viel zum tun… Zu aller erst brauchen wir wieder einen eigenen Projekt Ordner und legen im Root unsere docker-compose.yaml an. Dann können wir schon beginnen.
NGINX installieren
Erweitern der docker-compose.yaml um NGINX
version: ‘3’
services:
nginx:
container_name: bokunowebsite
networks:
- reverse-proxy
image: nginx:latest
restart: unless-stopped
volumes:
- ./nginx/conf/:/etc/nginx/conf.d/
- ./data/:/var/www/html/
- ./logs/nginx/:/var/log/nginx/
networks:
reverse-proxy:
external:
name: reverse-proxy
Hier schnell zusammengefasst: Wir haben den Service „NGINX“ definiert, der über den Namen „bokunowebsite“ im „reverse-proxy“-Netzwerk erkennbar ist. Da wir keine spezielle Konfiguration benötigen, ziehen wir das Image direkt von DockerHub (nginx:latest). Der Container versucht sich selbst neu zu starten, wenn er aufgrund eines Fehlers abstürzt, es sei denn, wir stoppen ihn. Unter „volumes“ geben wir ihm drei Ordner mit, die wir synchron halten wollen. Ein Docker-Container verliert nämlich alle seine Daten, wenn er neu gebaut wird. Speichern wir diese Daten jedoch in einem Volume, werden sie synchronisiert und wir können Datenverlust vorbeugen. Das Mapping ist immer lokal. Im Verzeichnis ./nginx/conf/ werden wir die Konfigurationsdatei unseres VHosts erstellen. Im Verzeichnis ./data/ liegt TYPO3 und unter ./logs/ wird das Error- und Access-Log abgelegt. Unter „networks“ geben wir nur kurz Bescheid, wo unser Webserver für den Reverse Proxy erreichbar ist.
PHP installieren
Erweitern der docker-compose.yaml um PHP
version: ‘3’
services:
nginx:
container_name: bokunowebsite
networks:
- internal
- reverse-proxy
image: nginx:latest
restart: unless-stopped
volumes:
- ./nginx/conf/:/etc/nginx/conf.d/
- ./data/:/var/www/html/
- ./logs/nginx/:/var/log/nginx/
php:
container_name: bokunophp
networks:
- internal
build:
context: .
dockerfile: php/Dockerfile
working_dir: /var/www/html
restart: unless-stopped
volumes:
- ./data/:/var/www/html/
- ./logs/php/:/var/log/fpm-php.www.log
- ./php/conf/:/usr/local/etc/php/conf.d/
networks:
internal:
driver: bridge
reverse-proxy:
external:
name: reverse-proxy
Nun haben wir zusätzlich zu nginx den PHP-Service deklariert. Neben dem, was ich zu NGINX bereits erklärt habe, gibt es nun einen Punkt build, der auf das Dockerfile zeigt, welches für den Build des PHP-Services verwendet wird. Das working_dir gibt an, in welchem Pfad wir uns im Docker-Container befinden, wenn der Build-Prozess ausgeführt wird. Der networks-Block wurde ebenfalls um den Bereich internal erweitert, der eine Netzwerkbrücke zwischen den Containern bildet. Dadurch können NGINX und PHP-FPM miteinander kommunizieren, ohne dass beide Services im selben Container betrieben werden müssen. Dies ermöglicht es uns auch, PHP-Versionen einfach zu wechseln. Man könnte sogar noch einen Schritt weiter gehen und die PHP-Versionen sowie den Reverse Proxy generell auslagern, was jedoch vom spezifischen Use Case abhängt, insbesondere wegen der zusätzlichen Services im PHP, die einzeln zu installieren sind. Zu den Volumes ist noch das Logging von PHP hinzugekommen, sowie der Pfad ./php/conf/, in dem unsere php.ini und die opcache.ini liegen.
php/Dockerfile
# Step 1: build PHP
FROM php:8.3-fpm
# Step 2: setting Workdir
WORKDIR /var/www/html
# Step 3: Linux Package Manager stuff
RUN apt-get update -y
RUN apt-get -y install gcc make autoconf libc-dev pkg-config libzip-dev
#Step 3.1: Libraries
RUN apt-get install -y --no-install-recommends \
git \
libz-dev \
libpq-dev \
libxml2-dev \
libmemcached-dev \
libldap2-dev libbz2-dev \
zlib1g-dev libicu-dev g++ \
libssl-dev libssl-doc libsasl2-dev \
curl libcurl4-openssl-dev
RUN apt-get install -y --no-install-recommends \
libgmp-dev firebird-dev libib-util
RUN apt-get install -y --no-install-recommends \
re2c libpng++-dev libwebp-dev libjpeg-dev libjpeg62-turbo-dev libpng-dev libxpm-dev libvpx-dev libfreetype6-dev
RUN apt-get install -y --no-install-recommends \
python3-lib2to3 libmagick++-dev libmagickwand-dev imagemagick
RUN apt-get install -y --no-install-recommends \
zlib1g-dev libgd-dev \
unzip libpcre3 libpcre3-dev \
libxslt-dev \
libtidy-dev libxslt1-dev libmagic-dev libexif-dev file \
libmhash2 libmhash-dev libc-client-dev libkrb5-dev libssh2-1-dev \
poppler-utils ghostscript libmagickwand-6.q16-dev libsnmp-dev libedit-dev libreadline6-dev libsodium-dev \
freetds-bin freetds-dev freetds-common libct4 libsybdb5 tdsodbc libreadline-dev librecode-dev libpspell-dev libonig-dev
#Step 4: Install PHP Extensions
#Step 4.1: install GD
RUN docker-php-ext-configure gd --with-jpeg --with-xpm --with-webp --with-freetype && \
docker-php-ext-install -j$(nproc) gd
#Step 4.2: install other php extensions
RUN docker-php-ext-install -j$(nproc) intl zip opcache mysqli exif
#Step 4.3: install other php extensions via pecl
RUN pecl install memcached && docker-php-ext-enable memcached
RUN pecl install apcu && docker-php-ext-enable apcu --ini-name docker-php-ext-10-apcu.ini
#Step 5: copy files into container for further processing
COPY data/ ./
#Step 5.1: create emty writable var dir
RUN rm -rf ./var/*
RUN mkdir -p ./var
RUN mkdir -p ./public/typo3temp
RUN mkdir -p ./public/fileadmin
#Step 5.2:permissions for www-data
RUN chown -R www-data:www-data \
./config/system/settings.php \
./public/typo3temp \
./config \
./public/fileadmin
RUN chown www-data:www-data \
/var/www/html \
./public \
./var
#Step 6: switch to www-data
USER www-data:www-data
Die Kommentare im Dockerfile geben im Prinzip alle nötigen Erklärungen. Zuerst beziehen wir PHP 8.3-fpm aus dem offiziellen PHP-Repository auf DockerHub. Das Working Directory wird auf /var/www/html gesetzt, um sicherzustellen, dass alle folgenden Befehle in diesem Verzeichnis ausgeführt werden. Die Schritte 3 und 4 installieren ausschließlich die Bibliotheken, die TYPO3 benötigt. Da die Berechtigungen bei dieser Installation stets ein zentrales Thema waren, werden sie im Schritt 5 gesetzt. Zunächst müssen die Dateien in den Docker-Container kopiert werden, damit sie für die nachfolgenden Befehle zugänglich sind. Anschließend stellen wir sicher, dass die erforderlichen Verzeichnisse für TYPO3 vorhanden sind und passen die Berechtigungen abschließend mit chown auf den Benutzer www-data:www-data an. Die übrigen Berechtigungen können dann über das TYPO3-Backend automatisch gesetzt werden. Im Schritt 6 wechseln wir den Benutzerkontext von root auf www-data:www-data, um sicherzustellen, dass der Container zur Laufzeit nicht mit Root-Rechten ausgeführt wird (#security).
PHP.ini in ./php/conf/php.ini
In der php.ini geben wir TYPO3-Einstellungen wie max_execution_time und max_input_vars an. Zusätzlich registrieren wir auch die Extensions, die wir im Dockerfile installiert haben.
#debug
log_errors = On
error_log = /var/log/php_errors.log
#website stuff
upload_max_filesize=20M
post_max_size=20M
always_populate_raw_post_data=1
max_execution_time=1200
max_input_vars=1500
memory_limit=512M
#extensions
extension=intl.so
extension=gd.so
extension=zip.so
extension=mysqli.so
zend_extension=opcache.so
opcache.ini in ./php/conf/opcache.ini
Da wir uns um das Caching selbst kümmern müssen, ist hier eine vorgefertigte opcache.ini, die jeweiligen Befehle werden als Kommentar erklärt.
; Determines if Zend OPCache is enabled
opcache.enable=1
; Determines if Zend OPCache is enabled for the CLI version of PHP
opcache.enable_cli=1
; The OPcache shared memory storage size.
opcache.memory_consumption=128
; The amount of memory for interned strings in Mbytes.
opcache.interned_strings_buffer=8
; The maximum number of keys (scripts) in the OPcache hash table.
; Only numbers between 200 and 1000000 are allowed.
opcache.max_accelerated_files=4000
; The maximum percentage of "wasted" memory until a restart is scheduled.
opcache.max_wasted_percentage=10
; When disabled, you must reset the OPcache manually or restart the
; webserver for changes to the filesystem to take effect.
opcache.validate_timestamps=1
; How often (in seconds) to check file timestamps for changes to the shared
; memory storage allocation. ("1" means validate once per second, but only
; once per request. "0" means always validate)
opcache.revalidate_freq=60
;If disabled, all documentation comments will be discarded from the opcode
;cache to reduce the size of the optimised code. Disabling this configuration
;directive may break applications and frameworks that rely on comment parsing
;for annotations, including Doctrine, Zend Framework 2 and PHPUnit.
opcache.save_comments=1
;The amount of shared memory to reserve for compiled JIT code. A zero value disables the JIT.
opcache.jit_buffer_size=256M
Composer
Erweitern der docker-compose.yaml um Composer
Um TYPO3 und seine Erweiterungen zu installieren, ergänzen wir nach dem erfolgreichen Start von NGINX und PHP nun Composer in unsere docker-compose.yaml:
services:
. . .
composer:
container_name: bokunocomposer
networks:
- internal
build:
context: .
dockerfile: composer/Dockerfile
working_dir: /build
volumes:
- ./data/:/build/
- ./.ssh/:/root/.ssh/
. . .
Im Wesentlichen ändert sich hierbei nichts Neues, außer dass wir die SSH-Schlüssel dem Container übergeben, damit wir auf unsere privaten Repositories zugreifen können. Der interessante Teil passiert im Dockerfile:
composer/Dockerfile
FROM composer:latest
WORKDIR /build
# Install required PHP extensions by TYPO3
RUN install-php-extensions intl gd mysqli exif
CMD [ "composer", "install", "--no-dev --optimize-autoloader --classmap-authoritative --apcu-autoloader --no-interaction" ]
CMD [ "composer", "dumpautoload", "--optimize --classmap-authoritative --apcu" ]
Wir verwenden die neueste Version von Composer. Dies muss eventuell angepasst werden, falls eine ältere PHP-Version verwendet wird. Derzeit wird PHP 8.3 ausgeliefert. Anschließend installieren wir schnell die erforderlichen PHP-Erweiterungen, die von Composer verlangt werden, wenn sie fehlen, und dann führen wir die einzelnen Composer Befehle aus. Zuerst composer install für die Installation der Pakete und danach composer dumpautoload um den autload neu zu erzeugen.
PHP-Pakete im nachhinein installieren / aktualisieren
Container leben nur so lange, wie sie gebraucht werden. Composer lebt zum Beispiel nur, bis er mit der Installation fertig ist, danach wird er wieder deaktiviert. Daher können wir Updates ganz einfach durchführen, indem wir folgenden Befehl verwenden:
docker-compose run composer
Dadurch wird der Container neu gestartet und alle Pakete installiert, die in der composer.lock Datei definiert sind.
Node / NPM (optional)
Erweitern der docker-compose.yaml um NPM
Mit der Extension ssch/typo3-encore ist es möglich kompilierte CSS und JS Dateien die mittels NPM und Webpack kompiliert werden zu integrieren.
services:
. . .
node:
container_name: bokunonpm
networks:
- internal
image: node:latest
working_dir: /build
volumes:
- ./data/:/build/
In diesem Abschnitt ist kein Dockerfile erforderlich, da keine Systemabhängigkeiten vorhanden sind, die zur Ausführung des Containers benötigt werden.
Interaktionen
Nun haben wir zwar den Container, dieser macht jedoch nichts, da wir kein Dockerfile hinterlegt haben. Daher müssen wir die Befehle händisch starten mit:
# Installieren von NPM Paketen nach node_modules
docker-compose run node npm ci
# Build JS und SCSS Files nach Definition in webpack.config.js
docker-compose run node npm run build
Container starten
Jetzt haben wir alles definiert und konfiguriert, aber der gesamte Spaß ist noch nicht online. Daher bauen wir jetzt den Container und starten ihn als Service:
docker-compose build
docker-compose up -d
Wenn alles erfolgreich durchläuft, ist unser Webserver nun im Reverse-Proxy-Netzwerk unter dem Hostnamen "bokunowebsite" erreichbar. Warum das wichtig ist, erkläre ich im nächsten Kapitel, insbesondere wenn wir unsere Webseite mittels SSL-Zertifikat über HTTPS betreiben wollen.
Gib mir Feedback!
Hab ich Blödsinn geschrieben oder etwas vergessen? Dann zögere nicht weiter und lass es mich sofort wissen indem du hier einen Kommentar hinterlässt!