🗂

「お兄ちゃん、それ開発用の箱だよ」──Dev Container を妹に教えてもらう

に公開

この記事で分かること

  • Dev Container が「環境のブレ」をどう減らそうとしているか
  • devcontainer.jsonimage / features / postCreateCommand / customizations の役割の違い
  • リポジトリをコンテナで開くまでの操作イメージ
  • 認証まわりをイメージに焼かない考え方(例: AWS の bind mount)

前提

: 俺、Docker は入れたんだけどさ。Dev Container って単語だけ聞いてて中身が曖昧なんだよね。

: 大丈夫。Docker が動いて、Cursor か VS Code に Dev Containers 拡張が入ってればスタートラインはクリア。あとこの記事の例は Ubuntu 24.04 ベースで Python 3.12 と Node LTS 想定だから、手元の devcontainer.json はバージョンだけ読み替えてね、お兄ちゃん。

Dev Container って、何するの?

: 開発コンテナって、本番サーバーと同じイメージ?

必ずしも同じじゃないことが多いよ。狙いは開発環境をそろえること.devcontainer/devcontainer.json を置いておくと、エディタが Docker でコンテナを起動して、ワークスペースをコンテナにマウントした状態で開発するの。

: つまり、俺の Mac の中を汚さずに、決まった Python とか Node が使える?

: そういう理解で OK。「自分のマシンでは動くのに他の人のでは動かない」を減らすのが主目的。仕様の中心はオープンな Development Containers(containers.dev) で、VS Code も Cursor もだいたいそれに沿ってるよ。

devcontainer.json、地図みたいなもん?

: JSON だけ見ると頭がこんがらがるんだけど。

: よく出るキーだけつかまえよう。これ説明用の抜粋だから、プロジェクトによって値は違うかも。

{
  "name": "Development Standard Package",
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
  "features": {
    "ghcr.io/devcontainers/features/python:1": { "version": "3.12" },
    "ghcr.io/devcontainers/features/node:1": { "version": "lts" }
  },
  "postCreateCommand": "bash .devcontainer/postCreate.sh",
  "customizations": {
    "vscode": {
      "extensions": ["ms-python.python", "charliermarsh.ruff"],
      "settings": { "editor.formatOnSave": true }
    }
  },
  "remoteUser": "vscode",
  "mounts": []
}

image が土台の OS で、features がその上に乗せる道具、って感じ?

: 合ってる。features で Python とか Node とか AWS CLI を宣言的に足せる。バージョンが JSON に残るから、誰がいつコンテナを作っても同じに寄せやすいの。

postCreateCommand は何するやつ?

コンテナが初めて作られたあとに 1 回だけ実行されるコマンド。Features だけだと足りない分——例えば uv のインストールとか npm install -g prettier とか——を .devcontainer/postCreate.sh にまとめるパターンがよくあるよ。

customizations はエディタの話?

: うん。コンテナ内に入れる拡張機能と、保存時フォーマットとか Python を Ruff にするとか、エディタ設定をコードとして固定する。README で「みんな同じ拡張入れて」と書くよりズレにくい。

鍵とか AWS、コンテナに直コピーしていい?

: AWS のキーとか、コンテナの中にコピーするのはアウトでしょ。

: その感覚で正解。イメージに秘密を焼き込まないのが原則。このリポジトリの例だと、ホストの ~/.aws をコンテナの vscode ユーザー配下に bind mount してる。

"mounts": [
  "source=${localEnv:HOME}${localEnv:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind,consistency=cached"
]

HOMEUSERPROFILE が両方あるの、なんで?

Unix 系と Windows のホスト両方でパスを組み立てやすくするための定番パターン。会社によっては SSO とか別の渡し方のほうが適切なこともある。一番きついのは平文キーをリポジトリにコミットすることだから、そこだけは絶対やめて。

じゃあ実際どうやって開くの

: 操作だけ手短に。手順が欲しい。

: 3 ステップ。

  1. リポジトリをクローンして Cursor / VS Code で開く
  2. Reopen in Container か、コマンドパレットの Dev Containers: Reopen in Container
  3. 初回はイメージ取得と Features で数分かかることがある。終わればターミナルも拡張もコンテナ側に揃う

: 初回だけ長いのは、環境を揃える処理が走ってるから?

: そう捉えていい。2 回目以降はキャッシュが効いてだいぶマシになることが多いよ。

よくある誤解

: コンテナに入ったら、プロジェクトのファイル全部コピーされるイメージなんだけど。

基本はマウント。編集してるのはいつものフォルダのまま、実行環境だけがコンテナ側に寄る感じ。Git の履歴も普通はホスト側のリポジトリに残る。厳密にはマウントの設定次第で挙動は変わるから、変なときはログを見てね。

まとめ

: ……なんか、お前いつからそんな詳しかったんだ。

: お兄ちゃんが聞くから説明しただけ。要点はこの 3 つ。

  • Dev Container は containers.dev の仕様を軸に、エディタと Docker で開発環境を揃える仕組み
  • image + features で土台とランタイム、postCreateCommand で追加分、と分けると読みやすい
  • 秘密はイメージに入れない。マウントや会社の認証方針で扱う

参考リンク

Discussion