
Shopware auf Kubernetes: Build, Test und Debugging
Tomasz GajewskiAktualisiert am 7 May 2025
Lesezeit: 13 Minuten
Mit zunehmender Skalierung steigt die Komplexität der Konfiguration einer Shopware-Infrastruktur deutlich an. Deshalb empfiehlt es sich, den gesamten Software-Stack – also Shopware mit allen erforderlichen Diensten – sowohl lokal als auch in der Continuous-Integration-Pipeline zu starten und zu testen. Besonders Integrations- und End-to-End-Tests sollten in einer sauberen, isolierten Umgebung laufen, um Zuverlässigkeit und Wiederholbarkeit sicherzustellen.
In diesem Artikel erläutere ich, wie du eine Shopware-6-Plattform für ein lokales Kubernetes-Cluster bauen und bereitstellen kannst und wie du PHP-Code direkt darin debuggst.
Alle Codebeispiele und Snippets in diesem Artikel stammen aus einem Proof-of-Concept-Projekt namens Shopware-Kube, dessen Hauptfunktionen sind:
- Erstellen von Container-Images mit Shopware-Anwendungsserver
- Nutzung von Minikube zur Einrichtung eines lokalen Kubernetes-Clusters
- Installation aller Komponenten, die Shopware im Cluster benötigt
- Deployment eines Shopware-Containers im Cluster
- Ausführen von Integrations- und End-to-End-Tests
Die meisten Codebeispiele habe ich zur Übersichtlichkeit gekürzt. Ich empfehle dir jedoch, den vollständigen Quellcode auf GitHub selbst zu analysieren.
Voraussetzungen für den Start mit Shopware auf Kubernetes lokal
Ich setze Grundkenntnisse über Kubernetes, seine Komponenten und die Architektur voraus.
Um mit dem Projekt zu starten, benötigst du folgende Tools auf deinem Rechner:
- Docker: Docker Engine auf Linux mit BuiltKit-Add-on oder Docker Desktop. Docker Desktop enthält BuiltKit bereits und unterstützt auch andere Betriebssysteme wie macOS und Windows.
- Kubernetes-Distribution, optimiert für die lokale Entwicklung. Meine Empfehlung ist Minikube. Es ist leichtgewichtig und für dieses Projekt vielseitig genug. Minikube läuft auf allen gängigen Betriebssystemen – Linux, macOS und Windows, sowohl auf x86- als auch ARM-Architektur (Windows ARM64 wird noch nicht unterstützt).
- Skaffold: Für effizientes Bauen, Testen und Deployen der Shopware-Anwendung. Skaffold setzt voraus, dass kubectl, kustomize und helm bereits installiert sind.
- Ktunnel: Zum Aufbau eines Reverse-Tunnels, der Xdebug-Verbindungen mit deinem Code-Editor ermöglicht.
Shopware-Implementierung zu Cloud-native E-Commerce transformieren
Cloud-native steht für eine Reihe von Praktiken zur Architektur, Entwicklung und Bereitstellung von Anwendungen auf Cloud-Infrastrukturen. Ein zentrales Ziel dieses Projekts ist es, eine horizontale Skalierung der Shopware-Anwendung zu ermöglichen – also die Verteilung auf mehrere Maschinen. Horizontale Skalierung ist ein entscheidender Schritt für Hochverfügbarkeit, um das Risiko von Ausfällen bei Deployments oder Anwendungsfehlern zu minimieren.
Hier kommen die Twelve-Factor-App-Prinzipien ins Spiel – ein Leitfaden für das Design cloud-nativer Anwendungen. Die Anwendung sollte stateless sein (keine temporären Dateien, außer ggf. auf tmpfs
), containerisiert und für schnellen Start sowie sauberes Herunterfahren optimiert werden.
Cloud-native Shopware-Konfiguration vorbereiten
Um dem 11. Faktor der Twelve-Factor-App (Logs als Streams) und dem 4. Faktor (angebundene Dienste) zu entsprechen und optimale Funktionalität im Cluster zu gewährleisten, benötigt Shopware eine spezielle Konfiguration für:
- Cache-Speicher
- Benutzersitzungen
- Medienspeicher
- Logs
Optimale Shopware-Cache-Konfiguration
Standardmäßig werden alle Cache-Daten in das lokale Dateisystem geschrieben, insbesondere in das Verzeichnis var/cache
. Für einfache Einzelserver-Setups ist das ausreichend. Bei Skalierung führt dies jedoch zu massiver Datenredundanz und Performance-Problemen, da jeder Container seinen eigenen lokalen Cache schreibt. Die Lösung ist eine Key-Value-Datenbank wie Redis, die von allen Shopware-Containern gemeinsam genutzt wird, um Anwendungs- und HTTP-Cache zu speichern. Eine typische Konfiguration sieht folgendermaßen aus:
# config/packages/framework.yaml
framework:
cache:
default_redis_provider: "%env(string:REDIS_CACHE_OBJECT_URL)%"
app: cache.adapter.redis_tag_aware
system: cache.adapter.system
pools:
cache.tags:
adapter: cache.app
cache.object:
default_lifetime: '3600'
adapter: cache.app
tags: cache.tags
cache.http:
default_lifetime: '7200'
adapter: cache.adapter.redis_tag_aware
provider: "%env(string:REDIS_CACHE_HTTP_URL)%"
tags: cache.tags
Beachte, dass für verschiedene Cache-Arten eigene Variablen verwendet werden. Das ermöglicht die Nutzung mehrerer Redis-Instanzen oder mehrerer Datenbanken innerhalb einer Instanz.
Redis als Session-Speicher einbinden
Wie beim Cache sollte auch der Standard-Session-Speicher (Dateisystem) bei Skalierung nicht eingesetzt werden. Bei mehreren HTTP-Anfragen eines Nutzers kann jede Anfrage von einem anderen Shopware-Container verarbeitet werden – der Nutzer würde so bei fast jedem Seitenaufruf eine neue Session starten und könnte sich nicht einloggen oder Produkte in den Warenkorb legen. Die Lösung ist eine zentrale Datenbank wie Redis. Redis bietet als In-Memory-Datenbank eine bessere Performance als relationale Datenbanken wie MySQL oder Netzwerkdateisysteme wie NFS oder Ceph. Um Redis für Sessions zu nutzen, ergänze Folgendes:
# config/packages/framework.yaml
framework:
...
session:
handler_id:
"%env(string:REDIS_SESSION_URL)%"
...
Shopware für S3-kompatiblen Objektspeicher für Medien konfigurieren
Objektspeicher mit S3-kompatibler API ist der Standard für cloud-native Speicherung von Mediendateien. Viele Anbieter haben dies inzwischen im Portfolio, und du bist nicht auf große Cloud-Provider beschränkt. Es gibt auch Open-Source-Alternativen zum Self-Hosting wie MinIO oder Rook. Für das Shopware-Kube-Projekt habe ich MinIO gewählt, da es sich in einem Single-Node-Cluster besonders einfach konfigurieren lässt.
Hier ein Beispiel für die S3-Speicherkonfiguration öffentlicher und privater Dateien in Shopware:
# config/packages/shopware.yaml
shopware:
...
filesystem:
public: &public-filesystem
type: "amazon-s3"
url: "%env(string:BUCKET_URL_PUBLIC)%"
config:
endpoint: "%env(string:BUCKET_ENDPOINT)%"
region: "%env(string:BUCKET_REGION_PUBLIC)%"
bucket: "%env(string:BUCKET_NAME_PUBLIC)%"
credentials:
key: "%env(string:AWS_ACCESS_KEY_ID)%"
secret: "%env(string:AWS_SECRET_ACCESS_KEY)%"
use_path_style_endpoint: true
options:
visibility: "public"
private: &private-filesystem
type: "amazon-s3"
config:
endpoint: "%env(string:BUCKET_ENDPOINT)%"
region: "%env(string:BUCKET_REGION_PRIVATE)%"
bucket: "%env(string:BUCKET_NAME_PRIVATE)%"
credentials:
key: "%env(string:AWS_ACCESS_KEY_ID)%"
secret: "%env(string:AWS_SECRET_ACCESS_KEY)%"
use_path_style_endpoint: true
options:
visibility: "private"
theme: *public-filesystem
sitemap: *public-filesystem
asset: *public-filesystem
...
Das obige Beispiel geht davon aus, dass Theme- und Asset-Dateien im Public-Bucket gespeichert werden (Shopware-Standard). Daher musst du das Storefront-Theme kompilieren und neue Assets bei jedem Deployment ins Cluster kopieren. Dies lässt sich mit einer Kubernetes-Job-Ressource erledigen, die bei jedem Deployment erstellt, ausgeführt und danach (bei Erfolg) gelöscht wird. Der Job muss den shopware install-Befehl wie im folgenden Beispiel ausführen:
# deploy/bases/app/shopware-init.yaml
...
if bin/console system:is-installed; then
echo "Running Shopware updates."
bin/console system:install
else
echo "Running Shopware first time install."
bin/console system:install --basic-setup
fi
...
Shopware-Logs konfigurieren
Stelle dir dutzende oder hunderte Container vor, die jeweils Logs generieren. Es ist nahezu unmöglich, alle zu durchsuchen, wie es bei einer Einzel-Server-Anwendung möglich wäre. Theoretisch könntest du ein gemeinsames Volume für Logs nutzen, aber das führt zu Performance-Problemen durch Netzwerk-Latenzen und Dateisperren bei gleichzeitigen Zugriffen. Cloud-native Logging bedeutet, Logs zentral zu indexieren und über ein Dashboard auffindbar zu machen. So lassen sich Logeinträge aggregieren, Anomalien erkennen und Alarme auslösen.
Damit Logs indexiert werden können, müssen sie an stdout und stderr statt in Dateien geschrieben werden. So kann ein Log-Indexer sie abgreifen. Diese Konfiguration erfüllt den 11. Faktor der 12-Factor-App. Empfehlenswert ist zudem das JSON-Format, das die Auswertung und Analyse vereinfacht. Eine Beispielkonfiguration für Monolog in Shopware sieht so aus:
# config/packages/monolog.yaml
monolog:
handlers:
main:
type: fingers_crossed
excluded_http_codes: [404, 405]
action_level: error
handler: nested
nested:
type: stream
path: "php://stderr"
level: error
formatter: 'monolog.formatter.json'
console:
type: console
process_psr_3_messages: false
channels: ["!event", "!doctrine"]
formatter: 'monolog.formatter.json'
Lokales Kubernetes-Cluster mit Minikube erstellen
Vor dem Deployment von Shopware musst du einige Minikube-Addons installieren:
- storage-provisioner: Aktiviert die automatische Bereitstellung von Persistent Volumes
- default-storageclass: Aktiviert die Standard-Storage-Class (hostpath im lokalen Dateisystem)
- ingress: Minikube-optimierter NGINX Ingress Controller
- ingress-dns: Ein dedizierter DNS-Server für Ingresses
Führe das Skript create_cluster.sh aus, um ein Minikube-Kubernetes-Cluster mit allen genannten Addons zu erstellen.
./create_cluster.sh
Lokale Test-Domains registrieren
Folgende lokale Domains für die Shopware-Installation müssen eingerichtet werden:
shopware.test
(für das Storefront)media.test
(für die öffentlichen Medien)
Der einfachste Weg ist, die gewünschten Domains in die Hosts-Datei deines Systems einzutragen. Je nach verwendetem Betriebssystem gehst du wie folgt vor:
echo $(minikube ip)' media.test shopware.test' | sudo tee -a /etc/hos
ts
- macOS
echo '127.0.0.1 media.test shopware.test' | sudo tee -a /etc/hosts
- Windows (
cmd
muss als Administrator ausgeführt werden)
echo 127.0.0.1 media.test shopware.test >> C:\Windows\system32\drivers\etc\hosts
Lokale Testdomains im Cluster verfügbar machen
Damit die Hosts media und storefront (shopware.test und media.test) auch innerhalb des Clusters aufgelöst werden können, müssen sie erreichbar sein. Dies ist beispielsweise für den Befehl theme:compile erforderlich.
Die von Minikube empfohlene Methode besteht darin, die CoreDNS-Konfiguration zu bearbeiten und die .test-Domains weiterzuleiten. Führe zunächst minikube ip aus, um die IP-Adresse des Minikube-Knotens anzuzeigen.
Öffne anschließend die CoreDNS ConfigMap zur Bearbeitung:
kubectl edit configmap coredns -n kube-system
Füge die DNS-Weiterleitungsregel für .test-Domains ein:
test:53 {
errors
cache 30
// forward . <minikube ip>
forward . 192.168.49.2
}
Die Forward-IP-Adresse entspricht dem Ergebnis von minikube ip
.
Die Konfiguration sieht dann beispielsweise folgendermaßen aus:
apiVersion: v1
data:
Corefile: |
.:53 {
log
errors
health {
lameduck 5s
}
ready
...
}
test:53 {
errors
cache 30
forward . 192.168.49.2
}
...
kind: ConfigMap
Shopware-Container-(Docker-)Image vorbereiten
Je kleiner das Container-Image und je weniger Bibliotheken enthalten sind, desto besser: Ein schlanker Container startet schneller und ist sicherer. Nach diesem Prinzip habe ich beschlossen, FrankenPHP als Anwendungsserver für Shopware auszuprobieren. Darin ist ein Caddy-HTTP-Server integriert.
Mit dem FrankenPHP-Builder lässt sich eine einzige ausführbare Binärdatei erstellen, die deine Shopware-Anwendung, alle Abhängigkeiten, PHP und den Caddy-Server enthält. Dieser Single-Binary-Ansatz führt zu einem kleineren und weniger komplexen Anwendungscontainer als PHP-FPM mit NGINX oder PHP-FPM mit Apache – besonders aus Sicherheitssicht ein Vorteil.
Dies wird durch das Projekt static-php-cli ermöglicht, mit dem sich Anwendungsexecutables bauen lassen. Da die gesamte Anwendung in einer einzigen Binärdatei enthalten ist, kann sie auf einem minimalen Container-Image wie debian-slim, alpine oder wolfi-base installiert werden. Die von den FrankenPHP-Mitwirkenden empfohlene Wahl ist allerdings Debian – daher habe ich mich dafür entschieden.
Shopware Docker-Image-Build
Das Dockerfile ist in folgende Schritte unterteilt:
- Einen Shopware-Builder vorbereiten.
- Mit dem Application Builder eine produktive Shopware-Anwendung bauen.
- Mit dem Application Builder eine Dev-Shopware-Anwendung bauen.
- PHP kompilieren und die Shopware-Anwendung als ausführbare Binärdatei erstellen.
- Das finale Produktions-Image erstellen, indem die Binärdatei der Anwendung darauf kopiert wird.
- Die Dev-Shopware-Anwendung in das Basis-Dev-image kopieren.
Shopware Docker-Container-Image-Build-Stufen
Deklariere die Build-Konfiguration in der Datei skaffold.yaml.
...
build:
artifacts:
- image: kiweeteam/franken-shopware
docker:
dockerfile: Dockerfile
target: app-prod
secrets:
- id: composer_auth
src: auth.json
- image: kiweeteam/franken-shopware-dev
docker:
dockerfile: Dockerfile
target: app-dev
secrets:
- id: composer_auth
src: auth.json
...
Die oben gezeigte Konfiguration stellt sicher, dass zwei Images erstellt werden – eines für die Produktion und eines für die Entwicklung. Sie erkennt dabei intelligent, ob im
Deployment der Kubernetes-Objekte auf beide Images verwiesen wird. Möchtest du beispielsweise die Dev-Anwendung bauen und bereitstellen, bietet Skaffold mit Profilen eine großartige Funktion. Wir definieren zwei Profile: dev und production, wobei production das Standardprofil ist.
# Standardprofil - Produktion
manifests:
kustomize:
paths:
- deploy/overlays/production
# Definition des Profils
profiles:
- name: dev
manifests:
kustomize:
paths:
- deploy/overlays/dev
- name: production
manifests:
kustomize:
paths:
- deploy/overlays/production
Diese Änderung stellt sicher, dass Kustomize das Dev-Shopware-Manifest nicht lädt und Skaffold das entsprechende Container-Image nicht erstellt.
skaffold build --file-output=.build-artifacts.json
Skaffold speichert Referenzen auf die Images in der
.build-artifacts.json
. Diese Datei wird für Deploy- und Verifizierungsbefehle benötigt.
Shopware im lokalen Kubernetes-Cluster bereitstellen
Nachdem du nun weißt, wie du ein Docker-Image deiner Shopware-Anwendung erstellst, gehen wir zum Deployment im lokalen Cluster mit allen Abhängigkeiten über:
- Operatoren und Controller, die Workloads wie folgt erstellen und steuern: HTTP-Reverse-Proxy (Ingress Controller), TLS-Zertifikatsmanager, Generator für zufällige Passwörter und Object-Storage-Manager (MinIO).
- Shopware-Backend-Dienste (MariaDB-Server, Redis, OpenSearch)
- Shopware-Anwendungsserver
- Shopware-Message-Scheduler und Consumer zur Ausführung asynchroner Backend-Aufgaben Shopware Kubernetes-Cluster – Übersicht
Shopware Kubernetes Cluster Übersicht
Projektstruktur für die Shopware-Bereitstellung
Die Struktur folgt dem Konzept von Bases und Overlays, das für Projekte empfohlen wird, die mit Kustomize mehrere Umgebungen wie Entwicklung, Test und Produktion verwalten. Als Argument wird der Pfad zu einem Overlay, beispielsweise „dev“, angegeben. Dieses Overlay umfasst die Basis-Manifeste und nimmt Anpassungen vor – etwa das Hinzufügen neuer, umgebungsspezifischer Ressourcen oder das Patchen beziehungsweise Löschen von Ressourcen aus den Bases.
In der Dev-Umgebung möchten wir beispielsweise einen Shopware-Container für die Entwicklung bereitstellen, diesen jedoch nicht in der Produktion einsetzen. Das Dev-Overlay enthält dafür ein zusätzliches Manifest, das den Shopware-Dev-Container bereitstellt.
Die Projektstruktur ist wie folgt aufgebaut:
deploy
├── bases
│ ├── app
│ ├── database
│ ├── opensearch
│ ├── redis
│ └── storage
└── overlays
├── dev
├── production
└── test
deploy/bases/app/app-server.yaml
Deployment des Shopware-Anwendungsservers im Produktivmodus.deploy/bases/app/ingress.yaml
Erstellt ein Ingress-Objekt, das den Applikationsservice und den Mediaservice mit lokalen Test-Domains bereitstellt: http://shopware.test und http://media.test.deploy/bases/app/task-scheduler.yaml
Deployment des Task-Schedulers, der für das Senden von Aufgaben an die Message Queue zuständig ist.deploy/bases/app/message-consumer.yaml
Deployment des Message-Consumers, der Aufgaben aus der Message Queue abarbeitet.deploy/bases/app/shopware-init.yaml
Initialisierungsjob, der bei jedem neuen Rollout ausgeführt werden muss – idealerweise vor dem Deployment von App-Server, Task-Scheduler und Message-Consumer. Er übernimmt folgende Aufgaben:- Installation oder Updates von Plugins und Apps
- Datenbankmigrationen
- Theme-Kompilierung
- Installation von Assets
- Grundinstallation von Shopware (frisch), falls die Datenbank in einem neu erstellten Kubernetes-Cluster leer ist
deploy/bases/database
Manifeste zur Bereitstellung eines MariaDB-Datenbankservers.deploy/bases/opensearch
Manifeste für den Basisbetrieb einer Single-Node-OpenSearch-Instanz.deploy/bases/redis
Manifeste für den Basisbetrieb einer Single-Node-Redis-Instanz. Es werden separate Instanzen für Cache und Kundensitzungen erstellt.deploy/bases/storage/minio-tenant.yaml
Stellt eine MinIO-Storage-Instanz (Tenant) bereit. Der MinIO-Operator muss bereits im Cluster installiert sein.deploy/overlays/dev/app/app-server-dev.yaml
Deployment des Shopware-Containers im Dev-Modus (mit aktiviertem Xdebug).deploy/overlays/dev
Quellverzeichnis für das Deployment der Dev-Anwendung.deploy/overlays/production
Quellverzeichnis für das Deployment im Produktionscluster.test/e2e/jobs/e2e.yaml
Job-Manifest für End-to-End-Tests auf Basis des Shopware Playwright Acceptance Tests Frameworks.test/e2e/jobs/step-summary.yaml
Job-Manifest, das die Zusammenfassung der End-to-End-Tests für GitHub Actions ausgibt.test/integration/jobs/integarion-tests.yaml
Job-Manifest zur Ausführung von Integrationstests.
Um zu prüfen, ob die Manifestdateien korrekt verarbeitet werden, führe die folgenden Befehle aus:
kustomize build deploy/overlays/dev
kustomize build deploy/overlays/production
Diese Befehle geben alle Kubernetes-Objekte aus, die auf den Cluster angewendet werden, oder zeigen eine Fehlermeldung, falls Probleme auftreten.
Konfiguration der Shopware-Deployments mit Skaffold
Die Deployment-Konfiguration in der skaffold.yaml besteht aus drei Teilen:
manifests:
Speicherort der Manifestdateien – deploy/overlays/dev für die Entwicklung,deploy/overlays/production
für die Produktiondeploy:
Einstellungen für das Deploymentdeploy.helm:
Liste der zu installierenden Helm-Charts
Beim Deployment verarbeitet Skaffold zuerst alle Helm-Charts und wendet anschließend die Manifeste an.
...
manifests:
kustomize:
paths:
- deploy/overlays/dev
deploy:
statusCheck: true
# Scheitern Sie den Einsatz, wenn er sich nicht innerhalb von 20 Minuten stabilisiert.
# Ein Kaltstart kann lange dauern, wenn viele Plugins aktiviert werden müssen.
statusCheckDeadlineSeconds: 1200
# Pods may initially start and crash but eventually self-heal. # This parameter allows for failures until # the deadline, set by the statusCheckDeadlineSeconds above.
tolerateFailuresUntilDeadline: true
kubectl:
defaultNamespace: shopware
helm:
# Installiere alle erforderlichen Operatoren
releases:
- name: kubernetes-secret-generator
repo: https://helm.mittwald.de
remoteChart: kubernetes-secret-generator
namespace: secret-generator
createNamespace: true
wait: true
version: 3.4.0
setValues:
image: # überschreibt die Standardwerte
registry: ghcr.io
# Dieses Image ist nicht offiziell, unterstützt aber sowohl amd64 als auch arm64.
repository: belodetek/kubernetes-secret-generator
tag: 0.0.4
...
Starten des Shopware-Deployments
Die Eingabedatei ist das Ergebnis zuvor gebauter Container-Images. Du kannst jedoch auch Build und Deployment mit einem einzigen Befehl ausführen:
skaffold deploy --build-artifacts=.build-artifacts.json
Zugriff auf Shopware im lokalen Kubernetes-Cluster per Webbrowser
Auf einem Linux-Host funktioniert alles direkt ohne weitere Anpassungen. Die Storefront ist erreichbar unter
Unter macOS und Windows musst du zusätzlich einen Tunnel öffnen, um die LoadBalancer-Services bereitzustellen.
minikube tunnel
Demostore-Startseite nach Projektstart und Öffnen des Tunnels
Integrationstests im Cluster ausführen
Integrationstests in der CI auf einem schlanken Kubernetes-Cluster sind besonders empfehlenswert, wenn auch deine Produktionsumgebung auf Kubernetes basiert. Dieses Vorgehen erhöht die Zuverlässigkeit der Tests und bezieht Kubernetes-spezifische Abhängigkeiten wie ConfigMaps, Secrets oder ServiceAccounts mit ein.
Um Integrationstests mit dem einfachen Befehl skaffold verify auszuführen, benötigst du einen benutzerdefinierten Job, der auf die in ConfigMaps und Secrets gespeicherten Shopware-Konfigurationen verweist.
Eine direkte Einrichtung in der skaffold.yaml ist nicht möglich. Nachfolgend findest du ein Beispiel für ein entsprechendes Job-Manifest:
# ./test/integration/jobs/integration-tests.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: integration-tests
spec:
backoffLimit: 4
template:
spec:
containers:
- name: integration-tests
# Verwenden Sie ein Dev-Image, auf dem phpunit vorinstalliert ist.
image: kiweeteam/franken-shopware-dev
envFrom:
- configMapRef:
name: shopware-app-config
- secretRef:
name: shopware-app-config
- secretRef:
name: database-credentials
env:
- name: APP_ENV
value: "test"
- name: SHOPWARE_ADMINISTRATION_PATH_NAME
value: "admin_$(SHOPWARE_ADMINISTRATION_PATH_SUFFIX)"
- name: DATABASE_URL
value: "mysql://$(MYSQL_USER):$(MYSQL_PASSWORD)@$(MYSQL_HOST):$(MYSQL_PORT)/$(MYSQL_DATABASE)"
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
key: CONSOLE_ACCESS_KEY
name: shopware-s3
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
key: CONSOLE_SECRET_KEY
name: shopware-s3
restartPolicy: Never
Als Nächstes fügst du einen neuen Verify-Job in der skaffold.yaml
hinzu. Das folgende Beispiel kann verwendet werden, um die Integrationstests des Demo-Plugins auszuführen.
# ./skaffold.yaml
...
verify:
...
- name: integration-tests
container:
name: integration-tests
image: kiweeteam/franken-shopware-dev
command:
- /bin/sh
args:
- -c
- |
set -e
echo "Running integration tests of shopware-demo-plugin..."
/shopware-bin php-cli vendor/bin/phpunit -c vendor/kiwee/shopware-demo-plugin/phpunit.xml
echo "Integration tests finished."
executionMode:
kubernetesCluster:
jobManifestPath: test/integration/jobs/integration-tests.yaml
...
Nutze die Datei .build-artifacts.json, die Referenzen auf die zuletzt mit dem Befehl skaffold build gebauten Container-Images enthält.
skaffold verify -a .build-artifacts.json
Alternativ kannst du Integrationstests auch mit kubectl ausführen. Der Dev-Shopware-Anwendungs-Pod (app-server-dev) muss jedoch bereits laufen, bevor du die Tests startest.
kubectl exec service/app-server-dev -n shopware -- /shopware-bin php-cli
vendor/bin/phpunit -c vendor/kiwee/shopware-demo-plugin/phpunit.xml
Wenn mehrere app-server-dev
-Pods vorhanden sind, spielt es keine Rolle, auf welchem die Tests ausgeführt werden – kubectl exec service
wählt einen beliebigen Pod aus, der dem Service zugeordnet ist.
Playwright End-to-End-Tests im Cluster ausführen
Für End-to-End-Tests gilt das gleiche Prinzip wie bei Integrationstests. Da hierbei nicht nur alle Services, sondern auch die Benutzeroberfläche einbezogen werden, lässt sich eine produktionsähnliche Umgebung simulieren.
Du kannst die Shopware Acceptance Test Suite nutzen, um Tests im Cluster einfach per skaffold verify-Befehl zu starten.
Zunächst erstellst du ein einfaches Testszenario, das Hinzufügen eines Produkts zum Warenkorb prüft.
# ./test/e2e/tests/CartTest.spec.ts
import {test, expect} from '../BaseTest';
test('Product detail test scenario',
async KiweeEu/({
ShopCustomer,
StorefrontProductDetail,
ProductData,
AddProductToCart
}) => {
await ShopCustomer.goesTo(StorefrontProductDetail.url(ProductData));
await ShopCustomer.attemptsTo(AddProductToCart(ProductData));
await ShopCustomer.expects(StorefrontProductDetail.offCanvasSummaryTotalPrice).toHaveText('€10.00*');
});
Die Playwright-Konfiguration muss die Storefront-URL, die Administrations-URL sowie die Admin-Zugangsdaten enthalten und sieht wie folgt aus:
# ./test/e2e/playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
process.env['SHOPWARE_ADMIN_USERNAME'] = process.env['SHOPWARE_ADMIN_USERNAME'] || 'admin';
process.env['SHOPWARE_ADMIN_PASSWORD'] = process.env['SHOPWARE_ADMIN_PASSWORD'] || 'shopware';
const defaultAppUrl = 'http://shopware.test/';
process.env['APP_URL'] = process.env['APP_URL'] ?? defaultAppUrl;
// make sure APP_URL ends with a slash
process.env['APP_URL'] = (process.env['APP_URL'] ?? '').replace(/\/+$/, '') + '/';
if (process.env['ADMIN_URL']) {
process.env['ADMIN_URL'] = process.env['ADMIN_URL'].replace(/\/+$/, '') + '/';
} else {
process.env['ADMIN_URL'] = process.env['APP_URL'] + 'admin_' + process.env['SHOPWARE_ADMINISTRATION_PATH_SUFFIX'] + '/';
}
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
timeout: 60000,
expect: {
timeout: 10_000,
},
retries: 0,
workers: process.env.CI ? 2 : 1,
reporter: process.env.CI ? [
['list'],
['@estruyf/github-actions-reporter', <GitHubActionOptions>{
title: 'E2E Test Results',
useDetails: true,
showError: true,
debug: true
}]
] : 'html',
use: {
baseURL: process.env['APP_URL'],
trace: 'retain-on-failure',
video: 'off',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
}
],
});
Abschließend benötigst du die Datei BaseTest.ts
, um alles aus dem Acceptance-Test-Suite-Framework zu exportieren, sowie die package.json zum Installieren aller Abhängigkeiten.
# ./test/e2e/BaseTest.ts
export * from '@shopware-ag/acceptance-test-suite';
// ./test/e2e/package.json
{
"dependencies": {
"@shopware-ag/acceptance-test-suite": "^11.6.0",
"playwright": "^1.10.0",
"@estruyf/github-actions-reporter": "^1.10.0"
}
}
Das Dockerfile zum Bau des Test-Docker-Images muss folgende Elemente enthalten:
- Installation der benötigten Bibliotheken und Abhängigkeiten
- Installation von Playwright
- Kopieren des Testcodes in das Container-Image
Führe anschließend den Build aus.
skaffold build --file-output=.build-artifacts.json
Die Option --file-output
erzeugt eine Datei, die die zuletzt gebauten Container-Images mit Namen und Tags aufgelistet – diese werden später benötigt.
Ein von Skaffold standardmäßig erstellter Test-Job enthält keine Referenzen auf die Shopware-Secrets im Cluster. Es ist jedoch möglich, ein benutzerdefiniertes Job-Manifest zu definieren, in dem du die für die Tests benötigten Secrets referenzierst.
# ./test/e2e/jobs/e2e.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: e2e
spec:
template:
spec:
...
containers:
- name: e2e
image: kiweeteam/shopware-e2e
# Shopware-Konfiguration und Geheimnisse für die e2e-Tests einbeziehen
envFrom:
- configMapRef:
name: shopware-app-config
- secretRef:
name: shopware-app-config
...
# ./skaffold.yaml
verify:
...
name: e2e
image: kiweeteam/shopware-e2e
...
executionMode:
kubernetesCluster:
jobManifestPath: test/e2e/jobs/e2e.yaml
Sobald alles eingerichtet und Shopware bereits im Cluster deployt ist, führe den Verify-Befehl aus und übergib die Datei mit dem Referenz-Image als -a
-Parameter.
skaffold verify -a .build-artifacts.json
Skaffold führt nun den End-to-End-Test-Job aus und zeigt die Testergebnisse an.
Wie debuggt man Shopware im Kubernetes-Cluster?
Eine umfassende Anleitung zum Debuggen einer Anwendung auf Kubernetes findest du in der Kubernetes-Dokumentation. Im Folgenden konzentrieren wir uns jedoch auf Shopware-spezifische Lösungen.
Wie kann man Shopware-Workloads inspizieren?
kubectl describe deployment/app-server -n shopware
Dieser Befehl zeigt vollständige Informationen zum aktuellen Status des Shopware-Deployments an, darunter:
- Die verwendete Deployment-Strategie
- Anzahl der Shopware-Pod-Replikate (gewünscht, verfügbar, nicht verfügbar, insgesamt)
- Liste der Init-Container und deren ausgeführte Befehle
- Details zum Shopware-Anwendungscontainer wie Image-Version, im Cluster freigegebene Ports, Befehle und Argumente, Ressourcenanforderungen und -limits (CPU, Speicher), verwendete ConfigMaps und Secrets sowie gemountete persistente Volumes
- Eine Historie der Statusmeldungen und Fehlermeldungen
Mit diesem Befehl erhältst du wertvolle Hinweise auf die Ursache eines fehlgeschlagenen Deployments.
Der describe
-Befehl kann auf jede Ressource angewendet werden, um detaillierte Informationen zum aktuellen Status zu erhalten – etwa auf Pods, Services, Ingresses und andere.
# Shopware-Service inspizieren
kubectl describe service/app-server -n shopware
# Alle Shopware-Pods mit Label app=shopware inspizieren
kubectl describe Pods -n shopware -l app=shopware
# Alle Ingresses im Shopware Namespace inspizieren
kubectl describe ingress -n shopware
Code-Debugging mit Xdebug im Kubernetes-Cluster
Um einen vollständigen Stacktrace nachvollziehen zu können, musst du die Vendor-Abhängigkeiten und das index.php-Skript von einem Dev-Pod auf dein lokales Dateisystem kopieren.
# Namen des Dev Pods von Shopware ermitteln
APP_DEV=$(kubectl get Pod -n shopware --no-headers -l app=shopware-dev -o=custom-columns=NAME:.metadata.name) && \# vendor/ und public/ aus dem app-server-dev-Container ins lokale Projektverzeichnis kopieren
kubectl cp shopware/${APP_DEV}:/app/vendor ./vendor -c app-server-dev && \
kubectl cp shopware/${APP_DEV}:/app/public ./public -c app-server-dev
Im nächsten Schritt aktivierst du Xdebug, um eine DBGP-Verbindung mit deiner IDE herzustellen. Verwende dazu das Tool ktunnel, um einen Reverse-Tunnel zwischen dem Shopware app-server-dev-Pod im Kubernetes-Cluster und deiner IDE zu öffnen.
tunnel inject deployment -n shopware app-server-dev 9003
Dieser Befehl injiziert einen Sidecar-Container, der einen Tunnel auf Port 9003 öffnet – dieser wird von Xdebug verwendet.
Xdebug-Verbindung mit Code-Editor
Zum Abschluss exponierst du den app-server-dev-Pod mit dem Port-Forward-Befehl, der den lokalen Port 8000 weiterleitet. Dadurch ist der Dev-server über http://localhost:8000 erreichbar. Achte darauf, diese URL auch dem Vertriebskanal hinzuzufügen, den du debuggen möchtest.
kubectl port-forward deploy/app-server-dev -n shopware 8000:8000
Füge die URL http://localhost:8000 dem Kanal zum Debuggen hinzu.
Die Storefront im Entwicklermodus, sobald Port 8000 an den app-server-dev weitergeleitet wurde.
PhpStorm / IntelliJ IDE für Debugging auf Kubernetes konfigurieren
Öffne: Settings > Languages & Frameworks > PHP > Servers
. Füge einen neuen localhost
-Server hinzu, falls dieser noch nicht vorhanden ist. Trage als Host localhost
und als Port 8000
ein. Aktiviere danach das Kontrollkästchen Use path mappings(...)
. Ordne anschließend das Verzeichnis custom
dem Pfad /app/custom
zu, public
zu /app/public
und vendor
zu /app/vendor
.
Nach dem Speichern bist du bereit zum Debuggen! Klicke auf Start Listening for PHP Connections
. Öffne im Browser die Seite, die du debuggen möchtest.
PHP-Server-Konfigurationsbildschirm in PhpStorm und IntelliJ
Schaltfläche zum Starten des Lauschens auf PHP-Debugging-Verbindungen
Beispiel für den Debug-Bildschirm in IntelliJ / PhpStorm
Visual Studio Code für das Debugging auf Kubernetes konfigurieren
Stelle zunächst sicher, dass die PHP Debug-Erweiterung von Xdebug bereits installiert ist.
Öffne oder erstelle anschließend die Datei launch.json
im .vscode
-Ordner deines Projektverzeichnisses. Passe darin die Einstellungen für Listen for Xdebug
an, indem du Pfadzuordnungen zwischen deinem lokalen Arbeitsbereich und dem Container hinzufügst oder aktualisierst. Die Konfiguration sollte wie folgt aussehen:
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/app/custom": "${workspaceRoot}/custom",
"/app/public": "${workspaceRoot}/public",
"/app/vendor": "${workspaceRoot}/vendor"
}
},
Voilà – jetzt ist alles bereit zum Debuggen! Klicke einfach auf die Debug- und Listen for Xdebug
-Schaltflächen und öffne im Browser die Seite, die du debuggen möchtest.
Debugging mit Xdebug in Visual Studio Code
Beispielhafte Debug-Ansicht in Visual Studio Code
Zusammenfassung
Eine cloud-native Shopware-Anwendung muss zustandslos sein, um horizontal skalierbar zu werden. Dies erreichst du durch:
- Die Konfiguration eines gemeinsamen Cache-Speichers wie Redis anstelle des
standardmäßig lokalen, dateibasierten Caches. - Die Konfiguration eines gemeinsamen Speichers für Medien- und private Dateien, idealerweise eines S3-kompatiblen Objektspeichers.
- Die Konfiguration der Protokollierung, sodass Logs in stdout und stderr gestreamt werden, um sie zentral – etwa in einem Index wie Loki oder Elasticsearch oder in SaaS-Lösungen wie New Relic oder Datadog – weiter zu aggregieren und zu speichern.
Um deine Shopware-Anwendung im lokalen Kubernetes-Cluster bereitzustellen, zu testen und zu debuggen:
- Containerisiere deine Shopware-Anwendung: Ziehe FrankenPHP als
Alternative zu PHP-FPM mit Apache oder NGINX in Betracht. Mit FrankenPHP kannst du die gesamte Anwendung zu einer eigenständigen Binärdatei bündeln und dadurch minimale sowie sicherere Basis-Container-Images verwenden. - Bauen und testen: Nutze Skaffold – ein praktisches Tool, um Shopware mit einfachen Befehlen auf Kubernetes zu bauen, bereitzustellen und zu testen: skaffold build erstellt Container-Images, skaffold run startet den gesamten Shopware-Stack lokal, und mit skaffold verify führst du die Tests aus.
- Debuggen: Um Shopware im Cluster zu debuggen, deploye Shopware mit
aktivierter Xdebug-PHP-Erweiterung. Nutze anschließend das Tool ktunnel, das Xdebug-Verbindungen mit deinem Code-Editor ermöglicht.
Ich lade dich herzlich ein, dich mit deinen Ideen, Anregungen oder gefundenen Problemen am Shopware-Kube-Projekt zu beteiligen. Wir bei Kiwee werden FrankenPHP weiterhin als Application Server für Shopware anpassen und testen.
Wenn du Beratung für deine cloudbasierte Shopware-Infrastruktur benötigst oder deinen eigenen Stack aufbauen möchtest, kontaktiere uns gerne! Wir helfen gern weiter.