Containers

Modernize Java and .NET applications remotely using AWS App2Container

Since the launch of AWS App2Container, customers have been asking for the ability to remotely manage the migrations of Java and .NET applications running on Windows or Linux hosts. Beginning with the version 1.2 of App2Container, users can accomplish containerization of their workloads without installing A2C software on the application servers. The remote execution feature enables you to containerize applications running in on-premise data centers. Users can now run App2Container on any central managed Windows or Linux host against an application server to containerize it and deploy it to Amazon Elastic Container Service (Amazon ECS) or Amazon Elastic Kubernetes Service (Amazon EKS).

In this blog, we will containerize a Java Spring Boot application running on an EC2 instance using App2Container installed in a Cloud 9 environment and deploy the containerized application to an Amazon EKS cluster. We will start with installation of A2C on an AWS Cloud9 environment and configure it with an EC2 instance running a Java spring boot application. The EC2 instance can be assumed as an application server running in your on-premises data center that has network connectivity from the A2C remote server (Cloud9 environment). A2C analyzes the application server and generates the artifacts required for modernization of the application. A containerized version of the spring boot application is then deployed to a new Amazon EKS cluster.

Set up App2Container for remote execution

To get started, create a Cloud9 environment with an Amazon Linux2 operating system. Create a new IAM role with AdministratorAccess policy, attach it to your Cloud 9 instance, and disable AWS managed credentials for this environment. The first set of steps listed here can be used to create the Cloud9 environment and configure the IAM role. The remainder of the blog has commands that will be run in the Cloud9 environment that will serve as the remote server for App2Container. If you do not opt to use Cloud9, you can use your own EC2 or on-premises Linux environment.
Refer to Identity and access management in App2Container permissions page for options of fine grained permissions while using App2Container.

Log in to the Cloud9 host to install and initialize App2Container in the Cloud9 environment. Install Docker service and create an S3 bucket to store artifacts. We will be using the us-east-1 Region for this demonstration, please change value of AWS_DEFAULT_REGION to the Region of your choice.

$ export AWS_DEFAULT_REGION=us-east-1

$ cd /tmp
$ curl -o AWSApp2Container-installer-linux.tar.gz https://app2container-release-us-east-1.s3.us-east-1.amazonaws.com/latest/linux/AWSApp2Container-installer-linux.tar.gz
$ tar xvf AWSApp2Container-installer-linux.tar.gz
$ echo y |sudo ./install.sh

Run the following to validate App2Container installation. Note that App2Container commands should be run as a user with root permissions.

$ sudo app2container --version

The App2Container tool requires Docker Engine to be installed on the application server where it’s being containerized. Follow the commands below to install Docker Engine, and start and enable service.
Note: You can skip the following instructions if you are using Cloud9. Docker Engine is installed by default in a Cloud9 environment.

$ sudo yum install docker -y
$ sudo systemctl start docker
$ sudo systemctl enable docker

An S3 bucket is required to store artifacts and AWS CloudFormation templates generated by App2Container. Create the S3 bucket as below. Amazon S3 bucket names must be unique globally. If you get the “Bucket name already exists” or “BucketAlreadyExists” error, you must use a different bucket name to create the bucket. As shown below, you are appending the date to the bucket name for a unique name.


$ aws s3 mb s3://a2c-remote-`date +%s`
make_bucket: a2c-remote-1617832770

$ aws s3 ls |grep a2c-remote
2021-04-07 21:59:31 a2c-remote-1617832770

Resize the EBS volume attached to your Cloud9 instance to 20GB. This is to ensure sufficient storage for the artifacts that will be generated in the later steps. Configure default region nameby running the command aws configure and specify only the region in this step while skipping other inputs.

Run the init command for one time initialization of the App2Container CLI. Ensure that you enter the S3 bucket name created in the previous step as the application artifacts in following steps will be stored in that bucket.

$ sudo app2container init
Workspace directory path for artifacts[default: /root/app2container]: 
Use AWS EC2 Instance profile 'arn:aws:iam::<Account ID>:instance-profile/<Role Name>' configured with this instance? (Y/N)[default: y]:  
Which AWS Region to use?[default: us-east-1]: 
Optional S3 bucket for application artifacts: a2c-remote-1617832770
Report usage metrics to AWS? (Y/N)[default: y]: 
Automatically upload logs and App2Container generated artifacts on crashes and internal errors? (Y/N)[default: y]: 
Require images to be signed using Docker Content Trust (DCT)? (Y/N)[default: n]: 
Configuration saved
$

Set up an application server running a Java spring boot application

In the following section, you will be using the AWS CLI to configure and run a Java spring boot application on an EC2 instance.

Please run the below command to create SSH key and import them to Secrets Manager in your AWS account. This key will be used by A2C tool for authentication from remote server to the application server. Refer to Manage secrets for AWS App2Container in the A2C documentation for more details.

$ ssh-keygen -t rsa -f ~/.ssh/a2crsakey -q -P ""
$ aws ec2 import-key-pair --key-name "A2CKEY" \
--public-key-material fileb://~/.ssh/a2crsakey.pub
$ B64KEY=$(base64 ~/.ssh/a2crsakey)
$ echo -e $'{\n "username": "ec2-user",\n "key": "'$B64KEY'"\n}' >> a2ckey.json
$ aws secretsmanager create-secret --name a2ckey --description "A2C secrets" --secret-string file://a2ckey.json

The following commands will create a user data shell script to install the spring boot application, security group for accessing the application host, and an EC2 instance that runs the sample Java application using the user data shell script. The security group in this example has ports 22 and 8080 open to the world but it is advised to restrict them to specific IPs in your application environment.

$ echo "User Data creation to install springboot application"
$ cat <<EOF >appuserdata.sh
#!/bin/bash
yum -y install git maven
git clone https://github.com/aws-samples/kubernetes-for-java-developers.git
cd kubernetes-for-java-developers/app
mvn spring-boot:run &
EOF

$ echo "Below commands creates security group and ingress rules"
$ aws ec2 create-security-group --group-name A2CSecurityGroup --description "A2C security group"
$ aws ec2 authorize-security-group-ingress --group-name A2CSecurityGroup \
--protocol tcp --port 22 --cidr 0.0.0.0/0
$ aws ec2 authorize-security-group-ingress --group-name A2CSecurityGroup \
--protocol tcp --port 8080 --cidr 0.0.0.0/0

$ echo "Below commands creates ec2 host with springboot application installed"
$ aws ec2 run-instances \
--image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 \
--count 1 --instance-type t2.medium --key-name A2CKEY \
--block-device-mapping "[ { \"DeviceName\": \"/dev/xvda\", \"Ebs\": { \"VolumeSize\": 32 } } ]" \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=APPHost}]' \
--security-groups A2CSecurityGroup \
--user-data file://appuserdata.sh

To validate the spring boot application, you can get the IP address of application server using the AWS CLI or from the AWS Management Console. Run the below command to get the IP address of the host and test the application server. Execution of the curl command should return a “Hello” response. The instance might take a few minutes to be in running state before responding to the curl command.

$ aws ec2 describe-instances --filter "Name=tag:Name,Values=APPHost" \
--query "Reservations[*].Instances[*].[PublicIpAddress, Tags[?Key=='Name'].Value|[0]]" \
--output text
$ curl http://<IP Address>:8080/hello - Note: IP address is masked in command for security reasons.
Hello

Containerize your application using the App2Container remote feature

Run this command to configure application server connectivity from Cloud9. You would need the IP or FQDN of the application server and ARN of the secret used to store the SSH key. Command to retrieve ARN for SSH key from Secrets Manager is listed below. The second command retrieves the EC2 instance on which the Java application is running.

$ aws secretsmanager list-secrets --query "SecretList[?Name=='a2ckey']"
$ aws ec2 describe-instances --filter "Name=tag:Name,Values=APPHost" \
--query "Reservations[*].Instances[*].[PublicIpAddress, Tags[?Key=='Name'].Value|[0]]" \
--output text
$ sudo app2container remote configure
Server IP address: <AppServerIP>
Server FQDN (Fully Qualified Domain Name): 
Authentication method to be used (key/cert)[default: key]: 
Secret ARN for remote connection credentials: arn:aws:secretsmanager:us-east-1:xxxxxxxxxxxx:secret:a2c-remote-1-ezY0tZ
Continue to configure another server? (y/N)[default: n]: 
? Configure successful, you can view hosts config file at: /root/.app2container-config/remote_hosts.conf

You can run remote inventory command to view Java applications running on the host. Each application is identified with a unique application ID. Note the application ID listed in inventory file, and replace it in subsequent steps where application ID is used.

$ sudo app2container remote inventory --target <AppServerIP>
✔ Server inventory has been stored under /root/app2container/remote/<AppServerIP>/inventory.json
? Remote inventory retrieved successfully

The remote analyze command generates a report of the application running on the host. You can review analysis.json and edit according to the application requirements. The container parameters and analysis info can be updated as required. In this demonstration, you will use default settings created by analyze command.

$ sudo app2container remote analyze --application-id  java-generic-dc310dd41a0a --target <AppServerIP>
? Analysis successful for application java-generic-dc310dd41a0a

? Next Steps:
1. View the application analysis file at /root/app2container/remote/<AppServerIP>/java-generic-dc310dd41a0a/analysis.json.
2. Edit the application analysis file as needed.
3. Start the extraction process using the following command: app2container remote extract --target <AppServerIP> --application-id java-generic-dc310dd41a0a

The remote extract command generates an application archive for the specified application server on the Cloud9 environment, which is your A2C remote server. This step takes a few minutes to complete.

$ sudo app2container remote extract --application-id  java-generic-dc310dd41a0a --target <AppServerIP>
? Extraction successful for application java-generic-dc310dd41a0a

? Next Step:
1. Please initiate containerization using "app2container containerize --input-archive /root/app2container/remote/<AppServerIP>/java-generic-dc310dd41a0a/java-generic-dc310dd41a0a-extraction.tar"

Next, you will generate Docker images out of the running application using containerize command.

$ sudo app2container containerize --input-archive /root/app2container/remote/<AppServerIP>/java-generic-dc310dd41a0a/java-generic-dc310dd41a0a-extraction.tar
✔ AWS prerequisite check succeeded
✔ Docker prerequisite check succeeded
✔ Extracted input archive
✔ Entry file generated
✔ Dockerfile generated under /root/app2container/java-generic-dc310dd41a0a/Artifacts
✔ Generated dockerfile.update under /root/app2container/java-generic-dc310dd41a0a/Artifacts
✔ Generated deployment file at /root/app2container/java-generic-dc310dd41a0a/deployment.json
? Containerization successful. Generated docker image java-generic-dc310dd41a0a

? You are all set to test and deploy your container image.

Next Steps:
1. View the container image with "docker images" and test the application with "docker run -it java-generic-dc310dd41a0a".
2. When you are ready to deploy to AWS, please edit the deployment file as needed at /root/app2container/java-generic-dc310dd41a0a/deployment.json.
3. Generate deployment artifacts using "app2container generate app-deployment --application-id java-generic-dc310dd41a0a".

Verify that the Docker image was created successfully using docker images command.

$ sudo docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
java-generic-dc310dd41a0a   latest              ae38e940aed9        49 seconds ago      4.27GB
amazonlinux                 2                   935e8db88df5        2 days ago     

In addition to creating Docker images, App2Container also creates artifacts required to modernize applications with Amazon ECS and Amazon EKS. For this demonstration, we will be using Amazon EKS. Update the file as below for the Amazon EKS configuration. Ensure that you change createEksArtifacts to True for EKS and createEcsArtifacts to False for ECS in the deployment file.

       "ecsParameters": {
              "createEcsArtifacts": false,
              ....
              ....
       "eksParameters": {
              "createEksArtifacts": true,
              "stackName": "a2c-java-generic-d15cb42f23d9-EKS",
              "reuseResources": {
                     "vpcId": "",
                     "reuseExistingA2cStack": {
                            "cfnStackName": ""
                     },
                     "sshKeyPairName": ""
              },
              "gMSAParameters": {
                     "domainSecretsArn": "",
                     "domainDNSName": "",
                     "domainNetBIOSName": "",
                     "createGMSA": false,
                     "gMSAName": ""
              }
       }

Deploy application using the App2Container remote feature
Next, you will use the generate app-deployment command with --deploy flag, which creates an Amazon ECR registry to store docker images, uploads CloudFormation templates to S3, and deploys the templates. The CloudFormation stack will create an EKS cluster with EC2 as worker nodes and deploys the pod/service in the EKS cluster. You can log in to the AWS Management Console and track the status in CloudFormation service for --deploy used in this step. You can deploy this application to an existing EKS cluster in your account by skipping the deploy flag and editing the CloudFormation template. The documentation for configuring container deployment has additional details of the parameters that can be modified as needed before deployment.

$ sudo app2container generate app-deployment  --application-id java-generic-dc310dd41a0a --deploy
✔ AWS prerequisite check succeeded
✔ Docker prerequisite check succeeded
✔ Created ECR Repository
✔ Uploaded CloudFormation resources to S3 Bucket: a2c-remote-1614362000
✔ Generated CloudFormation Master template at: /root/app2container/java-generic-dc310dd41a0a/EksDeployment/amazon-eks-entrypoint-new-vpc.yaml
✔ Initiated CloudFormation stack creation. This may take a few minutes. To track progress, open the AWS CloudFormation console.
? Deployment successful for application java-generic-dc310dd41a0a

? You're all set to use AWS CloudFormation to manage your application stack.

Successfully created EKS stack a2c-java-generic-dc310dd41a0a-EKS.  Check the  AWS CloudFormation Console for details.
We have also set up your kubectl config to access your cluster at: /root/.kube/config

Next Steps:
1. Setup a pipeline for your application stack using:

    app2container generate pipeline --application-id java-generic-dc310dd41a0a
$ 

Finally, the Java application is deployed in EKS now. A Load Balancer Endpoint is deployed for the application service and endpoint name can be found on the EC2 page of the AWS Management Console or by running kubectl get services command as below.

$ sudo curl --silent --location -o /usr/local/bin/kubectl \
   https://amazon-eks.s3.us-west-2.amazonaws.com/1.17.11/2020-09-18/bin/linux/amd64/kubectl
$ sudo chmod +x /usr/local/bin/kubectl
$ sudo aws eks update-kubeconfig --name <EKS Clustername>

$ sudo /usr/local/bin/kubectl get pods
NAME                                                    READY   STATUS    RESTARTS   AGE
java-generic-dc310dd41a0a-deployment-867b54f57f-t6gtq   1/1     Running   0          41m

$ sudo /usr/local/bin/kubectl get services
NAME                                TYPE           CLUSTER-IP     EXTERNAL-IP                                                               PORT(S)          AGE
java-generic-dc310dd41a0a-service   LoadBalancer   172.20.14.56   <DNS Name>.<region>.elb.amazonaws.com   8080:31043/TCP   41m
kubernetes                          ClusterIP      172.20.0.1     <none>                                                                    443/TCP          57m

You can test from curl utility or from web browser as below. DNS Name and Region are masked for security reasons. Replace them with correct values from previous section output.

$ curl http://*<DNS Name>**.<region>*.elb.amazonaws.com:8080/hello
Hello

You’ve successfully ported a Java spring boot application running on a standalone host to Amazon EKS using the App2Container remote execution feature. If a similar exercise was to be done for a .NET application running on a Windows server, the login credentials should be saved in Secrets Manager while rest of the process remains same. While we used Cloud9 environment as a worker machine which served as the Linux worker host, you can also do the same from a Windows Server 2016 or 2019.

AWS App2Container is offered for free. You only pay for the actual usage of AWS services like EC2, ECS, EKS, and S3. For more details, please refer to App2Container FAQs, App2Container documentation, and other AWS blogs. Have technical questions or feature requests related to App2Container tool? Email us at: app2container-support@amazon.com