November Happy Hour will be moved to Thursday December 5th.

Marco ter Horst
Oct 10, 2011
  13777
(4 votes)

Load balancing EPiServer CMS

Load balancing EPiServer can be quite tricky and there are a few key configuration items to be set.
All the information below has been gathered from several different articles, blog posts, etc. on the Internet. The purpose of this blog is to provide you with the tools to successfully setup a Load balanced EPiServer environment.

Introduction

For one of our customers we have the following EPiServer setup:
Functional:
A customer website with both a public and a private pages, using Forms bases authentication + (of course) EPiServer CMS pages.
The public part of website is available over http, the private and CMS pages over https.
Customers seamlessly roam back and forth between http and https pages.
Technical:
The architecture consists of 3 servers, all virtuals in the TerreMark eCloud: 2 EPiServer application servers and 1 database server.

Web servers:
Windows 2008 R2
4 VPU’s
6GB Memory

Both http and https traffic is load balanced over both application servers. Load balancing is configured with stickiness based on source IP address with a TTL of 5 minutes.
On both application servers the EPiServer application is served by a “Web Garden” of three Worker Processes.
So in effect EPiServer is served by 6 Worker Processes in total.

Database server:

Windows 2008
2 VPU’s
8GB Memory


Next to the standard EPiServer database, the application uses five more databases (i.e. store and product databases).

Arch

 

Challenges

With the setup described above, we are faced with a couple of challenges.

- Content & Cache Updates
- Shared VPP Data
- Unauthenticated Session States
- Authenticated Session States

The first two are actually easy because they are well documented in several EPiServer articles.
For the latter two some .net trickery is required.

Content & Cache Updates
Make sure the following section is uncommented in your web.config on all nodes:

<system.serviceModel>
  <extensions>
    <bindingElementExtensions>
      <add name="udpTransport" type="Microsoft.ServiceModel.Samples.UdpTransportElement, EPiServer.Implementation" />
    </bindingElementExtensions>
  </extensions>
  <services>
    <!-- Before deployment, you should remove the returnFaults behavior configuration to avoid disclosing information in exception messages -->
    <service name="EPiServer.Events.Remote.EventReplication" behaviorConfiguration="DebugServiceBehaviour">
      <endpoint name="RemoteEventServiceEndPoint" contract="EPiServer.Events.ServiceModel.IEventReplication" binding="customBinding" bindingConfiguration="RemoteEventsBinding" address="soap.udp://239.255.255.19:5000/RemoteEventService" />
    </service>
  </services>
  <client>
    <endpoint name="RemoteEventServiceClientEndPoint" address="soap.udp://239.255.255.19:5001/RemoteEventService" binding="customBinding" bindingConfiguration="RemoteEventsBinding" contract="EPiServer.Events.ServiceModel.IEventReplication" />
    <!--Client configuration for the ImageEditor, the client name has to be "ImageServiceClientEndPoint"-->
    <!-- Only uncomment if the Image Service is hosted in an external application
    <endpoint
     name="ImageServiceClientEndPoint"
     address="the address of the hosting application"
     binding="the binding the hosting application is using"
     bindingConfiguration="ImageServiceBinding"
     contract="EPiServer.ImageLibrary.IImageService" />
     -->
  </client>
  <behaviors>
    <serviceBehaviors>
      <behavior name="DebugServiceBehaviour">
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <customBinding>
      <binding name="RemoteEventsBinding">
        <binaryMessageEncoding />
        <udpTransport multicast="True" />
      </binding>
    </customBinding>
    <!-- Only uncomment if the Image Service is hosted in an external application
    <binding type used by the Image Service>
      <binding name="ImageServiceBinding" maxReceivedMessageSize="20000000">
        <readerQuotas maxArrayLength="20000000" />
        <security mode="None" />
      </binding>
    </binding type used by the Image Service>
     -->
  </bindings>
</system.serviceModel>

 

This enables content updates, which are made e.g. on node CMS01 to be propagated to node CMS02.

More information here.

 

Shared VPP Data

Use the following steps to enable use of shared VPP Folder:

1) Create a new user
2) Make this user member of the IIS IUSRS\ group (remove all other group memberships for extra security)
3) Make sure the IIS IUSRS\ group has appropriate rights on “non default” folders. (E.g. Logging folders)
4) Configure your Application Pool to run as this user
5) On the Server with the VPP folders, repeat steps 1) & 2)
6) Give the IIS IUSRS\ group appropriate rights on the VPP Folders (and sub folders)

More information here.

 

Unauthenticated Session States

By default Microsoft Internet Information Server saves session states “In Process”. Meaning in the Worker Process of the web application.
In our scenario we will want to use a Session State Database. Using the ASP.NET State Service (part of default install) to save session states to a database.
Don’t worry about performance. This actually works very well and very fast.
Here is all the info you need to configure your website to use it.

After following the steps from the KB article to setup the DB. You add this piece of configuration to your web.config on all nodes:

<sessionState mode="SQLServer" sqlConnectionString="data source=10.1.1.1;user id=aspstate;password=******" cookieless="false" timeout="20" />

Make sure the user id is created on the DB server and has DBO rights (only) on the ASPstate DB.

 

Authenticated Session States

Picture this scenario:
- a user opens a public page and is routed by the load balancer over port 80 to CMS01
- he clicks a link to use a feature on the website only available for logged in users and is redirected to the login page. This is a SSL encrypted page and the users is routed by the load balancer over port 443 to CMS02
- the user fills in his credentials and is redirected back to the original public page to CMS01

Unless we perform the steps below, the user will not be logged in and the following error will appear in the Windows System Eventlog:

Event code: 4005
Event message: Forms authentication failed for the request. Reason: The ticket supplied was invalid.
Event time: 10/10/2011 2:29:32 PM
Event time (UTC): 10/10/2011 12:29:32 PM
Event ID: 2ddd863e47534f53a182f84a9a613070
Event sequence: 75
Event occurrence: 1
Event detail code: 50201
 
Application information:
    Application domain: /LM/W3SVC/2/ROOT-1-129627223033590000
    Trust level: Full
    Application Virtual Path: /
    Application Path: D:\Inetpub\wwwroot\
    Machine name: CMS01
 
Process information:
    Process ID: 2716
    Process name: w3wp.exe
    Account name: username
 
Request information:
    Request URL:
http://website/UI/javascript/system.aspx
    Request path: /UI/javascript/system.aspx
    User host address: ip address
    User: 
    Is authenticated: False
    Authentication Type: 
    Thread account name: username

 

This is what happened: after the post of login credentials on CMS02. The server encrypted the VIEWSTATE using the node’s machine key.
Now when the users arrived back on the node CMS01, this node was unable to decrypt the VIEWSTATE and therefore did not accept the user as validated user.

To fix this, we need to make sure all servers in the farm use the same machine key.

There are a ton of websites who provide online machine key generation. Like this one: http://aspnetresources.com/tools/machineKey

Just click generate and add it to your web.config. Make sure you add it between system.web tags:

<system.web>
<machineKey validationKey="7F252ED2FAF2BB70FDFB7AF2CC5864981D4933F795FDBC5E5B54DB2589148AF1BD10E277044C0426C6C47601AE2074F3B5E7012D6" decryptionKey="00C0C09D8E7D0DCB3535A29A00E3FBA39DF3300" validation="SHA1"/>
</system.web>

 

Good luck!

Smile

Oct 10, 2011

Comments

Oct 10, 2011 04:50 PM

Nice post! Just one thing, why do you use Web Gardens?

Oct 11, 2011 09:53 AM

I agree with evest, very nice article!

Marco ter Horst
Marco ter Horst Oct 12, 2011 07:35 PM

We see a significant performance boost when we implement a Web Garden.
IIS Request queue drops, CPU usage drops the web application just runs a lot smoother.

There is only one pitfall: the worker processes sometimes grow to 1.2GB memory usage or even more. Therefore we decided to configure 3 (and not more) WP instances and set the max. memory usage for the WP's @ 900MB. So if a WP goes over the 900MB threshold, it's recycled to a fresh WP.

Please login to comment.
Latest blogs
Adding Geolocation Personalisation to Optimizely CMS with Cloudflare

Enhance your Optimizely CMS personalisation by integrating Cloudflare's geolocation headers. Learn how my Cloudflare Geo-location Criteria package...

Andy Blyth | Nov 26, 2024 | Syndicated blog

Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog