AWS Cloud Operations Blog

Accelerate End-to-End Application Modernization with AWS App2Container and AWS Migration Hub Refactor Spaces

This blog post was written with contributions from Gaurav Parashar who is prior AWS

Customers often have challenges accelerating the modernization of their applications. The complexity of refactoring a monolith application often provides hurdles in depth of expertise, time and effort. In this blog, we will explore two mechanisms that can help you accelerate your modernization journey. 1) creating a modernized container from a monolith in an automated fashion, and 2) creating an abstraction layer to abstract your end users from the changes as you incrementally refactor your application.

Our solution consists of four main steps:

  • Containerize the monolithic application using AWS App2Container (A2C)
  • Create an environment to host the application as you begin incrementally refactoring, and rerouting functions to the new microservices.
  • Launch microservices to replace existing functionality from the monolith.

Reroute traffic from the monolith to the new microservices.

Conceptual Architecture Diagram on End to End Modernization.

Figure.1 Conceptual Architecture Diagram on End to End Modernization.

Containerize the Monolithic Application Using App2Container (A2C).

Modernizing traditional applications running in virtual machines is often complex, challenging, and involves significant time and costs. Moving legacy applications to containers is often a good starting point for the application modernization journey. Containers provide many benefits, such as:

  • Portability – Containers are designed to be lightweight and portable.  This helps as you move them between development, testing, and production environments.
  • Efficiency – Containers consume fewer resources than virtual machines.  This is because they share the same operating system kernel as the host system, which helps them to start up faster.
  • Scalability – Containers can be easily scaled up or down, depending on the workload, making it to manage the resources needed to run an application.
  • DevOps Enablement – Containers can be a key enabler of DevOps practices.  They help with automated deployment and testing of applications.  This also reduces time to add new features and updates into production.

App2Container (A2C) is a command line tool that helps transform existing applications running in virtual machines (along with bare metal and EC2 instances) into containers. It does this transformation without requiring any changes to your code. A2C discovers applications, identifies dependencies, and generates relevant artifacts for deployment. You can choose to deploy to Amazon Elastic Container Service (Amazon ECS), Amazon Elastic Kubernetes Services (EKS), and AWS App Runner. A2C also provides integration with AWS CodeBuild and AWS CodeDeploy, which creates a repeatable way to build and deploy containerized applications. A list of the various application types that A2C currently supports can be found here.

Here are the high-level steps that we will follow to containerize our monolith using A2C:

  1. Discover and analyze applications using the app2container inventory & app2container analyze commands.
  2. Create deployment artifacts using the app2container containerize and app2container generate app-deployment commands.
  3. Deploy the application using the app2container generate app-deployment command by passing a – -deploy flag.
  4. Optionally, you can also create a CI/CD pipeline for your application.

You can view all commands that App2Container provides here.

For our example, we have a monolithic Java application running on an EC2 instance that we will containerize and later deploy to Amazon ECS. This could also be an application that is running on-premises.

There are a few prerequisites that must be completed to use A2C in your environment. After completing those prerequisites, you will install the A2C binary on your source server (where your application is running). You can follow the installation steps for your source server depending on your operating system.

After installing the A2C tool, we must perform a one-time initialization. This helps us configure the global settings required to set up the A2C environment. We will use the following command and provide information for these fields as prompted. Once we have completed the setup and the initialization tasks, we will begin to analyze our application.

sudo app2container init

Screenshot of when you run App2Container init

Figure 2. Output of App2Container init

Next, we will discover the applications running on the server using the following command. In this example, A2C was able to identify the JAVA application running on the source server and also attach an identifier to this application (java-tomcat-1fd8d877).

app2container inventory

Screenshot when you run App2Container inventory

Figure 3. Output of running App2Container inventory

Then, we analyze the application by using the following command. This analyzes the running applications and identifies the dependencies that are required for containerization. The output of this command is an analysis.json file that will be used in the later steps.

app2container analyze

Screenshot when you run App2Container Analyze

Figure 4. Command output of running App2Container Analyze

The analysis.json file contains details regarding the application, ports, application type, environment variables, and their dependencies. The analysis.json file has both an editable, and a non-editable section. The editable section includes the containerParameters section where you can specify your preferred settings like ContainerBaseImage or Imagetag. These parameters will be used during the containerization process. The non-editable section includes application-level analysis information which A2C will use during containerization, such as OS data, ports in use, dependencies, and software libraries. For this example, we won’t be changing the analysis.json file.

Now we will containerize and deploy the application. Currently A2C supports deployment of containerized applications to Amazon ECS, Amazon EKS, and AWS App Runner. In this example, we will containerize the applications and deploy to an Amazon ECS cluster.

To start the containerization process, we will use the following command. Be sure to replace the application-id to match your own.

app2container containerize –application-id java-tomcat-1fd8d877

Screenshot when you run containerize command

Figure 5. Output of running containerize command

This command will create a Docker image for our application. This image is based on the parameters present in the analysis.json file generated a few steps earlier. Additionally, it will also create a Deployment.json file and save this in your current working directory. This file is used for configuring deployment of application containers.

Screenshot of docker images generated from A2C

Figure 6. Output of docker images generated from A2C

Depending on the container orchestration platform that you choose for your application deployment, you can edit the file accordingly. As an example, since we are choosing Amazon ECS as our container orchestration platform, we will configure createEcsArtifacts as True (as shown in the screenshot). All of the fields in this file are configurable and you can refer here for more details around each field.

Deployment.json file generated by App2Container

Figure 7. Deployment.json file generated by App2Container

Once we have configured our deployment.json file, we will generate the artifacts needed to deploy our application. A2C uses Amazon ECR as the container registry.

app2container generate app-deployment --application-id java-tomcat-1fd8d877

Generating App Deployment using App2Container

Figure 8. Generating App Deployment using App2Container

This command creates an Amazon ECR repository and pushes the container image for our application to the repository. Since we chose Amazon ECS as our platform choice, it created the Amazon ECS task definition, and the necessary CloudFormation templates to provision the infrastructure. It also pushed these to an S3 bucket. This is the S3 bucket that we used when we initialized A2C.

At this stage, we have used A2C to extract and containerize our application and create the deployment artifacts. Now we are ready to deploy to AWS.

To deploy the application to AWS, we will use the previous command adding a –deploy flag to it.

app2container generate app-deployment --application-id java-tomcat-1fd8d877 --deploy

Deployment of Container Image using App2Container

Figure 9. Deployment of Container Image using App2Container

As we see in the screenshot, A2C initiates the CloudFormation stack creation and deploys the components in the AWS account. It also provides the URL where our application can be accessed.

So far, we have used A2C to containerize the monolith and deploy it to an Amazon ECS cluster as a containerized application. Next, we will look at how to use AWS Migration Hub Refactor Spaces to help create an environment to refactor the application. It will use the strangler fig pattern to help create microservices from the monolith application.

Create a Refactor Environment and Default Route to the Monolith Using Refactor Spaces

AWS Migration Hub Refactor Spaces is recommended for modernizing applications using the strangler fig pattern for incremental refactoring. Refactor Spaces helps reduce the undifferentiated heavy lifting of building and operating AWS infrastructure for incremental refactoring. You can use Refactor Spaces to help reduce risk when evolving applications into microservices or extending existing applications with new features written in microservices.

We will start by creating an environment and application in the Refactor Space console (AWS CDKSDKCLI, or CloudFormation can also be used). A Refactor Spaces environment contains all the applications, services, and AWS infrastructure used route traffic while you incrementally refactor the monolith application. It orchestrates AWS services using best practices to simplify refactoring, shield application consumers from infrastructure changes as you modernize, and achieve deployment independence.

A video example of the creating a Refactor Spaces environment and application can be found here.

Create Refactor Spaces Environment

Figure 10. Create Refactor Spaces Environment

Once we have created the environment and the application, we will create a service and configure the service endpoint to our monolith application endpoint. The endpoint is from A2C as shown in the following screenshot.

Create Refactor Spaces Service

Figure 11. Create Refactor Spaces Service

We will set this service as the default route for this application and create this in an active state.

Once the service and the route are created, we can then access our monolith using the Proxy URL as shown in the following screenshot. The API Gateway API provisioned by Refactor Spaces is now the new front door to our monolith.

API Gateway Proxy URL

Figure 12. API Gateway Proxy URL

Create Microservices from the Monolith

When transitioning from a monolithic architecture to a microservices-based architecture, it is important to start breaking down the monolith to smaller, independent services.

A common approach is to identify the functional boundaries of the monolith and group related functionality together into separate services. You can read more here to find how can you approach it using domain-driven design.

Create Routes to the Microservices

Now that our microservices have been created and deployed, we are ready route traffic to them. This will route the traffic away from the monolith, which holds the default route we created.This follows the strangler fig pattern of incrementally moving traffic away from the monolith and to the new microservices.

Refactor Spaces simplifies this process for us by removing some of the heavy lifting.  After we create a service for each of our microservices, we then add the routes for the services.

Service endpoints are one of two types: an HTTP/HTTPS URL, or an AWS Lambda function.

A Refactor Spaces route is a proxy matching rule that forwards a request to a service. Each request is run against the set of routes configured in the application. If a route matches, the request is sent to the target service configured for that route. Routes are configured on the application’s Amazon API Gateway proxy.

You can also use AWS Proton to create a scalable self-service platform for developers by following the steps mentioned here.

Clean up

To avoid incurring charges for this demo example, you can do the following steps.

a)   Go to the Refactor Spaces console and delete the routes and services that you created for your monolith and each microservice.

b)   Delete the Refactor Spaces application. Then delete the Refactor Spaces environment that you created.

c)   For the resources that A2C created for you, go to CloudFormation and delete the stack named ‘a2c-java-tomcat-17d8d877-ECS’. Note that this name would be different for your setup.

Conclusion

In this blog, we containerized our monolithic application using AWS App2Container (A2C) and created the environment needed to run our application. We created a refactor environment using AWS Migration Hub Refactor Spaces to help reduce the heavy lifting of building and operating AWS infrastructure required for modernizing applications.  This also helped us reduce risk to application consumers as we incrementally built new microservices while we deconstructed our monolith.

Seshanth Kannappan

Seshanth Kannappan is a Senior Modernization Solutions Architect at AWS. He is currently working with customers to accelerate modernization of their applications and enhance developer productivity. In his free time, he likes to watch live music, theater and try new restaurants in New York City.

Steven Dolan

Steven Dolan is a Sr. Technical Account Manager at AWS with more than 16 years of industry experience, including roles in cloud architecture, systems engineering, and network administration.