Setup a BC container and use Traefik to access it
Hello there 👋
Have you ever wondered how to access your colleague’s local container, so that you can test a new feature right away? Instead of making your local container available we should follow a different approach. Let’s not think of local machines, but rather the cloud or actually temporary resources. Ultimatly, we want to set up a container, deploy our code, provide access to it, test the new feature, then stop and remove the container.
Fair enough… we are going to create a Traefik (https://traefik.io/) container and a Dynamics 365 Business Central container with the help of the BcContainerHelper and some additional docker labels. Our Traefik container than listens on the Docker Deamon to know excactly where to forward the HTTP requests to.
Introduction
As a brief introduction, there are multiple ways to make the container accessible. Before we dive into the Getting started section, we want to make sure that we have a rough overview of what the alternatives to Traefik are.
Transparent Network
Define a transparent network in Docker that you can choose when creating your container. This will handle the container as a physical client in your local network, which comes with some security concerns. As you can already tell, it’s usually not allowed (and shouldn’t be) in your companys network.
The good thing about this is, that each container has its own IP address, making it accessible in your local network without having to spend too much time on managing port mappings.
These are the two steps to create a container in a transparent network.
- Create the transparent network
docker network create --driver=transparent transparentnetwork
- Add the network to the additional parameters (New-BcContainer)
-additionalParameters "--network=transparentnetwork"
The additional parameters allow us to provide additional docker commands.
Port Mapping
The Port Mapping from the container to the docker host is a common thing. This might very well fit your needs if you are just working with one docker container.
The basic syntax is as follows: -p HOST-PORT:CONTAINER-PORT
So you might define your additional parameters as follows: -additionalParameters "-p 3080:80"
.
Now the container port 80 is mapped to the host port 3080, which allows us to access the BC container web client at http://cbserver:3080
.
However, if you were to manage multiple docker containers on one machine, it becomes more and more difficult to keep track of all the used ports. Especially when we are dynamically creating containers, we don’t want to worry about that too much and instead just run the container and access it.
Reverse Proxy
For me, this is the easiest way (once you figured it all out). I recommend this way as it’s easy to setup and forwards all the HTTP requests to the specific container by the rule you defined. You could also provide a signed SSL certificate and provide access to your container from outside your network.
Getting started
For the scope of this blog post, I’d like to keep it simple and just focus on our need to provide access to docker containers within our local network from otter clients. This allows us to develop on one machine and automatically deploy our changes to a docker container on our server.
Requirements
- Docker
- BcContainerHelper
I used Docker Desktop to install Docker on my server. As things turns out, Traefik doesn’t really support it1, so you would be better off to just install the Docker (standalone) Engine.
Setup
We begin with the creation of a Traefik container.
Traefik container
As Business Central is still running on a windows-based OS, we have to get a Traefik installation based on windows, so that it can than pick-up the correct Docker Deamon that will listen to all the otter windows-based containers, including our Business Central container which we want to expose in our local network.
We actually don’t have to create an image ourselfs, as Tobias Fenster2 has already done that (see DockerHub: tobiasfenster/traefik-for-windows)34.
docker run -d -p 80:80 -p 443:443 -p 8080:8080 -v C:\traefikforwindows\config:c:\etc\traefik -v \\.\pipe\docker_engine_windows:\\.\pipe\docker_engine_windows tobiasfenster/traefik-for-windows:v2.9.6
Depending on your Docker Deamon you might need to change the docker_engine_windows to something else, e.g. docker_engine. You can find the name of your Docker Deamon in the “%USERPROFILE%.docker\daemon.json” file.
BC container
One thing we should keep in mind right now, is the fact that we leverage the Traefik support for listening on Docker Labels (see: https://doc.traefik.io/traefik/providers/docker/). There are also many other so called providers you can use. I definitely recommend you to read the Traefik documentation5.
The following script is a minimal version to give you an impression on how it can be done:
$publicDnsName = 'cbserver'
$containerName = 'bcserver'
$Credential = New-Object pscredential 'admin', (ConvertTo-SecureString -String 'P@ssword1' -AsPlainText -Force)
$additionalParameters = @(
"-l `"traefik.enable=true`"",
"-l `"traefik.http.routers.$($containerName)-webclient.service=$($containerName)-webclient@docker`"",
"-l `"traefik.http.services.$($containerName)-webclient.loadBalancer.server.port=443`"",
"-l `"traefik.http.services.$($containerName)-webclient.loadBalancer.server.scheme=https`"",
"-l `"traefik.http.routers.$($containerName)-webclient.rule=PathPrefix(``/$($containerName)``)`"",
"-l `"traefik.http.routers.$($containerName)-webclient.entrypoints=websecure`"",
"-l `"traefik.http.routers.$($containerName)-webclient.tls=true`""
)
New-BcContainer `
-accept_eula `
-auth NavUserPassword `
-artifactUrl (Get-BCArtifactUrl -type OnPrem -country 'us' -version '21') `
-containerName $containerName `
-Credential $Credential `
-isolation hyperv `
-shortcuts None `
-updateHosts `
-additionalParameters $additionalParameters `
-multitenant:$false `
-useSSL `
-PublicDnsName $publicDnsName `
-installCertificateOnHost
Remember that we ultimately want to define rules for all the other ports as well, such as OData, SOAP, devEndpoint, etc. In order for you to access all the other Business Central components6 you need to add additional docker labels to that container. This is neccessary to connect to the different ports. Maybe I’ll provide a full script for that later…
Access your BC container from another client
At this point we can access the container from a different host machine by sending the HTTP request to the previously defined path prefix rule /bcserver. As my server is named cbserver (..apologies for the confusing names at this point..) the full path I’d send the request to is: https://cbserver/bcserver
You can already see that Traefik, in combination with a CI/CD pipeline that deploys your code at a certain stage, can be a powerful ally that allows you to provide access to these newly created containers. These can be named after the ID of the Pull Request in combination with the shortcut name of your extension app for example.
You can think of it like GitHub Codespaces, which basically allows you to check out a branch into a newly created container and continue developing based on that container and also access the web server. I’ll follow up on that with a blog post soon.
Conclusion
It’s very easy to make your containers accessible in your internal network so that otter clients can access the web-client and start testing the features. With the reverse proxy its also possible to make your containers available outside of your own network for example to demo it to your customers.
Make sure to watch the following presentation of Tobias Fenster2 on NAV TechDays 2019. He also explains the usage of Container Orchestration in his scenario, which is taking it one step further.
Hope you learned something.
See you around 🦦
-
https://github.com/microsoft/navcontainerhelper/issues/2839 ↩
-
https://hub.docker.com/r/tobiasfenster/traefik-for-windows ↩
-
https://tobiasfenster.io/running-traefik-in-a-container-on-a-modern-windows-server-version ↩
-
https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/deployment/product-and-architecture-overview ↩