Dagster 日常操作指南:改程式、加套件、用 Dokploy 新增專案

Dagster 跑在 Dokploy 上以後,日常要改 code、加 Python 套件、或建新的 code location 怎麼做?三種場景各自對應的最短步驟。

Dagster 日常操作指南:改程式、加套件、用 Dokploy 新增專案

把 Dagster 部署到 Dokploy 之後,最常遇到的不是部署本身,而是「改完程式怎麼讓它生效」、「加了新套件要不要重啟」、「想把第二個專案塞進同一個 UI 要動哪裡」。這篇筆記把這三種日常操作整理成最短的 SOP。

前提:Dagster 以 docker-compose 的形式部署,四個 service 是 postgresuser_code(gRPC code server)、webserverdaemon。程式碼透過 bind mount 從遠端主機的某個資料夾掛進 user_code/app,code location 由 workspace.yaml 宣告。

本文假設你已經把 code 資料夾獨立成遠端主機上自己掌管的路徑(例如 /home/<USER>/dagster-projects/<PROJECT>/),而不是放在 Dokploy File Mount 裡。這樣 Dokploy 在 Deploy 時不會覆蓋你手改的檔案。

一、改程式:不重啟容器,UI 點一下 Reload

Dagster 的 gRPC code server 在容器內執行,載入 definitions.py 後會把 asset/job 定義常駐。你改檔案不會立刻生效,但不需要重啟容器

SOP:

  1. SSH 到遠端主機,編輯專案資料夾下的 Python 檔
  2. 打開 Dagster UI → 左側 Deployment(或 Code locations)→ 找到該 code location → 點 Reload

Reload 會讓 code server 在容器裡重開一個 subprocess 重新 import 模組,幾秒鐘搞定。這是寫 asset / 調 job 時的標準循環,不要每次都跑 docker restart,更不要動 Dokploy Deploy。

什麼時候 Reload 會失效

  • 你改的不是 definitions.py 本身,而是某個 import 鏈上被 cache 的模組:Dagster 會重開 subprocess,Python 不會用舊的快取,所以一般不會卡。
  • 你裝了新的 Python 套件但還沒重啟容器:Reload 只是重新 import,不會觸發 pip install,看下一節。

二、加 Python 套件:改 requirements.txt 後 restart 一個容器

compose 的 user_code service 啟動指令裡通常會這樣寫:

command:
  - sh
  - -c
  - "pip install --cache-dir /root/.cache/pip dagster==${DAGSTER_VERSION} dagster-postgres
     && (pip install --cache-dir /root/.cache/pip -r /app/requirements.txt 2>/dev/null || true)
     && dagster code-server start -h 0.0.0.0 -p 4000 -f definitions.py"
Code language: PHP (php)

也就是 pip install -r requirements.txt 只在容器啟動時跑一次。你把 pandas 寫進 requirements.txt,code 裡 import pandas 照樣會 ModuleNotFoundError,直到容器重啟。

SOP:

  1. 編輯 requirements.txt
  2. 重啟 user_code 容器
  3. 等容器 healthy(約 10–30 秒,有 pip cache 時更快)
  4. UI 點一次 Reload 讓新套件進 code server

一個常見的誤區

以為在 UI 點 Reload 就會重跑 pip install——不會。Reload 只重新 import 模組。真要新套件生效,必須讓 container 的 entrypoint 重跑一次,所以 docker restart 是最短路徑;Dokploy Deploy 也可以,但那會把整個 compose 重新 up 一輪,多花一倍時間。

為什麼不把 pip install 寫進 Docker image

這篇筆記假設的 setup 每次啟動都跑 pip install,好處是:改依賴不用 rebuild image、開發期迭代快。壞處是冷啟動慢(首次 2–3 分鐘)。進入穩定期可以改成自己 build 一個 pre-installed 的 image 替換 python:3.x-slim,但這不是日常操作要碰的事。

三、在 Dokploy 上新增第二個 Dagster 專案

Dagster 的 UI 可以同時顯示多個 code location,彼此是完全獨立的 Python 環境——你可以一個專案 pin dagster==1.13.1,另一個跑 1.14,互不干擾。每個 code location 對應一個獨立的 gRPC 容器。

步驟 1:在遠端主機建立新專案資料夾

ssh <USER>@<REMOTE_SERVER>
mkdir -p /home/<USER>/dagster-projects/ml_pipeline
cat > /home/<USER>/dagster-projects/ml_pipeline/definitions.py <<'PY'
from dagster import asset, Definitions

@asset
def my_first_ml_asset():
    return "hello from ml_pipeline"

defs = Definitions(assets=[my_first_ml_asset])
PY
touch /home/<USER>/dagster-projects/ml_pipeline/requirements.txt
Code language: PHP (php)

強烈建議每個專案資料夾 git init 各自一個 repo,之後好做版本控制與協作。

步驟 2:在 Dokploy UI 改 compose 加 service

打開 Dokploy 的 compose 編輯頁,複製一份 user_code service 改名成新專案名稱,主要改三件事:

  • service name(也是 workspace.yaml 要 reference 的 hostname)
  • volume 的 bind 路徑指向新專案資料夾
  • 保留相同的 healthcheck、environment、networks 設定
  ml_pipeline:
    image: python:3.14-slim
    working_dir: /app
    command:
      - sh
      - -c
      - "pip install --cache-dir /root/.cache/pip dagster==${DAGSTER_VERSION} dagster-postgres
         && (pip install --cache-dir /root/.cache/pip -r /app/requirements.txt 2>/dev/null || true)
         && dagster code-server start -h 0.0.0.0 -p 4000 -f definitions.py"
    environment:
      DAGSTER_HOME: /opt/dagster
      DAGSTER_POSTGRES_USER: dagster
      DAGSTER_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      DAGSTER_POSTGRES_DB: dagster
    volumes:
      - /home/<USER>/dagster-projects/ml_pipeline:/app
      - ../files/dagster_home:/opt/dagster
      - pip_cache:/root/.cache/pip
    networks: [dagster_net]
    healthcheck:
      test: ["CMD", "dagster", "api", "grpc-health-check", "-p", "4000"]
      interval: 10s
      timeout: 5s
      retries: 10
      start_period: 120s
    restart: always
Code language: PHP (php)

注意 port 是 4000,不用改也不用暴露到宿主:webserver/daemon 在 compose 網路內以 service name 解析,不同 code server 之間 port 相同不衝突,只要 hostname(service name)不同即可。

步驟 3:在 workspace.yaml 加一個 grpc_server

workspace.yaml 裡每個 grpc_server 就是一個 code location:

load_from:
  - grpc_server:
      host: user_code
      port: 4000
      location_name: "main"
  - grpc_server:
      host: ml_pipeline
      port: 4000
      location_name: "ml_pipeline"
Code language: JavaScript (javascript)

host 對應 compose 裡的 service name,location_name 是 UI 側欄顯示的名字,可以隨意取。

步驟 4:Dokploy 按 Deploy

Dokploy 會把新的 compose 送到遠端跑 docker compose up -d——這個指令只會建立新的容器(ml_pipeline),不會動到其他已經 running 的容器,所以現有的 hello asset 跟 run history 全都保留。

部署完幾十秒後,Dagster UI 左側 Code locations 就會出現第二個 location,你可以切換到它去看 asset graph、排 run。

疑難:UI 上新 location 顯示 red / Failed to load

  • 多半是 pip install 還沒跑完。code server 冷啟動第一次要裝 dagster + dagster-postgres + 你自己的依賴,首次約 120 秒(start_period 就是為此設定)。等 healthcheck 綠了再看。
  • 檢查 daemon logdocker logs <COMPOSE_APP_NAME>-daemon-1 --tail 50。如果 gRPC 連不到、或 DAGSTER_POSTGRES_* env 沒設,都會在這裡暴露。
  • Health check 失敗dagster api grpc-health-check 回 non-zero 表示 code server 還沒 import 完 definitions.py,或是 definitions.py 有 syntax error。直接看 code server 的 log:docker logs <COMPOSE_APP_NAME>-ml_pipeline-1

快速對照表

做什麼 要動什麼 生效方式
改 asset / job 程式碼 編輯專案資料夾的 .py UI 點 Reload(幾秒)
加 Python 套件 編輯 requirements.txt docker restart <COMPOSE_APP_NAME>-user_code-1(秒級)
新增 code location Dokploy UI 改 compose + workspace.yaml Dokploy Deploy(約 30 秒)
dagster.yaml Dokploy Mounts 編輯 Dokploy Deploy

小結

把「程式碼」從 Dokploy File Mount 搬到遠端主機上你自己管的資料夾,是 Dagster + Dokploy 這套組合最重要的一個架構決定。做了這一步,日常操作就簡化成「改檔 → Reload」——不用每次都經過 Dokploy 的 Deploy cycle,也不用擔心手改被 Dokploy DB 覆蓋。

Dokploy 負責結構(哪些 service、怎麼連接),你負責內容(寫什麼 asset、裝什麼套件)——這是最順的分工。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *