Deploying a legacy ASP.NET application to Docker

Deploying a legacy ASP.NET application to Docker involves couple of steps –

a) Need to divide the application into several independent modules.

b) Need to think about how to handle session on distributed app deployed in Docker.

c) One proxy gateway is required to forward the request to the proper container.

Just to recap, what is Docker? In simple language, we can say that, it is a software used to build and run distributed application without bothering about the environment. The environment can be a development system, a dedicated production server or cloud. In Docker, we need to package both the application and the run-time required to run the application.

Let us start our discussion, with a simple Hospital Management System. In Hospital Management System, we have several departments like Front Office, Billing Section, Stores, Laboratory etc. Our existing application is having the below architecture –

HMS Legacy App

Here, the UI will interact with users and contains several projects for several departments or modules. Services is a collection of WCF services used for Business Logic execution and DB interactions. HMS DB is required for persistent storage.

The recommendation for Docketized or Containerized application, is that it should be light weight. There is no such limitation with the size of the application, but my practical experience is saying that it should be as light as possible. That’s why .NET core is much suitable for Docker.

Let us consider our application, here we need to divide the entire HMS App into several modules. Each module would represent one department. Here the decision of deciding the number of modules has to be taken heuristically. If a functionality of a department is really limited, then we can combine two smaller departments into a single module. We can also divide a large department into several smaller modules. Anyway for our simplicity, lets consider each department as a single module.

Now we have to think about the communications between modules that is communication between two different containers or servers. Here we have to use out-proc session management scheme, because in-proc session can’t be shared between two different containers or servers. There are two different approaches for out-proc session, persistent based (SQL Server) or state server based. Both the approaches are costlier in terms of time than in-proc session, so use session variables effectively and wisely. ASP.NET State server is better than SQL server in terms of response time.

So, lets build our first container with State Server. The below script can be used in Dockerfile to create image for ASP.NET state service –

FROM microsoft/aspnet:3.5
CMD sc config aspnet_state start=auto
CMD net start aspnet_state
CMD powershell Set-ItemProperty Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters -Name  AllowRemoteConnection -Value 1
CMD net stop aspnet_state
CMD net start aspnet_state

Hope, reader is having idea about creating container from image, so we think that a container is created with the above image and the hostname of the container is provided as stateserver.

Now, lets consider creating a module. All the module creation would follow the same process. Lets take Store module. Here, We have to do some changes in web.config file of the Store module. The below code is required to paste inside system.web configurations.

<machineKey validationKey=”469D6068F82EF6E2AXXXXXXXXXXXXXF7409C60F8F092C771B6XXXXXXXXXXX672D1620BBB6CB650834C7EA7E7C02F94BA56BA0A7E68BEE9592289A”
decryptionKey=”E4B9D70E8570F31E6690AE3EE0EAFXXXXXXX371″ validation=”SHA1″ />
<sessionState cookieless=”false” mode=”StateServer” stateConnectionString=”tcpip=stateserver:42424″ timeout=”30″ />

The above keys are required to maintain same type of session keys across all the containers. If we don’t explicitly specify the keys, then the session id would be generated with different keys maintained in different servers. So, the same user is having different session IDs in different servers, so a session variable saved in one module or container won’t be accessible in a different container or module.

The keys can be generated from IIS –

Machine Key of IIS

We have to also specify the state server address or hostname.

The main change is related to session, but it is recommended that you should try to optimize each page of the module. Once it is done, we can create image of the module. The below script can be used for that –

FROM microsoft/aspnet:3.5
COPY ./PublishedFolder /inetpub/wwwroot
COPY ./infra /infra
WORKDIR /inetpub/wwwroot
RUN [“powershell”,”../../infra/createapppools.ps1″]
RUN [“powershell”,”../../infra/setpools.ps1″]
WORKDIR /windows/system32/inetsrv
CMD appcmd.exe set apppool /apppool.name:WHISAppPool /enable32BitAppOnWin64:true
WORKDIR /

The last step is to create a proxy server. Now the question may arise, why is it required? Think from the users perspective, user should think that he is accessing a single website, he should not have felling that he is accessing multiple websites spread across several containers. Also, for security reasons, we should only open proxy server to the outside world and not the other servers.

We can use IIS as a reverse proxy server. The job of this server is to forward the request to the container responsible for serving the request. We need a web.config file with the below details,

<rewrite>
 <rules>
 <rule name=”Reverse Proxy to Stores” stopProcessing=”true”>
 <match url=”^/stores/(.*)” />
 <action type=”Rewrite” url=”http://hmsstores/stores/{R:1}” />
 </rule>

 <rule name=”Reverse Proxy to FrontOffice” stopProcessing=”true”>
 <match url=”^/frontoffice/(.*)” />
 <action type=”Rewrite” url=”http://hmsfrontoffice/frontoffice/{R:1}” />
 </rule>
 </rules>
 </rewrite>

The above section needs to write under system.web section. Here, the container should have URL ReWrite Module and ReRouting module installed. The below script can be used inside Dockerfile,

FROM microsoft/aspnet:3.5
# Copy the required directorie
COPY ./PublishedFolder /inetpub/wwwroot
COPY ./Infra /Infra
WORKDIR /infra
RUN [“powershell”, “msiexec”, “/i”, “requestRouter_amd64.msi”, “/qn”, “/lime”, “./Error1.log”]
CMD net start msiserver
RUN [“powershell”, “msiexec”, “/i”, “rewrite_amd64.msi”, “/qn”, “/lime”, “./Error2.log”]
WORKDIR /inetpub/wwwroot
RUN [“powershell”,”../../infra/createapppools.ps1″]
RUN [“powershell”,”../../infra/setpools.ps1″]
RUN [“powershell”, “../../infra/EnableProxy.ps1”]
WORKDIR /windows/system32/inetsrv
CMD appcmd.exe set apppool /apppool.name:WHISAppPool /enable32BitAppOnWin64:true
WORKDIR /

EnableProxy.ps1 contains the below line –

Set-WebConfigurationProperty -pspath MACHINE/WEBROOT/APPHOST -filter System.WebServer/proxy -name enabled -Value True

Here, the user would be accessing pages with the URL like http://hmsapp/stores/sample.aspx but, our proxy server would re-write the URL as http://hmsstores/stores/sample.aspx. Note that, we are not doing any re-direction, so users URL won’t get change here. User will see that the request is getting served from hmsapp, but it is actually getting served from hmsstores.

The new architecture can be like below –

Architecture of a Docketized App

Hope, the above post would be helpful for the readers who are interested on deploying legacy ASP.NET application on “Docker for Windows”.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

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