Visual Studio Codeで使えるリモート環境のdevcontainerが意外と便利そうだったのでまとめ
試してたらたまたまVisual Studio Code(vscode)のdevcontainer(Remote Container)が、Remote SSH経由でリモート環境でも使えることを知ったので、devcontainer用の環境構築方法やdevcontainerの構築方法についてまとめてみた
今まではローカル環境のdockerか、codespaceでしか利用できないのかなと思っていたのだけど、リモート含めて利用できるとかなり便利そうな印象だったので一通り試してみました
最近はRemote SSHでリモート環境を利用するケースが多いのでリモート環境で使えないならそんなに使えないかなと思ってたんだけど、普通にRemote SSH経由でdevcontainer使えたのでかなり便利そうだった
devcontainerについてはこちらを参照してもらえればと
はじめに
結論から言うと以下のようなことを試した結果をこの記事にまとめている
- devcontainer(Remote Container)用のサーバー環境構築
- 構築したサーバーで実際に動くdevcontainer設定を行ったサンプルリポジトリの作成
そのためこの記事の構成は以下の内容
- devcontainerサーバーの環境構築例
- リポジトリの設定の説明
- 補足
devcontainer関連の記事は色々あるけど、devcontainer周りは公式ドキュメント含めて実際に利用する際に必要なレベルの情報が一纏めになっていないと感じていたので、今回試した内容+調べたことをまとめた感じ
今回はGo言語のWebアプリを想定したサンプルだけど、個人的には普通にアプリ開発をする上で必要なdevcontainer周りの知識を学ぶための情報やリンクはこの記事でまとめられたんじゃないかという気がしている
作成したサンプルリポジトリはこちら
(多分codespaceでも動くと思うので気になったら一度動かしてもらえるとわかりやすいと思う)
サンプルリポジトリについて
サンプルリポジトリは
- Go言語のWebサーバー(Hello World)を実装
- devcontainer側で
8080
ポートを固定で開けてあるので、http://localhost:8080
でサンプルアプリにアクセス可能 - airを使用したLive reloadを使った開発が可能
-
.vscode/launch.json
を設定してあるのでvscodeのメニューからデバッグ実行が可能 - おまけレベルのユニットテストを実装
- Docker Composeを利用してGo言語 + postgresqlのコンテナを起動
- postgresqlは以下の理由で入れているだけでサンプルアプリ的には実際には利用していない
- devcontainerでDocker Composeを利用する方法の勉強用
- Go言語コンテナからpostgresqlへ接続できることを示すため
- postgresqlは以下の理由で入れているだけでサンプルアプリ的には実際には利用していない
- devcontainer内でdocker build ~ pushが可能(docker-in-docker構成)
あたりを入れてあるので、個人的にはコンテナ化されたGo言語のWebアプリケーションをdevcontainerで使用する例としてはまぁ最低限の内容になってるかなという気持ち
リポジトリのベースとして下記を参考に作成した
下記の方法で構築したサーバーでこのリポジトリをcloneしてきてvscodeで開くとdevcontainerで開き直すかを聞かれるので開き直すとこんな感じになる
あたりまえなんだけど普通にvscodeのまんまで、左下とかを見るとcodespaceみたいな感じで接続してるコンテナの情報が表示される
このリポジトリの試し方は、はじめに
$ make setup
で必要なツールをインストールしてから
$ make run
でairを使ったサーバーの起動
(portforwardしてるのでこの状態で http://localhost:8080 を開くとレスポンスが返ってくるはず)
$ make lint
$ make test
でそれぞれlint(golangci-lint)や go test
の実行などを行えるようになっている
$ make build
でDocker Imageのビルドが可能というのが基本的なところ一通り
devcontainerの設定
devcontainer.json
devcontainer.json
は基本下記のリファレンスを見ながら書いていくことになる
もしくはRemote Containerの Remote-Containers: Add Development Container Configuration Files
コマンドを使って生成するか
今回のサンプルリポジトリでの設定例と項目の簡単な説明は以下のような感じ
{
// 名前
"name": "devcontainer-example",
// この例ではdocker-composeを利用しているので、そのパスを指定している
// docker composeのcontextは .devcontainer になるらしい
"dockerComposeFile": "docker-compose.yml",
// devcontainerでshellなど?で使用するdocker composeのservice名
"service": "app",
// 指定したserviceコンテナのworkspaceフォルダ
"workspaceFolder": "/workspace",
// 指定したポートが始めからvscodeのポート機能で手元環境にforwardされるようになる
"forwardPorts": [8080],
// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/script-library/docs
// にあるスクリプトをdevcontainerで実行できるプラグイン機構
// 今回の例では見ての通りdocker-in-dockerの設定を行ってくれるfeatureを設定してる
"features": {
"docker-in-docker": {
"version": "latest",
"dockerDashComposeVersion": "v2"
}
},
// devcontainerとして開くvscode側の設定カスタマイズ
"customizations": {
"vscode": {
"settings": {
"go.toolsManagement.checkForUpdates": "local",
"go.useLanguageServer": true,
"go.gopath": "/go",
"go.goroot": "/usr/local/go"
},
// devcontainerに自動でインストールするvscode extention
// extentionのページの歯車アイコンをクリックすると出てくる「拡張機能 ID のコピー」というやつからここに貼るIDを知ることができる
"extensions": [
"golang.Go",
"ms-azuretools.vscode-docker",
"shardulm94.trailing-spaces",
"eamodio.gitlens",
"donjayamanne.githistory",
"mhutchie.git-graph"
]
}
},
// host側の実行ユーザー(だと思う)で、devcontainerで開いたフォルダのファイルはこのユーザーに権限が無いと保存などができない
"remoteUser": "vscode"
}
Dockerfile
基本はdocker-composeを利用しているが、メインとなるGo言語用のイメージについては一部手を加えているためDockerfileで作成している
実際には特に制限はないのかも知れないが、devcontainer用のベースイメージをMicrosoftが公開しているため、これをベースにイメージを作成した
- レジストリ: https://hub.docker.com/_/microsoft-vscode-devcontainers
- リポジトリ: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers
以下のDockerfileに手を加えてpsqlコマンドをインストールしているだけ
Dockerfileの中身(&& apt-get -y install --no-install-recommends postgresql-client
のところだけ追加している)
# [Choice] Go version (use -bullseye variants on local arm64/Apple Silicon): 1, 1.18, 1.17, 1-bullseye, 1.18-bullseye, 1.17-bullseye, 1-buster, 1.18-buster, 1.17-buster
ARG VARIANT=1-bullseye
FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT}
# [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] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
# && apt-get -y install --no-install-recommends <your-package-list-here>
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends postgresql-client
# [Optional] Uncomment the next lines to use go get to install anything else you need
# USER vscode
# RUN go get -x <your-dependency-or-tool>
# USER root
# [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
docker-compose.yml
docker-compose.yml
は下記をベースに作成した
基本的にはただの docker-compose.yml
ファイルで、説明があった方が良さそうな設定は以下のあたりかなと思う
-
VARIANT: 1.18-bullseye
: DockerfileのベースとなるGo言語のバージョンを指定 -
security_opt:
とcap_add:
: デバッガー用に設定 -
init: true
docker-in-docker用に設定を追加
version: '3.8'
volumes:
postgres-data:
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
# [Choice] Go version 1, 1.18, 1.17
# Append -bullseye or -buster to pin to an OS version.
# Use -bullseye variants on local arm64/Apple Silicon.
VARIANT: 1.18-bullseye
# Options
NODE_VERSION: "lts/*"
env_file:
# Ensure that the variables in .env match the same variables in devcontainer.json
- .env
# Security Opt and cap_add allow for C++ based debuggers to work.
# See `runArgs`: https://github.com/Microsoft/vscode-docs/blob/main/docs/remote/devcontainerjson-reference.md
security_opt:
- seccomp:unconfined
cap_add:
- SYS_PTRACE
init: true
volumes:
- ..:/workspace:cached
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
# Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function.
network_mode: service:db
# Uncomment the next line to use a non-root user for all processes.
# user: vscode
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)
db:
image: postgres:latest
restart: unless-stopped
volumes:
- postgres-data:/var/lib/postgresql/data
env_file:
# Ensure that the variables in .env match the same variables in devcontainer.json
- .env
# Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally.
# (Adding the "ports" property to this file will not forward from a Codespace.)
launch.json
.vscode/launch.json
を以下の用に設定することでvscodeのメニューからデバッグ実行が可能になるっぽい
ドキュメントは下記のようだけどdevcontainer.jsonのリファレンスと比べるとだいぶ読みにくい
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Server",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/server.go"
}
]
}
devcontainerサーバーの環境構築
devcontainerサーバー用のLinuxのリモート環境を構築する
(別に手元のPCのVMでも動くと思う)
devcontainer(Remote Container)用の環境構築
devcontainer環境に必要なものは
- docker
- docker compose(plugin)
だけなので、これらをインストールする
それとは別に、devcontainer実行用に vscode
ユーザーを作成しておくと root
ユーザーを利用しなくても良いようなので作成しておく
実際にどのユーザーを使うかはdevcontainer側の設定次第だが vscode
と node
ユーザーが一般的なようなので作っておいた方が無難な気がする
docker composeはバージョンが違うとdevcontainerが動かなかったりしたので、もし動かないようなら新しいバージョンを試してみても良いかも
$ apt-get update
$ apt-get upgrade -y
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
$ add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ apt update
$ apt-get install -y docker-ce docker-ce-cli containerd.io
$ mkdir -p ~/.docker/cli-plugins
$ curl -sSL https://github.com/docker/compose/releases/download/v2.10.2/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
$ chmod +x ~/.docker/cli-plugins/docker-compose
$ groupadd vscode
$ useradd -s /bin/bash -m -g vscode vscode
$ useradd -s /bin/bash -m -g vscode node
$ cat << EOS > /etc/sudoers.d/vscode
vscode ALL=(ALL:ALL) NOPASSWD: ALL
node ALL=(ALL:ALL) NOPASSWD: ALL
EOS
構築したサーバーでdevcontainerを使う
構築したサーバーでdevcontainerを使うにはvscodeでRemote SSHで .devcontainer.json
や .devcontainer/devcontainer.json
のあるフォルダを開くとvscodeからdevcontainerとして開き直すか聞かれるので開き直すだけ
リポジトリをcloneしたりするユーザーが vscode
や node
以外だとパーミッションエラーで保存したり変更できないケースがあるので、下記の例のようにパーミッションの設定を行って置いた方が良い
$ git clone git@github.com:bells17/devcontainer-example.git
$ chmod -R vscode:vscode devcontainer-example
devcontainer(Remote Container)を便利に使うようにするための設定
SSH ForwardAgentの有効化
git操作などでssh-agentを利用できるようにサーバー側で有効化する
(実際にssh-agentを利用するためにはクライアント側でも別途 ssh-add
による設定が必要だけど省略)
これをやっておかないとdevcontainerから git push
とかできない
$ cat << EOS > /etc/ssh/sshd_config.d/forwardingagent.conf
AllowAgentForwarding yes
EOS
$ systemctl reload sshd
git設定
devcontainer(Remote Container)だとhost側の ~/gitconfig
をコンテナ側に同期する仕組みがあるので、エイリアスなどを設定している場合は適時設定しておいた方が良い
個人的には普段自分が設定してるgitのエイリアスそのままでgitが使えるのですごい楽
また最低限以下は設定しておかないと git commit
できないので設定しておく
(普段使ってる ~/.gitconfig
があるならそれをそのままコピーすればOK)
git config --global user.email <email>
git config --global user.name <name>
パーミッション
先程 vscode
ユーザーを作成したが、実際にどのユーザーを使うかについては .devcontainer/devcontainer.json
の remoteUser
の設定に依存するみたい
ファイルの保存などについてはこのユーザーに権限が無いとできないので、devcontainerで開くソースコードを置くディレクトリは remoteUser
で使用するユーザーがwriteしたりできるよう chmod -R
でパーミッション設定しておく必要がある
remoteUser
でroot以外を使う話については下記のリンクあたりで解説されている
なのでそもそもソースコードを置くディレクトリを作るなど、このサーバーで基本的な操作を行うユーザーははじめから vscode
でできるようにしておいた方が無難だと思う
devcontainerまとめ
コンテナのベースイメージ
にせっかくMicrosoftが公開してくれているベースイメージがあるので、特に理由がなければこれらをベースにして独自イメージを作るのが良さげ
remoteUserで指定するユーザー
を見る限り
UID/GID が 1000 の非 root ユーザー (通常は
vscode
またはnode
と呼ばれます)
とあって
のいくつかを見ても vscode
が使われているようなので、基本 vscode
使っておけば無難では?という感じ
(host側でも設定 vscode
ユーザーだけ作っておけば良いので)
devcontainerでKubernetesを使いたい
試してないけどdocker-in-dockerができるならkind動きそうだよね
あとはBridge to Kubernetesというツールで既存のクラスターを使うという方法もあるらしい
featureについて
どうやらdevcontainer.jsonのfeatureで設定すると、対応するscritpを実行して環境構築とかをしてくれるプラグイン機構がこのfeatureというやつのことのよう
何故か探した限りvscodeのドキュメントにもfeature機能の一覧が見当たらないようなんだけど、下記のscript-libraryがこのfeatureの一覧のよう
例えばgoだと以下のように設定して使えるって書いてある
"features": { "golang": "latest" }
また下記を見る限りユーザー独自のfeatureを使うこともできるみたいなので今後に期待
(軽く見た感じまだ開発中な機能なのかなという印象)
devcontainerの仕組みってどうなってんの?
よくわからないけどエラーになったときのログみると
$HOME/.vscode-server/bin/e4503b30fc78200f846c62cf8091b76ff5547662/node \
$HOME/.vscode-remote-containers/dist/dev-containers-cli-0.245.2/dist/spec-node/devContainersSpecCLI.js up \
--workspace-folder /path/to/repo \
--workspace-mount-consistency cached \
--id-label devcontainer.local_folder=/path/to/repo \
--log-level debug \
--log-format json \
--config /path/to/repo/.devcontainer/devcontainer.json \
--default-user-env-probe loginInteractiveShell \
--mount type=volume,source=vscode,target=/vscode,external=true \
--skip-post-create
みたいなコマンド実行してるっぽいので
- vscodeのRemote SSHがhostにインストールしたnodeのバイナリを使って
-
devContainersSpecCLI.js
というのを実行してdevcontainerを作ってるっぽい
というのが想像できた
js実行してるのになんでhost側の設定いらないのかな?って思ってたけどRemote SSHがnodeのバイナリ置いてるからなのかってのが結構面白かったポイント
ちなみに devContainersSpecCLI.js
は実行ファイル名は下記の devcontainer
コマンドとサブコマンドやオプションが同じみたいだから多分同じものなんじゃないかと思ってる
devcontainer cli
個人的には今の所devcontainerってvscodeのRemote SSHでしか使わなそうだしこれなにに使うんかな?と思ってたら使用例についてもいくつか書かれてた
軽く見てみた感じ
- CIでdevcontainerを利用
- Docker Imageのビルドでdevcontainerを利用
- vimなどのエディタでdevcontainerを利用
といったサンプルがあるようだった
devcontainer自体について
なんかいつの間にかDevelopment Containersなる仕様が作られてたらしくサイトがあった
ここで仕様とかが公開されていて下記のリポジトリでスキーマ定義とかもあるみたい
もしかしたら今後はvscodeだけじゃなくてdevcontainerの仕様準拠のツールとかも出てくるのかな?
今のところgitpodのissueとかみるとあまり動きはなさそう
↓
と思ったらgitpodがopenvscode-serverなるものを作ってた!
(これ中身調べたい〜)
codespace側の仕組み
TODO ↓あたりを調べる
感想
- (少なくともリモート環境で)devcontainer使う際に知りたいことは周辺知識含めてだいたい知れた気がしてる
- この記事でもそれらをまとめられたと思うので自分でわかんなくなったときはこの記事見返そうかなと思ってる
- host側にはあまり制約事項が無くて、dockerとか最低限のツールがあれば、あとはdevcontainer側が吸収するようになってるのが良さそう
- わざわざ仕様まで作ったのでvscodeのRemote Containerでも利用するツールをdevcontainer以外にもオプションで選べるようになってればdevcontainerが未対応の機能に対応したツールとか使えて更に良さそう
- エディタ側もdevcontainerの仕様が公開されているので、vscodeに限らずdevcontainerを使える未来も割とすぐに来そう(LSPみたいに広く使われるようになると開発環境が大きく変わりそう)
- devcontainerって既にvscodeっていうエディタというプラットフォームを抑えてたのでかなり強いよなーと感じた
おまけ
記事の画像もGithubで管理できるらしかったので今回はこれも試してみた
Discussion