VSCode 容器开发环境
作为从没有插件的 v0.x 年代开始使用 VSCode 的铁杆用户,所有的开发几乎全都使用 VSCode 进行。
由于 VSCode 本身只是一个编辑器,要实现各种功能需要大量的插件支持,想要达到 100 个插件并不是一个复杂的事情。
而实际上并非所有环境都需要所有的插件,让 VSCode 保持一个功能刚好够用的 “IDE” 是很有必要的。
自动启用/禁用插件
最简单的想法是,每个文件夹/工作区可以配置启用哪些插件。
目前 VSCode 存在该功能,但是对用户黑盒。通过右键插件可以控制在当前工作区禁用(不影响其他工作区),所有工作区级别的禁用/启用并没有一个对用户可见的文件存储,也就无法像 settings.json
或 extensions.json
那样放置在文件夹内。
虽然对于同一台电脑,用户并没有手动维护的必要性,但是如果文件夹路径修改,可能就需要重新配置
在 Github 上,有人提出了 这个问题,希望可以在 extensions.json
中添加 disabled
字段来控制禁用哪些插件,但 VSCode 团队认为 extensions.json
存在副作用,由于其是存放在 .vscode
文件夹内,原则上是和代码绑定的,用户 A 使用哪些插件不应该影响用户 B。
(看上去很有道理,但是 settings.json
实际上已经存在这个问题了,而且用户可以通过配置 .gitignore
来规避)
最近,官方推出了 settings-profile,可以把上面提到的 “黑盒配置” 共享出来。但是这并没有完美解决问题,仍然不是一个比较优雅的解决方案(甚至可以认为完全没有解决)。
Remote Container
VSCode 最有名的功能是 Remote SSH,图形界面远程开发怎么想都很跨时代。和这个功能一起推出的是 Remote WSL 和 Remote Container,前者由于经常在 WSL 开发,所以用得很多,而后者则由于存在一定的学习门槛因此没用过。
首先简单描述下 Remote Container 的大致思路,用户通过预先配置开发环境容器(可以是一个镜像,也可以是一个 Dockerfile,亦或是 Docker Compose),VSCode 会自动拉取/构建对应的镜像文件,并按照配置启动容器,并将代码挂载入容器内进行开发
这里以 golang 开发为例,我们希望容器包含如下内容
- go 二进制
- go 的相关插件
- go 的基本配置
- 一些个人习惯的配置
为了方便在基础 Golang 镜像的基础上进行配置,使用 Dockerfile 来生成镜像。
镜像本身没有需要特别注意的地方,唯一可能会有问题的是用户权限,vscode 会自动绑定当前用户/用户组和容器内的用户/用户组,因此需要确保容器内拥有对应的配置
ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=$USER_UID RUN groupadd --gid $USER_GID $USERNAME \ && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME USER ${USERNAME}
接下来就是控制如何启动容器。
对于端口映射,这里有两种不一样的实现,但是效果差别不大
- 容器本身的端口映射:在容器创建前通过
-p
指定,容器只要运行就会自动映射 - VSCode 转发:类似 ssh 的端口转发,只要会话连接存在,转发就可用。但是如果连接关闭(ssh 断开/vscode 关闭,那么转发就会停止,即使服务器/容器还在运行)
前者不支持修改,创建容器时配置了哪些,就只能是哪些,“几乎”不能修改(参考“如何为已经启动的容器添加映射端口”);而后者则支持动态转发。
前者的配置通过 "appPort": [ 3000, "8921:5000" ]
,后者则是 "forwardPorts": [3000, 3001]
如果需要指定映射的端口,可以通过 "forwardPorts": [3000, 3001]
来配置,也可以在容器启动后手动指定。
容器默认会挂载代码目录,但是如果有特殊的需要也可以通过 "mounts": ["source=${localWorkspaceFolder}/app-scripts,target=/usr/local/share/app-scripts,type=bind,consistency=cached"]
指定挂载其他目录。
为了保持开发环境的“干净”,不建议加这些东西
对于一些特殊功能,可以使用官方的 feature
来进行配置,比如 Docker in Docker、git
或是一些基础的开发环境。不过这部分完全可以写在 Dockerfile 里,可控性更高一点。
大部分常用的配置就这些,如果不希望容器名过于随机,可以使用 "runArgs": ["--name=dev-go-example"]
强行指定
在 vscode 关闭后,容器会自动停止,可以通过配置 "shutdownAction": "none"
禁用
下面给出一份 golang 的简单开发环境
{ "name": "golang", "build": { "dockerfile": "Dockerfile" }, "runArgs": [ "--name=dev-go-example" ], "extensions": [ "golang.go", "ms-azuretools.vscode-docker" ] }
FROM golang:1.17.0 ARG USERNAME=vscode ARG USER_UID=1000 ARG USER_GID=$USER_UID RUN groupadd --gid $USER_GID $USERNAME \ && useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME USER ${USERNAME} RUN go install github.com/cweill/gotests/gotests@latest RUN go install github.com/fatih/gomodifytags@latest RUN go install github.com/josharian/impl@latest RUN go install github.com/haya14busa/goplay/cmd/goplay@latest RUN go install github.com/go-delve/delve/cmd/dlv@latest RUN go install honnef.co/go/tools/cmd/staticcheck@latest RUN go install golang.org/x/tools/gopls@latest RUN curl -o ${HOME}/.bashrc \ -k \ "https://proxy.ohyee.cc/gist.githubusercontent.com/OhYee/87228bbce831b4b7027e3d6407e7b2f8/raw/.bashrc"
Remote SSH + Remote Container
值得一提的是,到现在位置,实际上大部分开发并不在本地进行,服务器开发往往可以获得更高配置。而值得一提的是,VSCode 支持通过 remote ssh 使用 remote container,也即使用服务器上的 Docker 进行开发。
这一切对于大部分场景而言,完全和本机一致,用户完全无感。特殊情况下可能会存在下面的问题
- 用户没有 docker 执行权限,需要确保用户在 docker 用户组,并且 docker 允许同组用户读写
# 将当前用户添加至用户组 usermod -a -G docker <当前用户> # 查看 docker socket 文件权限(应该是 srw-rw----) ls -l /var/run/docker.sock
- 服务器上有多个用户,而且大家都在用 remote-container。由于临时文件可能存在权限冲突,可以考虑把不同的用户临时文件放在不同的文件夹内,比如在
/etc/profile.d/
添加一个tmp.sh
文件并写入下下面的内容export TMPDIR=/tmp/${USER} mkdir -p ${TMPDIR}
当然,如果只想修改自己用户的临时文件夹,也可以加入到
~/.bashrc
就目前而言,微软和 Docker 的合作出的内容可谓是非常贴心的,从 WSL + Docker 到 Remote SSH + Docker,存在很多非常细节上的正向优化,完全不像是那个开发 Windows 的微软。
后记
为不同的项目配置不同的容器开发环境,一方面可以保证开发环境的干净。虽然 vscode 的插件存在启动触发判断,但是这取决于开发者的自觉,一个 python 插件完全可以申请 java 的触发。而即使同样是同一种语言,有时插件间也可能存在冲突,不同的项目需要选择不同的插件使用。
另一方面,一个可移植的开发环境有助于别人环境搭建。开源或是公司项目自不必说,保持开发环境一致至关重要。而即使是个人项目,时间长了环境部署也可能会存在问题,而且在同时多个项目开发时,由于不同项目环境可能存在冲突,有存在引入玄学 bug 的可能。
参考资料
- microsoft/vscode Issue #4023 Feature Request: Enable/disable extensions from config file 9
- microsoft/vscode-remote-release Issue #2347 EACCES: permission denied, mkdir '/tmp/vsch
- Developing inside a Container using Visual Studio Code Remote Development
- devcontainer.json reference
- 【环境搭建】WSL下基于Docker的深度学习环境配置
- 如何优雅地使用 Docker