I recently did a blog post about how to get an ASP.NET 5 application to run in a Windows Server container using Docker. However, I kept thinking about that solution, and started wondering if I could add IIS Application Request Routing to the mix as well. What if I could have containers at different ports, and have IIS and ARR routing incoming requests to different ports based on the host for example. And apparently I could. So I decided to write another post about how I got it going.
Disclaimer: There is still some kinks to work out regarding the routing. Right now, I have to manually change the routing to point to the correct container IP every time it is started, as I don’t seem to find a way to assign my containers static IP addresses…
Disclaimer 2: I have no clue about how this is supposed to be done, but this seems to work…
Adding a domain name to my server (kind of)
The first thing I needed to do, was to solve how to get a custom domain name to resolve to my server, which in this case was running in Azure. The easiest way to solve this is by going to the Azure portal and looking at my machines Virtual IP address, and add it to my hosts file with some random host.
This is a volatile address that changes on reboots, but it works for testing it out. However, it would obviously be much better to get a reserved IP, and maybe connect a proper domain name to it, but that was too much of a hassle…
Next, I opened my hosts file and added a couple of domain names connected to my machine’s IP address. In my case, I just added the below
40.127.129.213 site1.azuredemo.com
40.127.129.213 site2.azuredemo.com
Installing and configuring IIS and ARR 3.0
The next step was to install IIS on the machine. Using nothing but a command line. Luckily, that was a lot easier than I expected… All you have to do is run
C:\>Install-WindowsFeature Web-Server
and you are done. And yes, you can probably configure a whole heap of things to install and so on, but this works…
With IIS installed, it was time to add Application Request Routing (ARR) version 3.0, and even if there are other ways to do this, I decided to use the Web Platform Installer. So I downloaded the installer for that to my machine using Powershell and the following command
PS C:\>wget -uri "http://download.microsoft.com/download/C/F/F/CFF3A0B8-99D4-41A2-AE1A-496C08BEB904/WebPlatformInstaller_amd64_en-US.msi" -outfile "c:\installers\WebPI.msi"
and then followed that up by running the installer
C:\installers\platforminstallercmd.msi
With the Web Platform Installer installed, I got access to a tool called WebPICMD.exe. It is available under “%programfiles%\microsoft\web platform installer”, and can be used to browse the available things that the installer can install etc. It can also be used to install them, which is what I needed. So I ran
C:\>“%programfiles%\microsoft\web platform installer\WebPICMD.exe” /Install /Products:ARRv3_0
which installs ARR v.3.0 and all the prerequisites.
After the ARR bits had been installed, it was time to configure IIS to use it… The first step is to run
C:\>“%windir%\system32\inetsrv\appcmd.exe” set apppool "DefaultAppPool" -processModel.idleTimeout:"00:00:00" /commit:apphost
This sets the idle timeout for the default app pool to 0, which basically means “never timeout and release your resources”. This is pretty good for an application that has as its responsibility to make sure that all requests are routed to the correct place…
Next, it was time to enable the ARR reverse proxy using the following command
C:\>“%windir%\system32\inetsrv\appcmd.exe” set config -section:system.webServer/proxy /enabled:"True" /commit:apphost
With the IIS configured and up and running with the ARR bits, it was just a matter of opening up a port in the firewall so that I could reach it. So I did that using Powershell
PS C:\>New-NetFirewallRule -Name "TCP80" -DisplayName "HTTP on TCP/80" -Protocol tcp -LocalPort 80 -Action Allow -Enabled True
By now, the IIS is up and running and good to go, with a port opened for the traffic. The next step is to set up the websites for the containers that will sit behind the ARR bits.
Creating a couple of ASP.NET web applications
The websites I decided to create were ridiculously simple. I just created 2 empty ASP.NET 5 web applications, and modified their default OWIN middleware to return 2 different messages so that I could tell them apart. I also changed them to use Kestrel as the server, and put them on 2 different ports, 8081 and 8082. I then published them to my local machine, zipped them up and uploaded them to my server and added a simple dockerfile. All of this is covered in the previous post, so I won’t say anything more about that part. However, just for the sake of it, the dockerfile looks like this
FROM windowsservercore
ADD app /app
WORKDIR /app
CMD kestrel.cmd
With the applications unzipped and ready on the server, I used Docker to create 2 container images for my applications
C:\build\kestrel8081>docker build -t kestrel8081 .
C:\build\kestrel8082>docker build -t kestrel8082 .
and with my new images created, I created and started 2 containers to host the applications using Docker. However, I did not map through any port, as this will be handled by the ARR bits.
C:\>docker run --name kestrel8081 -d kestrel8081
With the containers up and running, there is unfortunately still a firewall in the way for those ports. So, just as in my last blog post, I just opened up the firewall completely for anything on the network that the containers were running on, using the following Powershell command
PS C:\>New-NetFirewallRule -Name "TCP/Containers" -DisplayName "TCP for containers" -Protocol tcp -LocalAddress 172.16.0.1/255.240.0.0 -Action Allow -Enabled True
Configuring Application Request Routing
The next step was to configure the request routing. To do this, I went over to the wwwroot folder
C:\>cd inetpub\wwwroot\
I then created a new web.donfig file, byt opening it in Notepad
C:\inetpub\wwwroot>notepad web.config
As there was no such file, Notepad asked if I wanted to created one, which I told it to do.
Inside the empty web.config file, I added the following
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Reverse Proxy to Site 1" stopProcessing="true">
<conditions trackAllCaptures="true">
<add input="{HTTP_HOST}" pattern="^site1.azuredemo.com$" />
</conditions>
<action type="Rewrite" url="http://172.16.0.2:8081/{R:1}" />
<match url="^(.*)" />
</rule>
<rule name="Reverse Proxy to Site 2" stopProcessing="true">
<conditions trackAllCaptures="true">
<add input="{HTTP_HOST}" pattern="^site2.azuredemo.com$" />
</conditions>
<action type="Rewrite" url="http://172.16.0.3:8082/{R:1}" />
<match url="^(.*)" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
This tells the system that any requests headed for http://site1.azuredemo.com/ should be redirected internally to http://172..16.0.2:8081/, and any request to http://site2.azuredemo.com/ should be redirected to http://172.16.0.3:8082/.
Disclaimer: Yes, I am well aware that redirecting to 172.16.0.2 & 3 like this is less than great. Every time the containers are started, the IP will change for them. Or rather, the IP actually seems to be assigned depending on the order they are requested. So if all containers are always started in the same order after a reboot, it should in theory work. But as I said, far from great. However, I don’t quite know how to solve this problem right now. Hopefully some smart person will read this and tell me…
The only thing left to do was to try and browse to my 2 applications, which returned
when browsing to http://site1.azuredemo.com, and
when browsing to http://site2.azuredemo.com. So it seems that adding IIS and ARR to my server and use it to route my requests to my containers is working.
That’s it! If you have any idea how to solve it in a better way, please tell me! If not, I hope you got something out of it!
Disclaimer: No, the azuredemo.com domain is not mine, and I have no affiliation to whomever does. I just needed something to play with, and thought that would work for this.
Disclaimer 2: My server is not online at that IP address anymore, so you can’t call it, or try and hack it or whatever you were thinking…
Cheers!