How to Run Sitefinity from a Container

April 03, 2019 Digital Experience, Sitefinity

In this experimental post, we’ll look into the benefits of running Sitefinity in a Windows Container, as well as its limitations. Read on for a step-by-step guide.

Getting Started with Containers and Sitefinity

The first steps of a project are always the hardest, and the purpose of this short post is to help you get started—to give DevOps the basics to kickstart experimentation and investigate the benefits of running Progress Sitefinity in Windows Containers. The blog post will describe some essentials about containers and provide steps to create a Docker image that can be used to run Sitefinity 11.2.

Note: While it is possible, this is an experimental post and not something officially supported by Sitefinity at this time.

What are Containers?

The easiest way to think about containers is that they are pretty much like virtual machines (VMs), with the major difference that they all share one common Operating System Kernel. If one needs to spin up 10 instances of a given app, they only need to provision 10 copies of the application files and one VM. Or there could be several different applications in NLB(Network Load Balancing) on just two VMs. If they used VMs instead they would need to spin up two instances of the same VM for every application, containing the same OS as well as the runtime dependencies and application files.

That is the one major benefit of using containers—rapid deployment and resource utilization. Apart from that, containers improve reliability and portability, allowing for a more robust transfer of applications between environments (dev/staging/live). You can read more specifically for Windows on Microsoft’s site, or in these two blog posts.

Pros and Cons of Using Containers

There are a number of benefits to using containers, but also a few drawbacks you should be aware of:

+ rapid deployment

+ resource utilization

+ portability - environment agnostic deployment (the app comes prepackaged with all dependencies – no more works on my machine problems)

+ sophisticated tools for orchestration – Docker Swarm, Kubernetes

- initial learning curve

- relatively new technology

Important Terms to Know when Developing with Containers

Container Host: Physical or Virtual computer system configured with the Windows Container feature.

Docker is one of the tools to create and manage container images. Just like any other container, a Windows Container can be managed with Docker.

Sandbox: This is the running container instance. It is a based on an image and captures all system changes to the file system, registry etc. in a separate layer leaving the original image untouched.

Container Image: Any running container (sandbox) can have its current configured state saved back into a new image, which can then be used to spawn new running container instances. Another way to create images is declaratively using a Dockerfile, which performs the same operations—it starts a sandbox, applies the changes and saves it to a new image.

Container OS Image: Every Windows Container image is based on one of the images provided by Microsoft.

Step-by-Step Guide: Running Sitefinity from a Docker Container

Note: If you have Symantec Endpoint Protection you might not be able to connect to the container unless it is uninstalled. You can read more about this issue here.

Let’s create an image that will be used to spawn containers on which to host Sitefinity. The version of container host that I am using is Windows 10 1809 and the Docker Desktop version is 2.0.0.3 (31259).

Note: After installing Docker Desktop you must switch to Windows Containers, as Sitefinity requires a Windows Kernel.

  1. Create a working directory called “docker-test.”
  2. Create the PowerShell script that will configure the IIS instance on the container to be able to host Sitefinity—create a file named “docker-test\iis-config.ps1.”
        Import-Module ServerManager;
    Add-WindowsFeature "FileAndStorage-Services";
    Add-WindowsFeature "Storage-Services";
    Add-WindowsFeature "Web-Server";
    Add-WindowsFeature "Web-WebServer";
    Add-WindowsFeature "Web-Common-Http";
    Add-WindowsFeature "Web-Default-Doc";
    Add-WindowsFeature "Web-Dir-Browsing";
    Add-WindowsFeature "Web-Http-Errors";
    Add-WindowsFeature "Web-Static-Content";
    Add-WindowsFeature "Web-Health";
    Add-WindowsFeature "Web-Http-Logging";
    Add-WindowsFeature "Web-Request-Monitor";
    Add-WindowsFeature "Web-Performance";
    Add-WindowsFeature "Web-Stat-Compression";
    Add-WindowsFeature "Web-Security";
    Add-WindowsFeature "Web-Filtering";
    Add-WindowsFeature "Web-App-Dev";
    Add-WindowsFeature "Web-Net-Ext45";
    Add-WindowsFeature "Web-Asp-Net45";
    Add-WindowsFeature "Web-ISAPI-Ext";
    Add-WindowsFeature "Web-ISAPI-Filter";
    Add-WindowsFeature "Web-Mgmt-Tools";
    Add-WindowsFeature "Web-Scripting-Tools";
    Add-WindowsFeature "Web-Mgmt-Service";
    Add-WindowsFeature "NET-Framework-45-Core";
    Add-WindowsFeature "NET-Framework-45-ASPNET";
    Add-WindowsFeature "NET-WCF-HTTP-Activation45";
    Add-WindowsFeature "RSAT";
    Add-WindowsFeature "RSAT-Role-Tools";
    Add-WindowsFeature "RSAT-AD-Tools";
    Add-WindowsFeature "RSAT-AD-PowerShell";
    Add-WindowsFeature "Telnet-Client";
    Add-WindowsFeature "PowerShellRoot";
    Add-WindowsFeature "PowerShell";
    Add-WindowsFeature "WAS";
    Add-WindowsFeature "WAS-Process-Model";
    Add-WindowsFeature "WAS-Config-APIs";
    Add-WindowsFeature "WoW64-Support";

    $sharepath = "C:\inetpub\wwwroot"
    $Acl = Get-ACL $SharePath
    # for the purpose of testing we grant the site location full access to every user in the container, this should be changed to the app pool identity and only for AppData
    $AccessRule= New-Object System.Security.AccessControl.FileSystemAccessRule("everyone","full","ContainerInherit,Objectinherit","none","Allow")
    $Acl.AddAccessRule($AccessRule)
    Set-Acl $SharePath $Acl
  3. Next, create a file named in “docker-test\Dockerfile”—it will be used for declaratively creating images.
    FROM microsoft/windowsservercore:1809
    SHELL ["powershell"]
    
    COPY iis-config.ps1 iis-config.ps1
    RUN Add-WindowsFeature Web-Server; \        
        Install-WindowsFeature NET-Framework-45-ASPNET ; \
        Install-WindowsFeature Web-Asp-Net45; \
        Invoke-WebRequest -UseBasicParsing \ 
            -Uri "https://dotnetbinaries.blob.core.windows.net/servicemonitor/2.0.1.6/ServiceMonitor.exe" \
            -OutFile "C:\ServiceMonitor.exe"; \
        "c:\iis-config.ps1";
    
    EXPOSE 80

    The line below instructs that the image we will build will be based on the Server Core OS image version 1809 (This is Windows Server without GUI).

    FROM microsoft/windowsservercore:1809

    This copies the script we created previously to the image file system.

    COPY iis-config.ps1 iis-config.ps1

    This executes PowerShell commands that install IIS and ASP.NET, as well as downloads the ServiceMonitor.exe tool and executes our iis-config script at the end.

    Note: Keep in mind that these will install the .NET and ASP.NET version that ships with Windows – in the case of 1809 this is .NET4.7.2. If you need to install a different version you have to manually download the package and install it.

    RUN Add-WindowsFeature Web-Server; \        
        Install-WindowsFeature NET-Framework-45-ASPNET ; \
        Install-WindowsFeature Web-Asp-Net45; \
        Invoke-WebRequest -UseBasicParsing \ 
            -Uri "https://dotnetbinaries.blob.core.windows.net/servicemonitor/2.0.1.6/ServiceMonitor.exe" \
            -OutFile "C:\ServiceMonitor.exe"; \
        "c:\iis-config.ps1";

    ServiceMonitor.exe is downloaded because it needs run when the container based on this image is run. It needs a long running process otherwise when the container is run it will simply exit.

  4. At the end, we instruct the container that will be based on this image to start this process when running.
    ENTRYPOINT ["C:\\ServiceMonitor.exe", "w3svc"]
  5. Finally build the image with the command below. Notice the “dot” at the end of the command - it represents that the current working directory will be used to search for dockerfile and other necessary files:
    docker build -t sitefinity-image .

    Now that we have the image we can use it to create containers and run different Sitefinity applications.

  6. In Powershell create the container:
    docker create --name MyNewSitefinityContainer sitefinity-image

    then copy all the webapp files to the container’s site directory (it is important that the source path ends with \. to copy just the files from the source).

    docker cp SitefinityWebAppFolderLocalPath\. MyNewSitefinityContainer:/c:/inetpub/wwwroot
  7. Next, start the container:
    docker start MyNewSitefinityContainer
  8. After the container has started, run this command to get the IP:
    docker inspect --format="{{.NetworkSettings.Networks.nat.IPAddress}}" MyNewSitefinityContainer
  9. Browse to the container’s website using its IP.

Final Notes on the Container Approach

Note: As this is easy to overlook, I just want to stress again - if you have Symantec Endpoint Protection you might not be able to connect to the container unless it is uninstalled. You can read more about this issue here.

Depending on the resources given to the container and the speed to the database, the app might be slower to initialize then if running locally. You need to access the SQL server database via the network using your host’s address and create a remote user to be used by Sitefinity app.

For the purpose of testing, we are manually copying the web app files after creating the container. This process could be optimized with a script inside the container which would run on startup and try to fetch the webapp from a URL, and then start the ServiceMonitor.exe. The URL could be passed to the container instance as an environment variable. This way simply spawning the container with a given URL as an environment variable will take care of the manual steps after creating one.

What’s Next

Give it a try and let us know how deploying Sitefinity in a container went for you. If you have any feedback on this experimental post, feel free to share it with us in the comments below or on our Feedback Portal.

Note that this post was originally published in May 2018, and has been refreshed and updated for accuracy and clarity.

Need a refresher or a head start? Sign up for our a free course designed to help developers and site managers who have little to no experience in developing against Sitefinity to get up to speed.

Todor Mitskovski

Todor is an experienced construction architect that switched occupational fields back in 2015 to enter the ever changing and exciting landscape of IT and software development. Enthusiastic about how new technologies change the very essence of who we are, he loves exploring the opportunities they give us. Find him on GitHub.