fix(Docker): change ENV arg from 'prod' to 'production' to match actual ini filename

This commit is contained in:
2025-10-29 23:26:45 +01:00
parent 70e45fb56e
commit d021c49906
11 changed files with 453 additions and 40 deletions

View File

@@ -51,7 +51,7 @@ RUN curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer && mv composer.phar /usr/local/bin/composer
# Installiere Xdebug nur im Entwicklungsmodus # Installiere Xdebug nur im Entwicklungsmodus
ARG ENV=prod ARG ENV=production
RUN if [ "$ENV" = "dev" ]; then \ RUN if [ "$ENV" = "dev" ]; then \
pecl install xdebug \ pecl install xdebug \
&& docker-php-ext-enable xdebug; \ && docker-php-ext-enable xdebug; \
@@ -108,3 +108,136 @@ RUN apt-get update && apt-get install -y gosu && apt-get clean && rm -rf /var/li
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["php-fpm"] CMD ["php-fpm"]
#############################################
# Production Stages (Extended Build)
#############################################
# Stage: Composer Dependencies (Production)
FROM base AS composer-deps-production
WORKDIR /app
# Copy composer files
COPY composer.json composer.lock ./
# Install production dependencies only
RUN composer install \
--no-dev \
--no-scripts \
--no-autoloader \
--ignore-platform-reqs \
--prefer-dist
# Copy application code
COPY . .
# Generate optimized autoloader
RUN composer dump-autoload --optimize --classmap-authoritative
# Stage: Frontend Build
FROM node:20-alpine AS frontend-build
WORKDIR /app
# Copy package files
COPY package.json package-lock.json ./
# Install npm dependencies
RUN npm ci --production=false
# Copy source files needed for build
COPY resources ./resources
COPY vite.config.js ./
COPY tsconfig.json ./
# Build production assets
RUN npm run build
# Stage: Production Runtime
FROM php:8.5.0RC3-fpm AS production
# Install system dependencies + nginx for production
RUN apt-get update && apt-get install -y \
nginx \
curl \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libwebp-dev \
libavif-dev \
libxpm-dev \
libsodium-dev \
libpq-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Install PHP extensions (same as base)
RUN docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
--with-webp \
--with-avif \
--with-xpm \
&& docker-php-ext-install -j$(nproc) gd
RUN docker-php-ext-install -j$(nproc) \
zip \
pdo \
pdo_mysql \
pdo_pgsql \
pcntl \
posix \
shmop \
bcmath \
sodium
# Install PECL extensions
RUN pecl install apcu redis-6.3.0RC1 \
&& docker-php-ext-enable apcu redis
# Configure APCu
RUN echo "apc.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
&& echo "apc.shm_size=128M" >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
# Set working directory
WORKDIR /var/www/html
# Configure PHP for production
COPY docker/php/php.production.ini /usr/local/etc/php/conf.d/zzz-custom.ini
COPY docker/php/opcache.ini /usr/local/etc/php/conf.d/opcache.ini
# Configure PHP-FPM (clear_env = no for Docker secrets)
COPY docker/php/www.production.conf /usr/local/etc/php-fpm.d/www.conf
# Configure Nginx for Traefik integration
COPY docker/nginx/nginx.production.conf /etc/nginx/nginx.conf
COPY docker/nginx/default.traefik.conf /etc/nginx/sites-available/default
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/default
# Copy application code and dependencies
COPY --chown=www-data:www-data . .
COPY --from=composer-deps-production --chown=www-data:www-data /app/vendor ./vendor
COPY --from=frontend-build --chown=www-data:www-data /app/public/assets ./public/assets
COPY --from=frontend-build --chown=www-data:www-data /app/public/.vite ./public/.vite
# Create required directories
RUN mkdir -p storage/logs storage/cache storage/uploads \
&& chown -R www-data:www-data storage \
&& chmod -R 775 storage
# Copy entrypoint script for production
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD curl -f http://localhost/health || exit 1
# Expose HTTP only (Traefik handles HTTPS)
EXPOSE 80
# Use production entrypoint
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
CMD ["nginx-php-fpm"]

View File

@@ -1,34 +0,0 @@
; php.ini für Produktion
include = php.common.ini
[opcache]
; Aktiviere OPcache
opcache.enable=1
; Aktiviere OPcache für CLI-Anwendungen (optional)
opcache.enable_cli=0
; Maximale Speichernutzung für Cache in MB
opcache.memory_consumption=128
; Maximale Anzahl an gecachten Dateien
opcache.max_accelerated_files=10000
; Wie oft wird der Cache validiert (0 = bei jedem Request)
; TEMPORÄR: 0 für einfachere Deployments während aktiver Entwicklung
; SPÄTER: Auf 60 erhöhen wenn System stabil ist
opcache.revalidate_freq=0
; Cache-Zeitstempel prüfen
; TEMPORÄR: 1 aktiviert für Deployment-Flexibilität
; SPÄTER: Auf 0 setzen für maximale Performance wenn stabil
opcache.validate_timestamps=1
; Performance-Optimierungen
opcache.interned_strings_buffer=16
; JIT (Just-In-Time Compilation) - Optional für PHP 8.0+
opcache.jit_buffer_size=100M
opcache.jit=1255
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
memory_limit = 256M
upload_max_filesize = 10M
post_max_size = 12M
max_execution_time = 30

View File

@@ -10,11 +10,14 @@ opcache.enable_cli=0
opcache.memory_consumption=128 opcache.memory_consumption=128
; Maximale Anzahl an gecachten Dateien ; Maximale Anzahl an gecachten Dateien
opcache.max_accelerated_files=10000 opcache.max_accelerated_files=10000
; Wie oft wird der Cache validiert (0 = bei jedem Request, empfohlen für Entwicklung) ; Wie oft wird der Cache validiert (0 = bei jedem Request)
; In Produktion höher setzen für bessere Performance ; TEMPORÄR: 0 für einfachere Deployments während aktiver Entwicklung
opcache.revalidate_freq=60 ; SPÄTER: Auf 60 erhöhen wenn System stabil ist
; Cache-Zeitstempel prüfen (0 für Produktionsumgebungen) opcache.revalidate_freq=0
opcache.validate_timestamps=0 ; Cache-Zeitstempel prüfen
; TEMPORÄR: 1 aktiviert für Deployment-Flexibilität
; SPÄTER: Auf 0 setzen für maximale Performance wenn stabil
opcache.validate_timestamps=1
; Performance-Optimierungen ; Performance-Optimierungen
opcache.interned_strings_buffer=16 opcache.interned_strings_buffer=16
; JIT (Just-In-Time Compilation) - Optional für PHP 8.0+ ; JIT (Just-In-Time Compilation) - Optional für PHP 8.0+

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Framework\Cache\Strategies;
/**
* Access pattern tracking for adaptive TTL calculation
*/
final class AccessPattern
{
/** @var array<int, Timestamp> */
private array $accessTimes = [];
public function __construct(
private readonly int $windowSize
) {}
public function recordAccess(): void
{
$this->accessTimes[] = Timestamp::now();
// Keep only recent accesses within the window
if (count($this->accessTimes) > $this->windowSize) {
array_shift($this->accessTimes);
}
}
public function getRecentAccessCount(): int
{
return count($this->accessTimes);
}
public function getTotalAccesses(): int
{
return count($this->accessTimes);
}
/**
* Calculate access frequency (accesses per hour)
*/
public function getAccessFrequency(): float
{
if (count($this->accessTimes) < 2) {
return 0.0;
}
$oldest = $this->accessTimes[0];
$newest = end($this->accessTimes);
$timeSpan = $newest->diff($oldest);
if ($timeSpan->toSeconds() <= 0) {
return 0.0;
}
$hours = $timeSpan->toSeconds() / 3600.0;
return count($this->accessTimes) / $hours;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Framework\Cache\Strategies;
/**
* Statistics for adaptive TTL optimization
*/
final class AdaptiveTtlStats
{
private int $hits = 0;
private int $misses = 0;
public function recordHitMiss(bool $isHit): void
{
if ($isHit) {
$this->hits++;
} else {
$this->misses++;
}
}
public function getHitRate(): float
{
$total = $this->hits + $this->misses;
return $total > 0 ? ($this->hits / $total) : 0.0;
}
public function getTotalRequests(): int
{
return $this->hits + $this->misses;
}
public function getHits(): int
{
return $this->hits;
}
public function getMisses(): int
{
return $this->misses;
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace App\Framework\Cache\Strategies;
/**
* Heat map entry for tracking cache key usage
*/
final class HeatMapEntry
{
/** @var array<array{timestamp: Timestamp, is_hit: bool, retrieval_time: ?float}> */
private array $accesses = [];
private int $totalHits = 0;
private int $totalMisses = 0;
private float $totalRetrievalTime = 0.0;
private int $retrievalTimeCount = 0;
public function __construct(
private readonly CacheKey $key
) {}
public function recordAccess(bool $isHit, ?Duration $retrievalTime = null): void
{
$this->accesses[] = [
'timestamp' => Timestamp::now(),
'is_hit' => $isHit,
'retrieval_time' => $retrievalTime?->toSeconds(),
];
if ($isHit) {
$this->totalHits++;
} else {
$this->totalMisses++;
}
if ($retrievalTime !== null) {
$this->totalRetrievalTime += $retrievalTime->toSeconds();
$this->retrievalTimeCount++;
}
// Keep only recent data to prevent memory bloat
$cutoff = Timestamp::now()->subtract(Duration::fromHours(48)); // Keep 48 hours
$this->accesses = array_filter(
$this->accesses,
fn($access) => $access['timestamp']->isAfter($cutoff)
);
}
public function getRecentAccesses(Timestamp $since): array
{
return array_filter(
$this->accesses,
fn($access) => $access['timestamp']->isAfter($since)
);
}
public function getHitRate(): float
{
$total = $this->totalHits + $this->totalMisses;
return $total > 0 ? ($this->totalHits / $total) : 0.0;
}
public function getAverageRetrievalTime(): float
{
return $this->retrievalTimeCount > 0 ? ($this->totalRetrievalTime / $this->retrievalTimeCount) : 0.0;
}
public function getTotalAccesses(): int
{
return count($this->accesses);
}
public function getLastAccessTime(): ?Timestamp
{
if (empty($this->accesses)) {
return null;
}
return end($this->accesses)['timestamp'];
}
public function getKey(): CacheKey
{
return $this->key;
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace App\Framework\Cache\Strategies;
/**
* Prediction pattern for a cache key
*/
final class PredictionPattern
{
/** @var array<array{timestamp: Timestamp, context: array}> */
private array $accesses = [];
/** @var array<CacheKey> */
private array $dependencies = [];
private mixed $warmingCallback = null;
public function __construct(
private readonly CacheKey $key
) {}
public function recordAccess(Timestamp $timestamp, array $context = []): void
{
$this->accesses[] = [
'timestamp' => $timestamp,
'context' => $context,
];
// Keep only recent accesses to prevent memory bloat
$cutoff = $timestamp->subtract(Duration::fromHours(168)); // 1 week
$this->accesses = array_filter(
$this->accesses,
fn($access) => $access['timestamp']->isAfter($cutoff)
);
}
public function addDependency(CacheKey $dependentKey): void
{
$this->dependencies[] = $dependentKey;
}
public function setWarmingCallback(callable $callback): void
{
$this->warmingCallback = $callback;
}
public function getKey(): CacheKey
{
return $this->key;
}
public function getRecentAccesses(int $hours): array
{
$cutoff = Timestamp::now()->subtract(Duration::fromHours($hours));
return array_filter(
$this->accesses,
fn($access) => $access['timestamp']->isAfter($cutoff)
);
}
public function getDependencies(): array
{
return $this->dependencies;
}
public function getWarmingCallback(): ?callable
{
return $this->warmingCallback;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Framework\Cache\Strategies;
/**
* Active warming job
*/
final readonly class WarmingJob
{
public function __construct(
public CacheKey $key,
public mixed $callback,
public string $reason,
public Timestamp $startTime
) {}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Framework\Cache\Strategies;
/**
* Warming operation result
*/
final readonly class WarmingResult
{
public function __construct(
public CacheKey $key,
public bool $successful,
public Duration $duration,
public string $reason
) {}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Framework\Cache\Strategies;
/**
* Write operation tracking
*/
final readonly class WriteOperation
{
public function __construct(
public CacheKey $key,
public int $valueSize,
public Duration $writeTime,
public Timestamp $timestamp
) {}
}