Test server: code in a Docker volume instead of a C:\ bind mount
With Docker Desktop on Windows the bind-mounted C:\hotel made every page take ~5-7 s; with the code on the Linux filesystem it takes ~0.1 s. - docker/Dockerfile.server bakes the repo into the image (.dockerignore keeps .env, settings.inc.php and caches out) - docker/server-entrypoint.sh copies the code into the app_code volume on start: pass 1 updates code but skips runtime folders (uploads, generated images, caches, downloaded translations, settings.inc.php), pass 2 adds missing files without overwriting - docker/php-server.ini: OPcache without timestamp checks, larger realpath cache - setup script waits for the container and checks the install marker inside the volume; .gitattributes keeps *.sh LF on Windows checkouts - TESTSERVER.md: speed section and one-time switch from the first version Tested with a fresh stack: full install + demo + hardening, re-run keeps DB/uploads, rebuilt image delivers code changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Build context for docker/Dockerfile.server (repo root).
|
||||||
|
# Runtime data and secrets stay out of the image.
|
||||||
|
.git
|
||||||
|
.env
|
||||||
|
.htaccess
|
||||||
|
config/settings.inc.php
|
||||||
|
cache/smarty/compile/*
|
||||||
|
cache/smarty/cache/*
|
||||||
|
cache/class_index.php
|
||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Shell scripts run inside Linux containers: always LF
|
||||||
|
*.sh text eol=lf
|
||||||
@@ -3,14 +3,26 @@
|
|||||||
# the app only listens on APP_BIND (default: all interfaces, port 8080).
|
# the app only listens on APP_BIND (default: all interfaces, port 8080).
|
||||||
# docker compose -f docker-compose.yml -f docker-compose.server.yml up -d --build
|
# docker compose -f docker-compose.yml -f docker-compose.server.yml up -d --build
|
||||||
# See docker/TESTSERVER.md and docker/nginx-qloapps.conf.
|
# See docker/TESTSERVER.md and docker/nginx-qloapps.conf.
|
||||||
|
#
|
||||||
|
# Speed: the code is baked into the image and copied into the volume "app_code"
|
||||||
|
# (Linux filesystem) on every start. A bind mount from C:\ makes every page ~40x slower.
|
||||||
|
# After "git pull": run the setup script again (rebuilds the image and restarts).
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app:
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/Dockerfile.server
|
||||||
ports: !override
|
ports: !override
|
||||||
- "${APP_BIND:-0.0.0.0:8080}:80"
|
- "${APP_BIND:-0.0.0.0:8080}:80"
|
||||||
|
volumes: !override
|
||||||
|
- app_code:/var/www/html
|
||||||
environment:
|
environment:
|
||||||
QLO_SSL: ${QLO_SSL:-1}
|
QLO_SSL: ${QLO_SSL:-1}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
db:
|
db:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
app_code:
|
||||||
|
|||||||
27
docker/Dockerfile.server
Normal file
27
docker/Dockerfile.server
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Server image: same PHP/Apache as docker/Dockerfile, plus the shop code baked in.
|
||||||
|
# On start, server-entrypoint.sh copies the code into the volume at /var/www/html
|
||||||
|
# (Linux filesystem). Docker Desktop bind mounts from C:\ are ~40x slower for PHP.
|
||||||
|
FROM php:8.3-apache
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
libfreetype6-dev libjpeg62-turbo-dev libpng-dev libwebp-dev \
|
||||||
|
libzip-dev libxml2-dev libicu-dev unzip rsync \
|
||||||
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
|
||||||
|
&& docker-php-ext-install -j"$(nproc)" pdo_mysql mysqli gd zip soap intl opcache \
|
||||||
|
&& a2enmod rewrite headers \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY docker/php.ini /usr/local/etc/php/conf.d/qloapps.ini
|
||||||
|
COPY docker/php-server.ini /usr/local/etc/php/conf.d/zz-server.ini
|
||||||
|
COPY docker/apache-hardening.conf /etc/apache2/conf-enabled/zz-hardening.conf
|
||||||
|
RUN sed -ri 's!AllowOverride None!AllowOverride All!g' /etc/apache2/apache2.conf
|
||||||
|
|
||||||
|
COPY . /opt/qloapps-src
|
||||||
|
COPY docker/server-entrypoint.sh /usr/local/bin/server-entrypoint.sh
|
||||||
|
# Windows checkouts may carry CRLF line endings
|
||||||
|
RUN sed -i 's/\r$//' /usr/local/bin/server-entrypoint.sh && chmod +x /usr/local/bin/server-entrypoint.sh
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
ENTRYPOINT ["server-entrypoint.sh"]
|
||||||
|
CMD ["apache2-foreground"]
|
||||||
@@ -70,6 +70,22 @@ nginx -t && systemctl reload nginx
|
|||||||
|
|
||||||
Wichtig ist `proxy_set_header X-Forwarded-Proto $scheme;` – daran erkennt QloApps HTTPS. Ohne die Zeile gibt es Umleitungsschleifen.
|
Wichtig ist `proxy_set_header X-Forwarded-Proto $scheme;` – daran erkennt QloApps HTTPS. Ohne die Zeile gibt es Umleitungsschleifen.
|
||||||
|
|
||||||
|
## Geschwindigkeit
|
||||||
|
|
||||||
|
Der Code wird ins Image gebaut und beim Start in das Docker-Volume `app_code` (Linux-Dateisystem) kopiert.
|
||||||
|
`C:\hotel` dient nur als Git-Checkout. Liegt der Code dagegen per Bind-Mount auf `C:\`, braucht jede Seite
|
||||||
|
mehrere Sekunden statt ~0,1 s (gemessen: 4,7 s gegenüber 0,13 s).
|
||||||
|
|
||||||
|
**Umstieg von der ersten Version** (Code noch auf `C:\hotel` eingebunden): einmal komplett neu aufsetzen,
|
||||||
|
die Testdaten gehen dabei verloren:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
cd C:\hotel
|
||||||
|
git pull
|
||||||
|
docker compose -f docker-compose.yml -f docker-compose.server.yml down -v
|
||||||
|
powershell -ExecutionPolicy Bypass -File docker\setup-testserver.ps1 -ShopDomain 192.168.178.20:8080 -NoSsl
|
||||||
|
```
|
||||||
|
|
||||||
## Fehlersuche
|
## Fehlersuche
|
||||||
|
|
||||||
QloApps speichert Domain und HTTPS-Modus in der Datenbank und **leitet jeden Aufruf dorthin um**.
|
QloApps speichert Domain und HTTPS-Modus in der Datenbank und **leitet jeden Aufruf dorthin um**.
|
||||||
@@ -98,7 +114,7 @@ docker compose -f docker-compose.yml -f docker-compose.server.yml exec -u www-da
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Status | `docker compose -f docker-compose.yml -f docker-compose.server.yml ps` |
|
| Status | `docker compose -f docker-compose.yml -f docker-compose.server.yml ps` |
|
||||||
| Logs | `docker compose -f docker-compose.yml -f docker-compose.server.yml logs -f app` |
|
| Logs | `docker compose -f docker-compose.yml -f docker-compose.server.yml logs -f app` |
|
||||||
| Update | `git pull`, dann das Setup-Skript erneut ausführen |
|
| Update | `git pull`, dann das Setup-Skript erneut ausführen (baut das Image neu, Daten bleiben) |
|
||||||
| Stoppen | `docker compose -f docker-compose.yml -f docker-compose.server.yml down` (Daten bleiben im Volume `db_data`) |
|
| Stoppen | `docker compose -f docker-compose.yml -f docker-compose.server.yml down` (Daten bleiben im Volume `db_data`) |
|
||||||
| Backup DB | `docker compose exec db mariadb-dump -uroot -p<DB_ROOT_PASSWORD> qloapps > backup.sql` |
|
| Backup DB | `docker compose exec db mariadb-dump -uroot -p<DB_ROOT_PASSWORD> qloapps > backup.sql` |
|
||||||
|
|
||||||
|
|||||||
15
docker/php-server.ini
Normal file
15
docker/php-server.ini
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
; Test/production server tuning (mounted by docker-compose.server.yml).
|
||||||
|
; With Docker Desktop the code sits on a Windows drive mounted into the container,
|
||||||
|
; where every file check is slow. These settings avoid re-checking PHP files.
|
||||||
|
; After a code update (git pull) restart the app container:
|
||||||
|
; docker compose -f docker-compose.yml -f docker-compose.server.yml restart app
|
||||||
|
|
||||||
|
opcache.enable = 1
|
||||||
|
opcache.validate_timestamps = 0
|
||||||
|
opcache.memory_consumption = 256
|
||||||
|
opcache.interned_strings_buffer = 32
|
||||||
|
opcache.max_accelerated_files = 30000
|
||||||
|
opcache.save_comments = 1
|
||||||
|
|
||||||
|
realpath_cache_size = 8M
|
||||||
|
realpath_cache_ttl = 3600
|
||||||
26
docker/server-entrypoint.sh
Normal file
26
docker/server-entrypoint.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Copy the code baked into the image into the web root volume, then start Apache.
|
||||||
|
# pass 1: update code; runtime folders (uploads, generated images, caches, downloaded
|
||||||
|
# translations, config/settings.inc.php) are skipped
|
||||||
|
# pass 2: add any file that is still missing (first start, new defaults), never overwrite
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SRC=/opt/qloapps-src/
|
||||||
|
DST=/var/www/html/
|
||||||
|
|
||||||
|
echo "server-entrypoint: updating code in $DST"
|
||||||
|
rsync -a --chown=www-data:www-data \
|
||||||
|
--exclude='/.git' --exclude='/.env' --exclude='/.htaccess' \
|
||||||
|
--exclude='/config/settings.inc.php' --exclude='/config/xml/' \
|
||||||
|
--exclude='/img/' --exclude='/upload/' --exclude='/download/' \
|
||||||
|
--exclude='/cache/' --exclude='/log/' --exclude='/translations/' --exclude='/mails/' \
|
||||||
|
--exclude='/modules/*/translations/' --exclude='/modules/*/views/img/' --exclude='/modules/*/logs/' \
|
||||||
|
--exclude='/themes/*/lang/' --exclude='/themes/*/cache/' --exclude='/themes/*/modules/*/translations/' \
|
||||||
|
"$SRC" "$DST"
|
||||||
|
|
||||||
|
echo "server-entrypoint: adding missing files"
|
||||||
|
rsync -a --ignore-existing --chown=www-data:www-data \
|
||||||
|
--exclude='/.git' --exclude='/.env' --exclude='/config/settings.inc.php' \
|
||||||
|
"$SRC" "$DST"
|
||||||
|
|
||||||
|
exec docker-php-entrypoint "$@"
|
||||||
@@ -81,10 +81,19 @@ $envVars = Read-EnvFile
|
|||||||
Step 'Build and start containers'
|
Step 'Build and start containers'
|
||||||
Invoke-Docker @compose up -d --build
|
Invoke-Docker @compose up -d --build
|
||||||
|
|
||||||
Step 'File permissions for www-data'
|
Step 'Wait for the app container'
|
||||||
Invoke-Docker @compose exec -T app sh -c 'cd /var/www/html && chown -R www-data:www-data img mails modules translations cache config upload download themes override log'
|
# the entrypoint first copies the code into the volume; wait until Apache answers
|
||||||
|
$ready = $false
|
||||||
|
for ($i = 0; $i -lt 60; $i++) {
|
||||||
|
& docker @compose exec -T app sh -c 'test -f /var/www/html/index.php && curl -s -o /dev/null http://127.0.0.1/robots.txt' 2>$null
|
||||||
|
if ($LASTEXITCODE -eq 0) { $ready = $true; break }
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
if (-not $ready) { throw 'App container did not come up; check: docker compose ... logs app' }
|
||||||
|
|
||||||
if (Test-Path config/settings.inc.php) {
|
# the code lives in the Docker volume, so check the install marker inside the container
|
||||||
|
& docker @compose exec -T app test -f /var/www/html/config/settings.inc.php
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
Step 'QloApps already installed (config/settings.inc.php exists) - skipping install'
|
Step 'QloApps already installed (config/settings.inc.php exists) - skipping install'
|
||||||
} else {
|
} else {
|
||||||
Step 'Install QloApps (German, newsletter off)'
|
Step 'Install QloApps (German, newsletter off)'
|
||||||
|
|||||||
Reference in New Issue
Block a user