【Windows】Docker Desktop for WindowsとVagrantの便利な環境を共存させる #2/3【Hyper-V】

※本稿は以下の一連の記事のうちの一つです。
[【Windows】Docker Desktop for WindowsとVagrantの便利な環境を共存させる #1/3【Hyper-V】](https://qiita.com/tmiki/items/bdc7ff43b3d8d636ec8f)
[【Windows】Docker Desktop for WindowsとVagrantの便利な環境を共存させる #2/3【Hyper-V】](https://qiita.com/tmiki/items/d5c63a2ca0573e01b321)　←**いまココ**
[【Windows】Docker Desktop for WindowsとVagrantの便利な環境を共存させる #3/3【Hyper-V】](https://qiita.com/tmiki/items/1d5edb4e482f0f38bfd2)

# 手順
## Hyper-V通常VM用のネットワーク（Hyper-V Internal with WinNAT）を設定する
次に、通常のVM用のネットワークを設定していきます。大きく以下の3ステップの作業となります。

- 新たにVirtual Switchを作成する
- 当該Virtual Switchに接続するホストNICにIPアドレスを設定する
- WinNATを利用し、当該Virtual Switchから外部のネットワークへの通信をNATするよう設定する

基本的には下記Microsoftの公式ドキュメントを通りの手順で設定していきます。

Set up a NAT network | Microsoft Docs
https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/setup-nat-network

### WinNATとDefault Switch
WinNATに関する、より具体的な説明、制約事項等は下記記事にまとまっています。併せて読むとWinNATに関する理解が深まります。

Windows NAT (WinNAT) — Capabilities and limitations | Virtualization Blog
https://blogs.technet.microsoft.com/virtualization/2016/05/25/windows-nat-winnat-capabilities-and-limitations/

なお、Windows 10 1709から「Default Switch」という、NAT/NAPT/DHCP機能を持つ仮想ネットワークが実装されています。これはVirtualBoxの「アダプター 1: 準仮想化ネットワーク (NAT)」と同様、当該ネットワーク下のホストはDHCPでIPアドレスを取得でき、またInternetに接続することも可能になります。

What’s new in Hyper-V for Windows 10 Fall Creators Update
https://blogs.technet.microsoft.com/virtualization/2017/11/13/whats-new-in-hyper-v-for-windows-10-fall-creators-update/
※上記Blogは2019/3/20で運用終了になった模様

ただ、私の環境では不安定で使えなかったので、「Default Switch」を利用するのはあきらめました。
コントロールパネルの「Network Connections」からホストOSのNIC一覧を確認することができますが、Default Switch用のNICとして「vEthernet (Default Switch)」というものが作られますが、これが消えたり表示されたりを繰り返し、最終的には消えてしまいました。

![DefaultSwitchBlinks_01.png](https://qiita-image-store.s3.amazonaws.com/0/137777/3be313a5-62ed-2a89-1ae6-538e97d3de46.png)
![DefaultSwitchBlinks_02.png](https://qiita-image-store.s3.amazonaws.com/0/137777/37820c54-a010-4e02-a6b3-11e90f9ff6af.png)


### PowerShellをAdministrator権限で実行する
では、実際に設定していきます。
スタートメニューから「powershell」と入力すると、Power Shellのアイコンが表示されます。「Run As Administrator」をクリックします。
![PowerShell_Administrator_01.png](https://qiita-image-store.s3.amazonaws.com/0/137777/8e7e321d-6be4-a8d4-5383-7d201241a04e.png)

その後、User Access Controlのポップアップが表示されるので、対象が間違っていないことを確認し、「Yes」を押します。
Power ShellがAdministrator権限で起動します。タイトルバーの左上に「Administrator:」と表示されていることを確認します。
![PowerShell_Administrator_02.png](https://qiita-image-store.s3.amazonaws.com/0/137777/2047cac0-24c8-6ac3-1844-df0f3a167032.png)


### 新たにVirtual Switchを作成する（New-VMSwitch）
PowerShellから下記のコマンドを実行します。Virtual Switchの名前は何でもよいですが、ここでは「Hyper-V Internal with WinNAT」とします。Virtual Switchの作成には数分かかります。

- SwitchName: Hyper-V Internal with WinNAT　（※任意に設定可能）
- SwitchType: Internal

```:New-VMSwitch
PS C:\WINDOWS\system32> New-VMSwitch -SwitchName "Hyper-V Internal with WinNAT" -SwitchType Internal

Name                         SwitchType NetAdapterInterfaceDescription
----                         ---------- ------------------------------
Hyper-V Internal with WinNAT Internal
```

上記コマンドが完了したら、Virtual Switchが生成されているかどうかを確認します。

```:Get-VMSwitch
PS C:\WINDOWS\system32> Get-VMSwitch

Name                         SwitchType NetAdapterInterfaceDescription
----                         ---------- ------------------------------
DockerNAT                    Internal
Hyper-V Internal with WinNAT Internal
```

念のため、Hyper-V ManagerからVirtual Switchを確認してみます。Hyper-V Managerを開き、右の「Actions」ペインから「Virtual Switch Manager...」をクリックします。
「Hyper-V Internal with WinNAT」というVirtual Switchが生成されていることが確認できます。
![WinNATConfiguration_01.png](https://qiita-image-store.s3.amazonaws.com/0/137777/0339f81f-0d8f-5dea-d3a1-e2f7f2bd6cad.png)

### 当該Virtual Switchに接続するホストNICにIPアドレスを設定する
先ほどの当該Virtual Switchの作成に伴い、これに接続されるホストNICも併せて生成されています。Get-NetAdapterコマンドレットで、存在するNICの一覧を確認できますので、確認しておきます。
下記例では、「ifIndex」が「32」のものが該当します。この「ifIndex」の値は次に使うので控えておきます。

```:Get-NetAdapter
PS C:\WINDOWS\system32> Get-NetAdapter

Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
----                      --------------------                    ------- ------       ----------             ---------
vEthernet (Hyper-V Int... Hyper-V Virtual Ethernet Adapter #2          32 Up           00-15-5D-14-25-10        10 Gbps
ローカル エリア接続       Realtek PCIe GBE Family Controller            9 Up           80-EE-73-2E-ED-3E         1 Gbps
vEthernet (DockerNAT)     Hyper-V Virtual Ethernet Adapter             26 Up           00-15-5D-14-25-0E        10 Gbps
```

NICにIPアドレスを付与するには、New-NetIPAddressというコマンドレットを利用します。対象のNICを指定するにあたり「ifIndex」の値を指定します。
なお、ここで設定するIPアドレスは、Hyper-V内のVMから見た場合にDefault Gatewayとなります。

- IPAddress: 192.168.254.1
- PrefixLength: 23
- InterfaceIndex: 32　（※Get-NetAdapterコマンドレットであらかじめ確認する）


```:New-NetIPAddress
PS C:\WINDOWS\system32> New-NetIPAddress -IPAddress 192.168.254.1 -PrefixLength 23 -InterfaceIndex 32


IPAddress         : 192.168.254.1
InterfaceIndex    : 32
InterfaceAlias    : vEthernet (Hyper-V Internal with WinNAT)
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 23
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Tentative
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : ActiveStore

IPAddress         : 192.168.254.1
InterfaceIndex    : 32
InterfaceAlias    : vEthernet (Hyper-V Internal with WinNAT)
AddressFamily     : IPv4
Type              : Unicast
PrefixLength      : 23
PrefixOrigin      : Manual
SuffixOrigin      : Manual
AddressState      : Invalid
ValidLifetime     : Infinite ([TimeSpan]::MaxValue)
PreferredLifetime : Infinite ([TimeSpan]::MaxValue)
SkipAsSource      : False
PolicyStore       : PersistentStore
```

なお、ここで設定したIPアドレスは、「Network and Sharing Center」からGUIで設定するものと同一です。「Control Panel\Network and Internet\Network Connections」から対象のNICのPropertiesを開き、IPv4の設定を確認してみましょう。

![WinNATConfiguration_06.png](https://qiita-image-store.s3.amazonaws.com/0/137777/1f961ac1-ac28-f539-bf27-7d5920bf54fb.png)
![WinNATConfiguration_07.png](https://qiita-image-store.s3.amazonaws.com/0/137777/85da2c7e-230a-168b-3e69-0763e0fc00c4.png)


### WinNATを利用し、当該Virtual Switchから外部のネットワークへの通信をNATするよう設定する
最後に、今回作成したVirtual Switchに所属する（正確には、指定したIPアドレスレンジに所属する）ホストからのトラフィックをNATするための設定を追加します。

PowerShellからNew-NetNatコマンドレットを実行します。パラメーターは下記を指定しました。

- Name: HyperVinternalWinNAT （※WinNAT設定に対する名称。任意のものを指定可能。）
- InternalIPInterfaceAddressPrefix: NAT対象となるIPアドレスレンジ

```:New-NetNat
PS C:\WINDOWS\system32> New-NetNat -Name HyperVinternalWinNAT -InternalIPInterfaceAddressPrefix 192.168.254.0/23


Name                             : HyperVinternalWinNAT
ExternalIPInterfaceAddressPrefix :
InternalIPInterfaceAddressPrefix : 192.168.254.0/23
IcmpQueryTimeout                 : 30
TcpEstablishedConnectionTimeout  : 1800
TcpTransientConnectionTimeout    : 120
TcpFilteringBehavior             : AddressDependentFiltering
UdpFilteringBehavior             : AddressDependentFiltering
UdpIdleSessionTimeout            : 120
UdpInboundRefresh                : False
Store                            : Local
Active                           : True
```

以上でネットワークの設定は完了です。


## Hyper-V通常VM用のネットワーク（Hyper-V Internal with WinNAT）からの疎通を確認する
### VagrantのProviderとしてHyper-Vを利用する場合の注意事項
#### Vagrant BoxはProvider依存
Vagrant BoxはProviderが違う場合はイメージを利用することができません。そのため、Hyper-V用のBoxを探す必要があります。

Basic Usage - Providers - Vagrant by HashiCorp
https://www.vagrantup.com/docs/providers/basic_usage.html

> Basic Provider Usage
> » Boxes
> Vagrant boxes are all provider-specific. A box for VirtualBox is incompatible with the VMware Fusion provider, or any other provider. A box must be installed for each provider, and can share the same name as other boxes as long as the providers differ. So you can have both a VirtualBox and VMware Fusion "precise64" box.

#### VagrantはHyper-Vの仮想ネットワークを操作しない
ProviderとしてHyper-Vを利用する場合、VagrantはVirtual Switchなどのネットワークに関する設定を何も行いません。設定済みのものを利用するよう動作します。

Limitations - Hyper-V Provider - Vagrant by HashiCorp
https://www.vagrantup.com/docs/hyperv/limitations.html

> Limited Networking
> Vagrant does not yet know how to create and configure new networks for Hyper-V. When launching a machine with Hyper-V, Vagrant will prompt you asking what virtual switch you want to connect the virtual machine to.
> 
> A result of this is that networking configurations in the Vagrantfile are completely ignored with Hyper-V. Vagrant cannot enforce a static IP or automatically configure a NAT.
> 
> However, the IP address of the machine will be reported as part of the vagrant up, and you can use that IP address as if it were a host only network.


### Vagrantから新たなインスタンスを起動する
今回はAmazon Linux 2のVMを起動したいと思います。
Hyper-V用のAmazon Linux 2というニッチ需要に対応するものがあるかなと思ったら、作ってくれていた方がいました。今回はありがたくこれを利用します。

Vagrant box Yojimbo108/AmazonLinux2 - Vagrant Cloud
https://app.vagrantup.com/Yojimbo108/boxes/AmazonLinux2


Vagrantfileの設定はまずはシンプルに下記のようにします。ホスト名は「stdsv3」とします。

```:Vagrantfile
Vagrant.configure("2") do |config|
  config.vm.box = "Yojimbo108/AmazonLinux2"

  config.vm.provider "hyperv" do |v|
    v.memory = 1024
    v.cpus = 1
  end

  config.vm.define :"stdsv3" do |c1|
    c1.vm.hostname = "stdsv3"
  end

end
```

#### Administrator権限でGitBashを起動する
Vagrantfileができたら、vagrant upを実行していきます。

その前に、Hyper-VをProviderに利用する場合、vagrantコマンドの実行にはAdministrator権限が必要になります。

まず、先ほどのPowerShellと同様の手順で、GitBashをAdministrator権限で実行します。
Context Menuに追加する方法については下記記事にまとめましたので併せて参考にしてください。

[【Windows】GitBashをcontext menuからAdministrator権限付きで実行する - Qiita](https://qiita.com/tmiki/items/0dab4fd17e9bed04ed91)

#### vagrant upを実行する
先ほど作成したVagrantfileのあるフォルダで、Administrator権限でGitBashを実行し、vagrant upを実行します。
Administrator権限でないと実行できませんので注意してください。

実行中、以下の2つについて聞かれますので、適宜入力します。

- どのVirtual Switchを利用するか
- SMBによるファイル共有用のユーザ名・パスワード

2つ以上のVirtual Switchが存在する場合、vagrant up実行後、下記のようにどのVirtual Switchを利用するか聞かれます。名称はこれまでの手順で作成したものが表示されています。
ここでは「2」を指定します。

```:vagrant&nbsp;up
$ vagrant up
Bringing machine 'stdsv3' up with 'hyperv' provider...
==> stdsv3: Verifying Hyper-V is enabled...
==> stdsv3: Verifying Hyper-V is accessible...
==> stdsv3: Importing a Hyper-V instance
    stdsv3: Creating and registering the VM...
    stdsv3: Successfully imported VM
    stdsv3: Please choose a switch to attach to your Hyper-V instance.
    stdsv3: If none of these are appropriate, please open the Hyper-V manager
    stdsv3: to create a new virtual switch.
    stdsv3:
    stdsv3: 1) DockerNAT
    stdsv3: 2) Hyper-V Internal with WinNAT
    stdsv3:
    stdsv3: What switch would you like to use?
```

Virtual Switchを指定すると、処理を継続したのち、SMB用のユーザ名・パスワードを聞かれます。ProviderとしてHyper-Vを利用する場合、shared_folder用のプロトコルとしてSMBを利用するので、この接続用のアカウントとなります。
自分の作業用のアカウントで良いでしょう。

```:vagrant&nbsp;up(続き)
$ vagrant up
Bringing machine 'stdsv3' up with 'hyperv' provider...
==> stdsv3: Verifying Hyper-V is enabled...
==> stdsv3: Verifying Hyper-V is accessible...
==> stdsv3: Importing a Hyper-V instance
    stdsv3: Creating and registering the VM...
    stdsv3: Successfully imported VM
    stdsv3: Please choose a switch to attach to your Hyper-V instance.
    stdsv3: If none of these are appropriate, please open the Hyper-V manager
    stdsv3: to create a new virtual switch.
    stdsv3:
    stdsv3: 1) DockerNAT
    stdsv3: 2) Hyper-V Internal with WinNAT
    stdsv3:
    stdsv3: What switch would you like to use? 2
    stdsv3: Configuring the VM...
==> stdsv3: Starting the machine...
==> stdsv3: Waiting for the machine to report its IP address...
    stdsv3: Timeout: 120 seconds
    stdsv3: IP: fe80::215:5dff:fe14:2511
==> stdsv3: Waiting for machine to boot. This may take a few minutes...
    stdsv3: SSH address: fe80::215:5dff:fe14:2511:22
    stdsv3: SSH username: vagrant
    stdsv3: SSH auth method: private key
    stdsv3: Warning: Remote connection disconnect. Retrying...
    stdsv3:
    stdsv3: Vagrant insecure key detected. Vagrant will automatically replace
    stdsv3: this with a newly generated keypair for better security.
    stdsv3:
    stdsv3: Inserting generated public key within guest...
    stdsv3: Removing insecure key from the guest if it's present...
    stdsv3: Key inserted! Disconnecting and reconnecting using new SSH key...
==> stdsv3: Machine booted and ready!
==> stdsv3: Preparing SMB shared folders...
    stdsv3: You will be asked for the username and password to use for the SMB
    stdsv3: folders shortly. Please use the proper username/password of your
    stdsv3: account.
    stdsv3:
    stdsv3: Username: ****
    stdsv3: Password (will be hidden):
Error! Your console doesn't support hiding input. We'll ask for
input again below, but we WILL NOT be able to hide input. If this
is a problem for you, ctrl-C to exit and fix your stdin.
     stdsv3: Password (will be hidden): ********

Vagrant requires administrator access to create SMB shares and
may request access to complete setup of configured shares.
==> stdsv3: Setting hostname...
The following SSH command responded with a non-zero exit status.
Vagrant assumes that this means the command failed!

service network restart

Stdout from the command:

Restarting network (via systemctl):  [FAILED]


Stderr from the command:

Job for network.service failed because the control process exited with error code. See "systemctl status network.service" and "journalctl -xe" for details.
```

最終的に、Amazon Linux 2内のnetwork設定でFAILEDとなります。おそらく、「Hyper-V Internal with WinNAT」ネットワーク内でDHCPサーバが稼働していないため、IPv4の設定に失敗したのでしょう。

ちなみに、IPv4の設定は失敗していますが、IPv6の設定は行われているので、「vagrant ssh」することが可能です。（IPv6経由での接続になります。）
「vagrant ssh」でVMにログインしたのち、手動でIPv4の設定を行い、ネットワークの疎通を確認していきます。

#### VM内のネットワーク設定を手動でおこなう
まずは、今回作成したVMにログインします。vagrant sshを実行します。

```:vagrant&nbsp;ssh
$ vagrant ssh
Last login: Fri Mar  1 15:21:31 2019

   __|  __|_  )
   _|  (     /   Amazon Linux 2 AMI
  ___|\___|___|

https://aws.amazon.com/amazon-linux-2/
```

ifconfigコマンドを実行し、現在のネットワーク設定を見てみます。IPv6の設定はあるものの、IPv4の設定がありません。

```:ifconfig
[vagrant@stdsv3 ~]$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
    inet6 fe80::215:5dff:fe14:2511  prefixlen 64  scopeid 0x20<link>
    ether 00:15:5d:14:25:11  txqueuelen 1000  (Ethernet)
    RX packets 5142  bytes 1149687 (1.0 MiB)
    RX errors 0  dropped 0  overruns 0  frame 0
    TX packets 474  bytes 92644 (90.4 KiB)
    TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
    inet 127.0.0.1  netmask 255.0.0.0
    inet6 ::1  prefixlen 128  scopeid 0x10<host>
    loop  txqueuelen 1000  (Local Loopback)
    RX packets 0  bytes 0 (0.0 B)
    RX errors 0  dropped 0  overruns 0  frame 0
    TX packets 0  bytes 0 (0.0 B)
    TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
```

手動でIPアドレスを設定します。今回は「192.168.254.13/23」を設定します。設定後、ifconfigコマンドを実行し、意図したとおりにIPv4アドレスが付与されたことを確認します。

```:ip&nbsp;address&nbsp;add
[vagrant@stdsv3 ~]$ sudo ip address add 192.168.254.13/23 dev eth0

[vagrant@stdsv3 ~]$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.254.13  netmask 255.255.254.0  broadcast 0.0.0.0
        inet6 fe80::215:5dff:fe14:2511  prefixlen 64  scopeid 0x20<link>
        ether 00:15:5d:14:25:11  txqueuelen 1000  (Ethernet)
        RX packets 5395  bytes 1185026 (1.1 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 617  bytes 110178 (107.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
```

「Hyper-V Internal with WinNAT」ネットワーク用のDefault Gatewayである「192.168.254.1」との通信ができるかどうか確認します。pingの応答はないものの、ARPの解決ができているので、L2レベルで通信できることは確認できました。
おそらく、当該ネットワークがPublic Networkになっているため、ホストOS側がICMPパケットを遮断しているものと推測できます。

```:ping
[vagrant@stdsv3 ~]$ ping 192.168.254.1
PING 192.168.254.1 (192.168.254.1) 56(84) bytes of data.
^C
--- 192.168.254.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1027ms

[vagrant@stdsv3 ~]$ arp -a
? (192.168.254.1) at 00:15:5d:14:25:10 [ether] on eth0
```

「192.168.254.1」をデフォルトゲートウェイとして追加します。

```:route&nbsp;add
[vagrant@stdsv3 ~]$ sudo route add default gw 192.168.254.1

[vagrant@stdsv3 ~]$ netstat -arn
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.254.1   0.0.0.0         UG        0 0          0 eth0
192.168.254.0   0.0.0.0         255.255.254.0   U         0 0          0 eth0
```

Internet上の適当なIPアドレス（今回は8.8.8.8を利用）にpingを実行します。対象のIPアドレスからping応答が返ってくることが確認できました。

```:ping
[vagrant@stdsv3 ~]$ 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=118 time=14.8 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=118 time=11.8 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 11.853/13.340/14.827/1.487 ms
```

最後に、Docker Desktop for Windowsから実行したcontainerのPublishしているポートに対して接続できるかどうか確認します。

```
[root@stdsv3 ~]# curl http://10.1.1.34:8080/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```


以上で、VM内からの疎通確認は完了です。
Internetに対して通信できること、Docker Desktop for Windowsのcontainerに接続できることが確認できました。

VM内の設定を毎回手動でおこなうわけにもいきませんので、これを自動的に設定する方法を考えます。大きく以下の2通りの方法で実現することとなります。

- VagrantによるVMインスタンス構築時、IPv4設定を併せて行う
- ホストOS側でDHCPサーバを稼働させ、「Hyper-V Internal with WinNAT」ネットワークに所属するゲストOSがDHCPでIPv4設定できるようにする


## Hyper-V通常VM用のネットワーク設定を自動化する
※続きます

[【Windows】Docker Desktop for WindowsとVagrantの便利な環境を共存させる #3/3【Hyper-V】](https://qiita.com/tmiki/items/1d5edb4e482f0f38bfd2)