devcontainerを使ってみよう
devcontainerを使ってみよう
devcontainerを使う上で知っておくと良さげな情報のまとめ記事です
前にRemote SSHでdevcontainerの環境を構築する記事を書いたので、今回はdevcontainer全般の情報をまとめてみました
tl;dr
- devcontainerを使うと開発環境をコンテナで構築できるよ(ランタイムとかツール類含めて!)
- docker composeだとアプリケーションを動作させる環境は作れるけどdevcontainerは開発環境ごと構築できて便利
- devcontainerを使うにはdockerとdevcontainerを利用できるエディタが必要
- devcontainer内でdocker composeを利用できるから、devcontainer用のコンテナ+ミドルウェアコンテナを用意すればアプリケーションを開発できる環境がまるっとコンテナで作れる
- devcontainer cliなどを使えばローカルでも利用できるし、codespaceやdevpod、coderなどを使えばリモート開発環境も作れるよ(gitpodはまだdevcontainer未対応だった)
devcontainerとは?
- Development Containersの略
- 開発環境をまるっとコンテナで構築できる
(devcontainer側の思想としてはCI環境でもdevcontainer使おうぜ!というのもあるみたい) - ローカルに必要なのはdockerとdevcontainer対応エディタだけ!
-
devcontainer.json
という設定ファイル元にdevcontainerを構成する
デモ
- goとredisのwebアプリケーションを
- docker composeで立ち上げて
- 開発したアプリケーションをDocker in Dockerでビルドしてイメージ作れて
- devcontainer接続時のメッセージをカスタムしていて
- ローカルに自作したfeatureを使ったredis-cliを(雑に)インストールするようになってる
って感じのサンプルリポジトリです
TODO template機能も試した例にしてみたい
サポートされてる機能
- devcontainerのベースとなるコンテナイメージ有り(必ずしも使う必要は無い)
- Dockerfileによるdevcontainerの構築
- docker composeによる複数コンテナによる開発環境の構築
- ライフサイクルスクリプトによる構成追加
- ポートフォワード機能
- プラグイン機能(feature)によるツールインストール
-
ホスト側の
$HOME/.gitconfig
の取り込み(vscodeのDev Containersプラグインによる) - ssh-agentのサポート(vscodeのDev Containersプラグインによる)
devcontainerを利用できるエディタ
- VSCode
- Visual Studio
- IntelliJ IDEA
頑張ればneovimとかでも使えるっぽい
emacsもやってる人がいる
devcontainer環境を構築するツール
- VSCode Dev Container拡張機能
- devcontainer cli
- coder (envbuilder)
- devenv
- devbox
- その他エディタによるDev Containerサポート機能
devcontainerを利用できるリモート開発環境
その他VSCodeを使用してる場合はVSCodeのRemote SSHとdevcontainerを組み合わせたり
基本となるdevcontainerイメージの中身
ubuntu
- https://github.com/devcontainers/images/blob/main/src/base-ubuntu/.devcontainer/Dockerfile
- https://github.com/docker-library/buildpack-deps/blob/93d6db0797f91ab674535553b7e0e762941a02d0/ubuntu/jammy/curl/Dockerfile
- https://git.launchpad.net/cloud-images/+oci/ubuntu-base/tree/oci/index.json?h=refs/tags/dist-jammy-amd64-20240405-ae89e7cd&id=ae89e7cda4e21aae14ec1c74cc095c013adf3ecb
golang
- https://github.com/devcontainers/images/blob/main/src/go/.devcontainer/Dockerfile
- https://github.com/docker-library/golang/blob/ea6bbce8c9b13acefed0f5507336be01f0918f97/1.22/bookworm/Dockerfile
- https://github.com/docker-library/buildpack-deps/blob/d0ecd4b7313e9bc6b00d9a4fe62ad5787bc197ae/debian/bookworm/scm/Dockerfile
- https://github.com/debuerreotype/docker-debian-artifacts/blob/44807175c12f847248c046022ef95862e5567c58/bookworm/Dockerfile
- https://hub.docker.com/_/scratch/
ライフサイクルスクリプト
ライフサイクルスクリプトを使うといくつかのタイミングでコマンドの実行を行うことができる
例えば onCreateCommand
に apt-get install -y xxx
のようにして必要なツールをインストールしたりなど
ドキュメントにある情報を表にすると要はこういうことだと思う
実行環境 | 実行タイミング | 実行回数 | |
---|---|---|---|
initializeCommand | ホストマシン | コンテナの作成中およびその後の起動時を含む、初期化中 | 1回以上 |
onCreateCommand | コンテナ | 開発コンテナの作成時 | 多分1回のみ |
updateContentCommand | コンテナ | onCreateCommand作成プロセス中にソース ツリーで新しいコンテンツが利用可能になった後→workspace-folderのmount後? | 少なくとも 1 回 |
postCreateCommand | コンテナ | devcontainerがユーザーに初めて割り当てられた後 → 要は利用可能状態になったタイミング? |
多分1回のみ |
postStartCommand | コンテナ | コンテナーが正常に起動されるたび | 1回以上 |
postAttachCommand | コンテナ | ツールがコンテナに正常に接続されるたび → 要はエディタで起動したdevcontainerを開く度? |
1回以上 |
waitFor | コンテナ | 接続する前にツールが待機する必要があるコマンド (複数指定可能) デフォルト: updateContentCommand → 要はエディタとかが接続する前に完了しておくコマンドを設定できる感じ? |
このあたりの細かい部分についてはdevcontainer cliの実装を見るのが一番ちゃんと理解できそうな気がしてる
例えば initializeCommand
の実行はこのあたりで行われているよう
devcontainer.json 内で使える変数一覧とtemplate
逆にここにない値を使って devcontainer.json
外からユーザー独自の設定を入れるのは難しい
このあたりtemplate機能を使うとある程度自由度を持って作れる可能性もある?
devcontainer.json
内で ${templateOption:favorite}
のような変数が使えるようになる
(そこまで柔軟性が高いわけではなくて、あんまりちゃんとしたイメージが湧かないので良い感じの例が欲しい)
templateの公式情報としてはこのあたりが役立つかもしれない
feature
要はシェルスクリプトによるプラグイン機能
- https://github.com/devcontainers/features/blob/main/src/kubectl-helm-minikube/devcontainer-feature.json
- https://github.com/devcontainers/features/blob/main/src/kubectl-helm-minikube/install.sh
ローカルにあるファイルもfeatureとして読み込むことができる
featureのスクリプトをいくつか見るとなんとなくわかると思うけど
-
apt
などがあって -
bash
などもある
のような暗黙の了解が実質的にはあるので、devcontainer用のベースイメージは実質Debian系のみとなってる気がしてる
devcontainer cli
devcontainer cliを使うとVSCode DevContainer拡張機能とか無しでdevcontainerの実行などが可能
https://github.com/bells17/compose-devcontainer-example を使用した場合の例はこんな感じ
$ npm install -g @devcontainers/cli
$ /path/to/dir
$ devcontainer up --workspace-folder .
[1 ms] @devcontainers/cli 0.59.1. Node.js v21.7.3. darwin 23.4.0 arm64.
{"outcome":"success","containerId":"a9959594af9746fc9032a103210611dd6ef67a37ddf8682372ab59a81643c7c1","composeProjectName":"compose-devcontainer-example","remoteUser":"vscode","remoteWorkspaceFolder":"/workspace"}
$ devcontainer exec --workspace-folder . make run
REDIS_HOST=redis REDIS_PORT=6379 PORT=3000 go run ./app/main.go
Server is running at http://localhost:3000
じゃあこれをどうやってエディタから使ったらよいのか、というのが多分前半でリンクを紹介したneovimとかemacsとかのやつだと思う
その他cliリポジトリに example-usage が紹介されてる
その他TIPS
- devcontainerに関する質問などはGitHub Discussionで可能
- 起動時のメッセージは
/usr/local/etc/vscode-dev-containers/first-run-notice.txt
で設定可能 -
devcontainer.json
の設定を予めコンテナイメージに組み込む-
コンテナイメージで下記のようなラベル設定をすることでいけるらしい
LABEL devcontainer.metadata='[{ \ "capAdd": [ "SYS_PTRACE" ], \ "remoteUser": "devcontainer", \ "postCreateCommand": "yarn install" \ }]'
- 独自のfeatureを公開したい場合
- セキュリティソフトの影響で通信の間に中間プロキシが挟まれるようなケースだと自己?証明書が設定されてしまうケースがある
- そういったケースだと証明書を予めコンテナ側の
/usr/local/share/ca-certificates/
に保存してsudo update-ca-certificates
することで証明書エラーを回避するしか手が思いつかない -
feature
側の通信に失敗するケースの場合、feature
のインストール処理に差し挟めるフックポイントがライフサイクルスクリプトには無いため - そのためこういった環境だとdevcontainerの設定を都度書き換えなければいけないという問題がある
- そういったケースだと証明書を予めコンテナ側の
個人的なdevcontainerの課題
- 個人別の設定を組み込みずらい → devcontainerの設定を外部からカスタムできる余地がほぼ無いので各自で勝手に書き換えるしか無い(templateがいい感じに使えれば解決する?)
- VSCode DevContainer 拡張機能独自の機能がいくつかある(devcontainer cliに含まれていないらしい)
- ドキュメントとexampleが不足してる(e.g. 公式ドキュメントだけではdocker composeを利用した例が無いので手探りで頑張らざるを得ない or あってもリンクなどがまとまっていない)
- VSCodeなどの一部エディタ以外を除いてエディタ側のサポートが公式側にあまり無さそう(LSPのようなエディタとdevcontainer側のやり取りをする仕様などが無い)
- 上記の中間プロキシ問題
おまけ
TODO devcontainerを深く理解するにはdevcontainer cliのコードを読むのが一番良さそうなので今度読んでみたい
あとgitpotが作ってるopenvscode-serverについて詳しい方色々教えて下さい
他にもはこのあたりとか
Discussion