WSL on Dockerでbridgeネットワークを使うと通信速度がやけに劣化する
Windows Subsystem for Linux に apt 経由で docker をインストール*1したものの、どうにもこうにも処理が遅い。 元々のきっかけは docker をビルドに利用する別のツールを利用していたのだけれど、1ビルドに数十分かかるのでいったいなぜだと調べ始めたこと。
結論から言うと、根本原因までは調べ切れてないですが暫定解決策はあるので、1ケースとして書き留めておきます。
TL;DR
- WSL on Docker を使っている場合に Docker コンテナ内部からの通信がものすごく遅い事象に遭遇した
- docker run なら --net=host, docker build なら --network host を使うことで暫定解決はできる
状況
- Microsoft Store上でのバージョン指定がない ubuntu (中身は xenial / 16.04) を選択
- WSLへのDockerの入れ方: https://qiita.com/yanoshi/items/dcecbf117d9cbd14af87
Client:
Version: 17.03.2-ce
API version: 1.27
Go version: go1.6.2
Git commit: f5ec1e2
Built: Thu Jul 5 23:07:48 2018
OS/Arch: linux/amd64Server:
Version: 17.03.2-ce
API version: 1.27 (minimum version 1.12)
Go version: go1.6.2
Git commit: f5ec1e2
Built: Thu Jul 5 23:07:48 2018
OS/Arch: linux/amd64
Experimental: false
検証の流れ
元々はdocker上でのビルドに数十分かかっておかしいと考えて調査していたら、コンテナ上でGitからのダウンロード速度(git clone)が25KiBとかしか出ていないことに気づいた。
どうやらネットワーク周りがかなりボトルネックになっているっぽいことまでを把握。
そこでまずはDocker内部に必要なツールをセットアップしてからあたろうと思って以下の通りのDockerfileを書く。
$ cat Dockerfile FROM ubuntu:latest RUN apt-get update RUN apt-get install -y curl iputils-ping traceroute dnsutils # Image Build sudo docker build .
で、この時点で既に問題が発生してしまう。 なんと apt-get update が10分ぐらいたっても成功しない。
Sending build context to Docker daemon 61.76 MB
Step 1/3 : FROM ubuntu:latest
---> 74f8760a2a8b
Step 2/3 : RUN apt-get update
---> Running in 479fe1cc6fe7
Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB]
... (中略) ...
Get:9 http://archive.ubuntu.com/ubuntu bionic/universe Sources [11.5 MB]
Get:9 http://archive.ubuntu.com/ubuntu bionic/universe Sources [11.5 MB]
Get:9 http://archive.ubuntu.com/ubuntu bionic/universe Sources [11.5 MB]
(以下繰り返し)
回避策から述べると、この時点で --network host を指定することでビルド時のネットワーク問題を一時的に回避する。
# ビルド時のネットワークとしてhostを利用する / 指定しない場合はデフォルトの bridge が利用される $ sudo docker build . --network host ... (中略) ... $ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> a88f6a8a4fa0 21 seconds ago 186 MB
あとはこのImageを使って中から外部向けの通信を行ってみる。
format.txt は curl を利用したネットワーク計測手段の内容をそのまま利用している。
$ sudo docker run --rm -it a88f # 以下、コンテナ内部からの処理 $ ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=120 time=9.14 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=120 time=8.94 ms 64 bytes from 8.8.8.8: icmp_seq=3 ttl=120 time=9.22 ms 64 bytes from 8.8.8.8: icmp_seq=4 ttl=120 time=9.52 ms # 単なるWebページの検索 $ curl -q -o /dev/null -w @format.txt https://www.google.co.jp/ url_effective : https://www.google.co.jp/ http_code : 200 http_connect : 000 time_total : 5.718374 time_namelookup : 5.520191 time_connect : 5.574668 time_appconnect : 5.646445 time_pretransfer : 5.647841 time_redirect : 0.000000 time_starttransfer : 5.715033 size_download : 10830 size_upload : 0 size_header : 715 size_request : 78 speed_download : 1894.000 speed_upload : 0.000 # 約30MBのファイルを落としてきた場合 curl -o a.exe -w @format.txt https://home.jeita.or.jp/page_file/JB1_0.exe url_effective : https://home.jeita.or.jp/page_file/JB1_0.exe http_code : 200 http_connect : 000 time_total : 291.270568 time_namelookup : 5.518307 time_connect : 5.554633 time_appconnect : 5.606024 time_pretransfer : 5.606144 time_redirect : 0.000000 time_starttransfer : 5.648392 size_download : 30870046 size_upload : 0 size_header : 275 size_request : 99 speed_download : 105984.000 speed_upload : 0.000
見た感じでは、ping自体は問題なさそうですが、DNS引くのにものすごく時間がかかってはいます。単なるWSLの上でダウンロードしてきた場合の数値は以下の通りなので、ダウンロード速度自体も1/30ぐらいになっていることが分かります。
$ ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data. 64 bytes from 8.8.8.8: icmp_seq=1 ttl=121 time=11.7 ms 64 bytes from 8.8.8.8: icmp_seq=2 ttl=121 time=9.23 ms 64 bytes from 8.8.8.8: icmp_seq=3 ttl=121 time=9.48 ms 64 bytes from 8.8.8.8: icmp_seq=4 ttl=121 time=9.05 ms $ curl -q -o /dev/null -w @format.txt https://www.google.co.jp/ url_effective : https://www.google.co.jp/ http_code : 200 http_connect : 000 time_total : 0.391 time_namelookup : 0.073 time_connect : 0.082 time_appconnect : 0.313 time_pretransfer : 0.313 time_redirect : 0.000 time_starttransfer : 0.387 size_download : 10943 size_upload : 0 size_header : 747 size_request : 80 speed_download : 27995.000 speed_upload : 0.000 $ curl -o b.exe -w @format.txt https://home.jeita.or.jp/page_file/JB1_0.exe url_effective : https://home.jeita.or.jp/page_file/JB1_0.exe http_code : 200 http_connect : 000 time_total : 9.976 time_namelookup : 0.137 time_connect : 0.152 time_appconnect : 0.367 time_pretransfer : 0.367 time_redirect : 0.000 time_starttransfer : 0.385 size_download : 30870046 size_upload : 0 size_header : 275 size_request : 99 speed_download : 3094353.000 speed_upload : 0.000
以上の内容から、
- WSLそのものは悪いわけではない (十分早い)
- コンテナ -> WSL -> 外部 という経路を通る場合に非常に遅くなる
という事象まではつかめました。
対策
http://docs.docker.jp/engine/userguide/networking/dockernetworks.html
Dockerの内部ネットワークとして、基本的に docker0 の bridge が使われる。
先に考察した通り、WSLからの通信は十分に早いため network に bridge を使わずに直接 host に割り当ててしまえば問題がさらに絞れる、ということで実際にやってみる。
$ sudo docker run --rm -it --net=host a88f $ curl -q -o /dev/null -w @format.txt https://www.google.co.jp/ url_effective : https://www.google.co.jp/ http_code : 200 http_connect : 000 time_total : 0.284695 time_namelookup : 0.128500 time_connect : 0.138440 time_appconnect : 0.214466 time_pretransfer : 0.215841 time_redirect : 0.000000 time_starttransfer : 0.279488 size_download : 10885 size_upload : 0 size_header : 715 size_request : 78 speed_download : 38327.000 speed_upload : 0.000 $ curl -o a.exe -w @format.txt https://home.jeita.or.jp/page_file/JB1_0.exe url_effective : https://home.jeita.or.jp/page_file/JB1_0.exe http_code : 200 http_connect : 000 time_total : 7.957994 time_namelookup : 0.256907 time_connect : 0.270424 time_appconnect : 0.326524 time_pretransfer : 0.326726 time_redirect : 0.000000 time_starttransfer : 0.341902 size_download : 30870046 size_upload : 0 size_header : 275 size_request : 99 speed_download : 3879608.000 speed_upload : 0.000
あっけないぐらい普通に動いた。 ので、WSL周りのbridge設定がどうにもよろしくないらしい、ということまでは分かった。
結論
WSL上で本番を動かすわけでもなく、単に開発時検証ビルドのみであれば複数のコンテナを同時に立てる必要はないので、一応オプションを指定することで問題は解決できそう。
ただ、複数のコンテナを組み合わせて動かすような場合はちゃんとDocker内部のネットワークを構成しないといけないから、そういった場合には多分使えない。
そもそも WSL on Docker を使うという選択肢が棘の道な気はするので、どうしようか迷うなあ...