Files
dreamweaver/docker-compose.ha.yml
zhangtuo c82d408ea1 feat: add HA infrastructure, CI/CD pipeline, and Redis/Celery hardening
- Add docker-compose.ha.yml for PostgreSQL/Redis HA setup with Patroni and Sentinel
- Add docker-compose.prod.yml for production deployment
- Add GitHub Actions CI/CD workflow (build.yml)
- Add install.cmd for Windows one-click setup
- Harden Redis connection with retry logic and health checks
- Add Celery HA config with Redis Sentinel support
- Add HA operations runbook
- Update README with deployment and architecture docs
- Move landing page spec to .claude/specs/design/
- Update memory intelligence PRD
2026-02-28 14:57:02 +08:00

311 lines
9.5 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# docker-compose.ha.yml
# HA 覆盖配置(建议与 docker-compose.yml 叠加使用)
#
# 启动示例:
# docker compose -f docker-compose.yml -f docker-compose.ha.yml up -d
services:
# ==============================================
# 应用服务 Sentinel 配置覆盖
# ==============================================
backend:
environment:
- DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER:-dreamweaver}:${POSTGRES_PASSWORD:-dreamweaver_password}@db:5432/${POSTGRES_DB:-dreamweaver_db}
- REDIS_SENTINEL_ENABLED=true
- REDIS_SENTINEL_NODES=redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379
- REDIS_SENTINEL_MASTER_NAME=mymaster
- REDIS_SENTINEL_DB=0
- REDIS_SENTINEL_SOCKET_TIMEOUT=0.5
backend-admin:
environment:
- DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER:-dreamweaver}:${POSTGRES_PASSWORD:-dreamweaver_password}@db:5432/${POSTGRES_DB:-dreamweaver_db}
- REDIS_SENTINEL_ENABLED=true
- REDIS_SENTINEL_NODES=redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379
- REDIS_SENTINEL_MASTER_NAME=mymaster
- REDIS_SENTINEL_DB=0
- REDIS_SENTINEL_SOCKET_TIMEOUT=0.5
worker:
environment:
- DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER:-dreamweaver}:${POSTGRES_PASSWORD:-dreamweaver_password}@db:5432/${POSTGRES_DB:-dreamweaver_db}
- REDIS_SENTINEL_ENABLED=true
- REDIS_SENTINEL_NODES=redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379
- REDIS_SENTINEL_MASTER_NAME=mymaster
- REDIS_SENTINEL_DB=0
- REDIS_SENTINEL_SOCKET_TIMEOUT=0.5
celery-beat:
environment:
- DATABASE_URL=postgresql+asyncpg://${POSTGRES_USER:-dreamweaver}:${POSTGRES_PASSWORD:-dreamweaver_password}@db:5432/${POSTGRES_DB:-dreamweaver_db}
- REDIS_SENTINEL_ENABLED=true
- REDIS_SENTINEL_NODES=redis-sentinel-1:26379,redis-sentinel-2:26379,redis-sentinel-3:26379
- REDIS_SENTINEL_MASTER_NAME=mymaster
- REDIS_SENTINEL_DB=0
- REDIS_SENTINEL_SOCKET_TIMEOUT=0.5
# ==============================================
# PostgreSQL 主库(覆盖默认 db
# ==============================================
db:
image: postgres:15-alpine
container_name: dreamweaver_db_primary
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-dreamweaver}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-dreamweaver_password}
POSTGRES_DB: ${POSTGRES_DB:-dreamweaver_db}
command:
- postgres
- -c
- wal_level=replica
- -c
- max_wal_senders=10
- -c
- max_replication_slots=10
- -c
- hot_standby=on
- -c
- hba_file=/etc/postgresql/pg_hba.conf
ports:
- "52432:5432"
volumes:
- postgres_primary_data:/var/lib/postgresql/data
- ./ops/postgres-ha/pg_hba.conf:/etc/postgresql/pg_hba.conf:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-dreamweaver} -d ${POSTGRES_DB:-dreamweaver_db}"]
interval: 10s
timeout: 5s
retries: 10
# ==============================================
# PostgreSQL 从库(基于 pg_basebackup 初始化)
# ==============================================
db-replica:
image: postgres:15-alpine
container_name: dreamweaver_db_replica
restart: unless-stopped
user: "postgres"
environment:
POSTGRES_USER: ${POSTGRES_USER:-dreamweaver}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-dreamweaver_password}
POSTGRES_DB: ${POSTGRES_DB:-dreamweaver_db}
PGDATA: /var/lib/postgresql/data
depends_on:
db:
condition: service_healthy
volumes:
- postgres_replica_data:/var/lib/postgresql/data
command:
- /bin/sh
- -ec
- |
if [ ! -s "$$PGDATA/PG_VERSION" ]; then
echo "Initializing replica from primary..."
until pg_isready -h db -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"; do sleep 2; done
export PGPASSWORD="$$POSTGRES_PASSWORD"
rm -rf "$$PGDATA"/*
pg_basebackup -h db -D "$$PGDATA" -U "$$POSTGRES_USER" -Fp -Xs -P -R
fi
chmod 700 "$$PGDATA"
exec postgres -c hot_standby=on
healthcheck:
test:
[
"CMD-SHELL",
"pg_isready -U ${POSTGRES_USER:-dreamweaver} -d ${POSTGRES_DB:-dreamweaver_db} && psql -U ${POSTGRES_USER:-dreamweaver} -d ${POSTGRES_DB:-dreamweaver_db} -tAc 'select pg_is_in_recovery();' | grep -q t",
]
interval: 10s
timeout: 5s
retries: 10
# ==============================================
# PostgreSQL 备份任务(每日一次,保留 7 天)
# ==============================================
postgres-backup:
image: postgres:15-alpine
container_name: dreamweaver_postgres_backup
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-dreamweaver}
POSTGRES_DB: ${POSTGRES_DB:-dreamweaver_db}
PGPASSWORD: ${POSTGRES_PASSWORD:-dreamweaver_password}
BACKUP_INTERVAL_SECONDS: ${BACKUP_INTERVAL_SECONDS:-86400}
depends_on:
db:
condition: service_healthy
volumes:
- postgres_backups:/backups
command:
- /bin/sh
- -ec
- |
while true; do
ts=$$(date +%Y%m%d_%H%M%S);
pg_dump -h db -U "$$POSTGRES_USER" -d "$$POSTGRES_DB" -F c -f "/backups/dreamweaver_$${ts}.dump";
find /backups -type f -name '*.dump' -mtime +7 -delete;
sleep "$$BACKUP_INTERVAL_SECONDS";
done
# ==============================================
# Redis 主库(覆盖默认 redis
# ==============================================
redis:
image: redis:7-alpine
container_name: dreamweaver_redis_master
restart: unless-stopped
ports:
- "52379:6379"
volumes:
- redis_master_data:/data
command: ["redis-server", "--appendonly", "yes", "--protected-mode", "no"]
networks:
default:
ipv4_address: 172.29.0.10
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
# ==============================================
# Redis 从库
# ==============================================
redis-replica:
image: redis:7-alpine
container_name: dreamweaver_redis_replica
restart: unless-stopped
depends_on:
redis:
condition: service_healthy
volumes:
- redis_replica_data:/data
command:
[
"redis-server",
"--appendonly",
"yes",
"--protected-mode",
"no",
"--replicaof",
"172.29.0.10",
"6379",
]
networks:
default:
ipv4_address: 172.29.0.11
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 10
# ==============================================
# Redis Sentinel (3 节点)
# ==============================================
redis-sentinel-1:
image: redis:7-alpine
container_name: dreamweaver_redis_sentinel_1
restart: unless-stopped
ports:
- "52631:26379"
depends_on:
redis:
condition: service_healthy
redis-replica:
condition: service_healthy
networks:
default:
ipv4_address: 172.29.0.21
command:
- /bin/sh
- -ec
- |
cat > /tmp/sentinel.conf <<EOF
port 26379
sentinel resolve-hostnames yes
sentinel announce-hostnames yes
sentinel monitor mymaster 172.29.0.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
protected-mode no
dir /tmp
EOF
exec redis-sentinel /tmp/sentinel.conf
redis-sentinel-2:
image: redis:7-alpine
container_name: dreamweaver_redis_sentinel_2
restart: unless-stopped
ports:
- "52632:26379"
depends_on:
redis:
condition: service_healthy
redis-replica:
condition: service_healthy
networks:
default:
ipv4_address: 172.29.0.22
command:
- /bin/sh
- -ec
- |
cat > /tmp/sentinel.conf <<EOF
port 26379
sentinel resolve-hostnames yes
sentinel announce-hostnames yes
sentinel monitor mymaster 172.29.0.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
protected-mode no
dir /tmp
EOF
exec redis-sentinel /tmp/sentinel.conf
redis-sentinel-3:
image: redis:7-alpine
container_name: dreamweaver_redis_sentinel_3
restart: unless-stopped
ports:
- "52633:26379"
depends_on:
redis:
condition: service_healthy
redis-replica:
condition: service_healthy
networks:
default:
ipv4_address: 172.29.0.23
command:
- /bin/sh
- -ec
- |
cat > /tmp/sentinel.conf <<EOF
port 26379
sentinel resolve-hostnames yes
sentinel announce-hostnames yes
sentinel monitor mymaster 172.29.0.10 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1
protected-mode no
dir /tmp
EOF
exec redis-sentinel /tmp/sentinel.conf
volumes:
postgres_primary_data:
postgres_replica_data:
postgres_backups:
redis_master_data:
redis_replica_data:
networks:
default:
ipam:
config:
- subnet: 172.29.0.0/24