🐳

devcontainerを使ってみよう

2024/04/23に公開

devcontainerを使ってみよう

devcontainerを使う上で知っておくと良さげな情報のまとめ記事です
前にRemote SSHでdevcontainerの環境を構築する記事を書いたので、今回はdevcontainer全般の情報をまとめてみました

https://zenn.dev/bells17/articles/remote-ssh-devcontainer

tl;dr

  • devcontainerを使うと開発環境をコンテナで構築できるよ(ランタイムとかツール類含めて!)
  • docker composeだとアプリケーションを動作させる環境は作れるけどdevcontainerは開発環境ごと構築できて便利
  • devcontainerを使うにはdockerとdevcontainerを利用できるエディタが必要
  • devcontainer内でdocker composeを利用できるから、devcontainer用のコンテナ+ミドルウェアコンテナを用意すればアプリケーションを開発できる環境がまるっとコンテナで作れる
  • devcontainer cliなどを使えばローカルでも利用できるし、codespaceやdevpod、coderなどを使えばリモート開発環境も作れるよ(gitpodはまだdevcontainer未対応だった)

devcontainerとは?

https://code.visualstudio.com/docs/devcontainers/containers

  • Development Containersの略
  • 開発環境をまるっとコンテナで構築できる
    (devcontainer側の思想としてはCI環境でもdevcontainer使おうぜ!というのもあるみたい)
  • ローカルに必要なのはdockerとdevcontainer対応エディタだけ!
  • devcontainer.json という設定ファイル元にdevcontainerを構成する

https://containers.dev/overview

https://containers.dev/

https://containers.dev/overview

https://code.visualstudio.com/docs/devcontainers/containers

デモ

https://github.com/bells17/compose-devcontainer-example

  • goとredisのwebアプリケーションを
  • docker composeで立ち上げて
  • 開発したアプリケーションをDocker in Dockerでビルドしてイメージ作れて
  • devcontainer接続時のメッセージをカスタムしていて
  • ローカルに自作したfeatureを使ったredis-cliを(雑に)インストールするようになってる

って感じのサンプルリポジトリです

TODO template機能も試した例にしてみたい

サポートされてる機能

devcontainerを利用できるエディタ

https://containers.dev/supporting#editors

  • VSCode
  • Visual Studio
  • IntelliJ IDEA

頑張ればneovimとかでも使えるっぽい

https://blog.kanezoh.com/entry/2024/02/17/112722

emacsもやってる人がいる

https://blog.agile.esm.co.jp/entry/2022/03/01/090000

https://happihacking.com/blog/posts/2023/dev-containers-emacs/

devcontainer環境を構築するツール

https://containers.dev/supporting#tools

devcontainerを利用できるリモート開発環境

https://containers.dev/supporting#services

その他VSCodeを使用してる場合はVSCodeのRemote SSHとdevcontainerを組み合わせたり

https://zenn.dev/bells17/articles/remote-ssh-devcontainer

基本となるdevcontainerイメージの中身

https://github.com/devcontainers/images

ubuntu

golang

ライフサイクルスクリプト

ライフサイクルスクリプトを使うといくつかのタイミングでコマンドの実行を行うことができる

例えば onCreateCommandapt-get install -y xxx のようにして必要なツールをインストールしたりなど

ドキュメントにある情報を表にすると要はこういうことだと思う

実行環境 実行タイミング 実行回数
initializeCommand ホストマシン コンテナの作成中およびその後の起動時を含む、初期化中 1回以上
onCreateCommand コンテナ 開発コンテナの作成時 多分1回のみ
updateContentCommand コンテナ onCreateCommand作成プロセス中にソース ツリーで新しいコンテンツが利用可能になった後→workspace-folderのmount後? 少なくとも 1 回
postCreateCommand コンテナ devcontainerがユーザーに初めて割り当てられた後
→ 要は利用可能状態になったタイミング?
多分1回のみ
postStartCommand コンテナ コンテナーが正常に起動されるたび 1回以上
postAttachCommand コンテナ ツールがコンテナに正常に接続されるたび
→ 要はエディタで起動したdevcontainerを開く度?
1回以上
waitFor コンテナ 接続する前にツールが待機する必要があるコマンド
(複数指定可能)
デフォルト: updateContentCommand
→ 要はエディタとかが接続する前に完了しておくコマンドを設定できる感じ?

https://containers.dev/implementors/json_reference/#lifecycle-scripts

このあたりの細かい部分についてはdevcontainer cliの実装を見るのが一番ちゃんと理解できそうな気がしてる
例えば initializeCommand の実行はこのあたりで行われているよう

https://github.com/devcontainers/cli/blob/c1c8b08263c6dca7cd79c97a2d0bc581fcef4f6c/src/spec-node/configContainer.ts#L66

devcontainer.json 内で使える変数一覧とtemplate

https://containers.dev/implementors/json_reference/#variables-in-devcontainerjson

逆にここにない値を使って devcontainer.json 外からユーザー独自の設定を入れるのは難しい

このあたりtemplate機能を使うとある程度自由度を持って作れる可能性もある?

https://containers.dev/implementors/templates/

devcontainer.json 内で ${templateOption:favorite} のような変数が使えるようになる

(そこまで柔軟性が高いわけではなくて、あんまりちゃんとしたイメージが湧かないので良い感じの例が欲しい)

templateの公式情報としてはこのあたりが役立つかもしれない

feature

要はシェルスクリプトによるプラグイン機能

https://containers.dev/features

https://github.com/devcontainers/features

https://containers.dev/implementors/features/

e.g. kubectl-helm-minikube

ローカルにあるファイルもfeatureとして読み込むことができる

https://github.com/kubernetes/kubernetes/blob/52bcdbcd9bafc520e7b5676eb484976de85c425f/.devcontainer/devcontainer.json#L39-L41

featureのスクリプトをいくつか見るとなんとなくわかると思うけど

  • apt などがあって
  • bash などもある

のような暗黙の了解が実質的にはあるので、devcontainer用のベースイメージは実質Debian系のみとなってる気がしてる

devcontainer cli

devcontainer cliを使うとVSCode DevContainer拡張機能とか無しでdevcontainerの実行などが可能

https://containers.dev/implementors/reference/

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 が紹介されてる

https://github.com/devcontainers/cli/tree/main/example-usage

その他TIPS

https://twitter.com/bells17_/status/1778387925144936947

個人的なdevcontainerの課題

  • 個人別の設定を組み込みずらい → devcontainerの設定を外部からカスタムできる余地がほぼ無いので各自で勝手に書き換えるしか無い(templateがいい感じに使えれば解決する?)
  • VSCode DevContainer 拡張機能独自の機能がいくつかある(devcontainer cliに含まれていないらしい)
  • ドキュメントとexampleが不足してる(e.g. 公式ドキュメントだけではdocker composeを利用した例が無いので手探りで頑張らざるを得ない or あってもリンクなどがまとまっていない)
  • VSCodeなどの一部エディタ以外を除いてエディタ側のサポートが公式側にあまり無さそう(LSPのようなエディタとdevcontainer側のやり取りをする仕様などが無い)
  • 上記の中間プロキシ問題

おまけ

TODO devcontainerを深く理解するにはdevcontainer cliのコードを読むのが一番良さそうなので今度読んでみたい

あとgitpotが作ってるopenvscode-serverについて詳しい方色々教えて下さい

https://github.com/gitpod-io/openvscode-server

他にもはこのあたりとか

https://github.com/coder/code-server

Discussion