devAlice
← Multi-OS

Dev Container — 统一 Mac / Windows / Linux 开发环境

使用 VS Code Dev Containers 消除操作系统差异。基于 Docker 的可复现开发环境,由全团队共享的单一 devcontainer.json 定义。

「在我机器上能跑」这句话,通常源于操作系统差异。Mac 有 brew 和 Apple Silicon,Windows 有 PowerShell 和 UTF-16,Linux 机器有 systemd。即使所有人安装了相同版本的 Node 和相同的包,细微差异仍然存在。

我认为 Dev Containers 解决的不只是环境一致性,而是入职成本。以前新团队成员需要花数天配置开发环境;如今通过 devcontainer.json 共享的容器化环境,克隆加一条命令就能获得完全相同的环境,因为环境定义本身已经成为代码库的一部分。

Dev Containers(VS Code Dev Containers + 标准 devcontainer.json)将操作系统差异封装在 Docker 容器内。整个团队在同一个 Linux 环境中工作,只有 IDE 运行在宿主机上。本指南介绍如何配置一个可在 Mac、Windows 和 Linux 宿主机上完全一致运行的开发容器。

TL;DR

  1. 安装 Docker 运行时(Mac:OrbStack/Docker Desktop,Win:Docker Desktop/WSL Docker,Linux:docker)
  2. VS Code + Dev Containers 扩展(ms-vscode-remote.remote-containers
  3. 在项目根目录添加 .devcontainer/devcontainer.json
  4. Cmd/Ctrl+Shift+PDev Containers: Reopen in Container — IDE 在容器内重新启动
  5. 新团队成员:克隆 → 执行该命令一次 → 获得完全相同的环境

前置条件

1. 为什么使用 Dev Containers

解决的问题

  • 「在我机器上能跑」 — 容器内操作系统和工具链完全一致
  • 加快上手速度 — 新员工无需阅读 30 行的 README,点击一次即可
  • 版本冲突 — 项目 A 用 Node 18,项目 B 用 Node 22 — 无需宿主机的 mise/nvm,完全隔离
  • OS 特有的构建问题 — 绕过 Apple Silicon ARM 问题、Windows 长路径问题等
  • 与 CI 保持一致 — 使用与 GitHub Actions 工作流相同的基础镜像,消除差距

Dev Containers 的不足

  • GUI 应用开发 — Mac 原生或 Windows 原生 UI 构建不适合放在容器中
  • GPU 密集型工作负载 — 部分 GPU 直通可行,但在宿主机上运行更稳定
  • 低配置机器 — Docker 本身额外占用 1–2GB 内存(16GB 配置下问题不大)

2. 最简 devcontainer.json

.devcontainer/devcontainer.json

{
  "name": "My Project Dev",
  "image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm",
  "features": {
    "ghcr.io/devcontainers/features/git:1": {},
    "ghcr.io/devcontainers/features/github-cli:1": {}
  },
  "postCreateCommand": "npm install",
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ]
    }
  },
  "forwardPorts": [3000, 5173],
  "remoteUser": "node"
}

关键字段说明:

字段作用
imageDocker 基础镜像(Node / Python / Go / Rust 等)
features附加工具(git、gh、docker-in-docker、awscli — 从目录中按需选取)
postCreateCommand容器创建后执行一次(安装依赖等)
customizations.vscode.extensions在容器内自动安装的 VS Code 扩展
forwardPorts暴露给宿主机的端口
remoteUser容器内用户(出于安全考虑,建议使用非 root 用户)

3. 首次运行

VS Code 命令面板(Cmd/Ctrl+Shift+P) → Dev Containers: Reopen in Container

  • 首次运行:约 1–3 分钟(拉取镜像 + 执行 postCreateCommand
  • VS Code 左下角显示 Dev Container: My Project Dev
# 容器内终端(VS Code Terminal)
uname -a
# Linux 1234abcd 6.10.x #1 SMP Debian ...
 
which node
# /usr/local/share/nvm/versions/node/v22.x/bin/node

无论宿主机操作系统如何,均使用相同的 Linux 环境。


4. 选择基础镜像

devcontainer.json 中的 image: 字段。官方目录:

语言 / 技术栈镜像
Node.jsmcr.microsoft.com/devcontainers/javascript-node:1-{18,20,22}-bookworm
Pythonmcr.microsoft.com/devcontainers/python:1-{3.11,3.12,3.13}-bookworm
Gomcr.microsoft.com/devcontainers/go:1-{1.22,1.23}
Rustmcr.microsoft.com/devcontainers/rust:1-bookworm
Javamcr.microsoft.com/devcontainers/java:1-{17,21}-bookworm
.NETmcr.microsoft.com/devcontainers/dotnet:1-9.0-bookworm
Universal(多语言)mcr.microsoft.com/devcontainers/universal:2-linux

建议:单一语言项目 → 使用对应语言的镜像;多语言项目 → 基础镜像 + features 组合。

也可以编写自定义 Dockerfile

{
  "name": "My Custom Dev",
  "build": {
    "dockerfile": "Dockerfile",
    "context": ".."
  }
}

5. features — 一行添加工具

features 块使用官方目录(https://containers.dev/features)中的模块。一行即可安装工具。

常用选项:

"features": {
  "ghcr.io/devcontainers/features/git:1": {},
  "ghcr.io/devcontainers/features/github-cli:1": {},
  "ghcr.io/devcontainers/features/docker-in-docker:2": {},   // 在容器内运行 docker
  "ghcr.io/devcontainers/features/aws-cli:1": {},
  "ghcr.io/devcontainers/features/terraform:1": {},
  "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {}
}

6. Docker-in-Docker — 容器内运行容器

当需要在开发容器内执行 docker run(测试 docker-compose、验证构建)时:

"features": {
  "ghcr.io/devcontainers/features/docker-in-docker:2": {
    "version": "latest",
    "dockerDashComposeVersion": "v2"
  }
}

或共享宿主机的 Docker socket(更轻量,但安全性略低):

"mounts": [
  "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
]

7. 宿主机 ↔ 容器文件挂载

工作区文件夹自动挂载到 /workspaces/{project}

追加挂载:

"mounts": [
  "source=${localEnv:HOME}/.aws,target=/home/node/.aws,type=bind,readonly",
  "source=${localEnv:HOME}/.gitconfig,target=/home/node/.gitconfig,type=bind"
]

macOS 挂载性能优化请参见 docker-setup §5.2(推荐 VirtioFS)。


8. 环境变量与密钥

静态环境变量

"containerEnv": {
  "NODE_ENV": "development",
  "LOG_LEVEL": "debug"
}

密钥(切勿提交)

挂载 .env 文件或通过 localEnv 传入:

"containerEnv": {
  "GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}",
  "OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}"
}

宿主机 shell 的环境变量会注入容器。请将 .env 添加到 .gitignore


9. 多容器(docker-compose 集成)

为 DB / Redis 等依赖服务使用多容器:

.devcontainer/docker-compose.yml

services:
  app:
    image: mcr.microsoft.com/devcontainers/javascript-node:1-22-bookworm
    volumes:
      - ..:/workspaces/myapp
    command: sleep infinity
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: dev
    volumes:
      - db-data:/var/lib/postgresql/data
volumes:
  db-data:

.devcontainer/devcontainer.json

{
  "dockerComposeFile": "docker-compose.yml",
  "service": "app",
  "workspaceFolder": "/workspaces/myapp"
}

VS Code 附加到 app 容器,db 容器自动启动。


10. 与 CI 保持一致 — devcontainer-cli

在 CI 中复用 devcontainer.json

npm install -g @devcontainers/cli
 
# 构建容器并运行命令
devcontainer up --workspace-folder .
devcontainer exec --workspace-folder . npm test

GitHub Actions:

# .github/workflows/test.yml
- name: Test in dev container
  uses: devcontainers/ci@v0.3
  with:
    runCmd: npm test

CI 通过 = 本地相同结果。


11. 验证

# 宿主机
docker --version
 
# 容器内(VS Code Terminal)
uname -a                # Linux ...
which node              # /usr/local/share/...
echo $NODE_ENV          # development(containerEnv 已生效)
node --version          # 容器中的 Node 版本
git --version           # 通过 features 安装

五项均在容器内正常响应 = 配置完成。


12. 故障排查

「Failed to connect」/ 容器启动失败

  • Docker 运行时是否正在运行?(Docker Desktop 菜单栏、orb statuscolima status
  • docker ps 检查是否存在端口/名称冲突
  • VS Code → 命令面板 → 「Dev Containers: Rebuild Without Cache」

构建耗时极长

  • 首次构建需要拉取基础镜像(多 GB)— 这是正常现象
  • 后续构建使用缓存。postCreateCommand 使用缓存(每次重建都会执行)
  • 区分 onCreateCommand(仅执行一次)和 postCreateCommand(每次重建都执行)

文件权限拒绝

  • 容器 UID 与宿主机 UID 不匹配(Linux 宿主机上尤为常见)
  • 设置 remoteUserupdateRemoteUserUID
    "remoteUser": "vscode",
    "updateRemoteUserUID": true

forwardPorts 不生效

  • 容器内应用是否绑定到 0.0.0.0::(而非 127.0.0.1)?
  • 示例:Next.js 需要 next dev -H 0.0.0.0

容器内无法读取宿主机 Git 配置

  • .gitconfig 自动挂载是可选的 — 请在 mounts 中显式添加
  • 或依赖 features 中的 git,它会复制 gitconfig

IDE 在大型 node_modules 下运行缓慢

  • node_modules 从 VS Code 的文件监控中排除(files.watcherExclude
  • 或将 node_modules 放在仅限容器的卷中,而非宿主机挂载:
    "mounts": [
      "source=${localWorkspaceFolderBasename}-node-modules,target=/workspaces/${localWorkspaceFolderBasename}/node_modules,type=volume"
    ]

13. 下一步


参考资料

更新日志

  • 2026-05-16:初稿。devcontainer.json 要点 · 基础镜像选择 · features · docker-compose 集成 · CI 集成 · 六个故障排查案例。