VS Code Dev Container 開發環境
Docker 部署 的 dev 堆疊已經有一份 devcontainer.json,但只給了設定檔跟一句「打開就能用」。本頁把它講清楚:VS Code 連進去之後實際發生了什麼事、程式碼編輯與執行的節奏跟平常有什麼不同,以及怎麼把 Claude Code 也裝進同一個容器,讓它跟你共用一模一樣的開發環境。
先備:讀過 安裝與環境 的 bind mount 說明,且已經照 Docker 部署 建好 dev/ 堆疊。
先裝好工具
- Docker Desktop(應該已經裝好)
- VS Code
- VS Code 擴充套件 Dev Containers(
ms-vscode-remote.remote-containers)
打開專案、連進容器
- VS Code 開啟
odoo-stack這個專案的根目錄(不是dev/子目錄,因為devcontainer.json裡的路徑都是相對於根目錄寫的)。 - 打開命令選擇區(
Cmd/Ctrl+Shift+P),執行 Dev Containers: Reopen in Container。 - 第一次會照 Docker 部署 的
dev/compose-dev.yml把整套 dev 堆疊(Odoo、Postgres、MailCatcher⋯)都跑起來,然後 VS Code 連進odoo這個服務的容器,不是另外建一個新容器。
連進去之後,VS Code 視窗左下角會顯示目前連在哪個容器;整合終端機、Python 語言伺服器(Pylance)、除錯功能全都在容器裡執行,不是在你的電腦上——這正是 Dev Container 的重點:你的編輯器介面在主機,但看到的檔案系統、跑的指令、裝的語言環境都是容器裡那一份,跟同事、跟正式環境用的是同一份。
看懂這份 devcontainer.json 在做什麼
{
"name": "Odoo 20 Dev",
"dockerComposeFile": ["../dev/compose-dev.yml"],
"service": "odoo",
"workspaceFolder": "/workspace",
"shutdownAction": "stopCompose",
"postCreateCommand": "pip3 install --user black isort",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"tamasfe.even-better-toml",
"oderwat.indent-rainbow",
"batisteo.vscode-django"
],
"settings": {
"python.defaultInterpreterPath": "/usr/bin/python3",
"python.analysis.typeCheckingMode": "basic",
"editor.formatOnSave": true
}
}
},
"remoteUser": "root",
"mounts": [
"source=${localWorkspaceFolder}/dev/odoo/extra-addons,target=/mnt/extra-addons,type=bind",
"source=${localWorkspaceFolder},target=/workspace,type=bind"
]
}
| 欄位 | 作用 |
|---|---|
dockerComposeFile + service |
告訴 VS Code:不要自己生一個容器,去啟動這份 Compose 檔、連到 odoo 這個服務 |
workspaceFolder |
VS Code 視窗打開後,檔案總管根目錄指到容器裡的哪個路徑 |
shutdownAction |
關閉 VS Code 視窗時,把整組 docker compose 服務一起停掉(stopCompose),不會留著背景繼續跑 |
postCreateCommand |
容器建立之後自動執行一次的指令,這裡拿來裝兩個 Python 格式化工具 |
customizations.vscode.extensions |
連進容器後,VS Code 自動幫你在容器裡安裝這幾個擴充套件(跟主機上裝的擴充套件是分開的兩份) |
python.defaultInterpreterPath |
指到容器內建的 Python,Pylance 才找得到 Odoo 核心套件(官方映像用 deb 安裝,Odoo 的 Python 模組在系統的 dist-packages 裡,跟 addons_path 設定的路徑是同一個),from odoo import ... 才會有自動完成、不會被畫紅線 |
remoteUser |
容器裡用哪個使用者身分跑指令;這裡設 root 圖方便,寫檔案不會卡權限 |
mounts |
見下面說明 |
為什麼 mounts 裡兩條路徑指到同一個地方
"mounts": [
"source=${localWorkspaceFolder}/dev/odoo/extra-addons,target=/mnt/extra-addons,type=bind",
"source=${localWorkspaceFolder},target=/workspace,type=bind"
]
這是同一份 bind mount 概念(見 安裝與環境)用了兩次,把同一個主機資料夾掛進容器的兩個不同路徑:
/mnt/extra-addons:Odoo 的addons_path設定指的就是這裡,Odoo 執行時從這裡讀模組。/workspace/dev/odoo/extra-addons:因為整個專案根目錄(${localWorkspaceFolder})也掛進了/workspace,同一份extra-addons資料夾當然也會出現在這條路徑下——這是 VS Code 檔案總管顯示的位置,跟上面那個是同一份檔案,改其中一邊,另一邊立刻跟著變(不是兩份拷貝)。
也就是說:你在 VS Code 左側檔案總管點開 dev/odoo/extra-addons/library/... 編輯,Odoo 讀取的 /mnt/extra-addons/library/... 就是改過的內容。
這樣開發,跟平常有什麼不一樣
最大差異:終端機已經在容器裡了,不用再打 docker compose exec 或 docker compose run 前綴。 odoo-bin 指令 教的所有指令,在這裡的整合終端機(Ctrl+`)直接下就好:
# 安裝、升級(不用加 docker compose run --rm odoo)
odoo -d mydb -i library --with-demo --stop-after-init
odoo -d mydb -u library --stop-after-init
# 互動式 shell
odoo shell -d mydb
其他好處:
- 自動完成看得懂 Odoo:
from odoo import api, fields, models這種寫法,Pylance 找得到定義、按住Cmd/Ctrl點下去能跳到 Odoo 核心原始碼,不會滿篇紅色底線。 - 改檔案即時生效:延續 bind mount 的老規矩——改 Python 方法內邏輯要重啟、改欄位或 XML 要
-u,見 odoo-bin 指令 的節奏表。 - 除錯:最簡單的方式還是在程式裡寫
breakpoint(),然後在整合終端機前景執行odoo -d mydb ...(不要加--stop-after-init),中斷點會直接跳出互動式除錯環境,見 測試與除錯。想要在 VS Code 的圖形介面設中斷點、按滑鼠單步執行,要另外裝debugpy並設定launch.json用「attach」模式連過去,做法因人而異,這裡先不深入。
加入 Claude Code 一起開發
Dev Container 除了給你自己用,也是 Claude Code 官方建議的執行方式之一:Claude Code 跑在容器裡面,看到的檔案、裝的工具跟你在 VS Code 裡看到的完全一樣;你在主機用編輯器瀏覽,Claude Code 在容器裡讀寫、執行指令,兩邊動的是同一份 bind mount 出來的原始碼。
1) 把官方 Feature 加進 devcontainer.json
Anthropic 官方維護一個 Dev Container Feature,加一行設定就能在容器裡裝好 Claude Code:
{
"features": {
"ghcr.io/devcontainers/features/node:1": {},
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
}
}
為什麼要多加一個 Node.js feature
Claude Code 的 CLI 需要 Node.js/npm 才能執行。Odoo 官方映像在建置過程中裝過 Node.js(只是為了編譯前端用的 rtlcss),用完就整個移除了,成品映像裡沒有 Node.js。Claude Code 的 feature 在偵測不到 Node.js 時會嘗試自動幫你裝,但保險起見,直接明講加上 ghcr.io/devcontainers/features/node:1 比較不會建置失敗。
2) 重新建置、登入
命令選擇區執行 Dev Containers: Rebuild Container,等它建完,開一個整合終端機(Ctrl+`):
第一次執行會請你用瀏覽器登入 Anthropic 帳號(或你組織設定的 Bedrock/Vertex/Foundry 認證方式)。跟著畫面上的提示做完就能開始用。
3) 兩種用法
這個 Feature 連進 VS Code 時,會自動幫你裝好 Claude Code 的 VS Code 擴充套件(anthropic.claude-code),所以有兩種等價的用法:
- 在整合終端機打
claude進互動模式(跟平常用 Claude Code CLI 完全一樣)。 - 用側邊欄的 Claude Code 擴充套件面板。
兩者共用同一份 ~/.claude 設定,登入一次兩邊都能用。
4) 讓下次重建容器不用重新登入
Dev Container 預設每次「Rebuild」都是全新的容器,家目錄會被清空,登入資訊也會不見。要留住登入狀態,加一個具名 volume 掛在 ~/.claude,並設定 CLAUDE_CONFIG_DIR 指到同一個路徑(因為上面的範例 remoteUser 是 root,家目錄是 /root):
{
"mounts": [
"source=${localWorkspaceFolder}/dev/odoo/extra-addons,target=/mnt/extra-addons,type=bind",
"source=${localWorkspaceFolder},target=/workspace,type=bind",
"source=claude-code-config-${devcontainerId},target=/root/.claude,type=volume"
],
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/root/.claude"
}
}
${devcontainerId} 讓每個專案有自己獨立的一份設定,不會跟別的專案共用同一個登入狀態。
5) 在這個環境裡,Claude Code 能直接做什麼
因為 Claude Code 執行在跟你一樣的容器裡,它下的指令不用加任何 docker compose 前綴,直接就是:
odoo -d mydb -u library --stop-after-init
odoo -d mydb -u library --test-tags /library --stop-after-init
實際帶著它做一個模組的節奏,跟你自己做完全一樣:請它照 實作:圖書借閱模組 的步驟寫欄位、寫檢視,改完讓它自己跑 -u 升級、跑測試驗證,有錯誤訊息貼給它看,它能直接讀 odoo-bin 指令 教的排錯方式對照。
安全提醒(官方文件原文的重點)
- 只在你信任的專案裡這樣用;容器內能讀寫的東西,理論上都可能被惡意程式碼碰到。
- 不要把主機的
~/.ssh、雲端憑證檔案這類機密掛進容器;要用 repo 專屬或短效的憑證。 - 上面的範例
remoteUser是root——Claude Code 在 root 底下執行時,不接受--dangerously-skip-permissions(跳過權限確認)這個參數,執行時一律會照常跳出工具呼叫確認,這其實是件好事,不用特別處理。
不想改用 Dev Container?Claude Code 也能跑在主機上
Dev Container 不是唯一選項。因為 addon 原始碼本來就用 bind mount 對應到主機的資料夾,Claude Code 完全可以留在你的主機上執行(例如平常在 VS Code 但沒開 Dev Container、或用終端機直接跑),一樣能讀寫同一份程式碼。差別只在於:執行 Odoo 相關指令時要自己加 docker compose exec/docker compose run 前綴,因為 Odoo、Python、psql 這些工具都只存在容器裡,不在主機上:
哪一種都可以,選你習慣的:在容器裡開發,指令比較短、環境完全一致;留在主機,設定比較單純、不用重建容器。odoo-bin 指令 那頁對這兩種模式的差異有更完整的說明。