feat(deployment): migrate to external Redis stack

Architecture Changes:
- Remove embedded Redis service from production configuration
- Remove port 80/443 direct bindings (Traefik handles routing)
- Update queue-worker and scheduler dependencies (Redis now external)
- Completes Redis stack migration following PostgreSQL pattern
- Resolves port conflict with Traefik reverse proxy

Related Files:
- deployment/stacks/redis/docker-compose.yml (Redis stack - already deployed)
- docker-compose.redis-override.yml (application integration - already created)

Migration Benefits:
- Architectural consistency with PostgreSQL stack pattern
- Better separation of concerns (infrastructure vs application)
- Independent Redis lifecycle management
- Shared Redis instance via app-internal network
- Eliminated port 80 binding conflict

Deployment Command:
docker compose -f docker-compose.base.yml -f docker-compose.production.yml \
  -f docker-compose.postgres-override.yml -f docker-compose.redis-override.yml up -d
This commit is contained in:
2025-11-04 22:31:37 +01:00
parent 7246e89448
commit 5d6edea3bb

View File

@@ -19,10 +19,8 @@ services:
# Production restart policy # Production restart policy
restart: always restart: always
# Production port mappings for Let's Encrypt # Note: Port mappings removed - Traefik handles all external routing
ports: # No direct port bindings needed, Traefik proxies traffic to this container
- "80:80" # HTTP for ACME challenge
- "443:443" # HTTPS for production traffic
# Override volumes - use Let's Encrypt certificates and mount application code # Override volumes - use Let's Encrypt certificates and mount application code
volumes: volumes:
@@ -152,82 +150,8 @@ services:
# Database service removed - using external PostgreSQL Stack (deployment/stacks/postgresql/) # Database service removed - using external PostgreSQL Stack (deployment/stacks/postgresql/)
# Connection via app-internal network using docker-compose.postgres-override.yml # Connection via app-internal network using docker-compose.postgres-override.yml
redis: # Redis service removed - using external Redis Stack (deployment/stacks/redis/)
# Production restart policy # Connection via app-internal network using docker-compose.redis-override.yml
restart: always
# Use Docker Secrets for Redis password
environment:
REDIS_PASSWORD_FILE: /run/secrets/redis_password
secrets:
- redis_password
# Security hardening
security_opt:
- no-new-privileges:true
# Don't set user here - we need root to read Docker Secrets in entrypoint
# Redis will run as root, but this is acceptable for this use case
cap_drop:
- ALL
cap_add:
- CHOWN # Required for creating appendonlydir with correct permissions
- DAC_OVERRIDE # Required for writing to /data volume owned by redis user
# Use entrypoint script to inject password from Docker Secret into environment
# This makes password available to both Redis startup AND health check
# Note: Script runs as root to read Docker Secrets, then starts Redis
entrypoint: ["/bin/sh", "-c"]
command:
- |
# Read password from Docker Secret (as root) and export for health check
export REDIS_PASSWORD=$(cat /run/secrets/redis_password 2>/dev/null || echo '')
# Start Redis with all settings as command line arguments (no config file to avoid conflicts)
if [ -n "$REDIS_PASSWORD" ]; then
exec redis-server \
--bind 0.0.0.0 \
--dir /data \
--save 900 1 \
--save 300 10 \
--save 60 10000 \
--appendonly yes \
--requirepass "$REDIS_PASSWORD"
else
exec redis-server \
--bind 0.0.0.0 \
--dir /data \
--save 900 1 \
--save 300 10 \
--save 60 10000 \
--appendonly yes
fi
# Production resource limits
deploy:
resources:
limits:
memory: 512M
cpus: '1.0'
reservations:
memory: 256M
cpus: '0.5'
# Stricter health checks
# Health check reads password directly from Docker Secret
healthcheck:
test: ["CMD-SHELL", "redis-cli -a \"$(cat /run/secrets/redis_password 2>/dev/null || echo '')\" ping | grep -q PONG"]
interval: 10s
timeout: 3s
retries: 5
start_period: 30s
# JSON logging
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
compress: "true"
labels: "service,environment"
queue-worker: queue-worker:
# Use pre-built image from registry (override build from base) # Use pre-built image from registry (override build from base)
@@ -292,11 +216,9 @@ services:
# Wait for dependencies to be healthy before starting # Wait for dependencies to be healthy before starting
depends_on: depends_on:
redis:
condition: service_healthy
php: php:
condition: service_healthy condition: service_healthy
# Note: PostgreSQL (postgres) is external service, connection via app-internal network # Note: PostgreSQL and Redis are external services, connection via app-internal network
# Scheduler (Cron Jobs) # Scheduler (Cron Jobs)
scheduler: scheduler:
@@ -366,11 +288,9 @@ services:
# Wait for dependencies to be healthy before starting # Wait for dependencies to be healthy before starting
depends_on: depends_on:
redis:
condition: service_healthy
php: php:
condition: service_healthy condition: service_healthy
# Note: PostgreSQL (postgres) is external service, connection via app-internal network # Note: PostgreSQL and Redis are external services, connection via app-internal network
# Certbot Sidecar Container for Let's Encrypt # Certbot Sidecar Container for Let's Encrypt
certbot: certbot: