昔作ったPerl/Pythonで書いたやつをコンテナ詰め詰めした際にdevcontainer使ってみました。FastCGIモジュールとかWindows環境でうまく動かせなかったので便利ですねこれ。
お仕事などではGoを書くことが多く、コンテナに詰めなくても開発マシン(WindowsやMac)とサーバホスト(Linux)でビルドすれば動くのでこの辺の知見は一から集める。
この記事見て「devcontainer簡単そう」って思って使ってみたらpostCreateComman
でのchmod +x
が必要になって、なんで要るんやろとか調べてたら沼にハマっていった(これについては最後に述べる)。
なお、わたしWindowsユーザなのでMacとかLinuxでどうなのかは謎1。以下書かれていることはすべてWindows 10 Pro(22H2)でのお話です。
たどり着いた答え
色々試行錯誤した結果、「python:3.11.0-slim-buster
をベースにしてfeaturesでcommon-utilsを入れる。」に収束しました。イメージサイズは334MBになります。
{ "image":"python:3.11.0-slim-buster", "features": { "ghcr.io/devcontainers/features/common-utils:2": { "username":"vscode" } } "containerUser":"vscode" }
これから話す話は基本的にPythonでの話です。Perlはprebuilt imageがないので、Alpineイメージに色々入れました。
試行錯誤の記録
だいたいここにあります。
script-libraryをpython:3.11.0-slim-buster
に入れる
イメージサイズは375MBでした。
FROM python:3.11.0-slim-buster RUN bash -c "$(curl -fsSL "https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/common-debian.sh")" \ && apt-get clean -y && rm -rf /var/lib/apt/lists/* USER vscode
最終行で「USER vscode
」とするのは、contaienrUser
でvscode
指定してるのと同じっぽい。
USER
とremoteUser
とcontainerUser
DockerfileではUSER指定できるし、devcontainer.jsonにはremoteUser
とcontainerUser
というkeyがあるけど、どう違うのか。
devcontainer.jsonのschemaから説明を取ってきた。
{ "remoteUser": { "type": "string", "description": "The username to use for spawning processes in the container including lifecycle scripts and any remote editor/IDE server process. The default is the same user as the container." }, "containerUser": { "type": "string", "description": "The user the container will be started with. The default is the user on the Docker image." } }
remoteUser
は「The username to use for spawning processes in the container including lifecycle scripts and any remote editor/IDE server process.」で、containerUser
は「The user the container will be started with. The default is the user on the Docker image.」とある。
remoteUser
はコンテナでLifecycle scripts(後述)やIDE(VSCode)を起動する時のユーザで、containerUser
はコンテナ起動時のユーザと書いてある。おそらく後者はコンテナENTRYPOINT起動するユーザなんだろなとdocker exec -it (コンテナID) sh
で実行中のdevcontainer乗り込んだらcontainerUser
でshが上がってきたので、たぶんそう。
とりあえずcontainerUser
だけ指定しておけば良さそうな気がするけど、MSのドキュメント見るとremoteUser
を主に使うような雰囲気を感じる。
ちなみに、Lifecycle scripts(postCreateCommand
など)が上記の説明通り指定されたユーザで動く一方で、Docker Desktop for Windows等は走らせるスクリプトをroot
でmountする(ことになっている)ので、sudo chmod
2としておかないと失敗することがあります(後述)。
prebuilt image
image
で参照するだけ。イメージ1.44Gもあってびっくりしたので撤退。ところでmcr.microsoft.com
ってタグ一覧わかりにくすぎてつらいですね3。
base imageにpython featureを入れる
base image(mcr.microsoft.com/devcontainers/base:debian
あたり)の上にfeaturesでPython入れると、Pythonをソースコードからビルドするので初回構築がなかなかに時間を食う上に1G越え。よく見るとmcr.microsoft.com/devcontainers/base:debian
だけで810MBある。
devcontainerで起動したときに fatal: unsafe repository
って言われる
issueが立ってる。
git config --global --add safe.directory ${containerWorkspaceFolder}
をする必要があるとな。素朴に考えるとコンテナ生成時4のlifecycle scriptsで叩くんですが、コンテナ内に.gitconfig
が既に存在する状態で起動するとVSCodeは.gitconfig
を転送しないらしくuser.email
などが未設定となってgit操作ができなくなる。
The extension will automatically copy your local .gitconfig file into the container on startup so you should not need to do this in the container itself.
ドキュメントにはそんな事書いてないぞ~のissue。 github.com
なので、コンテナ生成時でなく起動時のlifecycle scriptでgit config
を叩けば良さそう。
{ "postCreateCommand": "git config --system --add safe.directory ${containerWorkspaceFolder}" }
Lifecycle Scriptsについて
Lifecycle scriptsの説明を見てると、コンテナ生成時にonCreateCommand
/updateContentCommand
/postCreateCommand
の3つが順次コンテナ内で走ると書いてある。で、waitFor
の説明で「デフォだとupdateContentCommand
終わったらツール(VSCode IDEとかのことだと思う)接続するよ」とか書いてあるんだけどそれ以外の違いがようわからん。
で、ぐぐるとGitHub Codespacesの説明がヒットする。
onCreateCommand
はプレビルドが作成されるときに 1 回だけ実行されますが、updateContentCommand
はテンプレートの作成時とそれ以降の更新時に実行されます。 インクリメンタル ビルドはupdateContentCommand
に含める必要があります。これらはプロジェクトのソースを表し、プレビルドの更新ごとに含める必要があるためです。
GitHub Codespaceで当該リポジトリいじるとして、最初にGitHub Codespaceを有効化したときにGitHub上でonCreateCommand
が走って、そのリポジトリが更新される(などの)際にupdateContentCommand
が走ってCodespaces用のコンテナイメージをよしなにするみたい。で、GitHub Codespaceで個々人の環境(かつ認証情報)で起動するものはpostCreateCommand
を使え、ということのよう。使ったことないんでわかんないんですが・・・。
ローカルで使うぶんには気にしなくて良さそうですが、onCreateCommand
はlinterみたいなのを入れるときに使ってupdateContentCommand
は依存ライブラリ入れるのに使うと良いのかなぁとかそんな雰囲気。
Lifecycle Scriptsでのexecutable permission設定が必要なのは何故だったのか。
Docker Desktop for Windows のドキュメントにはこう書いてあるので本当はchmod +x
いらないはず。
When sharing files from Windows, Docker Desktop sets permissions on shared volumes to a default value of 0777 (read, write, execute permissions for user and for group). https://docs.docker.com/desktop/troubleshoot/topics/#volumes
でも実際にいじってみるとDocker Desktop (Windows)がファイルのexecutable permissionを何らかの形で覚えているような挙動をする5。なんかがおかしい。変だな~と思ってぐぐるとissue見つけた。
書いてある通り、fileのownerがroot
でなくvscode
などになったりするのも生じている6。
file ownerの話はさておき、file permissionが覚えてるの謎(たぶんfile ownerも覚えてそう)ですが現状を観測してみます7。
PS C:\Users\walkure> docker run --rm -it --mount type=bind,source=C:\Users\walkure\test,target=/poyo alpine sh / # ls -l /poyo total 0 -rwxrwxrwx 1 root root 0 Jan 14 10:55 a.txt <--- 最初は +x されてた / # chmod -x /poyo/a.txt <--- -x した / # ls -l /poyo total 0 -rw-rw-rw- 1 root root 0 Jan 14 10:55 a.txt <--- -x が反映 / # exit PS C:\Users\walkure\test> docker run --rm -it --mount type=bind,source=C:\Users\walkure\test,target=/fuga alpine sh / # ls -l /fuga/ total 0 -rw-rw-rw- 1 root root 0 Jan 14 10:55 a.txt <--- -x が保存されてる / # chmod +x /fuga/a.txt <--- +x した / # exit PS C:\Users\walkure> docker run --rm -it --mount type=bind,source=C:\Users\walkure\test,target=/poyo alpine ls -l /poyo total 0 -rwxrwxrwx 1 root root 0 Jan 14 10:55 a.txt <--- +x が反映
Rancher Desktop(Windows)でdevcontainerが上がってこない
Rancher Desktopのdocker-buildx.exe
がDocker CLI拡張として認識される場所に入ってなくて、VSCodeでdevcontainer起動する際にdocker buildx
が失敗してコケる。同様の理由でWSL上でもdocker buildx
ができない。
%USERPROFILE%\.docker\cli-plugins
乃至C:\ProgramData\Docker\cli-plugins
とかにdocker-buildx.exe
をコピってやれば良いんだけど。
- user idやfile owner/permissionの話など、それなりに変わると思われる。↩
-
featuresやscript libraryで
vscode
ユーザを生成するとsudoersに追加してくれる。↩ - Microsoft Artifact Registry (mcr.microsoft.com) にあるコンテナーイメージのタグ一覧を確認する - Qiita↩
-
onCreateCommand
/updateContentCommand
/postCreateCommand
のみっつ。↩ - Dockerデーモン再起動しても変わらないのでどこかに永続化されてそうだけど、よくわからない・・・。ファイルはNTFS上にいるので、どうなっているのか。↩
-
root
でmountされているならばvscode
ユーザでchmod
したら怒られるのが正しいのに、chmod
がsudo
なくても動く挙動を観測していた。↩ - Docker Desktop 4.16.1 (95567)およびRancher Desktop v1.7.0でやってみました↩