Skip to content

feat: 別ホスト (Windows/WSL・別 PC・EC2) の Docker に dev コンテナを立ち上げ、VS Code もそこへ接続する #162

Description

@takemi-ohama

背景・課題

devbase up は コマンドを実行した環境 (Mac) の Docker にしか dev コンテナを立てられない。設定で「このプロジェクトのコンテナは別ホストの Docker に立てる」と指定し、devbase up で立ち上がる VS Code もそのホスト上のコンテナへ接続できるようにしたい。

想定用途

現在の自宅環境は Windows PC と Mac が 1 台ずつ。手元の Windows の VS Code → Remote-SSH で Mac → Mac 上のコンテナ という構成で使っている(コンテナを多重起動したときの手元操作の遅延・ダウンを避けるため)。これに対して:

やりたいこと 理由
Windows / WSL 上にコンテナを立てたい CUDA が使える GPU は Windows にしかない
3 台目の自宅 PC のコンテナを操作したい 負荷分散
AWS EC2 に入れた Docker ホストのコンテナを操作したい クラウド側の計算資源

いずれも「操作は手元 (Windows VS Code / Mac の端末)、コンテナは別ホスト」で、接続方法は Docker のリモート制御 (docker context / DOCKER_HOST) か SSH。

調査結果

既にあるもの

VS Code 起動側 (lib/devbase/editor/opener.py) は 「Windows VS Code → Remote-SSH(Mac) → Mac の Docker」の跨ホスト構成に対応済み で、attach URI に @ssh-remote+<host> をネストし、payload に settings.context (docker context 名) を埋める仕組み (build_attach_uri) と、DEVBASE_EDITOR_SSH_HOST / DEVBASE_EDITOR_DOCKER_CONTEXT の env がある (docs/user/environment-variables.md 「跨ホスト」)。

つまり VS Code の Dev Containers 拡張は「docker context を指定してコンテナへ attach する」経路を既に持っており、devbase もその URI を組める。足りないのは devbase up 本体が別ホストの Docker を相手に動くこと と、それを 設定で宣言できること の 2 点。

devbase up がローカルホストに依存している箇所

docker / docker compose の呼び出し自体は subprocess で CLI を叩いているだけなので、DOCKER_CONTEXT (または DOCKER_HOST) を渡せば volume 作成・network 作成・compose up/down・docker exec (ウィンドウタイトル書き込み)・snapshot・イメージビルド (docker buildx build --load) はそのまま別ホストの daemon に向く。問題になるのは「ローカルの事情」を前提にしている次の箇所。

箇所 現状 リモート時に起きること
bin/devbase:37 DOCKER_GID uname が Darwin なら 0、それ以外はローカルの /etc/group Mac から Linux ホストへ向けると group_add: ["0"] になり docker.sock に触れない。リモート側の docker gid が要る
compose.yml の bind mount (~/devbase:/work/devbase, ~/.aws 等) ~ は compose を実行した側 (Mac) の HOME で展開される リモートには /Users/<name>/... が無く、Linux daemon は存在しないパスを空ディレクトリとして作ってしまうので黙って空になる
/var/run/docker.sock bind リモートが Linux なら同じパスで OK Windows 側の Docker Desktop 直結だと不可 (WSL 内の dockerd を使う前提にする)
pre-up / deploy フック ホスト側 (Mac) で実行 docker を叩くだけなら context 経由で問題なし。ローカルパスへ clone して build context にするフックはリモートでは使えない (buildx が context を送るので実は動く可能性あり、要検証)
機密の注入 (_inject_secrets) Mac のプロセス環境変数に載せて compose に渡す compose クライアントは Mac で動くので 変更不要。平文ファイルはリモートへ渡らない (むしろ好都合)
env ファイル (env_file: - env) compose クライアントが読む 変更不要
イメージの存在確認 / 鮮度判定 (_ensure_images) docker compose config / docker image inspect context 経由でリモートのイメージを見る。ホストごとにイメージを別途ビルドする必要がある (arm64 Mac と x86_64 Linux でアーキが違う点も含めて)
VS Code 起動 上記のとおり settings.context を組める ローカル端末 (Mac 直) + リモート context の組み合わせでは docker_context が付かない (ssh_host があるときだけ解決している) ので、ここを「設定があれば常に付ける」に広げる必要がある

提案内容

1. 設定は gitignore される projects/<name>/project.local.yml に書く

# projects/<name>/project.local.yml  (gitignore 対象。個人・機材ごとの設定)
docker:
  context: gpu-wsl        # `docker context ls` に出る名前。未指定なら現在の context (= 従来どおり)
  home: /home/takemi      # リモート側の HOME。bind mount の `~` をこの値で展開する
  # gid: 999              # リモートの docker グループ gid。省略時は起動時に自動取得 (下記)

project.yml には書かない。 projects/* は devbase-samples (共有 git リポジトリ) への symlink で、project.yml はチームの正。「このプロジェクトを GPU ホストで動かす」は個人の事情であり、home / gid に至っては完全に機材依存なので、共有ファイルに載せると同じ project.yml を使う他の人の devbase up が壊れる。

project.local.yml を選ぶ理由:

  • 前例に沿う: projects/<name>/.env (機密) が「同じディレクトリに置く gitignore ファイル」として既にあり、devbase-samples の .gitignore に 1 行足すだけ。devbase 本体側は projects/* ごと ignore 済み
  • env / .env の役割を崩さない: env はコンテナへ渡す変数、.env は機密。「devbase 自身のふるまい」は project.yml 系に置く、という docs/user/project-yml.md の区分を保ったまま「共有 / 個人」で分ける
  • 接続先の実体は書かない: ssh://user@host ではなく docker context の名前だけを書き、名前 → 接続先は各マシンの docker context create gpu-wsl --docker "host=ssh://takemi@winpc" に委ねる。ssh 鍵・TLS・WSL/EC2 の違いは docker 側の問題として devbase から切り離せる

読み込みは project.yml → project.local.yml の順で深いマージ (local が勝つ)。project.yml 側で docker: を書いた場合は未知キーとしてエラーにし、共有ファイルに機材依存の情報が混ざる事故を型で防ぐ。project.local.yml は docker: のほか scale / open_editor の個人上書きも受けられると便利だが、初期スコープには含めない。

優先順位は CLI --context > env DEVBASE_DOCKER_CONTEXT (グローバル .env / プロジェクト env) > project.local.yml の docker.context > 現在の docker context。一時的に別ホストへ振りたい場合は env か CLI で上書きする。

repos[].host (Git ホスト) と紛らわしいので、キー名は host ではなく docker.context にする。

2. devbase up を context 対応にする

  • 解決した context を DOCKER_CONTEXT 環境変数として docker / docker compose の全呼び出しに渡す (utils/docker.py / commands/container.py / volume/manager.py / snapshot / editor/window_title.py)。--context を個別に足すより漏れがない
  • DOCKER_GID を リモート側で解決する: docker.gid 明示があればそれ、無ければ docker run --rm -v /var/run/docker.sock:/s alpine stat -c %g /s 相当で 1 回だけ取得してキャッシュ。bin/devbase の uname 判定はローカル context のときだけ使う
  • bind mount の ~ を docker.home で展開する: 生成する .docker-compose.scale.yml の段階で ~/ 始まりの host path を書き換える (volume/compose.py)。docker.home 未指定でリモート context のときは 警告を出す (黙って空ディレクトリになる事故を防ぐ)
  • _ensure_images はそのまま context 経由でリモートのイメージを見る。無ければリモートでビルドされる (buildx がビルドコンテキストを送るので containers/ はローカルにあればよい)
  • devbase ps / down / login / logs / snapshot も同じ context 解決を通す (login は docker compose exec なので ssh 経由でそのまま入れる)

3. VS Code はホスト構成ごとに URI を出し分ける

devbase up を実行する場所 コンテナの場所 開き方
Mac 端末 (ローカル VS Code) Mac (従来) フラット URI (変更なし)
Mac 端末 (ローカル VS Code) リモート context attached-container+{containerName, settings.context=<ctx>} — Mac の Dev Containers 拡張がその context 経由で attach する。既存 build_attach_uri の docker_context を「設定があれば常に付ける」に広げるだけ
Windows VS Code → Remote-SSH(Mac) の統合端末 Mac (現状の使い方) ネスト URI (実装済み、変更なし)
Windows VS Code → Remote-SSH(Mac) の統合端末 リモート context (例: WSL / EC2) ネスト URI attached-container+{…, settings.context=<ctx>}@ssh-remote+mac。Mac の Dev Containers が context 経由で attach する。Windows → Mac → WSL(Windows) と一周するが、Mac が司令塔なら仕組みとしては素直
Windows VS Code → Remote-SSH(Mac) の統合端末 WSL (Windows 自身) 上の経路でも動くはずだが、Windows 側 VS Code に同名の context を作って Windows 直で attach させた方が短い。devbase up が手元で叩くコマンドを提示する (既存の print_command 経路の応用)

Dev Containers 拡張は settings.context を attach 先の docker context 名 として解釈する (拡張の「実行中のコンテナーにアタッチ」が非既定 context で生成する authority と同じ形式)。既に実装済みの跨ホスト構成で settings.context が実機動作していることが根拠。

4. 段階分け

  1. project.local.yml の読み込み + docker.context + DOCKER_CONTEXT 伝播 + DOCKER_GID リモート解決 — devbase up/down/ps/login が別ホストで動く。VS Code はローカル VS Code + settings.context まで
  2. docker.home による bind mount の書き換えと警告 — ~/devbase 等を持つプロジェクトが動く
  3. Remote-SSH 統合端末 + リモート context のネスト URI、および WSL 直 attach の提示
  4. ドキュメント: docs/user/project-yml.md に project.local.yml の節を追加、devbase-samples の .gitignore に project.local.yml を追加、environment-variables.md の「跨ホスト」を「リモート Docker」として再構成、WSL / EC2 それぞれの docker context create 手順 (ssh 鍵、WSL 内 sshd と dockerd、EC2 は ssh 経由で TLS 不要)

代替案

案 内容 採らない理由
A. リモートへ ssh して向こうで devbase up を実行する リモートに devbase 一式 + projects/ + 機密鍵を配布し、Mac は ssh でコマンドを投げるだけ 機密鍵と projects/ を各ホストに複製することになり、#159 の機密ストア方針と噛み合わない。各ホストの devbase 版ずれも管理対象になる。「compose クライアントは手元、daemon はリモート」の docker context 方式なら追加配布物ゼロで済む
B. project.yml に docker.context を書く ファイルが増えない project.yml は devbase-samples で共有される正。context 名だけでも「自分は GPU ホストで動かす」という個人の事情を他の人に押し付けることになり、home / gid は機材依存そのもの。同じ project.yml を使う他の人の up が壊れる
C. env の DEVBASE_DOCKER_CONTEXT だけで済ませ、ファイルを足さない 変更が最小 env はコンテナへ渡す変数の置き場で、しかも devbase-samples で共有される (gitignore ではない)。グローバル .env に書くと全プロジェクトが同じホストへ向く。ただし上書き手段としては残す (優先順位参照)
D. $DEVBASE_ROOT/local.yml のような 1 枚の個人設定に全プロジェクト分をまとめる 個人設定が 1 箇所に集まる プロジェクト単位の設定が projects/<name>/ の外に散る。.env が「プロジェクトの隣に置く gitignore ファイル」として既にあるので、そちらに揃える方が探しやすい
E. TCP + TLS (tcp://host:2376) で daemon を公開する ssh 不要 証明書配布が要る。ssh transport なら既存の ssh 鍵で済み、EC2 も同じ手順。docker context は両方受けるので devbase 側は区別しなくてよい (ユーザが context 作成時に選ぶ)

補足

  • Windows 側は WSL2 内の dockerd (Docker Desktop の WSL 統合、または WSL 内に直接インストール) を前提にする。ssh は WSL 内の sshd に張る (Windows OpenSSH → WSL は経路が増えるだけ)。CUDA は --gpus all / deploy.resources を該当プロジェクトの compose.yml に書く話なので本 issue の範囲外
  • リモート側に必要なのは docker CLI + dockerd + sshd のみ (ssh transport は docker system dial-stdio をリモートで実行する)
  • イメージはホストごとに別物 (arm64 Mac / x86_64 Linux)。devbase build も context 経由でリモートビルドになるので、初回 up はビルド時間がかかる
  • 関連: docs/user/environment-variables.md 「跨ホスト」節、lib/devbase/editor/opener.py の build_attach_uri / resolve_docker_context、issues/old/PLAN31_3_up-open-editor.md

振り返り: #162 (comment)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions