We are looking to use the load balancing function in Azure Web Apps with our Umbraco 11 system and wanted to see if anyone else in the forum had previously done this.
My understanding from the following 2 articles is:
3 - Create a couple of small classes that implement IServerRoleAccessor one for each of the different server roles:
"public class SchedulingPublisherServerRoleAccessor : IServerRoleAccessor
{
public ServerRole CurrentServerRole => ServerRole.SchedulingPublisher;
}"
"public class SubscriberServerRoleAccessor : IServerRoleAccessor
{
public ServerRole CurrentServerRole => ServerRole.Subscriber;
}"
4 - Replace the default IServerRoleAccessor for the your custom registrars. You'll can do this by using the SetServerRegistrar() extension method on IUmbracoBuilder from a Composer or directly in your startup.cs.
"// This should be executed on your single SchedulingPublisher server
builder.SetServerRegistrar
"// This should be executed on your Subscriber servers
builder.SetServerRegistrar
I am trying to learn how this works, so that I am able to understand the deployment process; is anyone able to provide (I am not a developer, so have very limited knowledge) any more simplified step-by-step instructions regarding steps 3 and 4, so that I can make these changes in the code myself?
I usually use an environment variable to toggle the server role:
public class MyComposer : IComposer
{
private readonly string _serverRole;
public Composer()
{
var serverRole = Environment.GetEnvironmentVariable("UMBRACO_SERVER_ROLE");
if (serverRole.IsNullOrWhiteSpace() == false)
{
_serverRole = serverRole;
}
}
public void Compose(IUmbracoBuilder builder)
{
// Explicitly set Server Role
if (_serverRole.InvariantEquals("SchedulingPublisher"))
{
builder.SetServerRegistrar<SchedulingPublisherServerRoleAccessor>();
}
else if (_serverRole.InvariantEquals("Subscriber"))
{
builder.SetServerRegistrar<SubscriberServerRoleAccessor>();
}
else if (_serverRole.InvariantEquals("Single"))
{
builder.SetServerRegistrar<SingleServerRoleAccessor>();
}
}
}
public class SchedulingPublisherServerRoleAccessor : IServerRoleAccessor
{
public ServerRole CurrentServerRole => ServerRole.SchedulingPublisher;
}
public class SubscriberServerRoleAccessor : IServerRoleAccessor
{
public ServerRole CurrentServerRole => ServerRole.Subscriber;
}
This class can be placed at the root of your project (alongside the appsettings.json files is fine).
The UMBRACO_SERVER_ROLE environment variable can then be set via the Web App's app settings in Azure, via the Configuration tab. (Set to SchedulingPublisher for the backoffice app, and Subscriber for the frontends)
Much easier than having 2 separate branches, as it removes the possibility of a merge accidentally changing the role to something else.
But if you do need to use 2 separate branches, then you can use the same code, but just remove the if statements and env variable code. e.g:
public class MyComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
// Explicitly set Server Role as Scheduling Publisher
builder.SetServerRegistrar<SchedulingPublisherServerRoleAccessor>();
}
}
public class SchedulingPublisherServerRoleAccessor : IServerRoleAccessor
{
public ServerRole CurrentServerRole => ServerRole.SchedulingPublisher;
}
Thank you Owain, that makes total sense to set the role in the Azure configuration as opposed to having 2 branches of code.
Apologies for my ignorance (I only understand the basics of how Umbraco works), but essentially I could create a file with solely the code above, place it in the root folder and it would set the roles - as long as I add the configuration in Azure?
What would the file name need to be called?
Would it need to be referenced anywhere else?
Presumably, we would always need at least 1 'Subscriber' Web App, in order for the site to work, e.g. we couldn't have 1 'SchedulingPublisher' Web App and then only turn on 'Subscriber' Web Apps as and when we needed the extra load?
... and the 2 types, e.g. 'SchedulingPublisher' and 'Subscriber' would need to be on different App Service Plans in order to get the benefit?
Apologies for my ignorance (I only understand the basics of how Umbraco works), but essentially I could create a file with solely the code above, place it in the root folder and it would set the roles - as long as I add the configuration in Azure?
No worries! That's correct :)
Here's a screenshot of one of my projects:
What would the file name need to be called?
The file can be named anything, I usually use Composer.cs for my project's main composer.
Would it need to be referenced anywhere else?
Nope, as the class extends the IComposer interface, Umbraco should pick it up automatically during boot.
Presumably, we would always need at least 1 'Subscriber' Web App, in order for the site to work, e.g. we couldn't have 1 'SchedulingPublisher' Web App and then only turn on 'Subscriber' Web Apps as and when we needed the extra load?
I think it would work fine with just a Scheduled Publisher, as all the Server Role does is change how Umbraco handles scheduled tasks, e.g.:
SchedulingPublisher
Responsible for executing scheduled tasks, e.g. scheduled publishing and unpublishing.
This is the one your content editors should use to edit content.
Ideally, this should only be used by your content editors but can also serve frontend requests.
Subscriber
Does not run any scheduled tasks.
Should not be used by your content editors.
More performant for frontend requests.
According to the docs, has Read Only access to the database (I am in doubt if this is actually true, as I had a client's content editor, mistakenly use a frontend app to edit content for almost a year, and nothing bad happened)
Single
Basically the same as SchedulingPublisher, but only used in a single app, non-load balanced, environments. (I don't know what the technical differences are)
By default, Umbraco delegates these roles automatically, but as you know, it's recommended to do this manually in a load-balanced environment.
So in theory, yes you could serve your frontend traffic with your backoffice editing app (the SchedulingPublisher), but it is recommended to keep the editing app and public frontends separate for the best performance. So, I would say, only do this if hosting costs are an issue.
... and the 2 types, e.g. 'SchedulingPublisher' and 'Subscriber' would need to be on different App Service Plans in order to get the benefit?
That would depend on how much traffic you're expecting, but for my clients, we always put the editing app and the frontends on their own separate app service plans for maximum performance.
When we deploy the LoadBalancer.cs file with this code, it is providing 6 errors in the logs:
"C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj" (default target) (1:7) ->
(CoreCompile target) ->
C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(1,28): error CS0246: The type or namespace name 'IComposer' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj]
C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(5,14): error CS1520: Method must have a return type [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj]
C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(33,55): error CS0246: The type or namespace name 'IServerRoleAccessor' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj]
C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(35,14): error CS0246: The type or namespace name 'ServerRole' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj]
C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(38,46): error CS0246: The type or namespace name 'IServerRoleAccessor' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj]
C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(40,14): error CS0246: The type or namespace name 'ServerRole' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj]
Thanks Owain. I have added these to the top of the 'LoadBalancer.cs' file, but it is still giving a deployment error.
I am not sure if it is the done thing on the forum (so apologies if not) but could I pay for your time, to jump on a call and see if you would be able to point me in the right direction of where it is failing?
Just 2 other questions if you are able to clarify:
1 - Typically should the base specification of the server have more compute power on the SchedulingPublisher or the Subscriber?
2 - If we have a custom domain linking to the Azure Web App, would we setup the Subscriber to point to the frontend domain, e.g. www.SITE.com and the SchedulingPublisher to a different domain - or what is standard practice for this?
Apologies I missed your previous reply! Glad to hear you got it working!
1 - Typically should the base specification of the server have more compute power on the SchedulingPublisher or the Subscriber?
We usually have the SchedulingPublisher and Subscribers on the same tiers, which is usually an S2. (although we're looking into switching to P0v3 as they seem to have the same performance as S2s but cheaper).
But I don't see any reason why they couldn't be on different tiers if needed.
2 - If we have a custom domain linking to the Azure Web App, would we setup the Subscriber to point to the frontend domain, e.g. www.SITE.com and the SchedulingPublisher to a different domain - or what is standard practice for this?
For us, this depends on the needs of the client, but usually, we have the frontend subscriber instances pointing to the live domain and the back office would either be accessed via the default Azure Web App URL or via a subdomain on the live domain (e.g. edit.example.com); We also always lock the actual back-office Web App behind Azure AD.
Load Balancing in Azure Web Apps with Umbraco 11
We are looking to use the load balancing function in Azure Web Apps with our Umbraco 11 system and wanted to see if anyone else in the forum had previously done this.
My understanding from the following 2 articles is:
https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/azure-web-apps
https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/flexible-advanced#explicit-schedulingpublisher-server
1 - We have to setup 2 branches of code (instead of just 1), e.g.
Master - Administrator
Master - Public Facing
2 - We edit the lines in the appsettings.json file on 'Master - Public Facing' branch to:
" ""Examine"": { ""LuceneDirectoryFactory"": ""TempFileSystemDirectoryFactory"""
3 - Create a couple of small classes that implement IServerRoleAccessor one for each of the different server roles:
"public class SchedulingPublisherServerRoleAccessor : IServerRoleAccessor { public ServerRole CurrentServerRole => ServerRole.SchedulingPublisher; }"
"public class SubscriberServerRoleAccessor : IServerRoleAccessor { public ServerRole CurrentServerRole => ServerRole.Subscriber; }"
4 - Replace the default IServerRoleAccessor for the your custom registrars. You'll can do this by using the SetServerRegistrar() extension method on IUmbracoBuilder from a Composer or directly in your startup.cs.
"// This should be executed on your single
SchedulingPublisher
server builder.SetServerRegistrar"// This should be executed on your
Subscriber
servers builder.SetServerRegistrarI am trying to learn how this works, so that I am able to understand the deployment process; is anyone able to provide (I am not a developer, so have very limited knowledge) any more simplified step-by-step instructions regarding steps 3 and 4, so that I can make these changes in the code myself?
Hi William,
I usually use an environment variable to toggle the server role:
This class can be placed at the root of your project (alongside the appsettings.json files is fine).
The
UMBRACO_SERVER_ROLE
environment variable can then be set via the Web App's app settings in Azure, via the Configuration tab. (Set toSchedulingPublisher
for the backoffice app, andSubscriber
for the frontends)Much easier than having 2 separate branches, as it removes the possibility of a merge accidentally changing the role to something else.
But if you do need to use 2 separate branches, then you can use the same code, but just remove the if statements and env variable code. e.g:
Hope that helps!
Thank you Owain, that makes total sense to set the role in the Azure configuration as opposed to having 2 branches of code.
Apologies for my ignorance (I only understand the basics of how Umbraco works), but essentially I could create a file with solely the code above, place it in the root folder and it would set the roles - as long as I add the configuration in Azure?
What would the file name need to be called?
Would it need to be referenced anywhere else?
Presumably, we would always need at least 1 'Subscriber' Web App, in order for the site to work, e.g. we couldn't have 1 'SchedulingPublisher' Web App and then only turn on 'Subscriber' Web Apps as and when we needed the extra load?
... and the 2 types, e.g. 'SchedulingPublisher' and 'Subscriber' would need to be on different App Service Plans in order to get the benefit?
No worries! That's correct :)
Here's a screenshot of one of my projects:
The file can be named anything, I usually use
Composer.cs
for my project's main composer.Nope, as the class extends the
IComposer
interface, Umbraco should pick it up automatically during boot.Here's the docs on composers: https://docs.umbraco.com/umbraco-cms/implementation/composing
I think it would work fine with just a Scheduled Publisher, as all the Server Role does is change how Umbraco handles scheduled tasks, e.g.:
By default, Umbraco delegates these roles automatically, but as you know, it's recommended to do this manually in a load-balanced environment.
So in theory, yes you could serve your frontend traffic with your backoffice editing app (the SchedulingPublisher), but it is recommended to keep the editing app and public frontends separate for the best performance. So, I would say, only do this if hosting costs are an issue.
That would depend on how much traffic you're expecting, but for my clients, we always put the editing app and the frontends on their own separate app service plans for maximum performance.
Thank you for your clear breakdown.
When we deploy the LoadBalancer.cs file with this code, it is providing 6 errors in the logs:
"C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj" (default target) (1:7) -> (CoreCompile target) -> C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(1,28): error CS0246: The type or namespace name 'IComposer' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj] C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(5,14): error CS1520: Method must have a return type [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj] C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(33,55): error CS0246: The type or namespace name 'IServerRoleAccessor' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj] C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(35,14): error CS0246: The type or namespace name 'ServerRole' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj] C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(38,46): error CS0246: The type or namespace name 'IServerRoleAccessor' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj] C:\home\site\repository\PROJECTFOLDER\LoadBalancer.cs(40,14): error CS0246: The type or namespace name 'ServerRole' could not be found (are you missing a using directive or an assembly reference?) [C:\home\site\repository\PROJECTFOLDER\COMPANY.Web.csproj]
Hi William,
You'll need to add the following usings, to the top of the LoadBalancer.cs file, to load the required assemblies:
I believe these are all you'll need, but your IDE should tell you if you need any others :)
Thanks Owain. I have added these to the top of the 'LoadBalancer.cs' file, but it is still giving a deployment error.
I am not sure if it is the done thing on the forum (so apologies if not) but could I pay for your time, to jump on a call and see if you would be able to point me in the right direction of where it is failing?
We managed to fix the deployment issues.
Just 2 other questions if you are able to clarify:
1 - Typically should the base specification of the server have more compute power on the SchedulingPublisher or the Subscriber?
2 - If we have a custom domain linking to the Azure Web App, would we setup the Subscriber to point to the frontend domain, e.g. www.SITE.com and the SchedulingPublisher to a different domain - or what is standard practice for this?
Hi William,
Apologies I missed your previous reply! Glad to hear you got it working!
We usually have the SchedulingPublisher and Subscribers on the same tiers, which is usually an S2. (although we're looking into switching to P0v3 as they seem to have the same performance as S2s but cheaper).
But I don't see any reason why they couldn't be on different tiers if needed.
For us, this depends on the needs of the client, but usually, we have the frontend subscriber instances pointing to the live domain and the back office would either be accessed via the default Azure Web App URL or via a subdomain on the live domain (e.g. edit.example.com); We also always lock the actual back-office Web App behind Azure AD.
Thank you. That is really helpful!
From further reading, it is recommended that for the Lucene/Examine configuration:
The single instance Backoffice Administrative Web App should be set to use SyncedTempFileSystemDirectoryFactory.
The multi-instance Scalable Public Web App should be set to use TempFileSystemDirectoryFactory.
Source: https://docs.umbraco.com/umbraco-cms/fundamentals/setup/server-setup/load-balancing/azure-web-apps
How do you configure that in the appsettings.json to avoid having to have 2 branches of code?
Hi William,
You can set this as an AppSetting in the Azure portal, by going to the "Configuration" menu, on your Web App, and adding a new application setting.
Microsoft Docs: https://learn.microsoft.com/en-us/azure/app-service/configure-common?tabs=portal#configure-app-settings
Any appsettings.json entry can be set here, you will just need to name it in a double underscores separated format.
E.g. this:
Would be set like this:
Name:
Umbraco__CMS__Examine__LuceneDirectoryFactory
Value:
SyncedTempFileSystemDirectoryFactory
Note: you can also use colons
:
instead of double underscores__
on Windows Web Apps, but Linux Web Apps only support double underscores.You can then use a package like Diplo God Mode or Cultiv Environment Variables Inspector to check if the app setting has been applied correctly.
Hope that helps! 😄
Thank you so much.
is working on a reply...