deploy/docker-compose.yml
Rama main · réplica de lectura del repositorio original.
# DataSteward · 单机全栈 compose(多架构:amd64/arm64)
#
# 快速开始:cp .env.example .env(填 LLM 配置)→ bash deploy/quickstart.sh
# 手动等价步骤见 infra/README.md。内存参考(默认 profile ≈9GB,+app ≈12GB):16GB 机器可跑。
#
# 绑定策略:数据库端口只绑 127.0.0.1,UI/服务端口绑 0.0.0.0(局域网可看)。
# 所有宿主端口都可用 .env 里的 DM_PORT_* 重映射(与已有服务冲突时改 .env 即可)。
#
# profiles:
# (默认) 数据基建:starrocks / postgres / flink×2 / neo4j
# app 应用层:dm-app(Streamlit) / dagster-webserver / dagster-daemon
# tools dm-cli(docker compose run --rm dm-cli <命令> 跑一次性任务)
# ollama 本地 LLM(可选;零云依赖,需 ~6GB 额外内存或 GPU;.env 里三行切换)
# dingtalk 钉钉 Stream 双向通道(可选;需 DINGTALK_APP_KEY/SECRET + role_map.yaml)
# sim-u8 用友 U8 仿真源(可选;x86 可用,aarch64 已知不工作——DEVLOG 坑22)
# om OpenMetadata 三件套(可选重型件,额外 ~6GB 内存)
name: datasteward
x-dm-app-common: &dm-app-common
image: datasteward/dm:latest
build:
context: ..
dockerfile: deploy/Dockerfile
env_file:
- path: ../.env
required: false
environment: &dm-app-env
DM_DATA_DIR: /data
DM_WH_HOST: starrocks
DM_WH_PORT: "9030"
DM_SRC_PG_HOST: postgres
DM_SRC_PG_PORT: "5432"
DM_FLINK_REST: http://flink-jobmanager:8081
DM_NEO4J_URI: bolt://neo4j:7687
# LLM:任意 OpenAI 兼容端点。默认取 .env(云 API 或本地 ollama 皆可);
# 未配置时回落到 compose 内 ollama 服务(--profile ollama 启用后即通)。
# 注意:若走宿主机自装的 ollama,用 http://host.docker.internal:11434/v1(host-gateway 已配)。
# 经验(DEVLOG 坑29):本地推理务必直连 ollama /v1,不要过 LiteLLM 一类网关——
# 网关对客户端断开不取消上游推理("取消黑洞"),长生成会占死并发槽。
DM_LLM_BASE_URL: ${DM_LLM_BASE_URL:-http://ollama:11434/v1}
DM_LLM_API_KEY: ${DM_LLM_API_KEY:-sk-local}
DM_LLM_MODEL: ${DM_LLM_MODEL:-qwen3:8b}
# 合成数据环境的"今天"(generate.py TODAY / dbt anchor_today 同值);接真库时删除此行回归墙钟
DM_ANCHOR_TODAY: "2026-06-25"
DAGSTER_PG_URL: postgresql://${DM_SRC_PG_USER:-dm}:${DM_SRC_PG_PASSWORD:-dm_dev_pass}@postgres:5432/dagster
DAGSTER_HOME: /dagster_home
DM_DBT_DIR: /app/transform/dbt
PYTHONUTF8: "1"
HF_HUB_OFFLINE: "1"
DM_EMBED_CACHE: /data/embed_cache
volumes:
- ../data:/data
- dagster_home:/dagster_home
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
services:
starrocks:
image: starrocks/allin1-ubuntu:latest
# 关键(DEVLOG 坑1):allin1 默认把 FE/BE 的 priority_networks 设成 127.0.0.1,导致跨容器
# Stream Load 重定向到 127.0.0.1:8040 不可达。entrypoint 预设为容器 IP。
entrypoint:
- /bin/bash
- -c
- |
IP=$$(grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' /etc/hosts | grep -v '^127\.' | awk '{print $$1; exit}')
[ -z "$$IP" ] && IP=$$(hostname -I 2>/dev/null | awk '{print $$1}')
echo "dm: using container IP $$IP for FE/BE priority_networks + director MYHOST"
grep -q '^priority_networks' /data/deploy/starrocks/fe/conf/fe.conf || echo "priority_networks = $$IP/32" >> /data/deploy/starrocks/fe/conf/fe.conf
grep -q '^priority_networks' /data/deploy/starrocks/be/conf/be.conf || echo "priority_networks = $$IP/32" >> /data/deploy/starrocks/be/conf/be.conf
sed -i "s|^MYHOST=127.0.0.1|MYHOST=$$IP|" /data/deploy/starrocks/director/run.sh
exec /data/deploy/entrypoint.sh
ports:
- "127.0.0.1:${DM_PORT_SR:-9030}:9030" # FE MySQL 协议
- "127.0.0.1:${DM_PORT_SR_HTTP:-8030}:8030" # FE HTTP / Stream Load
- "127.0.0.1:${DM_PORT_SR_BE:-8040}:8040" # BE HTTP
volumes:
- sr_data:/data/deploy/starrocks
mem_limit: 4g
healthcheck:
test: ["CMD-SHELL", "mysql -h127.0.0.1 -P9030 -uroot -e 'show frontends' >/dev/null 2>&1 || exit 1"]
interval: 15s
timeout: 10s
retries: 40
start_period: 120s
restart: unless-stopped
postgres:
image: pgvector/pgvector:pg16
command:
- "postgres"
- "-c"
- "wal_level=logical" # Flink CDC(Debezium/pgoutput)必需
- "-c"
- "max_wal_senders=30"
- "-c"
- "max_replication_slots=30"
environment:
POSTGRES_USER: ${DM_SRC_PG_USER:-dm}
POSTGRES_PASSWORD: ${DM_SRC_PG_PASSWORD:-dm_dev_pass}
POSTGRES_DB: ${DM_SRC_PG_DB:-dm}
ports:
- "127.0.0.1:${DM_PORT_PG:-15432}:5432"
volumes:
- pg_data:/var/lib/postgresql/data
mem_limit: 512m
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 20
restart: unless-stopped
flink-jobmanager:
image: flink:1.18-scala_2.12-java11
command: jobmanager
ports:
- "0.0.0.0:${DM_PORT_FLINK:-8081}:8081" # Flink Web UI
environment:
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: flink-jobmanager
jobmanager.memory.process.size: 1024m
parallelism.default: 1
execution.checkpointing.interval: 5s
volumes:
- ../infra/flink-lib/flink-sql-connector-postgres-cdc-3.0.1.jar:/opt/flink/lib/flink-sql-connector-postgres-cdc-3.0.1.jar:ro
- ../infra/flink-lib/flink-connector-starrocks-1.2.9_flink-1.18.jar:/opt/flink/lib/flink-connector-starrocks-1.2.9_flink-1.18.jar:ro
mem_limit: 1200m
restart: unless-stopped
flink-taskmanager:
image: flink:1.18-scala_2.12-java11
command: taskmanager
depends_on:
- flink-jobmanager
environment:
# 19 表 CDC 是单作业 parallelism=1,2 个槽已够(笔记本档;大机器可调大)
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: flink-jobmanager
taskmanager.numberOfTaskSlots: 2
taskmanager.memory.process.size: 1728m
parallelism.default: 1
volumes:
- ../infra/flink-lib/flink-sql-connector-postgres-cdc-3.0.1.jar:/opt/flink/lib/flink-sql-connector-postgres-cdc-3.0.1.jar:ro
- ../infra/flink-lib/flink-connector-starrocks-1.2.9_flink-1.18.jar:/opt/flink/lib/flink-connector-starrocks-1.2.9_flink-1.18.jar:ro
mem_limit: 2g
restart: unless-stopped
neo4j:
image: neo4j:5-community
environment:
NEO4J_AUTH: neo4j/${DM_NEO4J_PASSWORD:-datasteward-dev}
# 笔记本档内存(大机器可上调 heap/pagecache)
NEO4J_server_memory_heap_initial__size: 512m
NEO4J_server_memory_heap_max__size: 512m
NEO4J_server_memory_pagecache_size: 256m
ports:
- "127.0.0.1:${DM_PORT_NEO4J_HTTP:-7474}:7474"
- "127.0.0.1:${DM_PORT_NEO4J_BOLT:-7687}:7687"
volumes:
- neo4j_data:/data
mem_limit: 1500m
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:7474 >/dev/null 2>&1 || exit 1"]
interval: 15s
timeout: 10s
retries: 20
restart: unless-stopped
# ---- 本地 LLM(可选,--profile ollama):拉起后在 .env 里把 LLM 三行指向它 ----
ollama:
profiles: ["ollama"]
image: ollama/ollama:latest
ports:
- "127.0.0.1:${DM_PORT_OLLAMA:-11434}:11434"
volumes:
- ollama_models:/root/.ollama
restart: unless-stopped
# 一次性:自动拉默认模型(qwen3:8b,~5GB;改 DM_OLLAMA_PULL 换模型)
ollama-init:
profiles: ["ollama"]
image: ollama/ollama:latest
depends_on:
- ollama
environment:
OLLAMA_HOST: http://ollama:11434
entrypoint: ["/bin/sh", "-c"]
command: ["ollama pull ${DM_OLLAMA_PULL:-qwen3:8b}"]
restart: "no"
# ---- 仿真 U8 源(可选,--profile sim-u8):azure-sql-edge 在 x86 正常;
# aarch64 新内核上 sqlservr 空转不监听(DEVLOG 坑22),aarch64 请用外部 MSSQL ----
sim-u8:
profiles: ["sim-u8"]
image: mcr.microsoft.com/azure-sql-edge:latest
environment:
ACCEPT_EULA: "1"
MSSQL_SA_PASSWORD: ${MSSQL_SA_PASSWORD:-SimU8!Passw0rd}
ports:
- "127.0.0.1:${DM_PORT_MSSQL:-11433}:1433"
volumes:
- u8_data:/var/opt/mssql
mem_limit: 2g
healthcheck:
# 必须显式 bash:CMD-SHELL 走 /bin/sh(dash),不支持 /dev/tcp
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/1433"]
interval: 10s
timeout: 5s
retries: 30
start_period: 30s
restart: unless-stopped
# ================= 应用层(profile: app)=================
dm-app:
<<: *dm-app-common
profiles: ["app"]
command: ["dm-app", "--server.port", "8501", "--server.address", "0.0.0.0", "--server.headless", "true"]
ports:
- "0.0.0.0:${DM_PORT_APP:-8501}:8501"
depends_on:
starrocks:
condition: service_healthy
postgres:
condition: service_healthy
mem_limit: 1g
healthcheck:
test: ["CMD-SHELL", "python -c \"import urllib.request;urllib.request.urlopen('http://127.0.0.1:8501/_stcore/health',timeout=5)\" || exit 1"]
interval: 20s
timeout: 10s
retries: 10
start_period: 30s
# 钉钉 Stream 双向(可选,--profile dingtalk):需 .env 里 DINGTALK_APP_KEY/SECRET,
# 并先 cp deploy/role_map.yaml.template role_map.yaml(否则 docker 会把缺失 bind 源建成目录)
dm-dingtalk:
<<: *dm-app-common
profiles: ["dingtalk"]
command: ["dm-dingtalk", "stream"]
volumes:
# 覆盖锚点 volumes(compose 合并对列表是整体替换),须把公共两条一并带上
- ../data:/data
- dagster_home:/dagster_home
- ../role_map.yaml:/app/role_map.yaml:ro # 角色映射热加载
depends_on:
starrocks:
condition: service_healthy
mem_limit: 512m
# dagster.yaml 必须落在 $DAGSTER_HOME 里才生效(否则退回 sqlite,双进程并发出 alembic 多头)
dagster-webserver:
<<: *dm-app-common
profiles: ["app"]
command: ["bash", "-c", "cp /app/deploy/dagster/dagster.yaml $$DAGSTER_HOME/ && exec dagster-webserver -h 0.0.0.0 -p 3070 -w /app/deploy/dagster/workspace.yaml"]
ports:
- "0.0.0.0:${DM_PORT_DAGSTER:-3070}:3070"
depends_on:
postgres:
condition: service_healthy
mem_limit: 768m
dagster-daemon:
<<: *dm-app-common
profiles: ["app"]
command: ["bash", "-c", "cp /app/deploy/dagster/dagster.yaml $$DAGSTER_HOME/ && exec dagster-daemon run -w /app/deploy/dagster/workspace.yaml"]
depends_on:
postgres:
condition: service_healthy
mem_limit: 768m
# 一次性任务跑批入口:docker compose run --rm dm-cli dm-load / dm-eval / dm-u8 full ...
dm-cli:
<<: *dm-app-common
profiles: ["tools"]
restart: "no"
command: ["bash"]
# ================= OpenMetadata(profile: om;可选重型件)=================
om-mysql:
image: mysql:8.3
profiles: ["om"]
environment:
MYSQL_ROOT_PASSWORD: ${OM_MYSQL_ROOT_PASSWORD:-om_dev_root}
MYSQL_USER: openmetadata_user
MYSQL_PASSWORD: ${OM_MYSQL_PASSWORD:-openmetadata_password}
MYSQL_DATABASE: openmetadata_db
command: ["--sort_buffer_size=10M"]
volumes:
- om_mysql:/var/lib/mysql
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h localhost -u root -p$${MYSQL_ROOT_PASSWORD} --silent"]
interval: 15s
timeout: 10s
retries: 20
restart: unless-stopped
om-opensearch:
image: opensearchproject/opensearch:2.19.0
profiles: ["om"]
environment:
discovery.type: single-node
DISABLE_SECURITY_PLUGIN: "true"
OPENSEARCH_JAVA_OPTS: "-Xms1g -Xmx1g"
volumes:
- om_opensearch:/usr/share/opensearch/data
healthcheck:
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health >/dev/null || exit 1"]
interval: 15s
timeout: 10s
retries: 20
restart: unless-stopped
om-migrate:
image: openmetadata/server:1.5.11
profiles: ["om"]
command: ["./bootstrap/openmetadata-ops.sh", "migrate"]
environment: &om-env
DB_DRIVER_CLASS: com.mysql.cj.jdbc.Driver
DB_SCHEME: mysql
DB_PARAMS: allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
DB_USER: openmetadata_user
DB_USER_PASSWORD: ${OM_MYSQL_PASSWORD:-openmetadata_password}
DB_HOST: om-mysql
DB_PORT: "3306"
OM_DATABASE: openmetadata_db
SEARCH_TYPE: opensearch
ELASTICSEARCH_HOST: om-opensearch
ELASTICSEARCH_PORT: "9200"
ELASTICSEARCH_SCHEME: http
depends_on:
om-mysql:
condition: service_healthy
om-opensearch:
condition: service_healthy
restart: "no"
openmetadata-server:
image: openmetadata/server:1.5.11
profiles: ["om"]
environment:
<<: *om-env
OPENMETADATA_CLUSTER_NAME: datasteward
SERVER_PORT: "8585"
ports:
- "0.0.0.0:${DM_PORT_OM:-8585}:8585"
depends_on:
om-migrate:
condition: service_completed_successfully
healthcheck:
test: ["CMD-SHELL", "curl -s http://localhost:8586/healthcheck >/dev/null || exit 1"]
interval: 20s
timeout: 10s
retries: 30
start_period: 60s
restart: unless-stopped
volumes:
sr_data:
pg_data:
neo4j_data:
u8_data:
dagster_home:
ollama_models:
om_mysql:
om_opensearch: