Im ersten Teil habe ich über die Vor- und Nachteile eines Docker Setups gesprochen und wie wir die Container aufteilen. In diesem Teil möchte ich näher auf Traefik, Shopware und dessen Datenbank eingehen.
Traefik
Traefik ist ein Reverse Proxy, welcher sich speziell an den Einsatz in der Docker Entwicklung richtet. Sicherlich kann man hier auch mit NGINX etc. arbeiten, aber Traefik bringt einige nette Features mit sich, mit dem eine Weiterleitung in nur wenigen Minuten ohne großes Vorwissen und aufwendige Konfiguration erreicht werden kann.
Wie einfach das geht seht Ihr in der aktuellen docker-compose.yml die ich einsetze:
version: '3.5'
services:
reverse-proxy:
image: traefik:v2.0
command: --api.insecure=true --providers.docker
ports:
- "80:80"
- "8889:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- traefik
networks:
traefik:
Wir nutzen hier die Version 2.0 – noch relativ neu, aber funktioniert soweit einwandfrei. Als provider ist hier docker gewählt – ports könnt Ihr nach gusto frei wählen. Ich habe mich hier für den Standardport 80 entschieden, so muss ich später im Projekt nicht ständig an den Port denken. In dem Fall allerdings nur wichtig für das Backend.
Also volume wird hier direkt der docker Socket gemounted und das Netzwerk heißt traefik. Also wie sieht das ganze dann aus wenn wir es mit docker-compose up -d starten?
Hier haben wir dann einen Überblick welche Services aktiv sind und wie deren Status ist.
Info: Das ganze setzt auf Vue.Js auf und kann als PWA installiert werden.
.htaccess Schutz
Nicht alle möchten, dass die Testumgebungen live sichtbar sind. Wer die URL kennt, kann sich theoretisch auf dem Shop umschauen. Für solche Fälle bietet Traefik sogenannte Middlewares für das bearbeiten von Requests. In unserem Fall nutzen wir dafür dann Basic Auth – so erscheint dann die übliche Login Maske. Das ganze packen wir einfach mit in unsere labels:
-"traefik.http.middlewares.thisauth.basicauth.users=swiss:$$apr1$$nb92341Rk$$swy9oAuHcz8YFXYdzwBwR1"
- "traefik.http.routers.${CONTAINER_PREFIX}_php73.middlewares=thisauth@docker"
Wichtig: Escaped eure $ Zeichen beim MD5 Hash mit einem $ Zeichen 😉
Shopware
Shopware ist in unserem Fall die Applikation die wir mit mehreren Versionen „parallel“ laufen lassen möchten. Dafür nutze ich ein kleines Shellscript welches Dinge wie ein git clone, composer update, git checkout etc.pp. ausführt.
Ihr wollt also einen Ordner mit euren Frameworkdaten im Projekt haben. Bei mir ist das der shopware Ordner in dem ich ein Git Repository clone und dort den jeweiligen Branch auschecke (in unserem Fall den Branch fr80)
bash build.sh fr80
#!/bin/bash
DIR="$( cd "$( dirname "$0" )" && pwd )"
CONTAINER=$1
DOMAIN="reitsport.ch"
# comment will simply generate the shell output Headline
function comment {
test=${#1}
init="###"
while [ $test -gt 0 ]
do
init=$init"#"
test=$((test-1))
done
echo -e "\n"$init"###"
echo -e "## $1 ##"
echo -e $init"###\n"
}
# Start traefik reverse proxy
function traefik {
cd $DIR/traefik && docker-compose up -d
cd $DIR || exit
}
# prepares some configs
function prepare-start {
# If you want to clone your repo or do other stuff
# git clone YOURREPOSITORY
# cd $DIR/shopware && git checkout -b "$CONTAINER"
# Copy config with correct database host
sed "s/PLACEHOLDER/$CONTAINER\_db_1/g" $DIR/php/config.php > $DIR/php/tmp_config.php
cp $DIR/php/tmp_config.php $DIR/shopware/config.php
# Set SQL statement for url matching the feature
SQL=("UPDATE s_core_shops SET host = REPLACE(host, 'www'," "'$CONTAINER'" ") WHERE 1;")
echo "${SQL[@]}" > $DIR/mysql/dumps/4local.sql
}
# load or unload container
function compose {
if [[ $1 = "up" ]]; then
CONTAINER_PREFIX=$CONTAINER docker-compose --project-name $CONTAINER up -d
else
CONTAINER_PREFIX=$CONTAINER docker-compose --project-name $CONTAINER down
fi
# delete old cache
rm -r $DIR/shopware/var/cache/production_*/
}
# set or unset host
function host {
if [[ $1 = "set" ]]; then
#Add host entry to match request on localhost
echo "127.0.0.1 $CONTAINER.$DOMAIN" >> /etc/hosts
else
sed -i "/127.0.0.1 $CONTAINER.$DOMAIN/d" /etc/hosts
fi
}
# Main output
comment "Shopware Docker Builder"
ESC=$(printf "\e")
PS3="$ESC[44m $ESC[97m $ESC[1m Please choose your options: $ESC[0m"
options=("Start" "Stop" "Stop all" "Quit")
select opt in "${options[@]}"
do
case $opt in
"Start")
comment "Start container $1"
traefik
prepare-start
host set
compose up
break
;;
"Stop")
comment "Stop container $1"
host unset
compose down
break
;;
"Stop all")
comment "Stop all containers"
docker stop $(docker ps -a -q)
break
;;
"Quit")
echo "Bye,bye..."
exit
;;
*) echo invalid option;;
esac
done
Die compose Funktion enthält hier die wichtigste Zeile:
CONTAINER_PREFIX=$CONTAINER docker-compose --project-name $CONTAINER up -d
zu finden. Den CONTAINER_PREFIX nutzen wir in unsere docker-compose.yml und ist wie auch jeder Branch in Git einzigartig.
version: '3.5'
services:
db:
build: mysql
environment:
MYSQL_ROOT_PASSWORD: app
MYSQL_USER: app
MYSQL_PASSWORD: app
MYSQL_DATABASE: app
expose:
- 3360
networks:
- traefik_traefik
#restart: always
volumes:
- ./mysql/dumps:/docker-entrypoint-initdb.d
php73:
build: php
links:
- db
volumes:
- ./shopware:/var/www/html
depends_on:
- db
networks:
- traefik_traefik
labels:
- "traefik.http.routers.${CONTAINER_PREFIX}_php73.rule=Host(`${CONTAINER_PREFIX}.reitsport.ch`)"
networks:
traefik_traefik:
external: true
Info: Das Netzwerk heißt traefik_traefik, da es im traefik Unterordner des Projektes sitzt.
Nach dem Start des Shellscripts mit sudo bash build.sh fr5 wird also traefik hochgefahren (falls noch nicht aktiv), die Container geladen und die Datenbanken importiert. Dazu im nächsten Punkt mehr.
Das ganze sieht dann in Phpstorm z.B. so aus:
Datenbank
Wer schon mal mit Shopware oder Magento gearbeite hat, weiß wie wichtig die Datenbank ist (ordernumber counters, Domains in der s_core_shops etc.). Ich habe hier einige SQL Dateien in das Repo hinzugefügt und einige nicht:
grant.sql
GRANT ALL ON *.* TO 'app'@'%';
dynamic.sql
-- Deactivate all cronjobs, remove SSL and make it noindex if it accidentally goes in the interwebs
UPDATE s_crontab SET active = 0 WHERE 1;
UPDATE s_core_shops SET secure = 0 WHERE 1;
UPDATE s_core_snippets SET value = 'noindex,nofollow' WHERE namespace = 'frontend/index/header' AND name = 'IndexMetaRobots';
-- Add custom updates or deletes here
UPDATE s_core_snippets SET value = '' WHERE name = 'scGoogleTagManager';
UPDATE s_core_snippets SET value = '' WHERE name = 'scEmarsysId';
structure.sql enthält die Datenbankstruktur und data.sql die eigentlichen Daten (also inserts). local.sql wird vom Shellscript mit einer einfachen Zeile für die Domain befüllt:
UPDATE s_core_shops SET host = REPLACE(host, 'www', 'fr80') WHERE 1;
Info: Die Zahlen sind notwendig um die automatischen Ausführung vom Mysql Container zu steuern. Alles was in docker-entrypoint-initdb.d steht wird nämlich automatisch importiert.
Und was bringt das ganze? || Zwischenfazit
Warum nicht einfach mit der lokalen Datenbank arbeiten und Features wie gewohnt über git ein und auschecken.
- Stets ein sauberes System bei allen Entwicklern
- Immer die neusten Daten aus der Datenbank
- Jedes Feature hat eine eigene Datenbank Instanz und ist daher komplett entkoppelt
- Schnelles wechseln zwischen größeren Branches
Nächster Part:
Bisher haben wir uns das ganze nur im lokalen System angeschaut. Dafür ist das ganze schon ganz praktisch, aber noch einen größeren Impact hat das ganze für das Testen von Features und Bugfixes im daily business für Endkunden oder interne Tester. Dafür benötigen wir für die Automatisierung Gitlab und Rancher. Denn wir möchten ja nicht immer lokal deployen und das ganze manuell mit einem Script ausführen. Dies diente lediglich zur Veranschaulichung des Prozesses. Wir werden im Part 3 zunächst auf die Infrastruktur eingehen, welche wir mit Digital Ocean zur Verfügung stellen und danach auf die ersten Schritt von Gitlab eingehen.
zum Part 3
Hi Micha,
danke wieder einmal für diesen lesenswerten Beitrag!
Nur ein kleiner Hinweis: die Verlinkung auf „Part 3“ fehlt 🙂
Hast Du Erfahrungen in der Konstellation Docker+Traefik+Docker-sync.
Weil ohne docker-sync mit dem normalen volume-shares macht das unter Mac nicht wirklich Spass 🙂
Viele Grüße
Olli