Support dynamic scaling for static .NET Framework apps
Overview
One of the key benefits of using the cloud for applications is elasticity, or the
ability to scale compute in or out based on demand. This enables you to only pay for
the compute capacity that you need, rather than provisioning for peak usage. Cyber
Monday, in which online retailers can quickly get many times more traffic than
normal (for example, thousands of percent within minutes
If you're bringing legacy .NET web applications to the cloud (for example, ASP.NET
Framework applications running on IIS), the ability to quickly scale load balanced
server farms may be difficult or impossible due to the stateful nature of the
application. User session data is stored within the memory of the application,
usually with ASP.NET session state
This proves to be challenging operationally. When increased capacity is required, you must intentionally provision and add servers. This can be a slow process. Taking nodes out of service in the event of patching or for unexpected failures can be problematic for the end user experience, losing state for all users associated with affected nodes. At best, this would require users to log in again.
By centralizing session state for ASP.NET applications and applying autoscaling
rules to legacy ASP.NET applications, you can take advantage of the elasticity of
the cloud and potentially take advantage of cost savings when running applications.
For example, you get cost reductions through compute scalability, but you also get
to choose from different pricing models available, such as reducing reserved instance
usage
Two common techniques include using Amazon DynamoDB as a session
state provider
The following diagram shows an architecture that uses DynamoDB as a session state provider.
The following diagram shows an architecture that uses ElastiCache (Redis OSS) as a session state provider.
Cost impact
To determine the benefits of scaling for a production application, we recommend that you model your actual demand. This section makes the following assumptions to model a sample application:
-
Instances that are added and removed from rotation are identical and no instance size variation is introduced.
-
Server utilization never drops below two active servers in order to maintain high availability of the application.
-
The quantity of servers scales linearly with the traffic (that is, twice as much traffic will require twice as much compute).
-
Traffic is modeled over the course of a month in six hour increments, with intra-day variation and one abnormal peak of traffic (for example, a promotional sale) for one day of 10x traffic. Weekend traffic is modeled at base utilization.
-
Nighttime traffic is modeled at base utilization, while weekday traffic is modeled at 4x utilization.
-
Reserved Instance pricing uses one-year, no-upfront pricing. Normal daytime pricing uses on-demand pricing while burst demand uses Spot Instance pricing.
The following diagram illustrates how this model takes advantage of elasticity in a .NET application rather than provisioning for peak usage. This results in a savings of approximately 68 percent.
If you use DynamoDB as a session state storage mechanism, then use the following parameters:
Storage: 20GB Session Reads: 40 million Session Writes: 20 million Pricing Model: On demand
The estimated monthly cost for this service is approximately $35.00 per month.
If you use ElastiCache (Redis OSS) as a session state storage mechanism, then use the following parameters:
Number of Nodes: 3 Node size: cache.t4g.medium Pricing Model: 1y reserved
The estimated monthly cost for this service is approximately $91.00 per month.
Cost optimization recommendations
The first step is to implement session state in a legacy .NET application. If
you're using ElastiCache as your state storage mechanism, follow the guidance from What is
the AWS SDK for .NET in the AWS SDK for .NET documentation. If you're using DynamoDB, then
follow the guidance from ElastiCache as an ASP.NET
Session Store
If the application uses the InProc session to
begin with, make sure that all objects that you plan to store in the session can be
serialized. To do this, use the SerializableAttribute
attribute to
decorate classes whose instances will be stored in the session. For example:
[Serializable()] public class TestSimpleObject { public string SessionProperty {get;set;} }
Additionally, the .NET MachineKey
must be the same between all the
servers in use. This is normally the case when instances are created from a common
Amazon Machine Image (AMI). For example:
<machineKey validationKey="some long hashed value" decryptionKey="another long hashed value" validation="SHA1"/>
However, it's important to ensure that if a base image is changed, it's configured
with the same .NET machine image (either configurable at the IIS or server level).
For more information, see SystemWebSectionGroup.MachineKey Property
Finally, you must determine the mechanism for adding servers to an Auto Scaling group in response to a scaling event. There are several ways to accomplish this. We recommend the following methods to seamlessly deploy .NET Framework applications to an EC2 instance in an Auto Scaling group:
-
Use EC2 Image Builder
to configure an AMI that contains the fully configured server and application. You can then use this AMI to configure the launch template of your Auto Scaling group. -
Use AWS CodeDeploy
to deploy your application. CodeDeploy enables integration directly with Amazon EC2 Auto Scaling. This provides an alternative to creating a new AMI for each release of the application.
Additional resources
-
Create images with EC2 Image Builder (EC2 Image Builder documentation)
-
Deploying .NET Web Applications Using AWS CodeDeploy with Visual Studio Team Services
(AWS Developer Tools Blog)