docker和容器化技术让运维有了质的飞跃,从此,部署软件再也无需担心软件运行所需的繁杂环境,只要拉取镜像然后运行就可以将应用连带其部署的环境一步到位。
但是回想起我们的开发过程,谁还不是依然需要先安装一堆环境(编译器、库等等),然后才能进行开发。如果我们恰好还需要开发各种语言、各种应用,那光是繁杂的环境就足以把电脑搞的乱糟糟。更何况,大多数语言会拥有很多版本,而且版本之间兼容性不佳(例如Python),在自己电脑上安装多个版本的python有时候已经让人血压升高,何况我还要记得哪个版本在哪,哪个版本的库在哪,不同的版本用的是哪一个包管理器等等繁琐的细节……
于是,dev container的想法就产生了。顾名思义,就是在容器中开发。这样一来,开发的应用连带这开发环境就被”连根拔起“,以后就再也不需要担心重新配置开发环境的麻烦了,要做的仅仅是确认一下机器上有没有docker,然后运行一个容器开发就可以。
下面就以python为例,来说明一下如何使用vscode进行容器化开发。vscode中的远程开发和容器开发插件让dev container从概念变成了现实。
我不想将容器运行在本地,于是采用了先远程开发连接服务器,再使用服务器上的容器开发的方式。但是不管怎样,本地vscode上安装了插件才能和远程连接开发。
要安装的核心插件是ms-vscode-remote.remote-ssh和ms-vscode-remote.remote-containers两个。如图所示:
安装之后,可以在左侧看到远程连接的选项卡,进入后如图所示:
上面的下拉框可以切换是主机还是container。我们要做的是先ssh连接一台主机,再打开这个选项卡选到containers,就可以管理主机上的容器了。
容器化开发的容器配置应该是因项目而异的。因为对于每个项目,所需要的环境都不尽相同。就算是两个python项目,开发中用到的包也不一样,不应该使用一个容器开发多个项目。因此,项目的容器配置会放在项目目录下。vscode已经为我们准备好了范式,我们可以在项目下使用.devcontainer目录,存放devcontainer.json和Dockerfile、docker-compose.yaml文件。
devcontainer.json是vscode对容器的配置文件,里面存放了要启动或构建的镜像、要附带安装的vscode插件、创建后执行的命令等一系列信息。Dockerfile中描述了我们特定项目需要的容器应该怎么构建。虽然可以直接使用一个官方镜像开发,但是最好还是做一些定制化,构建项目个性化的镜像。docker-compose.yaml中指定了镜像应该如何运行。
以下就是python项目中对应配置文件的示例:devcontainer.json:
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.3/containers/python-3
{
"name": "Python 3",
"build": {
"dockerfile": "Dockerfile",
"context": "..",
"args": {
// Update 'VARIANT' to pick a Python version: 3, 3.9, 3.8, 3.7, 3.6.
// Append -bullseye or -buster to pin to an OS version.
// Use -bullseye variants on local on arm64/Apple Silicon.
"VARIANT": "3.9",
// Options
"NODE_VERSION": "lts/*"
}
},
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.profiles.linux": {
"bash": {
"path": "/bin/bash"
}
},
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.languageServer": "Default",
"python.linting.enabled": false,
"python.linting.pylintEnabled": false,
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-python.python",
"formulahendry.code-runner"
// "ms-python.vscode-pylance"
]
}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [9000],
// Use 'portsAttributes' to set default properties for specific forwarded ports. More info: https://code.visualstudio.com/docs/remote/devcontainerjson-reference.
// "portsAttributes": {
// "9000": {
// "label": "Hello Remote World",
// "onAutoForward": "notify"
// }
// },
// Use 'otherPortsAttributes' to configure any ports that aren't configured using 'portsAttributes'.
// "otherPortsAttributes": {
// "onAutoForward": "silent"
// },
// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "pip3 install -r requirements.txt",
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "root",
// claws: use this option to run as root in container, else you can not save file because the project is in /root
"containerUser": "root"
}
几个需要说明的地方:
Dockerfile:
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/blob/v0.202.3/containers/python-3/.devcontainer/base.Dockerfile
# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
ARG VARIANT=3.9
FROM mcr.microsoft.com/devcontainers/python:0-${VARIANT}
# [Optional] Allow the vscode user to pip install globally w/o sudo
# ENV PIP_TARGET=/usr/local/pip-global
# ENV PYTHONPATH=${PIP_TARGET}:${PYTHONPATH}
# ENV PATH=${PIP_TARGET}/bin:${PATH}
# RUN if ! cat /etc/group | grep -e "^pip-global:" > /dev/null 2>&1; then groupadd -r pip-global; fi \
# && usermod -a -G pip-global vscode \
# && umask 0002 && mkdir -p ${PIP_TARGET} \
# && chown :pip-global ${PIP_TARGET} \
# && ( [ ! -f "/etc/profile.d/00-restore-env.sh" ] || sed -i -e "s/export PATH=/export PATH=\/usr\/local\/pip-global:/" /etc/profile.d/00-restore-env.sh )
# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
# ARG NODE_VERSION="none"
# RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
COPY requirements.txt /tmp/pip-tmp/
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \
&& rm -rf /tmp/pip-tmp
# [Optional] Uncomment this section to install additional OS packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends git
# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
简单说明:
安装远程插件后,vscode打开项目目录时会自动循环是否启动container后重新打开,选是就可以快速构建启动容器,然后再容器中打开挂载的项目目录进行开发。如果后来对容器配置进行了修改,可以用Ctrl+Shift+p快速打开命令面板,输入dev container rebuild,选择搜索出来的选项进行重新构建打开:
启动的开发容器会自动运行在docker后台,在remote插件中可以对容器进行管理,包括start、stop、remove等,而且状态一目了然,非常方便。