Dagster 日常操作指南:改程式、加套件、用 Dokploy 新增專案
把 Dagster 部署到 Dokploy 之後,最常遇到的不是部署本身,而是「改完程式怎麼讓它生效」、「加了新套件要不要重啟」、「想把第二個專案塞進同一個 UI 要動哪裡」。這篇筆記把這三種日常操作整理成最短的 SOP。
前提:Dagster 以 docker-compose 的形式部署,四個 service 是 postgres、user_code(gRPC code server)、webserver、daemon。程式碼透過 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:
- SSH 到遠端主機,編輯專案資料夾下的 Python 檔
- 打開 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:
- 編輯
requirements.txt - 重啟
user_code容器 - 等容器 healthy(約 10–30 秒,有 pip cache 時更快)
- 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 log:
docker 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、裝什麼套件)——這是最順的分工。