Together but separate - Maintainable Sitecore multi-site instances

Sitecore supports having multiple website stored within a single Sitecore instance.   This has a number of advantages for content authors including:

  • It makes it easy to share content between the different sites, and 
  • One DMS database is used for all the sites in the instance it is easy to gather information about users from across the multiple sites and use it to drive personalisation.  

However there is a down-side to having multiple sites within the same Sitecore instance, there is one IIS web site containing all the code and configuration files for all the sites in the instance.  This can impact long term support of the Sitecore instance:

  • When you make updates or perform deployments for one site you will impact the other sites in that instance. Whenever the \bin folder or web.config of your Sitecore (or any .NET) site you cause the worker process to recycle, and some deployments (e.g. those requiring breaking code and Sitecore changes across multiple delivery servers) can need a site outage to preserve a good experience for users during the deployment.  If these changes only affect one Sitecore site, you don't want to have to take them all offline (especially you don't want to take your main site offline for changes to a secondary site)
  • If you are making changes to the code for one site within your multi-site setup, you want to be able to have some certainty that other sites aren't impacted so that your regression testing effort is reduced.  Even more importantly if you potentially have multiple agencies building sites that need to be served from the same instance, you need to be sure that their code is well segregated.
  • Before IIS8 you could only have one SSL certificate per IIS site, so you could only have multiple Sitecore sites in an instance supporting HTTPS connections if they shared a base domain and you used a wildcard SSL certificate (e.g. *.foo.com).  IIS8 now supports SNI (Server Name Indication) that promises to remove this restriction.

So to build a Sitecore site to support multiple sites that we can support and maintain over time, we want to be able to ensure that we can:
  • Take individual sites (and individual delivery servers) offline separately
  • Keep the files and Sitecore items needed to deploy each site as separate as possible

Load balancer separation

If you have a Sitecore instance running multiple sites, you are most likely already using a load balancing device such as an F5 BIG-IP or Citrix Netscaler to distribute incoming requests across multiple servers.  Load balancers generally present one or more virtual IP (VIP) endpoints to service incoming requests, and then distribute those request amongst a number of servers in a Server Pool using a balancing algorithm such as round robin or least connections.  Load balancers allow the servers in their pool to be taken in an out of rotation, and in fact will monitor the servers and automatically take them out of service if they become unresponsive.  Most load balancers can also be configured to store a custom outage page which is server if there are no servers available in the server pool to serve web requests.

To be able to take an individual site offline whilst leaving the other sites still serving traffic, we set up a separate VIP for each logical site in the Sitecore instance and in each server pool put each of the delivery servers serving the Sitecore instance.  The resulting configuration looks like this:

Now we can choose to take an individual site (e.g. foo.com) offline for incoming requests by simply removing all the servers for the foo.com server pool on the load balancer.  The bar.com site continues to server request as usual.

The other advantage of this approach on servers not supporting SNI is that load balancers can be configured to enable SSL offload meaning that the SSL certificates are stored in the load balancer and a separate certificate can be configured for each site.

Gotchas

One gotcha to be aware of whenever you are using a load balancer in front of your site is that incoming connections from clients terminate at the load balancer, which then creates a separate network connection to you server.  This means that by default all traffic will look like it is coming from the inside of your load balancer, which doesn't do wonders for GeoIP based personalisation.

In some cases the load balancer can be configured to impersonate the source IP of the incoming request (although this can cause other network routing issues).  Fortunately it is usually quite straightforward to have the load balancer add an X-Forwarded-For HTTP header to the request containing the original source IP address.  Sitecore analytics can be easily configured to use this value rather than the IP on the network packets to associate with Visits allowing GeoIP personalisation to work as expected.  This is done by including the following setting in the Sitecore.Analytics.config file.

<setting name="Analytics.ForwardedRequestHttpHeader" value="X-Forwarded-For" />

Code module separation

An update of a Sitecore site might generally consist of one or more of the following artifacts:
  • Sitecore elements
    • Layouts
    • Sublayouts (or other Renderings)
    • Templates
    • Placeholders 
    • Edit Frame Buttons
  • .NET Code
    • Assemblies
    • ASPX/ASCX files (or views)
  • Configuration files
To reduce the risk of changes from one site affecting another, we maintain each of these types of artifacts in separately for global items (things that are common to all sites, such as Sitecore templates which specify metadata to be provided with all content published by the organisation), and for each site.

The diagram below shows an example of how assemblies would be packaged in the above example with 


We use TDS for deploying Sitecore items into our production environments, and these follow the same approach with global and site specific TDS projects, however we also keep separate packages for Core, Master and Master Content for each of these sites (with Master Content generally only for setting up fresh non-production environments with seed content structure, not for production deployments).  The TDS projects we would use for the foo/bar example would be as follows.  You can see that this leads to a bit of a proliferation of projects for large across the board deployments, but gives the flexibility required to develop and release updates to different sites separately.  

  • Global.TDS.Core
  • Global.TDS.Master
  • Global.TDS.MasterContent
  • Foo.TDS.Core
  • Foo.TDS.Master
  • Foo.TDS.MasterContent
  • Bar.TDS.Core
  • Bar.TDS.Master
  • Bar.TDS.MasterContent

Conclusion

Sitecore makes creating multi-site instances really easy and powerful, but implementing multi-site without thinking about how ongoing development and deployments to the sites will work can quickly make the whole solution a maintenance nightmare.  We've found taking the fairly simple steps listed above gives the flexibility required to support a complex multi-site Sitecore implementation.

Comments