4、环境搭建:使用 Docker Compose 部署 Dagster 全家桶(Dagit、Daemon、Postgres)

说实话,很多初学者在 Dagster 上栽的第一个跟头,不是代码写错了,而是环境没搭好。我见过太多人花了一整天在本地装依赖、配 Python 版本、调数据库驱动……最后心态崩了。

我个人习惯,但凡涉及多服务协作的项目,一律用 Docker Compose。你想想看,Dagster 全家桶至少包含三个组件:Dagit(Web UI)、Daemon(调度守护进程)、Postgres(持久化存储)。手动配?太累了。用 Compose 一条命令搞定,不香吗?

为什么非要用 Docker Compose?

Dagster 的架构其实挺清晰的,但服务一多,依赖关系就复杂了。我画了张图,你看一眼就明白:

Dagster Docker Compose 部署架构图 Dagster 全家桶 Docker Compose 部署架构 用户/浏览器 Dagit (Web UI) Daemon (调度) Postgres (存储) 代码目录 说明 Dagit 提供 Web 界面 Daemon 负责定时调度 Postgres 存储元数据

说白了,Dagit 是门面,Daemon 是后台苦力,Postgres 是仓库。三者缺一不可。

第一步:准备项目目录结构

我习惯把项目文件组织得干净一点。你跟着我来:

dagster-project/
├── dagster.yaml          # Dagster 实例配置
├── workspace.yaml        # 代码仓库配置
├── docker-compose.yml    # 服务编排文件
├── Dockerfile            # 自定义镜像(可选)
└── my_dagster_project/   # 你的管道代码
    ├── __init__.py
    ├── assets.py
    └── schedules.py

嗯,这里要注意:workspace.yaml 是 Dagster 1.0 之后才强制的。老版本可能不需要,但新项目我建议直接上。

第二步:编写 docker-compose.yml

这是核心。我直接给你一个我生产环境验证过的版本:

version: "3.8"

services:
  postgres:
    image: postgres:14
    environment:
      POSTGRES_USER: dagster
      POSTGRES_PASSWORD: dagster
      POSTGRES_DB: dagster
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U dagster"]
      interval: 10s
      timeout: 5s
      retries: 5

  dagit:
    build: .
    ports:
      - "3000:3000"
    environment:
      DAGSTER_HOME: /opt/dagster
    volumes:
      - .:/opt/dagster
    depends_on:
      postgres:
        condition: service_healthy
    command: dagit -h 0.0.0.0 -p 3000

  daemon:
    build: .
    environment:
      DAGSTER_HOME: /opt/dagster
    volumes:
      - .:/opt/dagster
    depends_on:
      postgres:
        condition: service_healthy
    command: dagster-daemon run

volumes:
  postgres_data:
我曾经踩过的坑: 第一次部署时,我没加 healthcheck,结果 Dagit 启动时 Postgres 还没就绪,直接报连接拒绝。加了 healthcheck 和 condition: service_healthy 之后,世界清净了。

第三步:配置 dagster.yaml

这个文件告诉 Dagster 怎么连数据库。我一般这样写:

storage:
  postgres:
    postgres_db:
      username: dagster
      password: dagster
      hostname: postgres
      db_name: dagster
      port: 5432

run_launcher:
  module: dagster.core.launcher
  class: DefaultRunLauncher

scheduler:
  module: dagster.core.scheduler
  class: DagsterDaemonScheduler
小提示: hostname 写的是 postgres,不是 localhost。因为在 Docker 网络里,服务名就是主机名。我第一次写 localhost 时,容器内部根本连不上——它以为自己在找本机的 5432 端口。

第四步:配置 workspace.yaml

这个文件告诉 Dagit 去哪里加载你的管道代码:

load_from:
  - python_module:
      module_name: my_dagster_project
      working_directory: /opt/dagster

说白了,就是告诉 Dagster:「嘿,去 my_dagster_project 这个 Python 包里找你的 Job 和 Asset。」

第五步:编写 Dockerfile

如果你不想每次都 build 镜像,也可以用现成的 dagster/dagster 镜像。但我个人习惯自己写一个,方便装额外依赖:

FROM python:3.9-slim

WORKDIR /opt/dagster

RUN pip install dagster dagit dagster-postgres

COPY . .

RUN pip install -e .
关键点: pip install -e . 这步很重要。它把你的项目以可编辑模式安装,这样你改代码后不用重新 build 镜像。开发阶段能省不少时间。

第六步:启动全家桶

所有文件就位后,一条命令搞定:

docker-compose up -d

等个十几秒,打开浏览器访问 http://localhost:3000,你应该能看到 Dagit 的界面了。

如果页面没出来,别慌。先看看日志:

docker-compose logs -f dagit

常见的错误无非两种:

  • Postgres 连不上 —— 检查 dagster.yaml 里的 hostname 是不是 postgres
  • 代码加载失败 —— 检查 workspace.yaml 里的 module_name 对不对

各组件端口一览

为了方便你排查,我把端口整理了一下:

组件 内部端口 外部映射端口 说明
Dagit 3000 3000 Web UI,浏览器访问
Daemon 无 无 后台进程,无需暴露端口
Postgres 5432 5432 数据库,可用 Navicat 等工具连接
我个人建议: 生产环境不要暴露 Postgres 的 5432 端口到宿主机。只在 docker-compose.yml 里写 ports: - "5432:5432" 是为了开发调试方便。上线前记得删掉。

验证部署是否成功

打开 Dagit 后,你会看到一个空的工作台。别急,这说明环境搭好了。你可以手动触发一个测试:

  1. 点击左侧的 Launchpad
  2. 选择一个 Job(如果你还没写,可以先创建一个简单的)
  3. 点击 Launch Run

如果运行记录出现在 Runs 页面,恭喜你,全家桶跑通了。

嗯,我记得第一次搭好环境时,看到 Dagit 页面里那个绿色的「Success」标记,心里特别踏实。环境搭好了,后面写代码才有底气。