Running NAV docker containers on a local Hyper-V Virtual Machine

2 Dec

There are already a lot of blog posts about running Dynamics NAV on Docker and how to deal with it. Here are some sites you should definitely start with:

https://blogs.msdn.microsoft.com/freddyk/tag/docker/
This is the best source to get up to date on the current status of NAV on Docker images.

https://www.axians-infoma.de/navblog/
For in-depth tips and tricks how to deal with Docker in general and NAV on Docker in particular.

https://github.com/Microsoft/nav-docker/
The official Microsoft repository for Dynamics NAV on Docker resources.

After playing around a lot with the NAV docker containers on Azure VM’s and local Hyper-V machines, I decided that it’s about time to get to a stable environment. Of course I could go with an Azure VM and run everything in there. Which is not the cheapest option and you don’t have access when you’re offline.

Running containers locally on Windows 10 is an option and I have certainly tried that. However, I ran into all kind of network issues and it didn’t work out really well. So I figured that running containers on a local Hyper-V VM would be the best option.

Then you have the choice between running everything inside the VM, including a desktop with Visual Studio Code, or to only run NAV containers inside the VM and connect to it from the desktop of the host machine, my Windows 10 laptop.

The easiest option is to run an Hyper-V VM with Windows Servier 2016 and desktop enabled so you can install VS Code and run it all on the VM However, it leaves me with two desktops: the desktop on my Windows 10 laptop and the one inside the VM. Maybe that is not bad after all, but I like to do everything from just one desktop, where I have access to all my files.

So the VM should just be server, just like any other server on Azure or at one of my customers. I have Visual Studio Code on my local machine and connect from my desktop directly to a NAV server, no matter if that NAV server is inside a container on a local VM, on an Azure VM or maybe directly installed on a server somewhere in the network.

The challenge was how to set this up in such a way that it works independently from the location I’m working. It should work while I’m at home, at a customer, in a hotel, at a conference, during a workshop or when I’m just offline.

The only way to achieve this is to make the VM independent of any network. It should have it’s own network settings. All containers inside the VM should get an ip-address that is in the same network subnet. This blog post is about setting up this scenario.

Here are the steps I followd to set up this scenario:

  1. Create a Hyper-V virtual network switch
  2. Create and configure a Hyper-V VM
  3. Install Docker on the VM
  4. Create a Docker transparent network in the VM
  5. Create and run the Docker container

Tip: In case you are not interested in the networking stuff, but you are looking for setting up a clean Windows Server 2016 with Desktop Experience and Docker, then skip to step 3.

1. Create a Hyper-V virtual network switch

It is a normal setup to attach a Hyper-V VM to the external network of the host. E.g. the same network as the LAN of Wifi of my laptop. As a result, de VM gets its own ip address when it starts. Which means that the ip-address can vary when you work on different locations.

To be able to give the Hyper-V VM a fixed ip address that is accessible by the host, you need to create an internal Hyper-V virtual network switch and establish a NAT network between.

I followed the steps as explained here and here. So in PowerShell on my Windows 10 desktop with Hyper-V installed I executed these lines:

New-VMSwitch -SwitchName "InternalSwitch" -SwitchType Internal
New-NetIPAddress -IPAddress 172.21.31.1 -PrefixLength 24 -InterfaceAlias "vEthernet (InternalSwitch)"
New-NetNat -Name MyInternalnetwork -InternalIPInterfaceAddressPrefix 172.21.31.0/24

As a result, I have an internal network, only visible for my laptop, in the ip-range 172.21.31.0 and bound to the new VM switch ‘InternalSwitch’. Only when I come across a network that uses the exactly the same subnet, I might have a problem. That’s something to keep in mind! If you follow these steps, you are free to use the same ip address range or pick another one as long as it is in one of these ranges:

  • 10.0.0.0  – 10.255.255.255
  • 172.16.0.0 – 172.31.255.255
  • 192.168.0.0 – 192.168.255.255

2. Create and configure a Hyper-V VM

I’m not going to explain how to create a Hyper-V VM with Windows Server 2016 Core installation on it. What is important is to connect that VM to the internal network. So in the settings of my VM (which I named “Deep Thought”) change the Network Adapter to the InternalSwitch that was created with PowerShell.

HyperV-VM-Settings

Then, inside the Hyper-V, change the network settings. Because the internal network does not have a DHCP server nor a DNS server, you need to set a fixed ip address and DNS servers. This is a one-time setting. Without DNS servers, the VM will not have access to the internet.

Connect to the VM using Hyper-V console. At the command prompt type in sconfig.

sconfig.png

This will give the Windows Server 2016 server configuration menu.

sconfig2.png

Choose 5 to open the Network Settings menu. and then choose the index number of the Microsoft Hyper-V Network Adapter.

sconfig3.png

The 169.254.x.x address is automatically assigned when no DHCP server could be found. Now choose 1 to set the network address to 172.21.31.2 and choose 2 to set the DNS servers to 8.8.8.8 and 8.8.4.4 (the DNS servers of Google).

sconfig4.png

After this, you should have internet access from the Hyper-V. Test it with a ping command to any site.

The VM network adapter is now configured for one ip address. Because every individual container inside the VM will get its own ip address, there will be multiple ip addresses going through the same VM network adapter. This is only possible when the VM network adapter is configured for MAC address spoofing. To enable this run the following command on your host. The vm name must be the name that is visible in your Hyper-V management console.

Get-VMNetworkAdapter -VMName "<vm name>" | Set-VMNetworkAdapter -MacAddressSpoofing on

3. Install Docker on the VM

This part is independent of all the network stuff and also works when you want to set up a clean Windows Server 2016 with Desktop Experience and run docker containers inside it.

First step is to make sure that the Windows Server is up to date. From the server config menu choose option 6 Download and Install Updates.

windowsserverupdates.png

When this is ready, start powershell and install the windows feature Containers with the PowerShell command

Install-WindowsFeature containers

Reboot the machine after the installation.

install-containers-feature.png

After the reboot log in again and open powershell. Now you must install the PowerShell module for Docker. This will install the docker service. Don’t forget to reboot after the installation.

Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name docker -ProviderName DockerMsftProvider
Restart-Computer -Force

 

install-docker-provider.png

A great post of these steps can also be found here.

From now on, you can use docker commands to pull images and run containers. I have posted earlier about this. But I definitely recommend to go with the PowerShell module navcontainerhelper, created by Freddy Kristiansen. Read his blog post about this and follow it on GitHub.

To install the navcontainerhelper, use the PowerShell command

Find-Module navcontainerhelper | Install-Module

 

install-navcontainerhelper.png

Tip: Skip to 5. Create and run a Docker container if you don’t want to use all this network stuff.

4. Create a Docker transparent network in the VM

Docker has several network options. By default, a NAT network is installed. New containers are connected to this NAT network and will not be visible from outside. To access the container from outside the VM, you need to map port numbers on the external network interface of the VM.

To avoid configurations like port 7049 is mapped to navserver1, port 7149 is mapped to navserver2, etc., I decided to install a docker transparent network. With a transparent network, the containers are directly connected to the external network of the VM. By providing an ip address to each container it becomes possible to access them directly from outside the VM by using their ip address. That is easier to work with, also because the ip addresses can be saved in the hosts file so I can even access the containers by name.

To create a docker transparent network we first need to delete the automatically created NAT network and tell docker to not create it anymore. In PowerShell we execute these lines:

Stop-Service Docker
Get-ContainerNetwork -Name nat | Remove-ContainerNetwork -Force

 

docker-remove-networks.png

Next step is to tell docker to not create the NAT network anymore. This is a setting in the daemon.json file. In a fresh installation, this file will not exist. So we are safe to run these commands in PowerShell:

'{"bridge":"none"}' | Set-Content C:\ProgramData\docker\config\daemon.json
Start-Service Docker
Get-ContainerNetwork

 

docker-create-daemon-json

The Get-ContainerNetwork now shows the only available network, the Hyper-V internal network.

Next step is to create the transparent network. I tried to use the docker network create command, but for some reason the created network did not what I expected. But the PowerShell cmdlet New-ContainerNetwork did the job.

New-ContainerNetwork -Name tlan -SubnetPrefix 172.21.31.0/24 -GatewayAddress 172.21.31.1 -Mode Transparent -DNSServers 8.8.8.8,8.8.4.4

 

docker-create-transparent-network.png

As you can see, the parameters use the same ip range as the Hyper-V internal network. The gateway is the ip address of the NAT network on the host machine. You may need to restart the docker service after the network has been created (Restart-Service docker).

The name tlan that is specificied in the parameters is important, because we use that name when we create the docker containers.

5. Create and run the Docker container

We are ready to create a NAV docker container. That can be done with the command New-NavContainer or New-CSideDevContainer. These commands were installed with the module navcontainerhelper that we installed earlier. For more info about these commands, use the Get-Help and read this blog post by Freddy.

New-NavContainer -accept_eula -containerName navserver -imageName microsoft/dynamics-nav -alwayspull -additionalParameters @("--network=tlan","--ip 172.21.31.3")

 

new-navcontainer1.png

The parameter additionalParameters contains these parameters:

  • –network=tlan
  • –ip 172.21.31.3

With these parameters, the docker engine will create a container that is attached to our internal network. As a result, the container can be connected to directly from the Windows 10 desktop, outside the VM. When I add this ip address to the hosts file, I can even reach the container by its name.

hosts-file.png

Tip: when you were looking to set up a clean Windows Server 2016 with Desktop Experience environment without all the network stuff, then you should just remove additionalParameters from the New-NavContainer command. The server will be available from the VM desktop after creation.

Final result

All these steps lead to this situation:

  • Hyper-V VM with Windows Server 2016 Core
  • Fixed ip address, indepent from my location
  • Containers have their individual ip address and are direct accessible from the Windows 10 desktop, no port mapping needed

This screenshot is from VS Code, sitting on my Windows 10 desktop, connecting directly to the docker container inside the Hyper-V VM (the new developer endpoint is automatically enabled on docker containers, no need to configure it):

symbols-downloaded.png

The vsix file can be downloaded:

container-files.png

And what’s more, with PowerShell I can directly tap into the VM and even into the container from my desktop. To enable this, you need first to run the PowerShell cmdlet Enable-PSRemoting on the VM.

powershell-vm-container.png

Wait, I’m not done yet. What about C/SIDE and exported objects? Well, now that the Hyper-V VM has an ip address, it’s simple to create a network drive mapping to the C: drive of the VM and access all container data.

vm-network-drive1.png

Every server has its own folder with C/SIDE in it:

vm-network-drive2.png

Just run it from there and open the database, directly from the desktop! Use sa as your user id and the password that you specified when you created the container.

cside-container2.png

cside-container.png

I hope you enjoyed this and got some inspiration how to set up a local development environment that it totally indepent of your actual network and transparent to your own desktop.

Ok, one final remark. Just 31 seconds to spin up a new NAV container, how about that?

container-logs.png

4 thoughts on “Running NAV docker containers on a local Hyper-V Virtual Machine

  1. Pingback: Running NAV docker containers on a local Hyper-V Virtual Machine - Kauffmann @ Dynamics NAV - Dynamics NAV Users - DUG

  2. Thanks for this detailed guide! After following all steps till the creation of a New-NavContainer, the VM / docker configuration part seems ok, although I cannot connect/ping to the container/IP from my host computer. Any hints?

    In addition, which approach would you recommend to ‘share’ docker containers accross colleagues (avoiding the Azure costs)? I cannot imagine having to go through this setup for a group of developers and consultants needing immediate / easy access to ‘an’ environment. (Whether it is just to test a specific CU / Cronus, play around in the latest NAV 2018 or connect to any of the dozens of customer development databases …)

    • Some steps you could do to check the setup:

      1. Check the VM network adapter: Get-VMNetworkAdapter -VMName”
      This should return the created internal switch. Check the status and the ip-addresses. It should show the ip-address assigned to the VM.

      2. Ping the VM Switch network adapter. With this you check if the VM Switch is visible and can be reached.

      3. Check the Remote Management settings on the VM. Use sconfig and then 4. Configure Remote Management. Then choose 3) to set server response to ping. You also may want to set remote management so you can manage the server using Server Manager from a remote computer.

      Sharing docker containers with colleagues means you have to set up a server in your network. With the standard docker nat network you have to set up port mapping. With the docker transparant network the containers will get an ip address from the network (assuming you have DHCP enabled on the network). In that case you don’t have to specify a fixed ip address when creating the container. The only caveat is that creating multiple containers can quickly fill up the DHCP and DNS tables, so this should be done with some policy.

  3. A typo?
    Should it be “option 8” in the below paragraph?
    “Choose 5 to open the Network Settings menu. and then choose the index number of the Microsoft Hyper-V Network Adapter”

Leave a Reply

Your email address will not be published.