mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-11-08 06:08:05 +00:00
feat(shopware): migrate to single Shopware base image and split services (web/worker/scheduler/init)
• Introduce init container and runtime-ready Dockerfile (Alpine) installing php83-gd/intl/pdo_mysql • Disable composer scripts in builder and ignore build-time ext reqs • New docker-compose template (web/worker/scheduler/opensearch) + persistent volumes • Use TRUSTED_PROXIES env; fix APP_URL formatting; set OPENSEARCH_HOST=opensearch • Replace SHOPWARE_PHP_CONTAINER refs with SHOPWARE_WEB_CONTAINER in tasks • Render and copy init.sh via volumes path • Remove old nginx/php split and legacy DB env task • Fix svc-db-postgres var: database_type now uses entity_name https://chatgpt.com/share/6907fc58-7c28-800f-a993-c207f28859c9
This commit is contained in:
90
roles/web-app-shopware/templates/Dockerfile.j2
Normal file
90
roles/web-app-shopware/templates/Dockerfile.j2
Normal file
@@ -0,0 +1,90 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 without installing
|
||||
RUN set -eux; \
|
||||
composer create-project "${SHOPWARE_PROD_VERSION}" /app --no-install
|
||||
|
||||
# 2) Install deps WITHOUT running scripts and ignoring build-time ext reqs
|
||||
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
|
||||
|
||||
############################
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# Copy the init script that your Compose mounts as volumes/init.sh in the build context
|
||||
COPY --chown=www-data:www-data volumes/init.sh /usr/local/bin/init.sh
|
||||
RUN chmod +x /usr/local/bin/init.sh
|
||||
|
||||
# Drop back to the app user
|
||||
USER www-data
|
||||
|
||||
# Default envs (override via .env / compose env_file)
|
||||
ENV APP_ENV=prod \
|
||||
APP_URL=http://localhost:8000 \
|
||||
TRUSTED_PROXIES=127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
|
||||
|
||||
# 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);'
|
||||
@@ -1,36 +1,81 @@
|
||||
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
|
||||
working_dir: {{ SHOPWARE_ROOT }}
|
||||
|
||||
{% include 'roles/docker-compose/templates/base.yml.j2' %}
|
||||
|
||||
{% set service_name = 'php' %}
|
||||
# -------------------------
|
||||
# INIT (runs once per deployment)
|
||||
# -------------------------
|
||||
{% set service_name = 'init' %}
|
||||
{% set docker_restart_policy = 'no' %}
|
||||
{{ service_name }}:
|
||||
{% include 'roles/docker-container/templates/base.yml.j2' %}
|
||||
image: "{{ SHOPWARE_PHP_IMAGE }}:{{ SHOPWARE_VERSION }}"
|
||||
container_name: "{{ SHOPWARE_PHP_CONTAINER }}"
|
||||
working_dir: /var/www/html
|
||||
volumes:
|
||||
- data:/var/www/html
|
||||
- /etc/timezone:/etc/timezone:ro
|
||||
- /etc/localtime:/etc/localtime:ro
|
||||
{% include 'roles/docker-container/templates/networks.yml.j2' %}
|
||||
{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %}
|
||||
{% set docker_restart_policy = DOCKER_RESTART_POLICY %}
|
||||
<<: *shopware
|
||||
container_name: "{{ SHOPWARE_INIT_CONTAINER }}"
|
||||
entrypoint: [ "sh", "/usr/local/bin/init.sh" ]
|
||||
|
||||
{% set service_name = 'nginx' %}
|
||||
{% set container_port = 80 %}
|
||||
{# -------------------------
|
||||
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' %}
|
||||
image: "{{ SHOPWARE_NGINX_IMAGE }}:{{ SHOPWARE_NGINX_VERSION }}"
|
||||
container_name: "{{ SHOPWARE_NGINX_CONTAINER }}"
|
||||
<<: *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 }}"
|
||||
volumes:
|
||||
- data:/var/www/html:ro
|
||||
depends_on:
|
||||
- php
|
||||
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 }}:
|
||||
@@ -44,11 +89,24 @@
|
||||
- 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' %}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# DOMAIN/URL
|
||||
DOMAIN={{ domains | get_domain(application_id) }}
|
||||
APP_URL="{{ domains | get_url(application_id, WEB_PROTOCOL) }}/"
|
||||
APP_URL="{{ domains | get_url(application_id, WEB_PROTOCOL) }}"
|
||||
|
||||
# Shopware
|
||||
APP_ENV={{ 'dev' if (ENVIRONMENT | lower) == 'development' else 'prod' }}
|
||||
APP_URL_TRUSTED_PROXIES=127.0.0.1
|
||||
TRUSTED_PROXIES=127.0.0.1
|
||||
INSTANCE_ID={{ application_id }}
|
||||
|
||||
# Database
|
||||
@@ -19,12 +19,11 @@ MESSENGER_TRANSPORT_DSN="redis://{{ SHOPWARE_REDIS_ADDRESS }}/2"
|
||||
CACHE_URL="file://cache"
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if SHOPWARE_OPENSEARCH_ENABLED %}
|
||||
# Search
|
||||
ELASTICSEARCH_URL="http://opensearch:9200"
|
||||
OPENSEARCH_URL="http://opensearch:9200"
|
||||
OPENSEARCH_HOST="search"
|
||||
OPENSEARCH_HOST="opensearch"
|
||||
OPENSEARCH_PORT_NUMBER="9200"
|
||||
OPENSEARCH_INITIAL_ADMIN_PASSWORD="{{ users.administrator.password }}"
|
||||
{% endif %}
|
||||
|
||||
49
roles/web-app-shopware/templates/init.sh.j2
Normal file
49
roles/web-app-shopware/templates/init.sh.j2
Normal file
@@ -0,0 +1,49 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
cd {{ SHOPWARE_ROOT }}
|
||||
|
||||
mkdir -p {{ SHOPWARE_ROOT }}/.infinito
|
||||
MARKER="{{ SHOPWARE_ROOT }}/.infinito/installed"
|
||||
|
||||
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] Installing Shopware..."
|
||||
php -d memory_limit=1024M bin/console system:install --basic-setup --create-database --force
|
||||
php -d memory_limit=1024M bin/console database:migrate --all
|
||||
php -d memory_limit=1024M bin/console database:migrate-destructive --all
|
||||
php bin/console user:create "{{ users.administrator.username }}" \
|
||||
--admin --password="{{ users.administrator.password }}" \
|
||||
--firstName="Admin" --lastName="User" --email="{{ users.administrator.email }}" || true
|
||||
php bin/console cache:clear || true
|
||||
php bin/console dal:refresh:index || true
|
||||
touch "$MARKER"
|
||||
chown -R {{ SHOPWARE_USER }}:{{ SHOPWARE_USER }} {{ SHOPWARE_ROOT }}
|
||||
echo "[INIT] Done."
|
||||
else
|
||||
echo "[INIT] Marker found, skipping install."
|
||||
fi
|
||||
Reference in New Issue
Block a user