Windows10 19033でWSL2を試す

Posted by 雅楽斎 on Friday, December 20, 2019

TOC

Windows10のリング

一般リリースされているWindows10とは別に、開発版Windows10を先行利用します。

開発版Windows10はMicrosoftアカウントで登録を行うことで別途費用がかかることなく利用可能となり、現在2004に相当する版はSlow Ringで利用可能になっています。だいぶ手続きが煩雑になるため、Slow RingのWindows10インストールについては手順を省略します。

今回利用しているバージョンは2004、OSビルドは19033.1になります。

WSL1とWSL2の違い

Windows10 1709(Fall Creators Update)で正式版となったWSLですがこのときのWSL1とWSL2は大きな違いがあります。

WSL1ではWindowsの中でWSL対応したディストロが動く形となり、WSL上で動作するバイナリはWindowsのシステムコールに逐次変換されたものが実行されていました。

WSL2はHyper-Vの上で動作するWSL2対応のディストロ(カスタマイズされたLinux Kernel)がWindowsを介さずに動作しています。また、動作するためのリソースもWindowsから割り当てられるのではなくHyper-Vから割り当てられるのでWindowsと分け合う形です。

WSL2のセットアップ

Windows Subsystem for Linuxを有効にする/Virtual Machine Platformを有効にする

管理者権限で起動したPowerShellで実行する場合

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform

コントロールパネルから設定する場合

スタートメニュー→Windowsシステムツール→「コントロールパネル」→「プログラム」を選択します。

「Windowsの機能の有効化または無効化」を選択します。

「Windows Subsystem for Linux」と「Virtual Machine Platform」にチェックを入れ、OKを押します。

OSの再起動を促されるので、そのまま再起動します。

デフォルトのWSLバージョンを2にする

PowerShellかコマンドプロンプトを起動して以下のコマンドを実行します。

wsl --set-default-version 2
WSL 2 との主な違いについては、https://aka.ms/wsl2 を参照してください

2020/03/13以降、上記コマンド実行時にカーネルコンポーネントのインストールをされるようになりました。

wsl --set-default-version 2
WSL 2 を実行するには、カーネル コンポーネントの更新が必要です。詳細については https://aka.ms/wsl2kernel を参照してください

上記のメッセージが表示される場合は、以下のURLからカーネル更新プログラムパッケージをダウンロードし、wsl_update_x64.msiをインストールします。

WSL 2 Linux カーネルの更新

WSL 2 Linux カーネルを手動で更新するための手順

正常にインストールが終われば、デフォルトバージョンを変更できるようになります。

WSLに対応しているディストロをMicrosoft Storeでインストールする

Microsoft Storeを開いて、Ubuntuとかをインストールする。

最後にWSLで使用するユーザー名とパスワードを設定します。

インストールが終わったら確認

wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-18.04    Running         2

ファイルコピーについて

WindowsとWSLの間での相互のファイルの扱いについてはWSL1とWSL2で共通です。

WindowsからWSLのファイルを参照する

「\\wsl$」にアクセスするとディストロのルートディレクトリが見えるので、その下のホームディレクトリをクイックアクセスに登録しておくと利便性が上がるでしょう。

WSLからWindowsのファイルを参照する

Windows側のドライブレターへは/mntの下にマウントされています。Cygwinの/cygdriveと同じ感覚ですね。私の場合はWSL側のホームディレクトリにWindows側のホームディレクトリのシンボリックリンクを貼っています。

$ ls -la /mnt
total 8
drwxr-xr-x  6 root     root     4096 Nov 29 19:13 .
drwxr-xr-x 23 root     root     4096 Dec 20 16:01 ..
drwxrwxrwx  1 hogehoge hogehoge  512 Dec 20 15:59 c
drwxrwxrwx  1 hogehoge hogehoge  512 Dec 20 15:57 d
drwxrwxrwx  1 hogehoge hogehoge 4096 Dec 20 15:59 e
drwxrwxrwt  2 root     root       40 Dec 20 16:01 wsl
$ ln -s /mnt/c/Users/hogehoge ~/winhome

良いところ

Dockerを使える

さっくりDocker CEをインストールします。

# apt-get install apt-transport-https  ca-certificates curl gnupg-agent software-properties-common
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
# add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -sc) stable"
# apt-get install docker-ce docker-ce-cli containerd.io

都度docker daemonを起動する必要がありますが、systemdは動いてないようです。

# systemctl start docker
System has not been booted with systemd as init system (PID 1). Can't operate.

なので、serviceコマンドでdaemonを起動します。

# service docker start
 * Starting Docker: docker                                               [ OK ]
# docker container run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:4fe721ccc2e8dc7362278a29dc660d833570ec2682f4e4194f4ee23e415e1064
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

Podmanも使える(追記)

インストール方法の変更

podman自体のインストール方法が変わりました(リポジトリ変更)のでインストール方法も書き直しました。こちらをご参照ください。

Podmanでコンテナ分離

なお、以前の方法でインストールしてしまった方は色々面倒だと思いますので復旧させる手順を記載します。

PPAの削除

Ubuntu用のPPAは既にメンテされておらず、ビルドもコケているままなので跡形もなく削除します。

# apt-get remove --purge podman

また、改めてインストールしようとしたらPPAのパッケージの優先度が990になっていたので、PPA自体を削除します。

# apt-cache policy podman
podman:
  Installed: 1.6.2-1~ubuntu18.04~ppa1
  Candidate: 1.6.2-1~ubuntu18.04~ppa1
  Version table:
     1.7.0~1 500
        500 http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_18.04  Packages
 *** 1.6.2-1~ubuntu18.04~ppa1 990
        990 http://ppa.launchpad.net/projectatomic/ppa/ubuntu bionic/main amd64 Packages
        100 /var/lib/dpkg/status
# add-apt-repository --remove ppa:projectatomic/ppa

 More info: https://launchpad.net/~projectatomic/+archive/ubuntu/ppa
Press [ENTER] to continue or Ctrl-c to cancel removing it.

上記コマンド実行後、Enterを押して削除します。

また、1.6.2でエラーが出まくっていた状態だと整合性がとれてないようなので、こんな感じのエラーが出る場合は一旦全て削除します。

$ podman images ls
ERRO[0000] open /proc/108/ns/user: no such file or directory
$ sudo rm -rf ~/.config/containers/ ~/.local/share/containers/

この後に改めてパッケージをインストールして実行することをおすすめします。

改めてパッケージをインストール

# apt-get install podman
$ podman -v
podman version 1.7.0
$ podman container run docker.io/hello-world
Trying to pull docker.io/hello-world...
Getting image source signatures
Copying blob 1b930d010525 done
Copying config fce289e99e done
Writing manifest to image destination
Storing signatures

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

やっとrootless containerの恩恵に預かれそうです。停止したコンテナの起動ができないバグ対応も手元で確認できました。

Unable to restart containers · Issue #4564 · containers/libpod

Is this a BUG REPORT or FEATURE REQUEST? (leave only one on its own line) /kind bug Description I've been having some issues with podman in the last couple of days and I believe I have narrowed...

WSLのディストロ自体がHyper-V上で動作しているため、Windowsの実行状態に関わらずWSLを実行できる

systemdは手元では動いていませんが、daemonの起動はserviceコマンドでできます。バックグラウンド動作や使用するリソースはWindowsとは別に使用されるので、より実際のLinuxに近い動作が可能となるはずです。

WSL1の時のようにUbuntu16.04にして管理者権限で起動したターミナルで起動したDockerがvolume mountもろくにできないみたいなことはなくなっています。

悪いところ

なぜかひたすらメモリリークしている

大量のファイルをWSL側にコピーするだけでそのファイルサイズの倍くらいのRAMを持っていって、返してくれません。

WSL1ではシステムコールを逐次変換するタイプのエミュレーターに近い方式だったので起こりようがない問題です。

複数のファイルをコピーしているので、たとえオンメモリで処理するようになっていたとしてもコピーが終わった分は返してほしいものです。当然RAMには限界があるので、RAMが枯渇するとWSL2が終了するわけです。タイミング次第ではWindows側のプロセスが終了します。

WSL 2 consumes massive amounts of RAM and doesn't return it · Issue #4166 · microsoft/WSL

Your Windows build number: 18917 What's wrong / what should be happening instead: WSL 2 starts using huge amounts of RAM after a while, just using it like normal. At the moment I'm using ph...

一応問題としてあがってはいるので、ここを見ながら様子見ですね。

RAM割当、swap割当を変更する

WSL2では、WSLに割り当てるRAMの量、swapの量を設定ファイルに記述することで最大量を決められます。

記述するファイルのパスはWindowsの%UserProfile%\.wslconfigです。ユーザーのホームディレクトリになりますね。

例えばこのような内容にして

[wsl2]
memory=6GB
swap=4GB

コマンドプロンプトかPowerShellからWSLをシャットダウンしてみるわけです。1

wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-18.04    Running         2

wsl --shutdown
$ free -h
              total        used        free      shared  buff/cache   available
Mem:           5.8G         61M        5.7G         68K         29M        5.6G
Swap:          4.0G          0B        4.0G

これでWSLから扱えるRAMが6GBとswapとして4GBが割り当てられる訳ですね。SSDの容量が逼迫してなければswapに無尽蔵に当てればなんとかなるはずなので当面これで凌ぐのがベターですかね。

/proc/sys/vm/drop_cachesに3を書き込む(追記)

上記Githubのissueを見ていたところ、ワークアラウンドとしてWSL上の/proc/sys/vm/drop_cachesに3を書き込むという方法が書かれていました。

Just in case, here’s a qworkaround to free the allocated caches/buffers memory without shutting down the whole WSL (ref):

これが何を意味するかというと、Linux Kernel2.6.16以降ではこのファイルに0、1、2、3を書き込むと、メモリキャッシュのクリアを強制的に行うとのこと。それぞれの数値の意味は以下の通り。

設定値manの説明
0デフォルト値(manに記載なし)
1ページキャッシュを開放
2dentryとinodeを開放
3ページキャッシュ、dentry、inodeを開放

dentryが個人的になんなのかよくわからないのですが、inodeというファイル単位に振られている識別子を開放ということは直近でアクセスのあったファイルはKernelのキャッシュに格納されているという理解をしています。

また、manには(恐らく)ダーティーページは開放されないので、操作する前にsyncを実行しておくべきとの記載もあります。

ということでやってみます。kernelパラメータの変更はechoでファイルに書き込むのと同じ操作をsysctlコマンドでもできるので、個人的な好みでこっちでやります2

まずは巨大なファイル群をコピーしてRAMを使っている状態にさせます。

そうしたらsyncとsysctlでキャッシュを追い出します。実行前後でfreeコマンドを実行して、どのくらい差がでるか確認します。

# free && sync && sysctl -w vm.drop_caches=3 && free
              total        used        free      shared  buff/cache   available
Mem:        6088868       70436      110788          72     5907644     5725172
Swap:       4194304           0     4194304
vm.drop_caches = 3
              total        used        free      shared  buff/cache   available
Mem:        6088868       71140     5962112          72       55616     5865680
Swap:       4194304           0     4194304

freeが110788から5962112に5.8GB増えているのがわかります。Windows側から見たタスクマネージャでも4.3GB程度RAMが開放されているのが見て取れます。

ひとまずwsl.exe --shutdownを実行せずとも強制的にRAMを開放する手段は確保できました。但しこれはsysctl -w vm.drop_caches=3を実行したタイミングでその時のメモリキャッシュを開放するので、詰まってきた時に実行する必要があります。

syncを実行することとコマンドの引数が長いのでaliasを切ってしまいます。

$ echo 'alias freedropcaches="sync && sudo sysctl -w vm.drop_caches=3"' >> ~/.bashrc

これで以降のログインからは以下のコマンドで処理ができるようになります(sudoを使っているので事前の一定時間にsudoしてない場合は実行ユーザーのパスワードが要求されます)

$ freedropcaches
[sudo] password for hogehoge:
vm.drop_caches = 3

正攻法で行くなら/proc/meminfoをinotifyか何かで監視して、中のSReclaimableが一定値を超えたら実行するような仕組みを作れれば運用が楽にできそうな気がしています。難しい…時間が取れたら作りましょう。参考になりそうなのはこちら。

inotifywait を使って,ファイルが作成されたらアクションを起こすスクリプトを書く - akihiko’s tech note

例えば,ディレクトリ /tmp/hoge に print.ps というファイルが作成されたら,それを自動的に印刷するようにしたい場合,ファイル作成イベントを監視する必要がある. inotifywait というコマンドを使えばそれが簡単にできるので,紹介する.

Xの起動ができない(VcXsrv)

Can't use X-Server in WSL 2 · Issue #4106 · microsoft/WSL

Your Windows build number: (Type ver at a Windows Command Prompt) Microsoft Windows [Version 10.0.18917.1000] with Ubuntu 18.04, WSL 2. VcXsrv X Server Version 1.20.1.4 What you're doing and wh...

この辺を見ると解決方法はあるようですが、的確な解決法をまだ見つけられてないので解決できたら追記します。

スポンサーリンク


  1. –terminate ディストロ名では設定値の読み込みが行われませんでした。 [return]
  2. リダイレクト使わなくてもいいので [return]

comments powered by Disqus