WSL 2 複数インスタンス同一 IP アドレス

WSL 2 で複数の distribution (ここではこれを WSL の「インスタンス」と呼ぶことにします) を導入して同時に起動すると同じ IP アドレスを使うのです。ちょっと直感に反していたのでまとめておきます。

まず前提として WSL の複数インスタンスを使うという話ですが、wsl.exe の export/import 機能を使えば同じバージョン Ubuntu でも複数のインスタンスを作成できます。ここでは元々の名前の Ubuntu というインスタンスとそれを export/import して作った Hetarena というインスタンスがあるものとして説明します。

その 2つ、Ubuntu と Hetarena を同時に起動して ifconfig で IP アドレスを確認すると、あら不思議、同じ IP アドレスを持っています。さらに Ubuntu で Apache を起動して Hetarena で netstat で listening しているポートを確認すると、なんと 80/TCP をリッスンしているではないですか!

さらにさらに Ubuntu で mysql を起動して Hetarena から以下のコマンドを打ってみると何と接続できて Ubuntu のデータベースの内容を確認することができます。

mysql -u myid -D mydb -h 127.0.0.1 -p

ちなみにホスト指定を ‘-h localhost’ とすると Unix ソケット経由の接続を試みこれはエラーとなります。ファイルシステムは明らかにインスタンス毎に独立しているし、ps コマンドを打ってみても他方のプロセスが見えるということはなさそうです。

ググってみると関連した issue が見つかり、そこで関係者により以下のように説明されていました。

All of the WSL 2 distros run on the same virtual machine, which has a singular virtualized networking interface controller.

https://github.com/microsoft/WSL/issues/4304

「WSL 2 の複数ディストロは同一 VM 上で動作する」って訳してもチンプンカンプンになりそうですが、面白い実装ですね。

WSL 2 のデフォルトユーザー指定 (2つめのインスタンス用)

Visual Studio Code で WSL 2 と連携してろくに設定せずに Terminal を開くと root でシェルが起動してしまったりします。それでは困るので以下のコマンドでデフォルトユーザーを設定することになります。

ubuntu config --default-user blogger323

参考: https://docs.microsoft.com/en-us/windows/wsl/wsl-config#change-the-default-user-for-a-distribution

ところが、です。私の場合は以下の記事を参考にしてディストリビューションの位置を変えたり 2つめの WSL インスタンスを作ったりしています。

https://stackoverflow.com/questions/38779801/move-wsl-bash-on-windows-root-filesystem-to-another-hard-drive

私の場合、Ubuntu を export 後、Hetarena という名前で import して 2つめのインスタンスを作りました。さて、この 2つめのインスタンスのデフォルトユーザーを変えるにはどうしたらよいでしょう?

hetarena config --default-user blogger323

の様なことをやっても当然のごとく “Command Not Found” なわけです。

ググっても答えは見つからず、結局レジストリ内を検索して見つけ出しました。以下のキーに Linux uid を設定することでデフォルトユーザーを指定できます。

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{distro-id}\DefaultUid

ここで distro-id はディストリビューション (WSL インスタンス) に割り当てられた ID です。DefaultUid と同じ階層に DistributionName というキーがあるのでこれで見分けることができます。

ちなみにレジストリキーでググって後から以下の Issue を見つけることができました。私は試していないですが、Linux ファイルシステムの設定ファイルに書く方法もあるようです。(追記: Microsoft のドキュメントを改めて確認すると記載があったのでこちらの方が正規の方法ですね!)

https://github.com/microsoft/WSL/issues/3974

Docker で WSL 2 エンジン有効化後、既存 Hyper-V ボリュームアクセス不可問題

まあ、当たり前と言えば当たり前なんですけどね。私は Docker に関して一般ユーザーなので、 ‘Use the WSL 2 based engine’ を有効化したとたんにこれまで使用できたボリュームにアクセスできなくなって焦りました。そんなときどうするかという話です。今更という感じの話題かも知れませんが…。

シナリオとしては以下の通りです。

  1. これまで Docker Desktop for Windows を Hyper-V backend で使用してきた。
  2. メリットが多いというので WSL 2 backend に切り替えた。
  3. 切り替えた後は既存ボリュームにアクセスできない!

さて、この時どうするかなのですが、まず焦らず もう一度 Hyper-V backend に戻します。backend を切り替えるだけでは Hyper-V 上の Docker サービス用 VM が削除されたりするわけではないので Docker Desktop で設定を元に戻せばこれまでの image や volume がまた使用できるようになります。

続いて volume の内容をエクスポートします。以下のように busybox を使用して Windows のファイルシステムと Docker ボリュームをマウントした後、ボリュームの内容を tar.gz ファイル on Windows ファイルシステムとしてエクスポートします。

(ボリュームを確認)
C:\>docker volume ls
 DRIVER    VOLUME NAME
 local     myvolume
(ボリュームをエクスポート)
C:\>docker run -v myvolume:/src -v C:\Temp:/dest -i -t busybox
 / # tar -zcvf /dest/volume-backup.tgz src
 / # exit

C:\>

あとは WSL 2 backend に切り替え新規ボリュームを作成してから .tgz を戻せばよいです。私の場合は WSL の Linux ファイルシステム上に適当に展開して ‘-v /home/blogger323/dockervol:/mnt’ のように bind mount するようにしました。

ちなみにですが、WSL 2 backend に切り替えた後のボリュームは以下のパスで Windows からアクセス可能です。

\\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes

参考情報:

Migrating existing containers from Hyper-V to WSL2 technology

追記: リソース消費量を抑えるには (2021.5.2)

WSL 2 を Docker エンジンとして選択すると、Docker 用の WSL distribution として以下のように docker-desktop、docker-desktop-data の 2つが新たに作成されます。

C:\>wsl.exe --list -v
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2 

上のリストで Ubuntu は元から使っていた distribution ですが、WSL integration の設定でこの Ubuntu と Docker を統合すると Docker を使っているときはこれら 3つの WSL 環境が起動することになります。本文では WSL integration の設定を行う想定で bind mount するとしましたが、WSL 用メモリの使用量は多くなりがち (参考)なので統合せず Docker volume を作ることで起動する WSL 環境を docker 関連の 2つのみにするという選択肢もありだと思います。

その場合も、本文最後に書いたパスを用いて Windows から直接 volume 内のファイルにアクセスすることが可能です。

更に追記 (2021.5.15):
よく考えると Docker Desktop を使わずに WSL で Linux 版 Docker を動かせばよいような気がしてきました。

WSL 2 で Windows から Linux マシンをどう参照するか問題

WSL 2 に mysql や apache を入れて Windows のプログラムから接続したくなることがあります。さてどうしましょうか。

build 18950 以上であれば localhost 指定で OK

Windows 10 build 18950 以上であれば Windows のループバックアドレス (localhost) のポートから自動的に Linux マシンにポートフォワードしてくれます。ですので Linux で Apache を動かして Windows のブラウザから http://localhost/ と指定してアクセスすることができます。

参考記事: https://www.atmarkit.co.jp/ait/articles/1909/09/news020.html

生のアドレスでアクセスしたい時もある

ただ、Web アプリ関連でリダイレクト等が絡むと localhost を使った URL では動かないケースがあるようです。私もそのあたりが原因でハマりかけたので開発時は Linux マシンのアドレスを使ってアクセスしています。ここで困るのが Linux マシンの IP アドレスを固定できないことです。調べた限りでは方法がなさそうです。

そこで策として考えられるのが、Windows の hosts ファイルを Linux マシン起動後に更新してホスト名を固定する方法です。世の中にはこれを Windows サービス化された方がいらっしゃいます。

https://github.com/shayne/go-wsl2-host

私の場合はこれだけのためにサービスを増やすのも大ごとのように感じてしまい、以下のような簡単なスクリプトを作って WSL 起動後に Windows 管理者権限で実行しています。元々 hosts ファイルはいじっておらず空のままだったので完全に置き換える動作にしていますが、ちょっと変えれば静的レコード+動的レコードみたいな使い方もできると思います。ご参考まで。

Linux 側 IP アドレス表示スクリプト

$ cat /usr/local/bin/genhosts
#!/bin/sh
IP_ADDR=hostname -I
while read -r line; do
  echo $IP_ADDR $line
done < /usr/local/etc/wslhosts

Windows 側から参照したいホスト名を記述するファイル

hetarena.com 開発環境の例ですw

$ cat /usr/local/etc/wslhosts
hetarena-dev.com
en.hetarena-dev.com
ura.hetarena-dev.com

Windows 側スクリプトファイル

以下の内容の .cmd ファイルを作って WSL 起動後に管理者権限で実行します。

wsl.exe genhosts > C:\Windows\System32\drivers\etc\hosts 

すると以下のような hosts ファイルが生成されます。

172.27.43.123 hetarena-dev.com
172.27.43.123 en.hetarena-dev.com
172.27.43.123 ura.hetarena-dev.com

これで Windows ブラウザから http://hetarena-dev.com を参照したりすることができます。

その他

  • WSL 2 のファイルシステムを参照したいときは ‘\\WSL$\’ を Explorer から開けばよいですね。
  • 逆に Linux から Windows を参照する場合は “ホスト名.mshome.net” というホスト名が使用できます。

関連情報: WSL2 Set static ip? 
https://github.com/microsoft/WSL/issues/4210