mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-11-03 03:38:15 +00:00
Compare commits
10 Commits
master
...
feature/sh
| Author | SHA1 | Date | |
|---|---|---|---|
| 48557b06e3 | |||
| 1cff5778d3 | |||
| 60e2c972d6 | |||
| 637de6a190 | |||
| f5efbce205 | |||
| d6f3618d70 | |||
| 773655efb5 | |||
| 7bc9f7abd9 | |||
| ec7b8662dd | |||
| d1ccfd9cdd |
@@ -114,6 +114,8 @@ defaults_networks:
|
||||
subnet: 192.168.104.48/28
|
||||
web-app-mini-qr:
|
||||
subnet: 192.168.104.64/28
|
||||
web-app-shopware:
|
||||
subnet: 192.168.104.80/28
|
||||
|
||||
# /24 Networks / 254 Usable Clients
|
||||
web-app-bigbluebutton:
|
||||
|
||||
@@ -81,6 +81,7 @@ ports:
|
||||
web-app-minio_api: 8057
|
||||
web-app-minio_console: 8058
|
||||
web-app-mini-qr: 8059
|
||||
web-app-shopware: 8060
|
||||
web-app-bigbluebutton: 48087 # This port is predefined by bbb. @todo Try to change this to a 8XXX port
|
||||
public:
|
||||
# The following ports should be changed to 22 on the subdomain via stream mapping
|
||||
|
||||
31
roles/docker-container/templates/healthcheck/http.yml.j2
Normal file
31
roles/docker-container/templates/healthcheck/http.yml.j2
Normal file
@@ -0,0 +1,31 @@
|
||||
{# ------------------------------------------------------------------------------
|
||||
Healthcheck: HTTP Local
|
||||
------------------------------------------------------------------------------
|
||||
This template defines a generic HTTP healthcheck for containers exposing
|
||||
a web service on a local port (e.g., Nginx, Apache, PHP-FPM, Shopware, etc.).
|
||||
|
||||
It uses `wget` or `curl` (as fallback) to test if the container responds on
|
||||
http://127.0.0.1:{{ container_port }}/. If the request succeeds, Docker marks
|
||||
the container as "healthy"; otherwise, as "unhealthy".
|
||||
|
||||
Parameters:
|
||||
- container_port: The internal port the service listens on.
|
||||
|
||||
Timing:
|
||||
- interval: 30s → Check every 30 seconds
|
||||
- timeout: 5s → Each check must complete within 5 seconds
|
||||
- retries: 5 → Mark unhealthy after 5 consecutive failures
|
||||
- start_period: 20s → Grace period before health checks begin
|
||||
|
||||
Usage:
|
||||
{% filter indent(4) %}
|
||||
{% include 'roles/docker-container/templates/healthcheck/http.yml.j2' %}
|
||||
{% endfilter %}
|
||||
------------------------------------------------------------------------------
|
||||
#}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:{{ container_port }}/ >/dev/null || curl -fsS http://127.0.0.1:{{ container_port }}/ >/dev/null"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
@@ -6,7 +6,7 @@ entity_name: "{{ application_id | get_entity_name }
|
||||
docker_compose_flush_handlers: true
|
||||
|
||||
# Docker Compose
|
||||
database_type: "{{ application_id | get_entity_name }}"
|
||||
database_type: "{{ entity_name }}"
|
||||
|
||||
## Postgres
|
||||
POSTGRES_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
|
||||
|
||||
34
roles/web-app-shopware/README.md
Normal file
34
roles/web-app-shopware/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Shopware
|
||||
|
||||
## Description
|
||||
|
||||
Empower your e-commerce vision with **Shopware 6**, a modern, flexible, and open-source commerce platform built on **Symfony and Vue.js**. Designed for growth and innovation, it enables seamless integration, outstanding customer experiences, and complete control over your digital business. Build, scale, and sell with confidence.
|
||||
|
||||
## Overview
|
||||
|
||||
This role deploys **Shopware 6** using **Docker**. It automates installation, migration, and configuration of your storefront, integrating with a central **MariaDB** database.
|
||||
Optional components like **Redis** and **OpenSearch** enhance performance and search capabilities, while **OIDC** and **LDAP** support integration with centralized identity systems such as **Keycloak**.
|
||||
|
||||
With automated setup, update handling, variable management, and plugin-based authentication, this role simplifies the deployment and maintenance of your Shopware instance.
|
||||
|
||||
## Features
|
||||
|
||||
* **Modern and Scalable:** A robust Symfony-based framework optimized for commerce innovation.
|
||||
* **Automated Setup & Maintenance:** Installs, migrates, and configures Shopware automatically.
|
||||
* **Extensible Architecture:** Optional Redis, OpenSearch, and plugin-based IAM integrations.
|
||||
* **Centralized Database Access:** Connects seamlessly to the shared MariaDB service.
|
||||
* **Integrated Configuration:** Environment and Docker Compose variables managed automatically.
|
||||
|
||||
## Further Resources
|
||||
|
||||
* [Shopware Official Website](https://www.shopware.com/en/)
|
||||
* [Shopware Developer Documentation](https://developer.shopware.com/)
|
||||
* [Shopware Store (Plugins)](https://store.shopware.com/en/)
|
||||
|
||||
## Credits
|
||||
|
||||
Developed and maintained by **Kevin Veen-Birkenbach**.
|
||||
Learn more at [veen.world](https://www.veen.world).
|
||||
|
||||
Part of the [Infinito.Nexus Project](https://s.infinito.nexus/code)
|
||||
Licensed under [Infinito.Nexus NonCommercial License](https://s.infinito.nexus/license).
|
||||
3
roles/web-app-shopware/TODO.md
Normal file
3
roles/web-app-shopware/TODO.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# to-dos
|
||||
- Implement OIDC
|
||||
- Implement LDAP
|
||||
73
roles/web-app-shopware/config/main.yml
Normal file
73
roles/web-app-shopware/config/main.yml
Normal file
@@ -0,0 +1,73 @@
|
||||
title: "{{ SOFTWARE_NAME }} Shop"
|
||||
features:
|
||||
central_database: true
|
||||
redis: true
|
||||
ldap: false # Not implemented yet
|
||||
oidc: false # Not implemented yet
|
||||
logout: true
|
||||
server:
|
||||
csp:
|
||||
flags: {}
|
||||
whitelist: {}
|
||||
domains:
|
||||
aliases: []
|
||||
canonical:
|
||||
- shop.{{ PRIMARY_DOMAIN }}
|
||||
docker:
|
||||
services:
|
||||
database:
|
||||
enabled: true
|
||||
|
||||
init:
|
||||
name: software-init
|
||||
cpus: 1.0
|
||||
mem_reservation: 1g
|
||||
mem_limit: 2g
|
||||
|
||||
|
||||
# Base PHP image used by all app services
|
||||
shopware:
|
||||
image: "ghcr.io/shopware/docker-base"
|
||||
version: "8.3"
|
||||
|
||||
web:
|
||||
name: "shopware-web"
|
||||
port: 8000
|
||||
cpus: 1.0
|
||||
mem_reservation: 1g
|
||||
mem_limit: 2g
|
||||
|
||||
worker:
|
||||
name: "shopware-worker"
|
||||
entrypoint: [ "php", "bin/console", "messenger:consume", "async", "low_priority", "--time-limit=300", "--memory-limit=512M" ]
|
||||
replicas: 3
|
||||
cpus: 1.0
|
||||
mem_reservation: 1g
|
||||
mem_limit: 2g
|
||||
|
||||
scheduler:
|
||||
name: "shopware-scheduler"
|
||||
entrypoint: [ "php", "bin/console", "scheduled-task:run" ]
|
||||
cpus: 0.5
|
||||
mem_reservation: 512m
|
||||
mem_limit: 1g
|
||||
|
||||
redis:
|
||||
enabled: true
|
||||
image: "redis"
|
||||
version: "7-alpine"
|
||||
cpus: 0.25
|
||||
mem_reservation: 256m
|
||||
mem_limit: 512m
|
||||
|
||||
opensearch:
|
||||
enabled: true
|
||||
image: "opensearchproject/opensearch"
|
||||
version: "2.12.0"
|
||||
name: "shopware-opensearch"
|
||||
cpus: 1.0
|
||||
mem_reservation: 2g
|
||||
mem_limit: 4g
|
||||
|
||||
volumes:
|
||||
data: "shopware_data"
|
||||
81
roles/web-app-shopware/files/init.sh
Normal file
81
roles/web-app-shopware/files/init.sh
Normal file
@@ -0,0 +1,81 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
# Paths / constants
|
||||
APP_ROOT="/var/www/html"
|
||||
MARKER="$APP_ROOT/.infinito/installed"
|
||||
|
||||
cd "$APP_ROOT"
|
||||
mkdir -p "$APP_ROOT/.infinito"
|
||||
|
||||
echo "[INIT] Checking database via PDO..."
|
||||
php -r '
|
||||
$url = getenv("DATABASE_URL");
|
||||
if (!$url) { fwrite(STDERR, "DATABASE_URL not set\n"); exit(1); }
|
||||
$p = parse_url($url);
|
||||
if (!$p || !isset($p["scheme"])) { fwrite(STDERR, "Invalid DATABASE_URL\n"); exit(1); }
|
||||
$scheme = $p["scheme"];
|
||||
if ($scheme === "mysql" || $scheme === "mariadb") {
|
||||
$host = $p["host"] ?? "localhost";
|
||||
$port = $p["port"] ?? 3306;
|
||||
$db = ltrim($p["path"] ?? "", "/");
|
||||
$user = $p["user"] ?? "";
|
||||
$pass = $p["pass"] ?? "";
|
||||
$dsn = "mysql:host=".$host.";port=".$port.";dbname=".$db.";charset=utf8mb4";
|
||||
} else {
|
||||
fwrite(STDERR, "Unsupported DB scheme: ".$scheme."\n"); exit(1);
|
||||
}
|
||||
$retries = 60;
|
||||
while ($retries-- > 0) {
|
||||
try { $pdo = new PDO($dsn, $user, $pass, [PDO::ATTR_TIMEOUT => 3]); exit(0); }
|
||||
catch (Exception $e) { sleep(2); }
|
||||
}
|
||||
fwrite(STDERR, "DB not reachable\n"); exit(1);
|
||||
'
|
||||
|
||||
if [ ! -f "$MARKER" ]; then
|
||||
echo "[INIT] Checking if database is empty..."
|
||||
# PHP exits: 0 = empty, 100 = non-empty, 1 = error
|
||||
if php -r '
|
||||
$url = getenv("DATABASE_URL");
|
||||
$p = parse_url($url);
|
||||
$db = ltrim($p["path"] ?? "", "/");
|
||||
$dsn = "mysql:host=".($p["host"]??"localhost").";port=".($p["port"]??3306).";dbname=".$db.";charset=utf8mb4";
|
||||
try {
|
||||
$pdo = new PDO($dsn, $p["user"] ?? "", $p["pass"] ?? "");
|
||||
$q = $pdo->query("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=".$pdo->quote($db));
|
||||
$cnt = (int)$q->fetchColumn();
|
||||
if ($cnt === 0) { exit(0); } else { exit(100); }
|
||||
} catch (Exception $e) { fwrite(STDERR, $e->getMessage()."\n"); exit(1); }
|
||||
'; then
|
||||
DBCHK=0
|
||||
else
|
||||
DBCHK=$?
|
||||
fi
|
||||
|
||||
if [ "$DBCHK" -eq 0 ]; then
|
||||
echo "[INIT] Installing Shopware (empty DB detected)..."
|
||||
# IMPORTANT: no --force; let Shopware run its internal steps only on empty DB
|
||||
php -d memory_limit=1024M bin/console system:install --basic-setup --create-database
|
||||
elif [ "$DBCHK" -eq 100 ]; then
|
||||
echo "[INIT] Database is not empty -> skipping system:install"
|
||||
else
|
||||
echo "[INIT] Database check failed (code $DBCHK)"; exit 1
|
||||
fi
|
||||
|
||||
# Safe to run (no-ops when up-to-date)
|
||||
php -d memory_limit=1024M bin/console database:migrate --all || true
|
||||
php -d memory_limit=1024M bin/console database:migrate-destructive --all || true
|
||||
|
||||
# Housekeeping
|
||||
php bin/console cache:clear || true
|
||||
php bin/console dal:refresh:index || true
|
||||
|
||||
# Marker + perms
|
||||
touch "$MARKER"
|
||||
chown -R www-data:www-data "$APP_ROOT"
|
||||
|
||||
echo "[INIT] Done."
|
||||
else
|
||||
echo "[INIT] Marker found, skipping install."
|
||||
fi
|
||||
26
roles/web-app-shopware/meta/main.yml
Normal file
26
roles/web-app-shopware/meta/main.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: "Kevin Veen-Birkenbach"
|
||||
description: "Open-Source Commerce (PHP/Symfony) with optional OIDC/LDAP, Redis & OpenSearch — containerized & automated."
|
||||
license: "Infinito.Nexus NonCommercial License"
|
||||
license_url: "https://s.infinito.nexus/license"
|
||||
company: |
|
||||
Kevin Veen-Birkenbach
|
||||
Consulting & Coaching Solutions
|
||||
https://www.veen.world
|
||||
galaxy_tags:
|
||||
- shopware
|
||||
- ecommerce
|
||||
- docker
|
||||
- symfony
|
||||
- oidc
|
||||
- ldap
|
||||
repository: https://s.infinito.nexus/code
|
||||
issue_tracker_url: https://s.infinito.nexus/issues
|
||||
documentation: "https://docs.infinito.nexus/"
|
||||
logo:
|
||||
class: "fa-solid fa-cart-shopping"
|
||||
run_after:
|
||||
- web-app-keycloak
|
||||
- web-app-mailu
|
||||
dependencies: []
|
||||
2
roles/web-app-shopware/schema/main.yml
Normal file
2
roles/web-app-shopware/schema/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
# Minimal schema placeholder (extend with your own config contract if desired)
|
||||
credentials: {}
|
||||
7
roles/web-app-shopware/tasks/01_setup.yml
Normal file
7
roles/web-app-shopware/tasks/01_setup.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
- name: Install & configure OIDC plugin (if enabled)
|
||||
include_tasks: setup/oidc.yml
|
||||
when: applications | get_app_conf(application_id, 'features.oidc')
|
||||
|
||||
- name: Install & configure LDAP plugin (if enabled)
|
||||
include_tasks: setup/ldap.yml
|
||||
when: applications | get_app_conf(application_id, 'features.ldap')
|
||||
7
roles/web-app-shopware/tasks/02_cleanup.yml
Normal file
7
roles/web-app-shopware/tasks/02_cleanup.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
- name: Remove OIDC plugin if disabled
|
||||
include_tasks: cleanup/oidc.yml
|
||||
when: not (applications | get_app_conf(application_id, 'features.oidc'))
|
||||
|
||||
- name: Remove LDAP plugin if disabled
|
||||
include_tasks: cleanup/ldap.yml
|
||||
when: not (applications | get_app_conf(application_id, 'features.ldap'))
|
||||
19
roles/web-app-shopware/tasks/03_admin.yml
Normal file
19
roles/web-app-shopware/tasks/03_admin.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# Ensures that the admin user exists and always has the desired password
|
||||
- name: "Ensure Shopware admin exists and has the desired password"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} sh -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console user:create "{{ users.administrator.username }}" \
|
||||
--admin \
|
||||
--password="{{ users.administrator.password }}" \
|
||||
--firstName="{{ users.administrator.username }}" \
|
||||
--lastName="{{ PRIMARY_DOMAIN | lower }}" \
|
||||
--email="{{ users.administrator.email }}" || true
|
||||
php bin/console user:change-password "{{ users.administrator.username }}" \
|
||||
--password="{{ users.administrator.password }}" || true
|
||||
php bin/console user:update "{{ users.administrator.username }}" \
|
||||
--email="{{ users.administrator.email }}" 2>/dev/null || true
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
10
roles/web-app-shopware/tasks/cleanup/ldap.yml
Normal file
10
roles/web-app-shopware/tasks/cleanup/ldap.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
- name: "Deactivate/uninstall LDAP plugin if present"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:deactivate INFX_LDAP_PLUGIN || true
|
||||
php bin/console plugin:uninstall INFX_LDAP_PLUGIN --keep-user-data || true
|
||||
php bin/console cache:clear
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
10
roles/web-app-shopware/tasks/cleanup/oidc.yml
Normal file
10
roles/web-app-shopware/tasks/cleanup/oidc.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
- name: "Deactivate/uninstall OIDC plugin if present"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:deactivate INFX_OIDC_PLUGIN || true
|
||||
php bin/console plugin:uninstall INFX_OIDC_PLUGIN --keep-user-data || true
|
||||
php bin/console cache:clear
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
46
roles/web-app-shopware/tasks/main.yml
Normal file
46
roles/web-app-shopware/tasks/main.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
- name: "Load docker, DB and proxy for {{ application_id }}"
|
||||
include_role:
|
||||
name: sys-stk-full-stateful
|
||||
vars:
|
||||
docker_compose_flush_handlers: false
|
||||
|
||||
- name: "Deploy {{ SHOPWARE_INIT_HOST }}"
|
||||
copy:
|
||||
src: init.sh
|
||||
dest: "{{ SHOPWARE_INIT_HOST }}"
|
||||
mode: "0755"
|
||||
notify:
|
||||
- docker compose up
|
||||
- docker compose build
|
||||
|
||||
- name: "Flush docker compose handlers"
|
||||
meta: flush_handlers
|
||||
|
||||
- name: Wait for Shopware HTTP endpoint
|
||||
wait_for:
|
||||
host: "127.0.0.1"
|
||||
port: "{{ ports.localhost.http[application_id] }}"
|
||||
delay: 5
|
||||
timeout: 300
|
||||
|
||||
- name: "Ensure admin user exists with correct password"
|
||||
include_tasks: 03_admin.yml
|
||||
|
||||
- name: "Warm up caches and index"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console messenger:consume --time-limit=60 --limit=100 || true
|
||||
php bin/console dal:refresh:index || true
|
||||
php bin/console cache:clear
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
|
||||
- name: Execute setup routines (OIDC/LDAP)
|
||||
include_tasks: 01_setup.yml
|
||||
|
||||
- name: Execute cleanup routines
|
||||
include_tasks: 02_cleanup.yml
|
||||
when: MODE_CLEANUP
|
||||
27
roles/web-app-shopware/tasks/setup/ldap.yml
Normal file
27
roles/web-app-shopware/tasks/setup/ldap.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
# Replace INFX_LDAP_PLUGIN with the actual plugin name you use
|
||||
- name: "Install LDAP admin plugin & activate"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:refresh
|
||||
php bin/console plugin:install --activate INFX_LDAP_PLUGIN || true
|
||||
php bin/console cache:clear
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
|
||||
- name: "Configure LDAP connection"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console system:config:set "InfxLdap.config.host" "{{ LDAP.SERVER.DOMAIN }}"
|
||||
php bin/console system:config:set "InfxLdap.config.port" "{{ LDAP.SERVER.PORT }}"
|
||||
php bin/console system:config:set "InfxLdap.config.bindDn" "{{ LDAP.DN.ADMINISTRATOR.DATA }}"
|
||||
php bin/console system:config:set "InfxLdap.config.password" "{{ LDAP.BIND_CREDENTIAL }}"
|
||||
php bin/console system:config:set "InfxLdap.config.userBase" "{{ LDAP.DN.OU.USERS }}"
|
||||
php bin/console cache:clear
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
26
roles/web-app-shopware/tasks/setup/oidc.yml
Normal file
26
roles/web-app-shopware/tasks/setup/oidc.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
# Replace INFX_OIDC_PLUGIN with the actual plugin name (Composer or local)
|
||||
- name: "Install OIDC plugin & activate"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console plugin:refresh
|
||||
php bin/console plugin:install --activate INFX_OIDC_PLUGIN || true
|
||||
php bin/console cache:clear
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
|
||||
- name: "Configure OIDC via system:config"
|
||||
shell: |
|
||||
docker exec -i --user {{ SHOPWARE_USER }} {{ SHOPWARE_WEB_CONTAINER }} bash -lc '
|
||||
set -e
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
php bin/console system:config:set "InfxOidc.config.clientId" "{{ OIDC.CLIENT.ID }}"
|
||||
php bin/console system:config:set "InfxOidc.config.clientSecret" "{{ OIDC.CLIENT.SECRET }}"
|
||||
php bin/console system:config:set "InfxOidc.config.discoveryUrl" "{{ OIDC.CLIENT.DISCOVERY_DOCUMENT }}"
|
||||
php bin/console system:config:set "InfxOidc.config.scopes" "openid profile email"
|
||||
php bin/console cache:clear
|
||||
'
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
92
roles/web-app-shopware/templates/Dockerfile.j2
Normal file
92
roles/web-app-shopware/templates/Dockerfile.j2
Normal file
@@ -0,0 +1,92 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# Shopware Application Image (Alpine-compatible)
|
||||
# ------------------------------------------------------------------------------
|
||||
# - Stage 1 (builder): use Composer to fetch Shopware while ignoring build-time
|
||||
# PHP extensions (we'll install them in the runtime image).
|
||||
# - Stage 2 (runtime): install required PHP extensions and copy the app + init.sh
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
############################
|
||||
# Stage 1: Builder
|
||||
############################
|
||||
FROM composer:2.7 AS builder
|
||||
ENV COMPOSER_ALLOW_SUPERUSER=1 \
|
||||
COMPOSER_NO_INTERACTION=1 \
|
||||
COMPOSER_PROCESS_TIMEOUT=900
|
||||
|
||||
WORKDIR /app
|
||||
ARG SHOPWARE_PROD_VERSION=shopware/production:6.7.3.1
|
||||
|
||||
# 1) Scaffold project without installing dependencies
|
||||
RUN set -eux; \
|
||||
composer create-project "${SHOPWARE_PROD_VERSION}" /app --no-install
|
||||
|
||||
# 2) Install dependencies (ignoring build-time extension checks) + add Redis transport
|
||||
RUN set -eux; \
|
||||
composer install \
|
||||
--no-dev \
|
||||
--optimize-autoloader \
|
||||
--no-progress \
|
||||
--no-scripts \
|
||||
--ignore-platform-req=ext-gd \
|
||||
--ignore-platform-req=ext-intl \
|
||||
--ignore-platform-req=ext-pdo_mysql; \
|
||||
composer require symfony/redis-messenger:^6.4 \
|
||||
-W \
|
||||
--no-scripts \
|
||||
--no-progress \
|
||||
--update-no-dev \
|
||||
--ignore-platform-req=ext-gd \
|
||||
--ignore-platform-req=ext-intl \
|
||||
--ignore-platform-req=ext-pdo_mysql \
|
||||
--ignore-platform-req=ext-redis
|
||||
|
||||
|
||||
############################
|
||||
# Stage 2: Runtime
|
||||
############################
|
||||
FROM ghcr.io/shopware/docker-base:8.3
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Install required PHP extensions in the Alpine-based runtime
|
||||
# (try php83-*, fall back to php82-*, then to generic)
|
||||
USER root
|
||||
RUN set -eux; \
|
||||
apk add --no-cache php83-gd || apk add --no-cache php82-gd || apk add --no-cache php-gd || true; \
|
||||
apk add --no-cache php83-intl || apk add --no-cache php82-intl || apk add --no-cache php-intl || true; \
|
||||
apk add --no-cache php83-pdo_mysql || apk add --no-cache php82-pdo_mysql || apk add --no-cache php-pdo_mysql || true; \
|
||||
apk add --no-cache php83-redis || apk add --no-cache php82-redis || apk add --no-cache php-redis || true
|
||||
|
||||
# Copy built application from the builder
|
||||
COPY --chown=www-data:www-data --from=builder /app /var/www/html
|
||||
|
||||
# Optional: snapshot of pristine app to seed an empty volume (used by init container)
|
||||
RUN mkdir -p /usr/src/shopware \
|
||||
&& cp -a /var/www/html/. /usr/src/shopware/. \
|
||||
&& chown -R www-data:www-data /var/www/html /usr/src/shopware
|
||||
|
||||
# Ensure writable directories exist with correct ownership
|
||||
RUN set -eux; \
|
||||
mkdir -p \
|
||||
/var/www/html/files \
|
||||
/var/www/html/var \
|
||||
/var/www/html/public/media \
|
||||
/var/www/html/public/thumbnail \
|
||||
/var/www/html/public/sitemap \
|
||||
/var/www/html/public/theme; \
|
||||
chown -R www-data:www-data /var/www/html
|
||||
|
||||
# Add trusted proxies wiring (Symfony reads env TRUSTED_PROXIES)
|
||||
RUN set -eux; \
|
||||
mkdir -p /var/www/html/config/packages; \
|
||||
if [ ! -f /var/www/html/config/packages/framework.yaml ]; then \
|
||||
printf "framework:\n trusted_proxies: '%%env(TRUSTED_PROXIES)%%'\n" > /var/www/html/config/packages/framework.yaml; \
|
||||
fi
|
||||
|
||||
# Drop back to the app user
|
||||
USER www-data
|
||||
|
||||
# Expose internal port & add a lightweight healthcheck
|
||||
EXPOSE 8000
|
||||
HEALTHCHECK --interval=30s --timeout=5s --retries=5 --start-period=20s \
|
||||
CMD php -r '$s=@fsockopen("127.0.0.1", 8000, $e, $t, 3); if(!$s) exit(1); fclose($s);'
|
||||
117
roles/web-app-shopware/templates/docker-compose.yml.j2
Normal file
117
roles/web-app-shopware/templates/docker-compose.yml.j2
Normal file
@@ -0,0 +1,117 @@
|
||||
x-environment: &shopware
|
||||
image: "{{ SHOPWARE_CUSTOM_IMAGE }}"
|
||||
volumes:
|
||||
- files:/var/www/html/files
|
||||
- theme:/var/www/html/public/theme
|
||||
- media:/var/www/html/public/media
|
||||
- thumbnail:/var/www/html/public/thumbnail
|
||||
- sitemap:/var/www/html/public/sitemap
|
||||
- "{{ SHOPWARE_INIT_HOST }}:{{ SHOPWARE_INIT_DOCKER }}:ro"
|
||||
working_dir: {{ SHOPWARE_ROOT }}
|
||||
|
||||
{% include 'roles/docker-compose/templates/base.yml.j2' %}
|
||||
|
||||
# -------------------------
|
||||
# INIT (runs once per deployment)
|
||||
# -------------------------
|
||||
{% set service_name = 'init' %}
|
||||
{% set docker_restart_policy = 'no' %}
|
||||
{{ service_name }}:
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
{% set docker_restart_policy = DOCKER_RESTART_POLICY %}
|
||||
<<: *shopware
|
||||
container_name: "{{ SHOPWARE_INIT_CONTAINER }}"
|
||||
entrypoint: [ "sh", "{{ SHOPWARE_INIT_DOCKER }}" ]
|
||||
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
|
||||
{# -------------------------
|
||||
WEB (serves HTTP on 8000)
|
||||
------------------------- #}
|
||||
{% set service_name = 'web' %}
|
||||
{% set container_port = applications | get_app_conf(application_id, 'docker.services.web.port') %}
|
||||
{{ service_name }}:
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
<<: *shopware
|
||||
{{ lookup('template', 'roles/docker-container/templates/build.yml.j2') | indent(4) }}
|
||||
container_name: "{{ SHOPWARE_WEB_CONTAINER }}"
|
||||
ports:
|
||||
- "127.0.0.1:{{ ports.localhost.http[application_id] }}:{{ container_port }}"
|
||||
depends_on:
|
||||
init:
|
||||
condition: service_completed_successfully
|
||||
{% filter indent(4) %}
|
||||
{% include 'roles/docker-container/templates/healthcheck/http.yml.j2' %}
|
||||
{% endfilter %}
|
||||
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
|
||||
{# -------------------------
|
||||
WORKER (async queues)
|
||||
------------------------- #}
|
||||
{% set service_name = 'worker' %}
|
||||
{{ service_name }}:
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
<<: *shopware
|
||||
container_name: "{{ SHOPWARE_WORKER_CONTAINER }}"
|
||||
pull_policy: never
|
||||
entrypoint: {{ SHOPWARE_WORKER_ENTRYPOINT }}
|
||||
depends_on:
|
||||
init:
|
||||
condition: service_completed_successfully
|
||||
# @todo Activate for swarm deploy
|
||||
# deploy:
|
||||
# replicas: {{ SHOPWARE_WORKER_REPLICAS }}
|
||||
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
|
||||
{# -------------------------
|
||||
SCHEDULER (cron-like)
|
||||
------------------------- #}
|
||||
{% set service_name = 'scheduler' %}
|
||||
{{ service_name }}:
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
<<: *shopware
|
||||
container_name: "{{ SHOPWARE_SCHED_CONTAINER }}"
|
||||
pull_policy: never
|
||||
entrypoint: {{ SHOPWARE_SCHED_ENTRYPOINT }}
|
||||
depends_on:
|
||||
init:
|
||||
condition: service_completed_successfully
|
||||
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
|
||||
{% if SHOPWARE_OPENSEARCH_ENABLED %}
|
||||
{% set service_name = 'opensearch' %}
|
||||
{{ service_name }}:
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
image: "{{ SHOPWARE_OPENSEARCH_IMAGE }}:{{ SHOPWARE_OPENSEARCH_VERSION }}"
|
||||
container_name: "{{ SHOPWARE_OPENSEARCH_CONTAINER }}"
|
||||
environment:
|
||||
- discovery.type=single-node
|
||||
- plugins.security.disabled=true
|
||||
- bootstrap.memory_lock=true
|
||||
- OPENSEARCH_JAVA_OPTS=-Xms{{ SHOPWARE_OPENSEARCH_MEM_RESERVATION }} -Xmx{{ SHOPWARE_OPENSEARCH_MEM_RESERVATION }}
|
||||
ulimits:
|
||||
memlock: { soft: -1, hard: -1 }
|
||||
depends_on:
|
||||
init:
|
||||
condition: service_completed_successfully
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
{% endif %}
|
||||
|
||||
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
|
||||
data:
|
||||
name: {{ SHOPWARE_VOLUME }}
|
||||
files:
|
||||
name: {{ entity_name }}_files
|
||||
theme:
|
||||
name: {{ entity_name }}_theme
|
||||
media:
|
||||
name: {{ entity_name }}_media
|
||||
thumbnail:
|
||||
name: {{ entity_name }}_thumbnail
|
||||
sitemap:
|
||||
name: {{ entity_name }}_sitemap
|
||||
|
||||
{% include 'roles/docker-compose/templates/networks.yml.j2' %}
|
||||
33
roles/web-app-shopware/templates/env.j2
Normal file
33
roles/web-app-shopware/templates/env.j2
Normal file
@@ -0,0 +1,33 @@
|
||||
# DOMAIN/URL
|
||||
DOMAIN={{ domains | get_domain(application_id) }}
|
||||
APP_URL="{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
|
||||
APP_DEBUG="{{ MODE_DEBUG | ternary(1, 0) }}"
|
||||
|
||||
# Shopware
|
||||
APP_ENV={{ 'dev' if (ENVIRONMENT | lower) == 'development' else 'prod' }}
|
||||
#TRUSTED_PROXIES=127.0.0.1
|
||||
INSTANCE_ID={{ application_id }}
|
||||
|
||||
# Database
|
||||
DATABASE_URL="mysql://{{ database_username }}:{{ database_password }}@{{ database_host }}:{{ database_port }}/{{ database_name }}"
|
||||
|
||||
# Redis (optional)
|
||||
{% if SHOPWARE_REDIS_ENABLED | bool %}
|
||||
REDIS_URL="redis://{{ SHOPWARE_REDIS_ADDRESS }}/0"
|
||||
CACHE_URL="redis://{{ SHOPWARE_REDIS_ADDRESS }}/1"
|
||||
MESSENGER_TRANSPORT_DSN="redis://{{ SHOPWARE_REDIS_ADDRESS }}/2"
|
||||
{% else %}
|
||||
CACHE_URL="file://cache"
|
||||
{% endif %}
|
||||
|
||||
{% if SHOPWARE_OPENSEARCH_ENABLED %}
|
||||
# Search
|
||||
ELASTICSEARCH_URL="http://opensearch:9200"
|
||||
OPENSEARCH_URL="http://opensearch:9200"
|
||||
OPENSEARCH_HOST="opensearch"
|
||||
OPENSEARCH_PORT_NUMBER="9200"
|
||||
OPENSEARCH_INITIAL_ADMIN_PASSWORD="{{ users.administrator.password }}"
|
||||
{% endif %}
|
||||
|
||||
# Mail (Mailu)
|
||||
MAILER_DSN="smtps://{{ users['no-reply'].email }}:{{ users['no-reply'].mailu_token }}@{{ SYSTEM_EMAIL.HOST }}:{{ SYSTEM_EMAIL.PORT }}"
|
||||
43
roles/web-app-shopware/vars/main.yml
Normal file
43
roles/web-app-shopware/vars/main.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
# General
|
||||
application_id: "web-app-shopware"
|
||||
database_type: "mariadb"
|
||||
entity_name: "{{ application_id | get_entity_name }}"
|
||||
|
||||
# Docker
|
||||
container_port: "{{ applications | get_app_conf(application_id, 'docker.services.web.port') }}"
|
||||
docker_compose_flush_handlers: true
|
||||
|
||||
# Shopware container/image vars
|
||||
SHOPWARE_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.shopware.version') }}"
|
||||
SHOPWARE_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.shopware.image') }}"
|
||||
SHOPWARE_CUSTOM_IMAGE: "{{ SHOPWARE_IMAGE }}:{{ SHOPWARE_VERSION }}"
|
||||
SHOPWARE_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}"
|
||||
SHOPWARE_USER: "www-data"
|
||||
SHOPWARE_ROOT: "/var/www/html"
|
||||
|
||||
# Split service container names
|
||||
SHOPWARE_INIT_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.init.name') }}"
|
||||
SHOPWARE_WEB_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.web.name') }}"
|
||||
SHOPWARE_WORKER_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.worker.name') }}"
|
||||
SHOPWARE_SCHED_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.scheduler.name') }}"
|
||||
SHOPWARE_INIT_HOST: "{{ [ docker_compose.directories.volumes, 'init.sh' ] | path_join }}"
|
||||
SHOPWARE_INIT_DOCKER: "/usr/local/bin/init.sh"
|
||||
|
||||
# Entrypoints & replicas
|
||||
SHOPWARE_WORKER_ENTRYPOINT: "{{ applications | get_app_conf(application_id, 'docker.services.worker.entrypoint') }}"
|
||||
SHOPWARE_SCHED_ENTRYPOINT: "{{ applications | get_app_conf(application_id, 'docker.services.scheduler.entrypoint') }}"
|
||||
SHOPWARE_WORKER_REPLICAS: "{{ applications | get_app_conf(application_id, 'docker.services.worker.replicas') }}"
|
||||
|
||||
# Search/Cache
|
||||
SHOPWARE_REDIS_ENABLED: "{{ applications | get_app_conf(application_id, 'docker.services.redis.enabled') }}"
|
||||
SHOPWARE_REDIS_ADDRESS: "redis:6379"
|
||||
SHOPWARE_OPENSEARCH_ENABLED: "{{ applications | get_app_conf(application_id, 'docker.services.opensearch.enabled') }}"
|
||||
SHOPWARE_OPENSEARCH_ENGINE: "{{ applications | get_app_conf(application_id, 'docker.services.opensearch.engine') }}"
|
||||
SHOPWARE_OPENSEARCH_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.opensearch.image') }}"
|
||||
SHOPWARE_OPENSEARCH_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.opensearch.version') }}"
|
||||
SHOPWARE_OPENSEARCH_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.opensearch.name') }}"
|
||||
SHOPWARE_OPENSEARCH_MEM_RESERVATION: "{{ applications | get_app_conf(application_id, 'docker.services.opensearch.mem_reservation') }}"
|
||||
SHOPWARE_OPENSEARCH_MEM_LIMIT: "{{ applications | get_app_conf(application_id, 'docker.services.opensearch.mem_limit') }}"
|
||||
|
||||
# IAM (true if either OIDC or LDAP is enabled)
|
||||
SHOPWARE_IAM_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') or applications | get_app_conf(application_id, 'features.ldap') }}"
|
||||
@@ -88,16 +88,15 @@
|
||||
taiga:
|
||||
|
||||
{% set service_name = TAIGA_FRONT_SERVICE %}
|
||||
{% set container_port = 80 %}
|
||||
{{ service_name }}:
|
||||
container_name: {{ TAIGA_CONTAINER }}-{{ service_name }}
|
||||
image: "{{TAIGA_DOCKER_IMAGE_FRONTEND}}:{{ TAIGA_VERSION }}"
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/ >/dev/null || curl -fsS http://127.0.0.1/ >/dev/null"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
{% filter indent(4) %}
|
||||
{% include 'roles/docker-container/templates/healthcheck/http.yml.j2' %}
|
||||
{% endfilter %}
|
||||
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
taiga:
|
||||
# volumes:
|
||||
@@ -152,22 +151,21 @@
|
||||
taiga:
|
||||
|
||||
{% set service_name = 'gateway' %}
|
||||
{% set container_port = 80 %}
|
||||
{{ service_name }}:
|
||||
container_name: {{ TAIGA_CONTAINER }}-{{ service_name }}
|
||||
image: nginx:alpine
|
||||
ports:
|
||||
- "127.0.0.1:{{ ports.localhost.http[application_id] }}:80"
|
||||
- "127.0.0.1:{{ ports.localhost.http[application_id] }}:{{ container_port }}"
|
||||
volumes:
|
||||
- {{ docker_repository_path }}taiga-gateway/taiga.conf:/etc/nginx/conf.d/default.conf
|
||||
- static-data:/taiga/static
|
||||
- media-data:/taiga/media
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1/ >/dev/null || curl -fsS http://127.0.0.1/ >/dev/null"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
{% filter indent(4) %}
|
||||
{% include 'roles/docker-container/templates/healthcheck/http.yml.j2' %}
|
||||
{% endfilter %}
|
||||
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
taiga:
|
||||
depends_on:
|
||||
|
||||
Reference in New Issue
Block a user