CodeSandbox の podman で Dev Containers を試す
以前に CodeSandbox の Dokcer(インテグレーションではないコマンドライン用)で Dev Containers を使えないか試してみてうまくいかなかった(直接的な原因は rootless docker なのでワークスペースのマウントに失敗する)。
その後、VSCode の Dev Containers 拡張機能で podman を使う方法をみかける。これならいけるのでは?ということで試してみている。
2023-02-09 時点でのまとめ
Codespaces などで利用していたリポジトリがそれなりに動作するようになった。
imageや dockerfile を使う(コンテナが 1 つの場合)
image
や build.dockerfile
を利用する場合(コンテナは 1 つの場合)は、下記で利用可能になる。
- GitHub のリポジトリを CodeSandbox へインポート
- CodeSandbox でリポジトリに環境変数
PODMAN_USERNS=keep-id
を追加する - CodeSandbox でブランチを作り、VSCode から開く
-
devcontainer.json
でcontainerEnv
にHOME
を設定する(コンテナのUSER
とremoteUser
が合致してない場合のみ必須?) - Dev Containers 拡張機能の設定で
Docker path
をpodman
へ変更する
この状態で Reopen in Container すると利用できる。また、GitHub の認証情報については VSCode で拡張機能を利用している場合は、そちらの認証情報が利用できる(GitHub CLI は別途考える必要がある)。
問題点は下記の通り。
-
Docker path
は状況によって手動で切り替える必要がある- ローカル環境で Docker を使う場合は毎回変更することになる
- Codespaces は(
.devcontaienr
を共通化できるようにしておけば)影響されない
- 最近開いた項目から開きにくい(どれも
/workspace
の Dev Container になるので判別しにくい) - CodeSandbox の機能が利用できない
- IDE にアバターなどが表示されない(IDE への同期は普通に行われる)
- (おそらく)VM の稼働状態にカウントされないのでサスペンドしやすい
- これが地味にめんどう
- Dev Contaner を使っていなくても)よくわからないタイミングで VM がリスタートされる
- CodeSandbox の拡張機能から開きなおす必要がある
- コンテナとイメージも消える(ワークスペースは残っている)
少し使ってみたが VM のリスタートに巻き込まれなければ「ちょっと重たい」くらいで使えるかも。Codespaces のように Git のブランチ別にコンテナを持ちやすいのもポイント高い。CodeSandbox でブランチを作れば分岐元のコンテナも複製されるので、ワークスペースの外側にちょっと保存していおたファイルも複製される。
正規の使い方でないし少し安定しないので、Codespaces の補完的な位置付けで使うとよさそうかな。
dependabot の bump でエラーになったときとか、github.dev だと難しいようなときに便利そう。
左は bump でエラーになったブランチで作った devconrainer、右は bump 前のブランチで作った devcontainer。同時に異なる依存パッケージをインストールして比較できるのは便利。
あとはターミナルの環境のカスタマイズをどれくらい利用するか(Codesapces にあわせてちょっとカスタマイズしすぎた)と、GitHub CLI の認証情報をどうするか、かな。
dockerComposeFile を使う場合
現状では少しむずかしい。
- GitHub のリポジトリを CodeSandbox へインポート
- CodeSandbox でリポジトリに環境変数
PODMAN_USERNS=keep-id
を追加する - CodeSandbox でブランチを作り、VSCode から開く
-
pdoman-compose
のdevelop
版をインストールする(それようの channel を作ってある) -
podman-compose
の絶対 PATH をdev.containers.dockerComposePath
へ指定する -
docker-compose.yml
を修正する- 各種 PATH を絶対 PATH へ変更する
- コンテナで
USER
を指定していない場合でroot
を使う場合は明示的に指定(USER
がないとPODMAN_USERNS
の影響を受ける)
-
docker-compose.yml
が他環境と共通化できない場合はdevcontainer.json
を別途用意する- VSCode では Dev Container 作成時に選択できる
docker-compose.yml
の例、こうなってしまうと共通化はできないので devcotainer.json
はわけてある。
-
context: "$HOME/workspace/.devcontainer/node"
などで絶対 PATH にしている -
pubsub.user
でroot
を指定している
こちらも少し使ってみたが、Dev Container ができてしまえば、あとは上記のコンテナ 1 つのときとあまりかわらない、かな。
とりあえず試す
下記のリポジトリで試す。これを選んだ理由はあまり凝ったことをしてないため(使っているイメージが一般的なもの)。また、ホームディレクトリに FontConfig の設定を配置してあるので、ユーザー環境の設定がうまくいっているかの確認もできる
まずは Dev › Containers: Docker Path
のみ変更して試してみた。
- CodeSandbox で import して拡張機能からブランチを開く
- Dev Container で reopen する
とくにエラーはなくマウントはできた。が、マウントされたファイル(ディレクトリー)はコンテナ内からは root:root
になっている。
次に .devcontainer/devcontainer.json
も変更してみる。下記のようにドキュメントにあったワークアラウンドを追加してコンテナをリビルドすると今度は node:node
になっていた。追記、ワークアラウンドは既存のコードに影響でないように設定できそう、詳細は返信の方に記述。
devcontainer/devcontainer.json
{
"name": "node",
"build": {
"context": ".",
"dockerfile": "Dockerfile"
},
"remoteUser": "node",
"runArgs": ["--userns=keep-id"],
"containerEnv": {
"HOME": "/home/node"
},
"customizations": {
"vscode": {
"extensions": ["esbenp.prettier-vscode", "marp-team.marp-vscode"],
"settings": {
"markdown.marp.themes": ["./themes/ogimage.css"],
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
}
}
}
ユーザーに設定した Fontconfig が反映されている(ホームディレクトリの設定が想定通り)。
node ➜ /workspaces/workspace (draft/kind-franklin) $ fc-match
NotoSansCJK-Regular.ttc: "Noto Sans CJK JP" "Regular"
試しにスクリプトを動かすと想定通りに動作した。
引数の意味など細かいことはあとで調べるとして、問題はいくつかある。
- CodeSandbox 側の機能が使えなくなる
- 上記設定をするとおそらく通常の Docker 環境(と Codespaces)では動作しなさそう
- CodeSandbox のときだけ設定変更する方法を考えてみる?
あとは dokcer-compose 的なものを使う方法も調べる。
"runArgs": ["--userns=keep-id"],
は環境変数を追加することでも同じことができる。
Only workaround I found is to set it in .bashrc/.zshrc:
export PODMAN_USERNS=keep-id
(補足しておくと、上記 issue にもあるけど、podman-compose では docker-compose.yml
で指定できる)
CodeSandbox ではリポジトリに環境変数を設定できるので試してみたら runArgs
を変更しなくても良くなった。
下記の環境変数はコンテナ側のユーザー指定があればとくに指定しなくてもいけるもよう(こっちはあっても他に影響はなさそうだし、指定しておいてもよいかも)。
"containerEnv": {
"HOME": "/home/node"
},
このような感じなので、CodeSandbox で Dev Container を使う場合でも Git リポジトリのコードに手を加えなくてよさそう。
あとは Docker Path
の指定をどうにかできればよいのだが。Reomote - SSH でつないでいるから、そっちに設定を持たすことはできないか?
コンテナ内のユーザーの id
$ id
uid=1002(node) gid=1002(node) groups=1002(node),998(nvm),999(npm)
--userns=mode
--userns
を指定しない場合 root
になる。その場合、Dev Container の id マッピングは動作しない(すでに存在している id にはマッピングされない)ので、マウントされたファイルの所有者はコンテナ側のユーザーではなく root:root
に見えるのかなと。
指定すると 1002
(これは CodeSandbox の VM 側の id)になり、さらに Dev Container の id マッピングが動作したということかと。
ということは、 keep-id:uid=200,gid=210
形式を使うと、id のコンフリクト問題を回避できるのかな?
あとは Docker Path の指定をどうにかできればよいのだが。Reomote - SSH でつないでいるから、そっちに設定を持たすことはできないか?
VSCode の 2023-01(1.75.0)でグッドタイミングな機能追加が来たと思ったがダメだった。
CodeSandox 用の Profile を作って Docker Path
を変更してたが、各 Profie に反映されてしまう。
エディターで profile 用の settings.json
に dev.containers.dockerPath
をコピーしてみたが、マウスホバーで下記にようになる。
This setting has an application scope and can be set only in the user settings file.
dockerComposeFile を使っている場合の対応
ドキュメントでは pdoman-compose
もいけるようなことも書いてあったが、CodeSandbox には入っていない。よって対応を考える必要がある。が、いまのところうまくいかない。
(これはもしかして、「世の中には podman-compose っていうのもあるのですよ」と紹介しただけ?)
Nix でインストール
パッケージがあったのでインストールしてみる。
$ nix-env -iA nixpkgs.podman-compose
VSCode の設定で docker-compose の PATH を podman-compose へ変更。
これは Dev Container 作成時にエラーとなる。PATH の整合性がとれないのかと思って、スクリプトを挟んで確認してみた。
#!/bin/sh
#set -e
echo "${PATH}" > /tmp/chk1.txt
command -v podman-compose > /tmp/chk2.txt
echo "${@}" > /tmp/chk3.txt
podman-compose "${@}" > /tmp/pc-stdout.txt 2> /tmp/pc-stderr.txt
echo "${?}" > /tmp/chk5.txt
結果、podman-compose
は実行されるのだが下記のエラーになっていた。
$ cat /tmp/pc-stderr.txt
usage: podman-compose [-h] [-v] [-f file] [-p PROJECT_NAME]
[--podman-path PODMAN_PATH] [--podman-args args]
[--podman-pull-args args] [--podman-push-args args]
[--podman-build-args args] [--podman-inspect-args args]
[--podman-run-args args] [--podman-start-args args]
[--podman-stop-args args] [--podman-rm-args args]
[--podman-volume-args args] [--no-ansi] [--no-cleanup]
[--dry-run]
{help,version,pull,push,build,up,down,ps,run,exec,start,stop,restart,logs}
...
podman-compose: error: argument command: invalid choice: 'config' (choose from 'help', 'version', 'pull', 'push', 'build', 'up', 'down', 'ps', 'run', 'exec', 'start', 'stop', 'restart', 'logs')
このときの引数は下記のようになっている。
$ cat /tmp/chk3.txt
-f /project/home/hankei6km/workspace/.devcontainer/docker-compose.yml config
回避する方法は不明。ということで docker-compose から podman を使う方法で試す。
docker-compose をコンテナで動かす
上のコメントで試したやつ。これは -f
でファイルを指定されるのでうまくいかない。
Docker インテグレーションのコンテナ内で試す
実は結構期待していた方法だが、
途中までは良い感じなのだがイメージのビルドで失敗する。
devcontainer cli でも試したが結果は同じ。
Building pubsub
Sending build context to Docker daemon 4.608kB
request returned Bad Request for API route and version http://%2Fvar%2Frun%2Fpodman%2Fpodman.sock/v1.41/build?buildargs=%7B%22PUBSUB_EMULATOR_HOST%22%3A%22pubsub%3A8043%22%2C%22PUBSUB_PROJECT_ID%22%3A%22abc%22%7D&cachefrom=%5B%5D&cgroupparent=&cpuperiod=0&cpuquota=0&cpusetcpus=&cpusetmems=&cpushares=0&dockerfile=Dockerfile&labels=%7B%7D&memory=0&memswap=0&networkmode=default&rm=1&shmsize=0&t=workspace_devcontainer_pubsub&target=&ulimits=null&version=1, check if the server supports the requested API version
ERROR: Service 'pubsub' failed to build : Build failed
Error: Command failed: docker-compose --project-name workspace_devcontainer -f /workspace/.devcontainer/docker-compose.yml -f /tmp/devcontainercli-root/docker-compose/docker-compose.devcontainer.build-1675272569757.yml build
podman-compose(develop 版)と devcontainer cli の組み合わせは動くもよう
podman-compose での挙動を確認するために devcontainer cli から使ってみたら動いてしまった。
podman-compose は pip から develop 版をインストールする。これで config
などのエラーになっていたコマンドが使える。
CodeSandbox は pip が入ってないので手動インストールする。
~/.local/bin
に PATH が通っていないので、下記のようにする。
~/.local/bin/pip3 install https://github.com/containers/podman-compose/archive/devel.tar.gz
devcontainer up
を下記のように実行する
devcontainer up --workspace-folder "${PWD}" --docker-path podman --docker-compose-path ~/.local/bin/podman-compose
ただし、build の context が正しく扱えていない状態になる。たぶんこれ。
context などを調整したら Dev Container が動作してしまった。
context 関連
-
docker-compose.yml
のcontext
に指定していたディレクトリーをワークスペース直下に コピー する(移動だと他でエラーになる)
その他、プロジェクト固有のもの
-
init: true
を外す、Docker in Docker 用に指定していた(と思う)ので無くても困らない、と思う - 各サービス(コンテナ)のユーザーを指定する
- イメージ側でユーザー指定がないとホスト側のユーザーになるのかな(
userns
の設定が影響?)
- イメージ側でユーザー指定がないとホスト側のユーザーになるのかな(
ここで作った Dev Container で試してみたが、pubsub のエミュレーターのサービス(コンテナ)に接続できている。
➜ workspace git:(draft/chk-cli) ✗ devcontainer exec --workspace-folder "${PWD}" --docker-path podman --docker-compose-path ~/.local/bin/podman-compose ping pubsub
PING pubsub (10.89.0.7) 56(84) bytes of data.
64 bytes from 10.89.0.7 (10.89.0.7): icmp_seq=1 ttl=64 time=0.330 ms
64 bytes from 10.89.0.7 (10.89.0.7): icmp_seq=2 ttl=64 time=0.082 ms
^C
➜ workspace git:(draft/chk-cli) ✗ devcontainer exec --workspace-folder "${PWD}" --docker-path podman --docker-compose-path ~/.local/bin/podman-compose bash -c 'curl -X GET "http://${PUBSUB_EMULATOR_HOST}/v1/projects/${PUBSUB_PROJECT_ID}/topics"'
{
}
{"outcome":"success"}
Dev Container を落とすとき
~/.local/bin/podman-compose -p workspacedevcontainer -f .devcontainer/docker-compose.yml down
なぜ devcontainer cli では動くか?
CodeSandbox に入ってる devcontainer
を使ったのだが、バージョンはこれを書いている時点(2022-02-03)で最新のもよう。
$ devcontainer --version
0.29.0
しかし、VSCode の拡張機能ではログを見る限りでは 0.25.x が使われているもよう。拡張機能の方も更新されると動作するような気もしないでもない。
podman-compose(develop 版) と Dev Containers 拡張機能でも暫定的に動かす方法がわかった
docker-compose の config
コマンドと podman-compose の config
コマンドの出力を比較してみたところ、拡張機能版で動かない理由が判明。
podman-compose の config
コマンドでは各種 PATH が相対 PATH のままになっている。
以前の devconfiner cli だとこれを扱えないもよう。
暫定的に docker-compose.yml
内の各 PATH を絶対 PATH へ変更したら拡張機能版でも Dev Container を作成できた。
podman-compose か拡張機能のどちらかが更新されれば解決するはずなので、それまでは一時的に docker-compose.yml
を変更する対応でもいいかな。
もう少し汎用的な手順にする
とりあえず動くようになったので、手順を少しまとめる。
環境変数とかは image
or Dcokerfile
を使うときと同じ。
Dev Contaners に対応できる podman-compose を入れる。
現状では対応できるバージョンはないのでこれを使う。
上記バージョンでは docker-compose.yml
内の各種 PATH を絶対 PATH に必要がある。Codespaces などとの共通の設定は難しそうなので devcontainer.json
を複数作る。
先日のVSCode の更新で複数 devcontaioner.json
を選択する UI がついた。
CodeSandbox 用の devcontainer.json
を作成し、そちらかはら絶対 PATH にした docker-compose.yml
を利用する。
毎回選択するのは面倒だが、podman-compose か devcontainer 拡張機能が対応するまでの暫定なので。
その他、Remote SSH でワークスペースへ再度接続しようとしてエラーになる場合について。
一旦エラーになると Dev Container を落とす必要がある。下記を CodeSandbox の IDE から実行
~/.local/bin/podman-compose -p workspacedevcontainer -f .devcontainer/docker-compose.yml down
podman compose
でエラーになる
docker-compose.yml
を使っている場合でエラーが出るようになった。
podman compose version --short
が原因のもよう(podman-compose
ではなく podman compose
)。Dev Contaiers 拡張機能のバージョンを 0.275.0
まで下げても同じ。あとで対策を考える、たぶん。
[2023-03-09T05:01:24.551Z] Start: Run: podman compose version --short
[2023-03-09T05:01:24.555Z] Stop (8 ms): Run: podman-compose version --short
[2023-03-09T05:01:24.580Z] Stop (29 ms): Run: podman compose version --short
[2023-03-09T05:01:24.580Z] Error: Command failed: podman compose version --short
podman compose
のとき podman-compose
を実行する偽のスクリプトを作ってみたが、結果は変わらず。ちょっと保留。
原因がだいたい判明。
podman-compose version --short
(-
あり)で特定以上のバージョンを返さないとエスカレーションして podman compose version --short
も実行しているもよう。
version
のときだけ docker-compose
を使うコマンドにさしかえたら回避できた。
処理を分岐させるのにバージョン番号を使われると Docker(Docker Compose)以外を使いにくくなるのだけど、VSCode 的には Docker 使えということなのか?
勘違いだった。
dev.containers.dockerComposePath
を絶対 PATH にしていないと podman compose version --short
を拡張機能側から実行しているもよう。絶対 PATH にしたら回避できた。
(devcontainer-cli だと絶対 PATH でなくても動く)