Modernising in AWS: Migrating from .NET to Containerised .NET Core
Migrating any workload can be daunting at first, especially if you’re migrating from something familiar and trusted, to something new and untested.
In this blog, we will take a high-level tour of some of the things you should consider when migrating your first .NET Core workloads into AWS.
What is .NET Core?
Originally released in 2016, .NET Core is an an open-source alternative to the traditional .NET framework – designed with multiple types of application development in mind (such as mobile and kiosk applications) – and is particularly well suited for web application workloads.
As well as being an official offering from Microsoft themselves, it is also platform agnostic; running equally well on Windows, MacOS and Linux alike.
Cost Optimisation & Operational Benefits
One of the most common triggers for .NET Core migrations is, unsurprisingly, Cost Optimisation.
Given that .NET Core is open-source and free under the MIT licence, previously expensive software licences can be avoided.
The Microsoft Visual Studio Code IDE is also free so, once again, your development spend can be curtailed further if you incorporate this into your tooling. As well as being free, VSC also has a wealth of features and additional plugins to increase development efficiency, and has become a leading IDE for all manner of development projects – even non-Microsoft ones.
Unlike the old .NET framework, which was heavily dependant on Windows, the platform-agnostic nature of .NET Core also means that you can run it in open-source Linux environments, for example.
.NET Core was specifically designed to be a lightweight development framework, so it’s intrinsically much better performing than its heavyweight predecessor. This is evident when performing a straight lift-and-shift migration of a monolith, but becomes far more apparent when migrating to service-oriented architectures.
With excellent support for microservices and containers, .NET Core is very well positioned to benefit from AWS ECS and its auto-scaling feature, for one example. Containers themselves can be sized relatively small, due to .Net Core’s small footprint and improved resource consumption.
There are also plenty of packages available on NuGet that provide Cloud performance enhancements – such as the AWS ElastiCache and CloudFront SDKs – so you can easily extend your application to make it more scalable and performant, as workload demand increases.
A Traditional Migration
Migrating legacy .NET applications can be painstaking and often frustrating process, especially given that several steps have to be carried out manually.
Although Microsoft and other vendors provide certain tooling to assist these actions, they are not fully conclusive and often fail, forcing the developer to remediate manually once more.
The true complexity of the migration process will obviously differ from one implementation to the next, and will be loosely determined by: the overall size of the application to be migrated; the number of dependencies; the age of both (e.g. far-outdated packages and project structures will inevitably present additional breakages, leading to increased refactoring work).
- Use the .NET Portability Analyzerto perform an automated compatibility check – this will help you identify unsupported packages and APIs before you commence the migration.
- Optionally, perform a manual audit of dependencies:
- Convert NuGet packages.config file to PackageReference settings in the project file.
- Retarget .NET project to latest stable .NET Framework version.
- Consider migrating your project to .NET Core using the .NET Upgrade Assistant.
- For post-migration troubleshooting, reference Windows.CompatibilityNuGet package to provide increased visibility of compatibility issues.
Inefficiencies of This Process
By this stage, assuming you were able to migrate to .NET Core successfully, and without too much hassle, you will still face the task of performing additional restructuring to support the containerisation of your app, followed by setting up AWS infrastructure to support integration and deployment into the Cloud.
If you intend to provision any meaningful CI/CD capability as part of this migration, there can be fairly significant effort required still.
The full time and cost implications of performing all of this work will prove to be, in some cases, prohibitive. In others, it will force unwanted trade-offs and compromises.
A Modern Migration (with AWS App2Container)
Happily, AWS have answered the call for a simpler, more reliable and highly automated tool for migrating legacy .NET applications into containers in the Cloud, and it’s called App2Container.
Simply put, AWS App2Container is a command-line tool for migrating and containerising legacy .NET and Java applications. It has been developed in partnership with some of AWS’ largest clients and APN partners, in order to significantly increase the speed and ease of modernising legacy applications.
How it works
- Scans and creates an inventory of application dependencies dynamically, at runtime.
- Extracts the application andits dependencies, and publishes them to Docker images.
- Automatically configures network ports – from public access to private, inter-container and cross-service connections.
- Generates AWS Elastic Container Service (ECS) Task Definitions (or Kubernetes pod definitions, if you prefer to target EKS over ECS).
- Automatically creates CI/CD pipelines in AWS CodePipeline.
- Pushes Docker images to AWS Elastic Container Repository (ECR) and deploys to ECS/EKS.
As you can see, App2Container does a lot of heavy lifting for you, and is a great way to start your AWS journey with .NET Core. It’s also a great way to standardise immutability of infrastructure and deployments, which you can of course evolve over time.
Understand Container Technologies
Given that AWS ECS and EKS both use Docker containers, it will benefit you to have at least some basic experience of running Docker containers, even if only locally.
We do, however, encourage you to take things a little further, and learn about multi-container deployments and best-practice patterns for doing this. An understanding of such concepts will stand you in good stead, both during and after migrating to AWS containers.
With the above, necessary foundational knowledge in place, you will far more quickly get to grips with the likes of ECS, which maps common Docker concepts via its proprietary features – ‘Task Definition’ is analogous to a Dockerfile, for example.
Similarly, if you plan to migrate to a Kubernetes managed deployment, using EKS, then you should prepare to spend time learning Kubernetes itself, prior to migration.
Check Your Application Is Supported
At the time of writing, App2Container supports migrating legacy .NET applications from Windows on IIS only.
For full, up-to-date compatibility information, see: A2C Compatibility.
Follow these steps to get your AWS account and App2Container correctly configured: https://docs.aws.amazon.com/app2container/latest/UserGuide/a2c-setup.html
Containerising Your Application
App2Container can containerise your legacy .NET application for deployment to Amazon ECS, Amazon EKS or AWS App Runner.
Follow the AWS-documented steps below to containerise your app.
- Step 1: Install App2Container
- Step 2: Initialize App2Container
- Step 3: Analyze your application
- Step 4: Transform your application
- Step 5: Deploy your application
- Step 6: Clean up
Configuring Your Application
Containerising your application and creating pipelines with App2Container requires configuration throughout the process.
When executed, App2Container will generate various JSON config files, which you can fine-tune for your own needs.
It is also possible to configure remote management of your App2Container containerisation workloads, so it can run on application servers, but be managed from another location.
See the links below to learn more.
Providing a fully comprehensive breakdown of the whole App2Container experience falls well outside the scope of this blog article. It’s also well documented by AWS themselves, so that it makes very little sense to repeat that information here.
For full details, see the official AWS App2Container Getting Started Guide.
App2Container acts in a generally secure manner, storing artefacts with encryption-at-rest enabled by default, enforcing encryption-in-transit for service integrations (including its use of Amazon ECR, Amazon ECS, Amazon EKS, AWS CloudFormation, CodePipeline, and Amazon S3), and ensuring that no secrets are persisted in plain-text or in log files.
Security, however, is an ongoing responsibility that is shared between AWS account owners and AWS themselves (dubbed the ‘Shared Responsibility Model’). You should be highly familiar with this model, and ensure that you take an active interest in continually improving your own security. Never rely on automation alone.
Additional AWS Services to Consider
We’ve already mentioned ECS, which offers managed compute for containers. But what about data persistence, logging and monitoring? These need to persist outside the containers, in order to facilitate horizontal scaling features.
Centralise your application and service metrics and logs with AWS CloudWatch.
Logs are streamed, and can be easily ingested by other services, such as ElasicSearch and Kibana for aggregation, or S3 for longer-term persistence, for example.
CloudWatch metrics (resource metrics) can be streamed to other AWS services for data analysis and to automate data-driven operations – such as adjusting instance sizes or triggering an auto-scaling action, for example.
CloudWatch Dashboards and alarms can be used to help you proactively monitor and respond to operations events, and uphold your own SLAs.
CloudWatch Events can provide customised, granular notifications of security service findings, storage service activities, compute state changes, and much, much more. CloudWatch Events integrates seamlessly with Simple Notification Service (SNS), AWS Lambda and various other AWS Services, to provide highly customisable event-driven processes.
Elastic File System provides managed NFS within the AWS Cloud. You can use this to provide shared filesystems between horizontally-scaled .NET Core instances, whether they are deployed directly onto EC2 instances or via containers. This is a common pattern for persisting filesystem state outside of running applications.
Furthermore, S3 is commonly used as a source for AWS CloudFront (CDN) distributions, so offloading your assets to S3 can be used to remove load from your .NET app layer, in more ways than one.
We also recommend using S3 for certain file storage, such as logs and audit trails, given that the storage costs are significantly lower for S3 compared to EBS or EFS. S3 is also better suited for storing large binaries.
Any memory-based caching that you might already have in your .NET application needs to be offloaded, if indeed you seriously intend to horizontally scale your application layer.
Although caching should never be treated as a defacto source-of-truth within your application, it does act to persist state, and that state needs to be consistent across the horizontal plane. It therefore needs to be migrated outside of the application.
AWS ElastiCache offers a managed service that supports both the MemCached and Redis caching engines. Direct integration with ElastiCache is achieved via the AWS SDK, which is available on NuGet.
AWS Relational Database Service (RDS) is a fully-managed database service, complete with multi-AZ support and cross-regional replication, automated backups, encryption-at-rest features.
Rather than self-host your SQL Server database in a standalone EC2 instance or ECS container, we recommend leveraging RDS, so you can focus your time on what matters most to your business: your customers.
You can also benefit from purchasing Reserved Instances for long-lived deployments, and save as much as 69% on your RDS costs versus on-demand pricing.
Consult an Expert
If you would like to learn more about migrating your application, or would prefer to have somebody else do it for you, then engaging with an experienced and verified AWS consulting partner is one of the best ways to progress.
Here at Ubertas Consulting, we offer expert advice on Windows Modernisation, as well as a range of other services. We routinely work on all manner of migration projects, from providing prescriptive guidance and consultation, to undertaking migration work on your behalf.
Consider a Well-Architected Review
Whilst migrating, you should consider applying for a AWS Well-Architected Review, regardless of whether you are new to AWS, or reasonably experienced.
Well-Architected Reviews are carried out by certified AWS APN partners, and cover the following 5 pillars:
- Cost Optimization
- Operational Excellence
- Performance Efficiency
Once a review is carried out, you will receive a report with recommended remediations. Immediate remediations are then carried out by us; your team can learn more about AWS best-practices along the way.
There are AWS funding programmes on offer for WAF reviews, making them an altogether cost-neutral exercise. With this in mind, there’s little reason not to go ahead with your first review!
Feel free to contact us at Ubertas Consulting for obligation-free advice about the WAF process and its funding options. We regularly perform a high number of WAF reviews and remediations, for all manner of businesses – from small startups, to multi-national streaming media organisations, manufacturers, government agencies, FinTech and security & compliance regulators.
Solutions Architect, Ubertas Consulting