CodeSandbox の Docker インテグレーション
CodeSandbox で Docker が使えるようになったとのメールが配信されてきた。
CodeSandbox はご無沙汰だったので少し試してみる。
どのような対応になるのか?
Sandbox[1] 内に .codesandbox/Dockerfile
を配置すれば利用できるとのこと。
This was a win already, but it did require some tinkering to make your local Docker config work in CodeSandbox. Well, not anymore! With this update, Docker works out of the box. Simply create a Dockerfile at
.codesandbox/Dockerfile
and we’ll make everything work, running exactly as it does on local!
ただし、Sandbox 全体がコンテナ内で動くわけではないらしい。
- コンテナは Sandbox でターミナルを開くと作成される(すでに存在していればそちらが使われる)
- コンテナは(Sandbox を利用する)ユーザー別に作成される
- 1 つの Sandbox を複数ユーザーで使う場合、個別に作成される、と思う(未確認)
- 各コンテナはネットワークなど共有している、らしい(ここも未確認)
ターミナルを開くとその中だけは exec で実行したシェル(zsh だった)に接続されるもよう。
Whenever you open a terminal, we check if there is a container running for your user. If it's not running, we start it, and we then start an
exec
session inside the container.Every container does share the network, files and processes between each-other. This allows you to run an HTTP server in one container, and connect to it from another container.
細かいことをいうと Docker ではなく Podman を使っているとのこと。
While we call this a Docker integration, we use rootless Podman to run the containers.
-
CodeSandbox では Git リポジトリを扱う用に(Sandbox とは別に)Repository がある。このスクラップではとくに明記しない場合 Sandbox は Repository も含む扱いとしている。 ↩︎
試してみる
すでに CodeSandbox のアカウントがあるなら、ブラウザーで下記のように操作すると Dockerfile を使える Sandbox を作成できる。
- ブラウザーで CodeSandbox のダッシュボードを開く
- 右上の Create をクリック
- 開いたダイアログで Docker を選択し、一覧から何か選ぶ
- しばらく待つと Sandbox が作成されて IDE で操作できるようになる
コンテナはターミナルを開くと作成されるということで ctrl
+ p
でコマンドパレットを開き New Terminal を実行[1]。
試しに Dockerfile
を編集してから Rebuild and Start
させると反映されていることは確認できた。
実際にコードを編集する場合ではどうなるのかななと、deno 用のテンプレートを使ってみた。
たしかにターミナル内では deno のコンテナなのだが、ウェブ IDE 側はそうではないのでエディターにエラーが表示された。この辺を調整する方法は不明。
-
日本語配列のキーボードを使ったからか
ctrl
+@
ではターミナルは開かなかった。 ↩︎
ライフサイクル
(Sandbox と Repository で違う可能性もあるが)Repository の場合は下記のような感じだった。
- Branch を開くと Repository 開始の段階で新しいイメージが作成される
-
tasks.json
はコンテナが作成されてその中で動く、と思う
-
- ターミナルを開くとコンテナが利用できる
- Sandbox がサスペンド(用語的に正しいかは不明)後に再接続した場合、イメージは再作成されない
- ターミナルを開くとコンテナを利用できるが前回と同じものかは不明
- ホスト名は同じで、
/root
と/workspace
が保持されているのも確認できるが同一コンテナかは不明
- ホスト名は同じで、
コマンドパレットから明示的に 。Rebuild and Restart Cotainers
を実行した場合も(現時点では)上記のファイルなどは再利用できる(この辺は Codespaces と少し違うのかな)
Rebuild and Restart Cotainers
を実行するとターミナルなどで使っていたコンテナは作成され、/workspace
以外は保持されない(/root
は保持されなかった)。
また、左上のメニューから Restart Branch
を実行すると削除される。
どのような場合にコンテナが再利用されているか?
コンテナ ID(的なもの?) を使って確認した。
コンテナ内からコンテナの ID を調べる方法は下記を参考に /proc/self/mountinfo
を利用する。
(cat /proc/self/mountinfo| grep overlay
で確認している)
-
tasks.json
とターミナルは別のコンテナになる。
tasks.json
について
- タスクを複数回実行しても同じコンテナが使われる(タスク実行毎に破棄していないもよう)
- 異なるタスク(
tasks.json
内で別のフィールドを作った)でも同じコンテナが使われる - VSCode から接続して VSCode 側で実行した場合も同じコンテナが使われる
ターミナルについて
- ターミナルをすべて閉じて新規作成しても同じコンテナが使われる(コンテナがないときだけ新規作成するのはドキュメントにあったはず)
- 複数のターミナルを作成しても同じコンテナが使われる
- VSCode から接続して VSCode 側のターミナルを開いた場合も同じコンテナが使われる
Rebuild and Restart Cotainers
を実行した場合は別のコンテナになる。
Dockerfile の変更(イメージ作成)
結構注意点がある。
debian
か ubuntu
ベースにできるイメージは 現時点はベースとなるイメージは debian
か ubuntu
を使う方が良いとのこと。ビルドのログを見るとユーザーの Dockerfile とは別にツールをインストールしているようなのでその辺の関係かと。
At this time, CodeSandbox currently only supports Debian and Ubuntu based images, for the best compatibility and user experience.
キャッシュ保存に利用できる容量は少ない
イメージのキャッシュは Sandbox に割り当てられているディスクから普通に使われる。Personal Free プランだと 4G 6G となる。また、明示的にキャッシュを消すコマンド(docker system prune
的なもの)は見当たらなかった。
よって、テンプレートなどから Sandbox を作成して Dockerfile
の FROM
を変更すると容量不足のエラーになりやすい。
この辺は Dockerfile
はある程度作っておいて Sandbox 作成時に指定することで対応している(GttHub のリポジトリから読み込んで作成するなど)。
root
でないと厳しい
ユーザーは コンテナ内にマウントされる workspace(/workspace
) は root:root
になっている。
ドキュメントなどでは「コンテナ内は root
が使えるぜ」的なことが書いてあるが、どちらかと言うと root
でないと厳しい。
その他
Sandbox 側からコンテナ側に結構介入しているぽい。きちんとは調べていないが /root
の下などはコンテナ開始時にそこそこ手が入っているように見える。
再ビルド
ビルド時に docker build
の --no-cache
や --pull
のようなことはできなさそう。
「Dockerfile
は変わっていないのだけど、パッケージが更新されているはずなので再ビルドしたい」ということはできないかな。
はそのままだと難しいかな)追記 2023-01-27
VSCode から利用(前提として、CodeSandbox はウェブの IDE とは別に VSCode の拡張機能から Sandbox を利用できる。これはどちらかというと CodeSpaces(DevContainers) ではなく Remote SSH ベースとなっている[2]。
では、今回の Docker インテグレーションはどのような使い方になるかというと。
- 接続した時点では Sandbox をホストとした Remote - SSH のような扱い
- 通常の手順でターミナルを開くと コンテナではなく Sandbox の環境になる
- 拡張機能なども コンテナではなく Sandbox 側の環境で動く
- コマンドパレットから CodeSandbox のターミナルを開くとコンテナの環境になる
ということで、たとえば Rust の環境を作ろうとしても VSCode 側でインストールした Rust Analyzer 拡張機能はコンテナ側で動作しないのでうまく動かない。またターミナルから code src/main.rs
のようにやってもエラーになる。
Sandbox では nix が使えるので頑張れば動かせるかもしれないが、それはあまりやりたくない。
-
きちんと比較できるメモがなかったので推測だが、コンテナ内に(podman のソケットなど)以前にはなかったものがいくつかマウントされるようになったなどの変化がある(そんなおもしろそうなものがマウントされてたら印象に残っているはず、たぶん)。 ↩︎
-
Remote - SSH はホスト側の Docker を使って Dev Container を作るこもできる。CodeSandbox でもその方法を利用できるかは別のスクラップで試している。 ↩︎
VSCode について追記 2023-01-27
CodeSandbox 拡張機能は 0.2.67 で試している。
通常の手順でターミナルを開いたときも Docker 環境になった。また、まだきちとした確認はできていないが、拡張機能などもコンテナ側で動くもよう。Rust も拡張機能をインストールしたら普通に動いた。ただし、最近 Rust がサポートされたのでその影響も捨てきれない(何か別の方法で検証がしたいが、どうやる?)。
2023-02-09 追記: 拡張機能もコンテナ側で動いていた(というか Remote SSH の接続先がコンテナ側になっている)。
- コンテナ側の
$HOME
の下に.vscode-server
ができている- CodeSandbox 側が何かしているのでなければ、接続先はこのサーバーのインスタンス
- Nix IDE 拡張機能は
nixpkgs-fmt
を外部コマンドとして実行する- コンテナ側だけにダミーの
nixpkgs-fmt
を置いて実行されるのを確認できた
- コンテナ側だけにダミーの
ということで、普通に VSCode から CodeSandbox に接続して Rust のデバッグしているスクリーンショット。
なお、ワークスペース別に拡張機能をインストールする方法として、以前のコメントではターミナルからスクリプトを動かしていたが、下記のような方法もあった。
これで、Dev Container に近いノリで使えそう。
あとは、1 良くない点があった。VSCode から CodeSandbox のブランチを直接作るとコンテナが開始されないぽい。これは、いったん dashboard でブランチを開けば大丈夫そう。
それでも VSCode から使いたい場合
Remote - Tunnels を使うとある程度は解決できた。下記のリポジトリは Rust 用の環境設定しつつ、code server で外部から接続できるようになっている。
Sandbox の機能は使えなくなるが、拡張機能やターミナルはコンテナ内の環境で動くので、Remote - Tunnels(code server)をコンテナで使っているようなノリで使えている。
ただし、VSCode 側からの操作では Sandbox のサスペンドは回避できないので気を抜くとサスペンドしてしまう。
作り方は下記の通り
code-server
Dockerfle
で code の CLI 版をインストールして動かすだけ、なのだが少し注意点があった。
最初は実行ファイルを /usr/local/bin/code
へ配置したのだが、これだと Sandbox 側から別のファイルを上書きされてしまう。今回は /usr/local/bin/code-cli
として配置した。
サーバーとして動かしたいので tasks.json
でタスク化したくなるが、これをやると他のユーザーが Sandbox へ接続するとログが丸見えになってしまう(デバイスを登録するときの文字列などが見えるのはよろしくない)。
しかたがないので、面倒だけど手動でターミナルを開いてコマンドを実行している。
拡張機能
インストールしたい拡張機能を Sandbox 側から指定できない、と思う。とりあず下記のようなスクリプトを配置しておき、新しいサーバーを利用する場合に VSCode 側のターミナルで手動実行することにより対応している。なお、Sandbox 側のターミナルや tasks.json
で実行してもインストールできない[1]。
#!/bin/bash
code --install-extension "rust-lang.rust-analyzer"
code --install-extension "vadimcn.vscode-lldb"
code --install-extension "esbenp.prettier-vscode"
-
昔は VSCode の外側からでもインストール先ディレクトリーを指定すればできた記憶があるけどこの辺は厳しくなった? ↩︎
GitHub の認証情報
コンテナ内でも /root/.githubtoken
にトークンが保存されている。ブランチのプッシュなどはこれを利用して行われるので、SSH の鍵などを設定する必要はない、と思う。
[credential]
helper = "!f() { echo \"username=$(cat ~/.githubtoken)\npassword=\"; }; f"
また、GitHub CLI でも export GITHUB_TOKEN="$(cat ~/.githubtoken)"
のように環境変数を設定することで利用できた。が、やってよいのかは不明。
あと、少し気になったのは、トークンをローテーションしていなさそうなところ。時間を空けて確認してみたが変化しなかった(とりあえず「24 時間は同じ値だった」というのは確認している)。よって、漏れたら怖いので取り扱いは要注意。
Dev Containers は?
We are also actively working on getting full support for Docker Compose, so you can easily build any full-stack project in CodeSandbox. This will most likely ship next week.
Oh, and did anyone say Dev Containers? 🤔 Stay tuned!
ということらしいので、期待してもよいのかな?
できれば Codespaces のノリで使えるようになるとうれしいけど、やはり(devcontainer-cli を使うような感じで)ターミナルの中だけかな?
とりあえずの感想
イメージの作り方が少し面倒なのと、コードを編集する場合にターミナルの中だけコンテナ環境というのはちょとクセが強いかなという印象。
どちらかというと、コード編集には直接関係しない(フォーマッターなどではない)開発用の DB サーバーとか動かしておくとよいのかなと。
あとは、tmux + vim のようなターミナル内で完結するような環境を作るという感じかな(ウェブ IDE があるのにそれはどうなのよ、という気もするけど)。
なお、Remote - tunnles(code server)を使う方法はそれなりに利用できそうかなと思っていたのだが、Dev Containers を使う方法もあるのでそっちを使うかなという感じである。
感想追記
VSCode ではターミナルだけなく IDE 側でもコンテナの環境になったので、感想がかなり変わる。
コンテナの扱いにいくつか注意点は必要だが、 DevContainer に近いノリで使える。
- (プランにもよるけど)大きなイメージをどっかんすっかんするのには向いていない
- リモートユーザー(コンテナ内のユーザー)は
root
でないと厳しい - rootless podman を意識しないとけいない(rootless というのがめんどう)
- コンテナ内からコンテナを使えるがおそらく outside of の構成、よってワークスペースをマウントするのが面倒など
個人的には「ちょっとした実験やワークフローの変更なら CodeSandbox で良いかも」という感じになってきている(そして実際に少し使ってみているが、わりと良い感触だったりする)。
このような感じなので、Dev Containers サポートがあるならかなり期待したくなる。
(逆に言うと、いまの時点で CodeSandbox 用のイメージとか作らない方がよいのかなと、
少し悩んでしまう)
ブランチとコンテナとイメージ
CodeSandbox のブランチは VM の環境をまるごと分岐させる、と思う。
イメージ的には Codespaces でブランチ別の Codespace を作る感じ(CodeSandbox のブランチはその辺を自動化し、環境のコピーも結構はやい)。
そして、これはかなり徹底しているようで、(おそらく)コンテナも分岐元からコピーされている。(※同一のインスタンスは再度確認すること)
Dockerfile
を変更して再ビルドするとイメージはブランチ内で保存される(分岐元との共有はなさそう)。よって、ブランチごとに df
をとると下記のようになる。
main
ブランチ
➜ /workspace git:(main) df -h
Filesystem Size Used Avail Use% Mounted on
fuse-overlayfs 6.2G 1.7G 4.2G 29% /
tmpfs 64M 0 64M 0% /dev
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
/dev/vdc 6.2G 1.7G 4.2G 29% /workspace
shm 2.0G 252K 2.0G 1% /dev/shm
overlay 3.9G 11M 3.7G 1% /etc/hosts
overlay 3.9G 11M 3.7G 1% /usr/local/bin/lsp
run 2.0G 88K 2.0G 1% /run/podman/podman.sock
tmpfs 2.0G 0 2.0G 0% /sys/kernel
tmpfs 2.0G 0 2.0G 0% /proc/scsi
tmpfs 2.0G 0 2.0G 0% /sys/firmware
tmpfs 2.0G 0 2.0G 0% /sys/fs/selinux
tmpfs 2.0G 0 2.0G 0% /sys/dev/block
Dockerfile
を変更して再ビルドしたブランチ
➜ /workspace git:(draft/sweet-cookies) ✗ df -h
Filesystem Size Used Avail Use% Mounted on
fuse-overlayfs 6.2G 3.8G 2.0G 66% /
/dev/vdc 6.2G 3.8G 2.0G 66% /workspace
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
tmpfs 64M 0 64M 0% /dev
overlay 3.9G 11M 3.7G 1% /etc/hosts
shm 2.0G 252K 2.0G 1% /dev/shm
overlay 3.9G 11M 3.7G 1% /usr/local/bin/lsp
run 2.0G 92K 2.0G 1% /run/podman/podman.sock
tmpfs 2.0G 0 2.0G 0% /sys/kernel
tmpfs 2.0G 0 2.0G 0% /proc/scsi
tmpfs 2.0G 0 2.0G 0% /sys/firmware
tmpfs 2.0G 0 2.0G 0% /sys/fs/selinux
tmpfs 2.0G 0 2.0G 0% /sys/dev/block