baodan/docker-compose.dify.yml

416 lines
17 KiB
YAML
Raw Normal View History

2026-08-05 17:51:16 +08:00
# BaoDan 开发环境 - 使用独立的 PostgreSQL 和 Redis
# 用法: docker compose -f docker-compose.baodan.yml up -d
services:
# ---- PostgreSQL ----
db:
image: pgvector/pgvector:pg15
environment:
POSTGRES_DB: ${POSTGRES_DB:-baodan}
POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?请在 .env 文件中设置 POSTGRES_PASSWORD}
volumes:
- baodan_pg_data:/var/lib/postgresql/data
- ./deploy/sql/init.sql:/docker-entrypoint-initdb.d/01-init.sql
- ./deploy/sql/init-plugin-db.sql:/docker-entrypoint-initdb.d/02-init-plugin-db.sql
ports:
- "5433:5432"
restart: unless-stopped
# ---- Redis ----
redis:
image: redis:7-alpine
volumes:
- baodan_redis_data:/data
ports:
- "6380:6379"
restart: unless-stopped
# ---- BaoDan API自定义镜像含 insurance Blueprint----
baodan-api:
build:
context: .
dockerfile: Dockerfile.dify-custom
image: baodanagent-api:local
user: root
command:
- gunicorn
- --bind
- 0.0.0.0:5001
- --workers
- ${INSURANCE_API_WORKERS:-4}
- --worker-class
- geventwebsocket.gunicorn.workers.GeventWebSocketWorker
- --worker-connections
- ${INSURANCE_API_WORKER_CONNECTIONS:-1000}
- --timeout
- "300"
- --graceful-timeout
- "30"
- --keep-alive
- "5"
- app:flask_app
environment: &baodan_api_environment
DB_TYPE: postgresql
DB_USERNAME: ${POSTGRES_USER:-postgres}
DB_PASSWORD: ${POSTGRES_PASSWORD:?请在 .env 文件中设置 POSTGRES_PASSWORD}
DB_HOST: db
DB_PORT: 5432
DB_DATABASE: ${POSTGRES_DB:-baodan}
SQLALCHEMY_POOL_PRE_PING: ${SQLALCHEMY_POOL_PRE_PING:-true}
SQLALCHEMY_POOL_RECYCLE: ${SQLALCHEMY_POOL_RECYCLE:-300}
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ""
REDIS_DB: 0
CONSOLE_API_URL: ${CONSOLE_API_URL:-http://localhost:5001}
CONSOLE_WEB_URL: ${CONSOLE_WEB_URL:-http://localhost:3000}
SERVICE_API_URL: ${SERVICE_API_URL:-http://localhost:5001}
APP_WEB_URL: ${APP_WEB_URL:-http://localhost:3000}
FILES_URL: ${FILES_URL:-http://localhost:5001}
SECRET_KEY: ${SECRET_KEY:?请在 .env 文件中设置 SECRET_KEY}
INIT_PASSWORD: ${INIT_PASSWORD:?请在 .env 文件中设置 INIT_PASSWORD}
LOG_LEVEL: ${LOG_LEVEL:-INFO}
INSURANCE_STORAGE_ROOT: ${INSURANCE_STORAGE_ROOT:-/app/api/storage/insurance}
POSTER_USER_MANUAL_UPLOAD_ENABLED: ${POSTER_USER_MANUAL_UPLOAD_ENABLED:-true}
MAIL_TYPE: ${MAIL_TYPE:-}
MAIL_DEFAULT_SEND_FROM: ${MAIL_DEFAULT_SEND_FROM:-}
SMTP_SERVER: ${SMTP_SERVER:-}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USERNAME: ${SMTP_USERNAME:-}
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
SMTP_USE_TLS: ${SMTP_USE_TLS:-false}
SMTP_OPPORTUNISTIC_TLS: ${SMTP_OPPORTUNISTIC_TLS:-false}
RESEND_API_KEY: ${RESEND_API_KEY:-}
RESEND_API_URL: ${RESEND_API_URL:-}
SENDGRID_API_KEY: ${SENDGRID_API_KEY:-}
MIGRATION_ENABLED: "true"
CELERY_BROKER_URL: redis://redis:6379/1
# 文件上传限制(取消限制)
UPLOAD_FILE_BATCH_LIMIT: "99999"
BATCH_UPLOAD_LIMIT: "99999"
# 向量数据库配置 - 使用 pgvector复用 PostgreSQL
VECTOR_STORE: pgvector
PGVECTOR_HOST: db
PGVECTOR_PORT: 5432
PGVECTOR_USER: ${POSTGRES_USER:-postgres}
PGVECTOR_PASSWORD: ${POSTGRES_PASSWORD:?请在 .env 文件中设置 POSTGRES_PASSWORD}
PGVECTOR_DATABASE: ${POSTGRES_DB:-baodan}
# 代码执行沙箱
CODE_EXECUTION_ENDPOINT: http://sandbox:8194
CODE_EXECUTION_API_KEY: ${CODE_EXECUTION_API_KEY:?请在 .env 文件中设置 CODE_EXECUTION_API_KEY}
# SSRF 代理
SSRF_PROXY_HTTP_URL: ""
SSRF_PROXY_HTTPS_URL: ""
# 保险模块配置(需在后台创建应用后填入真实 API Key
BAODAN_CHAT_API_KEY: ${BAODAN_CHAT_API_KEY:?请在 .env 文件中设置 BAODAN_CHAT_API_KEY}
BAODAN_WORKFLOW_API_KEY: ${BAODAN_WORKFLOW_API_KEY:?请在 .env 文件中设置 BAODAN_WORKFLOW_API_KEY}
BAODAN_KB_API_KEY: ${BAODAN_KB_API_KEY:?请在 .env 文件中设置 BAODAN_KB_API_KEY}
BAODAN_API_URL: "http://baodan-core-api:5001"
# 智能问答原子准入(灰度开启前先将 MAX_ACTIVE 配为不高于 Dify 有效上限)
CHAT_GATEWAY_ADMISSION_V1: ${CHAT_GATEWAY_ADMISSION_V1:-false}
CHAT_GATEWAY_MAX_ACTIVE: ${CHAT_GATEWAY_MAX_ACTIVE:-}
CHAT_GATEWAY_USER_MAX_ACTIVE: ${CHAT_GATEWAY_USER_MAX_ACTIVE:-1}
CHAT_GATEWAY_ACQUIRE_TIMEOUT_MS: ${CHAT_GATEWAY_ACQUIRE_TIMEOUT_MS:-3000}
CHAT_GATEWAY_LEASE_SECONDS: ${CHAT_GATEWAY_LEASE_SECONDS:-180}
CHAT_GATEWAY_RENEW_SECONDS: ${CHAT_GATEWAY_RENEW_SECONDS:-30}
CHAT_GATEWAY_RETRY_AFTER: ${CHAT_GATEWAY_RETRY_AFTER:-3}
CHAT_AGENT_ACCESS_V1: ${CHAT_AGENT_ACCESS_V1:-false}
CHAT_AGENT_ACCESS_DEFAULT_ALLOW: ${CHAT_AGENT_ACCESS_DEFAULT_ALLOW:-false}
# 后台生成链路灰度开关
GENERATION_OUTBOX_V1: ${GENERATION_OUTBOX_V1:-false}
GENERATION_OUTBOX_PUBLISH_V1: ${GENERATION_OUTBOX_PUBLISH_V1:-false}
GENERATION_SPLIT_QUEUES_V1: ${GENERATION_SPLIT_QUEUES_V1:-false}
POSTER_COPY_ASYNC_V1: ${POSTER_COPY_ASYNC_V1:-false}
MODEL_POOL_V1: ${MODEL_POOL_V1:-false}
GENERATION_USER_MAX_RUNNING: ${GENERATION_USER_MAX_RUNNING:-2}
GENERATION_USER_MAX_QUEUED: ${GENERATION_USER_MAX_QUEUED:-20}
GENERATION_HEARTBEAT_SECONDS: ${GENERATION_HEARTBEAT_SECONDS:-30}
INSURANCE_CREDENTIAL_MASTER_KEY: ${INSURANCE_CREDENTIAL_MASTER_KEY:-}
INSURANCE_CREDENTIAL_KEY_VERSION: ${INSURANCE_CREDENTIAL_KEY_VERSION:-1}
INSURANCE_CREDENTIAL_MASTER_KEYS: ${INSURANCE_CREDENTIAL_MASTER_KEYS:-}
INSURANCE_METRICS_TOKEN: ${INSURANCE_METRICS_TOKEN:-}
# 访客模式true=免登录使用false=需要登录
GUEST_MODE: ${GUEST_MODE:-false}
# 插件服务配置
PLUGIN_DAEMON_URL: http://plugin_daemon:5002
PLUGIN_DAEMON_KEY: ${PLUGIN_DAEMON_KEY:?请在 .env 文件中设置 PLUGIN_DAEMON_KEY}
PLUGIN_DAEMON_TIMEOUT: "600.0"
# 启用市场(用于安装模型供应商插件)
MARKETPLACE_ENABLED: "true"
EXPLORER_ENABLED: "false"
# CORS 配置(不能用 * 配合 credentials必须明确列出 origin
CONSOLE_CORS_ALLOW_ORIGINS: ${CONSOLE_CORS_ALLOW_ORIGINS:-http://localhost:3000}
WEB_API_CORS_ALLOW_ORIGINS: ${WEB_API_CORS_ALLOW_ORIGINS:-http://localhost:3000}
# 自动初始化管理员配置
ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@example.com}
ADMIN_NAME: ${ADMIN_NAME:-admin}
ADMIN_PASSWORD: ${ADMIN_PASSWORD:?请在 .env 文件中设置 ADMIN_PASSWORD}
ADMIN_WORKSPACE: ${ADMIN_WORKSPACE:-BaoDan Workspace}
# JWT 配置
JWT_SECRET: ${JWT_SECRET:?请在 .env 文件中设置 JWT_SECRET}
JWT_EXPIRE_SECONDS: ${JWT_EXPIRE_SECONDS:-7200}
# 企微配置(生产环境需替换为真实值)
# 在企微后台 → 应用管理 → 接收消息 → 设置接收消息服务器 中获取
WECOM_CORP_ID: ${WECOM_CORP_ID:-} # 企业ID
WECOM_SECRET: ${WECOM_SECRET:-} # 应用Secret
WECOM_TOKEN: ${WECOM_TOKEN:-} # 回调Token自定义
WECOM_AES_KEY: ${WECOM_AES_KEY:-} # 消息加密密钥43位字符
# OAuth 回调基础地址企微后台「网页授权及JS回调域」需与此一致
BASE_URL: ${BASE_URL:-http://localhost:5001}
# 前端地址OAuth 成功后重定向到前端)
FRONTEND_URL: ${FRONTEND_URL:-http://localhost:9080}
# 群机器人 Webhook用于群聊消息回复必须配置才能在群里回复消息
# 在企微群聊中添加群机器人,获取 Webhook 地址后填入
WECOM_GROUP_WEBHOOK: ${WECOM_GROUP_WEBHOOK:-} # 群机器人Webhook必须配置
ports:
- "5001:5001"
depends_on:
- db
- redis
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- baodan_api_storage:/app/api/storage # 持久化 RSA 私钥和上传文件
- ./api/insurance:/app/api/insurance # 挂载自研代码(开发用,改代码即时生效)
restart: unless-stopped
# ---- BaoDan Core APIDify /v1 独立进程池,避免与 Insurance SSE 争用 Worker----
baodan-core-api:
build:
context: .
dockerfile: Dockerfile.dify-custom
image: baodanagent-api:local
user: root
command:
- gunicorn
- --bind
- 0.0.0.0:5001
- --workers
- ${CORE_API_WORKERS:-4}
- --worker-class
- geventwebsocket.gunicorn.workers.GeventWebSocketWorker
- --worker-connections
- ${CORE_API_WORKER_CONNECTIONS:-1000}
- --timeout
- "300"
- --graceful-timeout
- "30"
- --keep-alive
- "5"
- app:flask_app
environment:
<<: *baodan_api_environment
MIGRATION_ENABLED: "false"
BAODAN_API_URL: "http://baodan-core-api:5001"
depends_on:
- db
- redis
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- baodan_api_storage:/app/api/storage
- ./api/insurance:/app/api/insurance
restart: unless-stopped
# ---- BaoDan Web ----
baodan-web:
build:
context: .
dockerfile: Dockerfile.web-custom
environment:
CONSOLE_API_URL: http://localhost:5001
CONSOLE_WEB_URL: http://localhost:3000
SERVICE_API_URL: http://localhost:5001
APP_API_URL: http://localhost:5001
APP_WEB_URL: http://localhost:3000
NEXT_PUBLIC_ALLOW_EMBED: "true"
NEXT_PUBLIC_APP_URL: http://localhost:3000
MARKETPLACE_ENABLED: "true"
MARKETPLACE_URL: https://marketplace.dify.ai
MARKETPLACE_API_URL: https://marketplace.dify.ai
ports:
- "3000:3000"
depends_on:
- baodan-api
restart: unless-stopped
# ---- Plugin Daemon ----
plugin_daemon:
image: langgenius/dify-plugin-daemon:0.6.1-local
environment:
DB_TYPE: postgresql
DB_USERNAME: ${POSTGRES_USER:-postgres}
DB_PASSWORD: ${POSTGRES_PASSWORD:?请在 .env 文件中设置 POSTGRES_PASSWORD}
DB_HOST: db
DB_PORT: 5432
DB_DATABASE: dify_plugin
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ""
REDIS_DB: 0
SERVER_PORT: 5002
SERVER_KEY: ${PLUGIN_DAEMON_KEY:?请在 .env 文件中设置 PLUGIN_DAEMON_KEY}
MAX_PLUGIN_PACKAGE_SIZE: 52428800
PPROF_ENABLED: "false"
DIFY_INNER_API_URL: "http://baodan-api:5001"
DIFY_INNER_API_KEY: ${DIFY_INNER_API_KEY:?请在 .env 文件中设置 DIFY_INNER_API_KEY}
PLUGIN_REMOTE_INSTALLING_HOST: "0.0.0.0"
PLUGIN_REMOTE_INSTALLING_PORT: 5003
PLUGIN_WORKING_PATH: "/app/storage/cwd"
FORCE_VERIFYING_SIGNATURE: "false"
PYTHON_ENV_INIT_TIMEOUT: 120
PLUGIN_MAX_EXECUTION_TIMEOUT: 600
PLUGIN_STORAGE_TYPE: local
PLUGIN_STORAGE_LOCAL_ROOT: "/app/storage"
PLUGIN_INSTALLED_PATH: plugin
PLUGIN_PACKAGE_CACHE_PATH: plugin_packages
PLUGIN_MEDIA_CACHE_PATH: assets
ports:
- "5002:5002"
- "5003:5003"
depends_on:
- db
- redis
volumes:
- baodan_plugin_data:/app/storage
restart: unless-stopped
# ---- Sandbox (代码执行沙箱,仅供内部 API 调用) ----
sandbox:
image: langgenius/dify-sandbox:0.2.15
restart: always
environment:
API_KEY: ${CODE_EXECUTION_API_KEY:?请在 .env 文件中设置 CODE_EXECUTION_API_KEY}
GIN_MODE: release
WORKER_TIMEOUT: 15
ENABLE_NETWORK: "true"
SANDBOX_PORT: 8194
volumes:
- baodan_sandbox_data:/app/storage
extra_hosts:
- "host.docker.internal:host-gateway"
# ---- BaoDan Worker ----
baodan-worker:
build:
context: .
dockerfile: Dockerfile.dify-custom
user: root
command: celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,dataset_summary,priority_dataset,priority_pipeline,pipeline,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation,workflow,schedule_poller,schedule_executor,triggered_workflow_dispatcher,trigger_refresh_executor,retention,workflow_based_app_execution,insurance
environment:
MODE: worker
DB_TYPE: postgresql
DB_USERNAME: ${POSTGRES_USER:-postgres}
DB_PASSWORD: ${POSTGRES_PASSWORD:?请在 .env 文件中设置 POSTGRES_PASSWORD}
DB_HOST: db
DB_PORT: 5432
DB_DATABASE: ${POSTGRES_DB:-baodan}
SQLALCHEMY_POOL_PRE_PING: ${SQLALCHEMY_POOL_PRE_PING:-true}
SQLALCHEMY_POOL_RECYCLE: ${SQLALCHEMY_POOL_RECYCLE:-300}
REDIS_HOST: redis
REDIS_PORT: 6379
REDIS_PASSWORD: ""
REDIS_DB: 0
SECRET_KEY: ${SECRET_KEY:?请在 .env 文件中设置 SECRET_KEY}
MIGRATION_ENABLED: "false"
LOG_LEVEL: INFO
INSURANCE_STORAGE_ROOT: ${INSURANCE_STORAGE_ROOT:-/app/api/storage/insurance}
POSTER_USER_MANUAL_UPLOAD_ENABLED: ${POSTER_USER_MANUAL_UPLOAD_ENABLED:-true}
MAIL_TYPE: ${MAIL_TYPE:-}
MAIL_DEFAULT_SEND_FROM: ${MAIL_DEFAULT_SEND_FROM:-}
SMTP_SERVER: ${SMTP_SERVER:-}
SMTP_PORT: ${SMTP_PORT:-587}
SMTP_USERNAME: ${SMTP_USERNAME:-}
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
SMTP_USE_TLS: ${SMTP_USE_TLS:-false}
SMTP_OPPORTUNISTIC_TLS: ${SMTP_OPPORTUNISTIC_TLS:-false}
RESEND_API_KEY: ${RESEND_API_KEY:-}
RESEND_API_URL: ${RESEND_API_URL:-}
SENDGRID_API_KEY: ${SENDGRID_API_KEY:-}
GENERATION_OUTBOX_V1: ${GENERATION_OUTBOX_V1:-false}
GENERATION_OUTBOX_PUBLISH_V1: ${GENERATION_OUTBOX_PUBLISH_V1:-false}
GENERATION_SPLIT_QUEUES_V1: ${GENERATION_SPLIT_QUEUES_V1:-false}
POSTER_COPY_ASYNC_V1: ${POSTER_COPY_ASYNC_V1:-false}
MODEL_POOL_V1: ${MODEL_POOL_V1:-false}
GENERATION_USER_MAX_RUNNING: ${GENERATION_USER_MAX_RUNNING:-2}
GENERATION_USER_MAX_QUEUED: ${GENERATION_USER_MAX_QUEUED:-20}
GENERATION_HEARTBEAT_SECONDS: ${GENERATION_HEARTBEAT_SECONDS:-30}
INSURANCE_CREDENTIAL_MASTER_KEY: ${INSURANCE_CREDENTIAL_MASTER_KEY:-}
INSURANCE_CREDENTIAL_KEY_VERSION: ${INSURANCE_CREDENTIAL_KEY_VERSION:-1}
INSURANCE_CREDENTIAL_MASTER_KEYS: ${INSURANCE_CREDENTIAL_MASTER_KEYS:-}
CELERY_BROKER_URL: redis://redis:6379/1
SSRF_PROXY_HTTP_URL: ""
SSRF_PROXY_HTTPS_URL: ""
PLUGIN_DAEMON_URL: http://plugin_daemon:5002
PLUGIN_DAEMON_KEY: ${PLUGIN_DAEMON_KEY:?请在 .env 文件中设置 PLUGIN_DAEMON_KEY}
PLUGIN_DAEMON_TIMEOUT: "600.0"
# 向量数据库配置 - 使用 pgvector
VECTOR_STORE: pgvector
PGVECTOR_HOST: db
PGVECTOR_PORT: 5432
PGVECTOR_USER: ${POSTGRES_USER:-postgres}
PGVECTOR_PASSWORD: ${POSTGRES_PASSWORD:?请在 .env 文件中设置 POSTGRES_PASSWORD}
PGVECTOR_DATABASE: ${POSTGRES_DB:-baodan}
depends_on:
- db
- redis
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- baodan_api_storage:/app/api/storage # 共享 API 存储RSA 私钥和上传文件)
- ./api/insurance:/app/api/insurance # 挂载自研代码(开发用)
restart: unless-stopped
# ---- Insurance 分类 Worker灰度关闭时空闲开启后消费新队列----
insurance-parse-worker:
extends:
service: baodan-worker
command: celery -A app.celery worker --pool=prefork --concurrency=${INSURANCE_PARSE_CONCURRENCY:-4} --prefetch-multiplier=1 --max-tasks-per-child=100 --loglevel=INFO -Q insurance_parse
insurance-copy-worker:
extends:
service: baodan-worker
command: celery -A app.celery worker --pool=prefork --concurrency=${INSURANCE_COPY_CONCURRENCY:-4} --prefetch-multiplier=1 --max-tasks-per-child=100 --loglevel=INFO -Q insurance_copy
insurance-image-worker:
extends:
service: baodan-worker
command: celery -A app.celery worker --pool=prefork --concurrency=${INSURANCE_IMAGE_CONCURRENCY:-2} --prefetch-multiplier=1 --max-tasks-per-child=100 --loglevel=INFO -Q insurance_image
insurance-render-worker:
extends:
service: baodan-worker
command: celery -A app.celery worker --pool=prefork --concurrency=${INSURANCE_RENDER_CONCURRENCY:-2} --prefetch-multiplier=1 --max-tasks-per-child=100 --loglevel=INFO -Q insurance_render
# Outbox 发布器只负责数据库领取和 Broker 发布,不执行重任务。
insurance-outbox-dispatcher:
extends:
service: baodan-worker
command: python -m insurance.generation.outbox_dispatcher
# ---- 保险前端 ----
baodanagent-web:
build:
context: .
dockerfile: Dockerfile.frontend
ports:
- "9080:80"
ulimits:
nofile:
soft: 65535
hard: 65535
volumes:
- ./frontend/dist:/usr/share/nginx/html:ro
depends_on:
- baodan-api
- baodan-core-api
restart: unless-stopped
volumes:
baodan_pg_data:
baodan_redis_data:
baodan_plugin_data:
baodan_api_storage:
baodan_sandbox_data: