DockerNATが一部のポートを潰しちゃう話

Docker Desktop for Windows と VirtualBox を共存できないか試していて、 もっとも納得できていない現象を紹介します。

本記事はトライしたけどできなかった系のお話から始まります。 さらに妥協した方法を模索しているうちに不可解な現象に遭遇しました。 たまたまその不可解な現象の回避方法を発見しましたが、 どうしてそれがうまく機能するのかはわかっていません。

教えてもらったことをきっかけに正しい理解を得ました。 詳しくはこちらの 追加記事 をご確認ください。

TL;DR

  • Docker Desktop for Windows の仮想スイッチ vEthernet (DockerNAT) が生きてると、VirtualBox のポートフォワーディングで 1688 から 2279 の範囲が利用できなくなる
  • ネットワークアダプタ vEthernet (DockerNAT) を無効化して再起動すると、 上記のポート番号の制限はなくなる
  • どうしてこんなことになるのか知ってる人がいたら教えて

背景

やりたかったことは単純で Docker Desktop for Windows (= Hyper-V)と VirtualBox を Windows 10 - 1903 上で共存させたかったのです。 知ってる人には有名な話なのですが Hyper-V と VirtualBox は長らく共存できないとされていました。 これはCPUの VT-x を取り合ってしまうためとされています。

しかし VirtualBox は 6.0 で、 フォールバックコアとして Hyper-V をサポートした とされています。 ネット上にも僅かですが 試してみた記事 があがってました。

自分は長らく VirtualBox を vagrant とともに使っていましたが、 ちょっと docker を本格的に使う必要に迫られ、 Hyper-V 及び Docker Desktop for Windows に取り組むため 共存を試みだしたのです。

もちろん VirtualBox 内の VM で dockerd を動かして リモートで使う方法もあったんですが、 ヘビーに使うなら特にボリュームマウントの複雑さもあり Hyper-V に頼ったほうが見通しが良いだろうと考えたのです。 また Hyper-V に完全移行する方法もあるのですが、 VirtualBox ベースの古いリソースもあるため現実的な選択肢ではありません。

結果的に Hyper-V と VirtualBox の共存はうまくいきませんでした。 上述の記事にもあるのですがCPUに依存しているのか、 それとも自分がまったく理解できていない他の理由によるものなのか、 とにかく Hyper-V と VirtualBox の同居は 2019/10/19 時点では諦めたほうが良いとの結論に至りました。

次善の策

とはいえ docker をヘビーに使いたいとの要求は依然生きており、 なんとかする必要がありました。 そこで気は進まないものの Hyper-V をインストールし、 必要に応じて Hyper-V の有効・無効を切り替える方法を採用しました。

Hyper-V は以下のコマンドを管理者権限で実行した後、 再起動することで無効化できます。

> bcdedit /set hypervisorlaunchtype off

再度有効化するコマンドは次のようになります。 当然再起動が必須です。

> bcdedit /set hypervisorlaunchtype auto

このことはググれば結構いろんなところで 言及 されてます。

問題発覚

Hyper-V が使えるようになった自分は Docker Deskto for Windows もインストールし、 一通り遊んでちゃんと機能することに満足しました。 これはインストールとセットアップの楽さにくわえ、 ボリュームマウントまでほんとよくできてます。

一通りの作業を終え VirtualBox を使えるようにするため、 Hyper-V を無効化して再起動したところ VirtualBox の VM が起動しません。 おっと記述が正確ではありません。 VM そのものは起動できています。 しかし vagrant up した際に vagrant はホストの 2222 ポートにフォワーディングされた ssh を待つのですが、 いっこうに接続できず結局タイムアウトで失敗してしまうのです。

netstat -anp tcp | grep LISTEN のようなコマンドで確認すると、 同じ VM からフォワーディングしている他のポート (例: 5432 = postgres) はすべて正しく開いているのですが 2222 番だけが開けていないのです。 もちろん他のアプリが 2222 を使ってる様子もありません。 たしか VirtualBox はそのポートが使えないときは ズラしてフォワーディングしてくれたはずなので、 ポートの競合という線はそもそも薄そうです。

いろいろ試す中で ssh を別のポート(9999とか)にフォワードすれば正しくできました。 より詳しくどの範囲が使えないか調べてみると 1688 から 2279 が使えないことがわかりました。 二分探索で境界を探したのでこの間には使える場所があるかもしれませんが。

自分の知識ではこのようにポートを範囲で無効化できるのは ファイアウォールしかありませんので Windowsファイアウォールの設定も精査してみましたが怪しいところはありません。 Docker や Hyper-V 固有のサービスもチェックしてみましたが、 最終的には Hyper-V の仮想スイッチであるネットワークアダプタ vEthernet (DockerNAT) がとの因果関係が見つかりました。

回避策

すでに TL;DR にも書きましたが VirtualBox を使うときには

> bcdedit /set hypervisorlaunchtype off

だけではなく vEthernet (DockerNAT) ネットワークアダプタを無効化した上で 再起動が必要です。 こうすることで 1688 から 2279 のポートへ VirtualBox がフォワードできるようになります。

また Hyper-V (Docker Desktop for Windows) を使う場合には

> bcdedit /set hypervisorlaunchtype auto

した上で vEthernet (DockerNAT) ネットワークアダプタを有効化してから 再起動しましょう。

原因は?

回避策はわかりましたが原因はまったくわかっていません。 むしろ vEthernet (DockerNAT) が犯人である確たる証拠もありません。 あるのは状況証拠だけ。 ですからなんでこんなことが起こるのか、自分には説明は一切できません。 DockerNAT との因果関係も、回避策を試す中でコレをやれば回避できたことから、 推測したものにすぎません。

もしちゃんと理由を説明できる人がいたら twitter ででも教えてください。

教えてもらったことをきっかけに正しい理解を得ました。 詳しくはこちらの 追加記事 をご確認ください。