1
0
forked from erp-dev/erp

feat: split the docker compose config file

This commit is contained in:
2025-12-22 16:17:28 +08:00
parent 6dbc4be6d8
commit 5b160ac393
16 changed files with 465 additions and 198274 deletions

23
.dockerignore Normal file
View File

@@ -0,0 +1,23 @@
.git
.idea
.vscode
.cursor
__pycache__/
*.py[cod]
*.pyd
.pytest_cache
.mypy_cache
.ruff_cache
.coverage
htmlcov/
# 本地环境变量(不要打进发布镜像)
.env
# 本地备份文件(体积大且可能包含敏感数据)
data-bak/
*.sql
# Celery Beat 本地持久化调度文件(不要进镜像,也不要污染构建上下文)
celerybeat-schedule*

View File

@@ -20,10 +20,16 @@ RUN pip install --upgrade pip \
COPY . /app
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
CMD ["python", "manage.py", "runserver", "0.0.0.0:8100"]
FROM base AS celery
CMD ["celery", "-A", "flower", "worker", "-l", "info", "--concurrency=2"]
# 默认产物:runtime 镜像(用于 web / celery_worker / celery_beat 共用,通过 compose 覆盖 command)
FROM base AS runtime
CMD ["uvicorn", "flower.asgi:application", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

150
docker-compose.dev.yml Normal file
View File

@@ -0,0 +1,150 @@
version: "3.8"
services:
postgres:
image: postgres:16-alpine
container_name: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: flower
ports:
- "5432:5432"
volumes:
- db_data:/var/lib/postgresql/data
restart: unless-stopped
pgadmin:
image: dpage/pgadmin4:latest
container_name: pgadmin
environment:
PGADMIN_DEFAULT_EMAIL: admin@admin.com
PGADMIN_DEFAULT_PASSWORD: admin
PGADMIN_CONFIG_SERVER_MODE: "False"
ports:
- "5050:80"
volumes:
- pgadmin_data:/var/lib/pgadmin
depends_on:
- postgres
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
rabbitmq:
image: rabbitmq:3-management-alpine
container_name: rabbitmq
environment:
RABBITMQ_DEFAULT_USER: guest
RABBITMQ_DEFAULT_PASS: guest
ports:
- "5672:5672"
- "15672:15672"
volumes:
- rabbitmq_data:/var/lib/rabbitmq
restart: unless-stopped
web:
build:
context: .
target: base
container_name: flower_web
command: python manage.py runserver 0.0.0.0:8100
ports:
- "8100:8100"
volumes:
- .:/app
environment:
DJANGO_SETTINGS_MODULE: flower.settings
PYTHONPATH: /app
DEBUG: "1"
ALLOWED_HOSTS: "localhost,127.0.0.1,0.0.0.0,8.148.215.233,yuwenerp.yuwen.cloud"
DB_HOST: postgres
DB_PORT: "5432"
DB_NAME: flower
DB_USER: postgres
DB_PASSWORD: postgres
depends_on:
- postgres
- redis
- rabbitmq
restart: unless-stopped
celery_worker:
build:
context: .
target: base
container_name: celery_worker
command:
- celery
- -A
- flower
- worker
- -l
- info
- --concurrency=2
- --pool=solo
volumes:
- .:/app
depends_on:
- postgres
- redis
- rabbitmq
environment:
CELERY_BROKER_URL: amqp://guest:guest@rabbitmq:5672//
CELERY_RESULT_BACKEND: redis://redis:6379/0
PYTHONPATH: /app
DB_HOST: postgres
DB_PORT: "5432"
DB_NAME: flower
DB_USER: postgres
DB_PASSWORD: postgres
restart: unless-stopped
celery_beat:
build:
context: .
target: base
container_name: celery_beat
command:
- celery
- -A
- flower
- beat
- -l
- info
- --schedule
- /var/lib/celery/celerybeat-schedule
volumes:
- .:/app
- celery_beat_data:/var/lib/celery
depends_on:
- postgres
- redis
- rabbitmq
environment:
DJANGO_SETTINGS_MODULE: flower.settings
PYTHONPATH: /app
DB_HOST: postgres
DB_PORT: "5432"
DB_NAME: flower
DB_USER: postgres
DB_PASSWORD: postgres
CELERY_BROKER_URL: amqp://guest:guest@rabbitmq:5672//
CELERY_RESULT_BACKEND: redis://redis:6379/0
restart: unless-stopped
volumes:
db_data:
redis_data:
pgadmin_data:
rabbitmq_data:
celery_beat_data:

135
docker-compose.prod.yml Normal file
View File

@@ -0,0 +1,135 @@
version: "3.8"
services:
postgres:
image: postgres:16-alpine
container_name: postgres
environment:
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
POSTGRES_DB: ${POSTGRES_DB:-flower}
# 仅绑定到本机,避免把数据库暴露到公网
ports:
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
volumes:
- db_data:/var/lib/postgresql/data
restart: unless-stopped
redis:
image: redis:7-alpine
container_name: redis
volumes:
- redis_data:/data
restart: unless-stopped
rabbitmq:
image: rabbitmq:3-management-alpine
container_name: rabbitmq
environment:
RABBITMQ_DEFAULT_USER: ${RABBITMQ_DEFAULT_USER:-guest}
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_DEFAULT_PASS:-guest}
volumes:
- rabbitmq_data:/var/lib/rabbitmq
restart: unless-stopped
web:
image: ${FLOWER_IMAGE:-flower-app:latest}
container_name: flower_web
command:
- uvicorn
- flower.asgi:application
- --host
- 0.0.0.0
- --port
- "8000"
- --workers
- "${UVICORN_WORKERS:-1}"
ports:
- "${WEB_PORT:-8000}:8000"
environment:
DJANGO_SETTINGS_MODULE: flower.settings
PYTHONPATH: /app
DEBUG: "${DEBUG:-0}"
ALLOWED_HOSTS: "${ALLOWED_HOSTS:-yuwenerp.yuwen.cloud}"
DB_HOST: ${DB_HOST:-postgres}
DB_PORT: "${DB_PORT:-5432}"
DB_NAME: "${DB_NAME:-flower}"
DB_USER: "${DB_USER:-postgres}"
DB_PASSWORD: "${DB_PASSWORD:-postgres}"
CELERY_BROKER_URL: "${CELERY_BROKER_URL:-amqp://guest:guest@rabbitmq:5672//}"
CELERY_RESULT_BACKEND: "${CELERY_RESULT_BACKEND:-redis://redis:6379/0}"
depends_on:
- postgres
- redis
- rabbitmq
restart: unless-stopped
celery_worker:
image: ${FLOWER_IMAGE:-flower-app:latest}
container_name: celery_worker
command:
- celery
- -A
- flower
- worker
- -l
- info
- --concurrency=${CELERY_CONCURRENCY:-2}
- --pool=solo
environment:
PYTHONPATH: /app
DB_HOST: ${DB_HOST:-postgres}
DB_PORT: "${DB_PORT:-5432}"
DB_NAME: "${DB_NAME:-flower}"
DB_USER: "${DB_USER:-postgres}"
DB_PASSWORD: "${DB_PASSWORD:-postgres}"
CELERY_BROKER_URL: "${CELERY_BROKER_URL:-amqp://guest:guest@rabbitmq:5672//}"
CELERY_RESULT_BACKEND: "${CELERY_RESULT_BACKEND:-redis://redis:6379/0}"
# 数据库备份输出目录(api_v1.tasks.backup_database 默认输出到 BASE_DIR/data-bak)
volumes:
# 默认落在 compose 同级目录的 ./data-bak,方便宿主机直接查看/拷贝
# 也可通过 BACKUP_DIR 指定绝对路径,如 /srv/flower/data-bak
- ${BACKUP_DIR:-./data-bak}:/app/data-bak
depends_on:
- postgres
- redis
- rabbitmq
restart: unless-stopped
celery_beat:
image: ${FLOWER_IMAGE:-flower-app:latest}
container_name: celery_beat
command:
- celery
- -A
- flower
- beat
- -l
- info
- --schedule
- /var/lib/celery/celerybeat-schedule
environment:
DJANGO_SETTINGS_MODULE: flower.settings
PYTHONPATH: /app
DB_HOST: ${DB_HOST:-postgres}
DB_PORT: "${DB_PORT:-5432}"
DB_NAME: "${DB_NAME:-flower}"
DB_USER: "${DB_USER:-postgres}"
DB_PASSWORD: "${DB_PASSWORD:-postgres}"
CELERY_BROKER_URL: "${CELERY_BROKER_URL:-amqp://guest:guest@rabbitmq:5672//}"
CELERY_RESULT_BACKEND: "${CELERY_RESULT_BACKEND:-redis://redis:6379/0}"
# Celery Beat 的持久化调度文件,避免重启后重复/错过调度
volumes:
- celery_beat_data:/var/lib/celery
depends_on:
- postgres
- redis
- rabbitmq
restart: unless-stopped
volumes:
db_data:
redis_data:
rabbitmq_data:
celery_beat_data:

View File

@@ -0,0 +1,148 @@
# 部署/开发运行说明(Docker Compose)
本项目已拆分为两套 Compose 文件:
- **开发环境**:`docker-compose.dev.yml`(源码挂载到容器,适合本地开发调试)
- **正式发布**:`docker-compose.prod.yml`(运行预构建镜像,不挂载源码,适合可分发部署)
> 旧的 `docker-compose.yml` 可视为历史文件;建议后续统一使用 dev/prod 两份文件。
---
## 开发环境如何启动(dev)
### 1)启动
在项目根目录执行:
```bash
docker compose -f docker-compose.dev.yml up -d --build
```
开发环境默认暴露端口:
- Web:`http://localhost:8100`
- Postgres:`localhost:5432`
- PgAdmin:`http://localhost:5050`
- RabbitMQ 管理台:`http://localhost:15672`
### 2)初始化数据库(首次)
```bash
docker compose -f docker-compose.dev.yml exec web uv run python manage.py migrate
docker compose -f docker-compose.dev.yml exec web uv run python manage.py createsuperuser
```
### 3)查看日志 / 重启
```bash
docker compose -f docker-compose.dev.yml logs -f web
docker compose -f docker-compose.dev.yml logs -f celery_worker
docker compose -f docker-compose.dev.yml logs -f celery_beat
docker compose -f docker-compose.dev.yml restart web
```
### 4)停止 / 清理
```bash
docker compose -f docker-compose.dev.yml down
# 连同数据卷一起删除(会清空数据库等)
docker compose -f docker-compose.dev.yml down -v
```
---
## 正式发布如何部署(prod,镜像分发)
### 1)构建发布镜像(构建机)
Dockerfile 已新增 `runtime` stage 且作为默认最终产物,因此直接构建即可:
```bash
docker build -t flower-app:20251220 .
```
> `docker-compose.prod.yml` 会通过 `FLOWER_IMAGE` 使用这个镜像标签。
### 2)导出镜像(用于分发)
```bash
docker save -o flower-app_20251220.tar flower-app:20251220
```
(可选)压缩:
```bash
gzip -9 flower-app_20251220.tar
```
### 3)服务器导入镜像
```bash
docker load -i flower-app_20251220.tar
# 或者 gzip 版本:gunzip -c xxx.tar.gz | docker load
```
### 4)准备备份目录(推荐)
生产环境的数据库备份目录建议使用宿主机可见路径,例如:
```bash
mkdir -p /srv/flower/data-bak
```
### 5)启动生产环境(不使用 .env 文件)
`docker-compose.prod.yml` **不再使用 `env_file: .env`**。你可以用系统环境变量(或 systemd 注入)提供关键配置。
最小示例(请替换为真实值):
```bash
export FLOWER_IMAGE=flower-app:20251220
export SECRET_KEY='请换成强随机值'
export ALLOWED_HOSTS='yuwenerp.yuwen.cloud'
export CSRF_TRUSTED_ORIGINS='https://yuwenerp.yuwen.cloud'
export DEBUG=0
# 备份目录(不设则默认 ./data-bak,相对 compose 文件所在目录)
export BACKUP_DIR=/srv/flower/data-bak
docker compose -f docker-compose.prod.yml up -d
```
说明:
- `web`/`celery_worker`/`celery_beat` **共用同一个镜像**,通过不同 `command` 启动(只需打包/分发一次)。
- 生产环境默认 `uvicorn` **workers=1**(与当前 SSE 实现兼容;若未来 SSE 改为 Redis/Channels 再考虑增大)。
- 数据库在生产 compose 中默认只绑定到 `127.0.0.1:5432`,避免对公网暴露。
### 6)验证运行
```bash
docker compose -f docker-compose.prod.yml ps
docker compose -f docker-compose.prod.yml logs -f web
```
---
## data-bak(备份目录)当前是如何处理的?
- **开发环境(dev)**:因为 `.:/app` 源码挂载,容器内 `/app/data-bak` 会直接对应宿主机项目目录的 `./data-bak`,你在宿主机上能直接看到备份文件。
- **生产环境(prod)**:`celery_worker` 将 `/app/data-bak` bind mount 到宿主机路径:
- 默认:`./data-bak`(相对 `docker-compose.prod.yml` 所在目录)
- 或者通过 `BACKUP_DIR=/srv/flower/data-bak` 指定绝对路径
---
## Dockerfile 现在该如何理解?
当前 `Dockerfile` 主要有三个阶段:
- `base`:安装依赖并 `COPY . /app`(开发 compose 使用 `target: base`)
- `celery`:保留作示例(目前不强依赖,prod compose 直接用同一镜像 + command 覆盖)
- `runtime`:作为默认最终产物,默认 `CMD` 为 `uvicorn ...`(prod 直接用该镜像)
并且已经添加 `.dockerignore`,确保 `.env`、`data-bak/` 等不会被打进发布镜像。

View File

@@ -195,6 +195,8 @@ DATABASES = {
'PASSWORD': env('DB_PASSWORD'),
'HOST': env('DB_HOST', default='localhost'),
'PORT': env('DB_PORT', default='5432'),
'CONN_HEALTH_CHECK': True,
'CONN_MAX_AGE': 60,
}
}