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 8 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

Now it might happen that you get an error message when you try to do Enter-PSSession. If  that error message read as follows, then there is a simple solution

Enter-PSSession : Connecting to remote server navserver failed with the following error message : The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting.

The reason for this message is that you are trying to connect to a server that is outside your domain. You could try to set up a connection over HTTPS, but that requires certificates and is more complex. Instead, follow the other suggestion from the message: add the VM to the TrustedHosts configuration setting on your local machine.

How to do that? Start with finding out the current setting of the TrustedHosts. The PowerShell command for that is:

Get-Item WSMan:\localhost\Client\TrustedHosts

In most cases, the value will be empty. Now simply add the name of your VM to the list.  The PowerShell command for that is:

Set-Item WSMan:\localhost\Client\TrustedHosts -value <computername>

PowerShell must be run in Administrative mode for this command!

You can also use the -value * to add all computernames. A detailed explanation of all options can be found here.

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

28 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”

  4. Hi

    Can you then link these docker NAV containers up to an external SQL database, such as one on your laptop or a networked server elsewhere on the network?

    Cheers

    Chris

  5. Pingback: CRS.RemoteNAVDockerHostHelper – my new PowerShell module - Microsoft Dynamics NAV Community

  6. Pingback: CRS.RemoteNAVDockerHostHelper – my new PowerShell module - Waldo's Blog - Microsoft Dynamics NAV - Dynamics NAV Users - DUG

  7. Also remember to install portainer in order to keep track of your images and containers. Its not that hard if you do it right 🙂

    1) make a folder C:\ProgramData\Containers\Portainer
    2) do a docker pull portainer/portainer
    3) open a firewall port to 2375 with
    netsh advfirewall firewall add rule name=”Docker” dir=in action=allow protocol=TCP localport=2375 enable=yes profile=domain,private,public
    4) run docker with:
    docker run -d –restart always –network=tlan –ip 172.21.31.3 –name portainer -h portainer -v C:\ProgramData\Containers\Portainer:C:\Data portainer/portainer
    5) edit the file daemon.json in the folder
    c:\programdata\docker\config
    Make sure you ONLY have a line saying:

    {“hosts”: [“tcp://0.0.0.0:2375″,”npipe://”]}

    (delete the line {“bridge”:”none”} )

    6) start powershell
    7) type Restart-Service *docker*

    8) Go to your main OS (outside Hyper-V) and start a browser and type:
    Portainer:9000
    9) add an user/password and create an endpoint called
    docker with the address 172.21.31.2:2375

    10) Have fun with your docker images and portainer 🙂

    Palle(at-sign)NAVspecialist DOT dk

    • Thanks Palle, for the info. I’m using Portainer as well in (almost) this exact way.

      Two remarks:
      Do *NOT* remove the line with “bridge”:”none” –> in my post I let you add this line to avoid the creation of a NAT network. Just add the hosts line.
      At step 8: don’t forget to add it to your hosts file.

      I will write a new blog post as a follow-up. 😉

      • I tried with the Bridge line and then the endpoint connection didn’t work. So I had to remove it.

        I also had some issues with

        Enter-PSSession I had to to in my mail os do a

        # Client RUN
        Enable-PSRemoting
        Set-Item WSMan:\localhost\Client\TrustedHosts -Value Odin -Force
        Restart-Service WinRM

        where Odin is the name of my Hyper-V machine.

        And regarding the hosts file.. I didn’t have to do that.

        I have started containers with the parameters as

        -additionalParameters @(“–network=tlan”,”–ip 172.21.31.3″) #NAV 2017
        -additionalParameters @(“–network=tlan”,”–ip 172.21.31.4″) #NAV 2018
        -additionalParameters @(“–network=tlan”,”–ip 172.21.31.5″) #NAV “365”

        And I can easily do a ping nav2017, ping nav2018 or ping nav365 because i do a

        –Name NAV2018 -h nav2018 when i spin up the image/container.

        • One more important detail when starting hyper-V. Did you also have to deactivate dynamic Memory?? I could not spin up the NAV docker images as hyper-v only used 950-1150 mb. I deactivated Dynamics Memory and set it to 8000 out of 16gig of memory on my machine.

          I have not digged into it further if the memory setting should be higher or lower. Currently I am running 3 nav images + portainer + an IIS

          • I have dynamic memory enabled on the Hyper-V VM without problems. The VM starts with 4GB by default, but can increase up to the max if needed. Most time sit ends up with about 12GB of memory (out of 32GB ;-))

        • Trying to understand your setup. I have the feeling you ended up with a different environment.
          Your local machine that runs the Hyper-V VM must know the container address, otherwise it will not be able to ping them. How would your local machine get these ip-addresses, other than a DNS or the hosts file? I’m always using -name and -h parameters (well, the NAV Container Helper does that for me), but then only the Hyper-V machine knows the ip-address, not the host (the local machine). You see why I’m confused about your setup?

          The Bridge line was removed because Docker should not create a NAT network on the Hyper-V VM, it should create a transparent lan instead.

          It’s correct that you need to enable PS Remoting on the Hyper-V VM. But… the Enable-PSRemoting should run on the Hyper-V VM and adding the server to the trusted hosts (the Set-Item command) should run on the host computer (your local machine).

  8. Thank you so much for the great article. This helped me setup my own docker environment in Hyper-V.
    I wanted to leave one comment about using Enter-PSSession. After setting things all that I could, I was still having issue with using Enter-PSSession, but I just found out that if I used the parameter -VMName everything worked fine! Below is a sample of what I used.
    EnterPSSession -VMName “”

  9. Pingback: CRS.RemoteNAVDockerHostHelper – Updated! - Microsoft Dynamics NAV Community

  10. Pingback: CRS.RemoteNAVDockerHostHelper – Updated! - Waldo's Blog - Microsoft Dynamics NAV - Dynamics NAV Users - DUG

  11. Thank you for such a detailed post on this topic.
    I am facing issue @ install-windowsfeature container, getting below error.

    “The role, role service, or feature name is not valid: ‘containers’. The name was not found.”

    I have checked by get-windowsfeature, and i could not find containers.
    server version: Microsoft Hyper-V Server 2016, build 10.0.14393
    is there any limitation that containers are not available to this version?

    • Microsoft Hyper-V Server 2016 only contains the hypervisor, no other Windows Server features.

      You should install Microsoft Server 2016 Standard on the VM. I have chosen to go with the core installation, but the desktop experience should also work fine.

      • Thanks ajk.

        since hyper-v server 2016 comes free and only with core, gave it a shot. will proceed with standard win server 2016 then.

  12. Hi Arend-Jan,

    First of all: Great post!

    But for some reason I can’t get it to run on the tlan.

    Executed:
    New-NavContainer -accept_eula -alwaysPull -imageName “microsoft/bcsandbox:12.0.21229.0” -containerName bc365 -additionalParameters @(“–network=tlan”,”–ip 172.21.31.3″) -Auth NavUserPassword

    But errors out with:
    Creating Nav container bc365
    Using image microsoft/bcsandbox:12.0.21229.0
    NAV Version: 12.0.21229.0-W1
    Generic Tag: 0.0.5.6
    Removing container bc365
    Removing C:\ProgramData\NavContainerHelper\Extensions\bc365
    Creating container bc365 from image microsoft/bcsandbox:12.0.21229.0
    46e804e38f60550b0425a73326265a040d1c6787350f8a5aa3ab77e323e0e434

    docker.exe: Error response from daemon: network tlan not found.

    I’ve followed all the steps and also when I execute Get-ContainerNetwork, I see:
    Name Id Subnets Mode SourceMac DNSServers DNSSuffix
    —- — ——- —- ——— ———- ———
    tlan dce6d846-14d7-4921-a043-1d4e03b5d431 {172.21.31.0/24} Transparent {8.8.8.8, 8.8.4.4}

    Do you have any idea why it can’t find the tlan network?

    Thanks,
    Peter

  13. Pingback: Export C/AL objects as AL from a database on a docker container on a remote host - Waldo's Blog - Microsoft Dynamics NAV - Dynamics 365 Business Central/NAV User Group - Dynamics User Group

  14. Pingback: How to install Docker on Windows 10 without Hyper-V - Kauffmann @ Dynamics NAV - Dynamics 365 Business Central/NAV User Group - Dynamics User Group

  15. Hi Arend-Jan,

    Thank you so much for the article.

    I’m trying to use the PS command Remove-ContainerNetwork to remove the docker nat network but this command is not available in Windows Server 2019. And in general the Docker-PS module has been deprecated.

    The native docker command: “docker network rm nat” returns an error:
    Error response from daemon: nat is a pre-defined network and cannot be removed.

    Would you be so kind to share with us how to solve this problem?

    Thanks.
    Emilia

    • I’m afraid I don’t have a solution for you. Recently I switched to running Docker on Windows 10 and I have completely uninstalled the Hyper-V setup.

      • Thank you for your quick response. How is your experience so far running Docker on windows 10? Any network issue as stated in the above article? Perhaps it is a good topic for a new blog article? It would be great to have you sharing some of your experiences on that topic.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.