Open8

How to use Docker on WSL2 with VSCode

ピン留めされたアイテム
aiskyaisky

Docker を WSL2 上で VS Code から使いたくなったので、わからなかった点をメモ。

aiskyaisky

VS Code から container を起動すると謎の位置に current directory がマウントされる。
なぜ。というかそもそも "volume", "mount" 等のワードがわかってなかったので調べた。

aiskyaisky

Qiita のこの記事「Docker の Volume がよくわからないから調べた」が詳しい。

Docker は container 外にデータを保存する手法として主に

  • anonymous volume
  • named volume
  • bind mount

の 3 つを提供しており、VS Code がデフォルトで行っているのは bind mount であった。

どの手法を使うかは開発環境か実行環境かで使い分けるべきで、
「Docker 開発ベストプラクティス」によれば、開発環境では bind mount を使うのが良いよう。
今回は開発環境での配布をメインに考えているので、bind mount を使うことにする。

ちなみに、WSL2 では\\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes以下に volume が保存されており、
WSL2 上からdocker volume lsで一覧を確認可能である。

あとで公式ドキュメント「Docker におけるデータ管理」を読む。

aiskyaisky

今まで無意識に打っていたdocker build -t <IMAGE:TAG> ..が何を意味するのかよくわからなくなってきたので調べた。

「Dockerfile のベスト・プラクティス」によれば、

ビルド・コンテクストの理解

docker build コマンドを発行するとき、現在作業しているディレクトリのことを ビルド・コンテクスト(buid context) と呼びます。デフォルトでは、Dockerfile はここにあると見なされますが、フラグ( -f )の指定によって違う場所も指定できます。 Dockerfile がどこにあるかに関係なく、現在のディレクトリ以下にある再帰的なファイルとディレクトリを、すべて Docker デーモンに対してビルド・コンテクストとして送信します。

とあり、.は build context を与える PATH らしい。
Dockerfile に PATH 依存の何かを含まない限りあまり気にしなくてよさそう。

aiskyaisky

VS Code で bind mount 先を指定するためには、devcontainer.json"workspaceMount" or "mounts"オプションを用いる。
"workspaceMount"は現在の workspace のデフォルトマウント先を変更でき、"mount"はさらに bind mount したい位置を指定できる。

例えば、現在 VS Code で開いているフォルダを container 内の/home以下に bind mount したいときは、

devcontainer.json
{
    "workspaceMount": "source=${localWorkspaceFolder},target=/home/${localWorkspaceFolderBasename},type=bind",
    "workspaceFolder": "/home",
    // ${localWorkspaceFolder} は PATH を
    // ${localWorkspaceFolderBasename} はフォルダ名を意味する
}

と書けば良い。
source以下の書き方は docker コマンドの--mountオプションを使うときと同じである。
ここで、"workspaceFolder"を指定しておかないと VS Code がデフォルトで/workspacefolderを開こうとしてしまってエラーが出るので注意。

詳しくは docker 公式の"Use bind mounts"とVS Code 公式の"devcontainer.json reference"
を参照のこと。

aiskyaisky

最後に container 内での integrated terminal の設定であるが、devcontainer.json以下の"setting"に VS Code の設定のようにそのまま書き込める。
例えば、デフォルトで、aptでインストールしたfishをシェルとして/homeを開きたかったら

devcontainer.json
{
    "settings": {
        "terminal.integrated.shell.linux": "/usr/bin/fish",
        "terminal.integrated.cwd": "/home",        
}

と書けば良い。
詳しくはVS Code公式の"Integrated Terminal"を参照。

aiskyaisky

ちなみになぜ Windows ではなく WSL2 上で開発を行うかについて少し書いておく。

Docker は Virtualbox などのホスト OS 型の仮想化ではなく、コンテナ型の仮想化を行うため、ベース OS が Linux である必要がある。
従って、Windows や Mac では、内部的には Linux の仮想 OS を立ち上げた上で docker を動かしているため、動作が重くなってしまう。

しかし、WSL2 からは Linux が native で動くようになったため、それを使えば Windows でも docker が native で動くようになった ( docker の "Use the WSL 2 base engine" というオプションはまさしくそのことを表している)。
しかし、Windows \leftrightarrow WSL2 のファイル転送は処理が重いようであるため、
結局、Windows では全て WSL2 上で作業を行うのが最も効率的である。

詳しくは、"Docker Desktop: WSL 2 Best practices"を参照。

aiskyaisky

WSL2 の不具合かもしれないが、docker を起動するとRelease file for http://... is not valid yet (invalid for another ...).というエラーが出るときがよくある。
これは WSL2 の時刻がずれていることが原因なので、

$ hwclock -s

と打ってあげると解決する。