One of the challenges with building cloud applications is finding the correct way to optimize it. To do so you need to have an insight into how your application performs in production and what are current bottlenecks and latencies which exist in your code. The usual cycle is that you monitor your application, you find existing bottlenecks, next you prioritize based on the latency or CPU/RAM impact and then you optimize your code. Finally, you need to monitor the app after the change to make sure that your fix actually helped.
There are a lot of APM (Application Performance Monitoring) tools out there that could be used to monitor your application in production and they have different ways of gathering and visualizing data about your application. Amazon Codeguru takes a step further in the direction of handling bottlenecks and provides several great additions to the way how the optimization cycle works:
- It provides automatic insights into what are possible ways to optimize your app. Service provides insights based on the combination of knowledge base and ML algorithms.
- It puts a dollar value on each bottleneck based on the instance which you are using. This feature enables you to prioritize optimization between multiple apps while also providing a way to calculate ROI for decreasing technical debt. Also, it provides a way to find methods that contribute the most to the latency of the app and estimate what are the possibilities for decreasing your app’s latency.
- It monitors data continuously and can notify you in case there are any anomalies in your app related. The service also generates cloudwatch metrics meaning you can add them in your main dashboard for low-level app monitoring and easier debugging.
In this post, I’ll cover a demo on how to use Amazon Codeguru with a Java application running on AWS Batch. I will use AWS Step Functions to handle AWS Batch invocations and will build the Java application using Maven.
We will cover the following:
- Building up and testing a Java application with Amazon CodeGuru Profiler
- Deploying Java application with AWS Batch and providing necessary permissions
- Monitoring application using Amazon CodeGuru dashboard
- Installed AWS CLI
- Installed Java 8 and Maven (optional)
- Installed docker
- Installed serverless frameworks with plugins
- Java project files (demoApplication.java, pom.xml)
- Dockerfile for the image which we will run
- Serverless config file which contains infrastructure which we will deploy
We will use maven to build our Java project. If you have Java 8 and maven installed, you can run the following commands to build the project and run it locally. You will also need to have your AWS credentials set up locally (https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html). Keep in mind that we will deploy necessary infrastructure in the next step so you would be able to run code successfully only once necessary AWS Lambda is deployed. Alternatively, you can deploy AWS Lambda manually and update demoApplication.java with its name. Also, you will need to set up profiling group “demoApplication” in CodeGuru dashboard (https://console.aws.amazon.com/codeguru/profiler/search):
git clone https://github.com/ryfeus/stepfunctions2processing.git cd aws-batch-with-profiler/docker mvn clean compile assembly:single
You can run your Java code locally with CodeGuru Profiler and it will report profiling data to AWS. It can be used for both testing your code locally and for profiling your code without deploying it to the cloud.
java -jar target/demo-1.0.0-jar-with-dependencies.jar
In this demo we are using CodeGuru Profiler library to run Profiler within application. Alternative way is to download Profiler jar directly (from https://d1osg35nybn3tt.cloudfront.net/) and the run it as an additional parameter for Java command. In this case you don’t need to update your code to include Profiler logic and you don’t need to include dependencies and repositories to your maven project.
Now let’s build docker image which we will run in AWS Batch by using docker.
docker build -t test-java-app-with-profiler .
Once image is built, let’s create repository in AWS ECR and push docker image there. We would need to login into ECR, create repository, tag built image and push it to ECR.
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <accountId>.dkr.ecr.us-east-1.amazonaws.com aws ecr create-repository --repository-name test-java-app-with-profiler docker tag test-java-app-with-profiler:latest <accountId>.dkr.ecr.us-east-1.amazonaws.com/test-java-app-with-profiler:latest docker push <accountId>.dkr.ecr.us-east-1.amazonaws.com/test-java-app-with-profiler:latest
Once the container image is pushed, we can install dependencies for the serverless framework and deploy services by using the following command:
cd aws-batch-with-profiler/aws-batch npm install serverless deploy
Here is how the output will look like:
Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Creating Stack... Serverless: Checking Stack create progress... ..... Serverless: Stack create finished... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service StepFuncBatchWithProfiler.zip file to S3 (32.94 KB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... .......................................................................................... Serverless: Stack update finished... Service Information service: StepFuncBatchWithProfiler stage: dev region: us-east-1 stack: StepFuncBatchWithProfiler-dev resources: 30 api keys: None endpoints: functions: async: StepFuncBatchWithProfiler-dev-async layers: None Serverless StepFunctions OutPuts endpoints: GET - https://<urlPrefix>.execute-api.us-east-1.amazonaws.com/dev/startFunction
We can use the url in the output to call deployed AWS Step Functions and AWS Batch. It could be done, for example, by using curl:
After that we can take a look at Step Functions execution graph at AWS Step Functions dashboard (https://console.aws.amazon.com/states/home):
We can check logs produced by the application in Cloudwatch group “aws/batch/job” (https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logsV2:log-groups/log-group/$252Faws$252Fbatch$252Fjob). It should look in the following way:
Once everything is set up and you’ve run multiple jobs with CodeGuru Profiler running, you can start monitoring the results. CodeGuru Profiler provides data in a flame graph format. You can see the stack of your code’ and libraries’ methods and for each method you can see the CPU or amount of time which was spent within the method. This allows you to see which methods give the most latency and what are the places for possible optimizations. One of the main advantages is that you also will be able to see cost per each method which provides a way to estimate savings of potential optimizations.
We deployed a demo Java application with Amazon CodeGuru Profiler on AWS Batch, wrapped by AWS Step Functions. You can use the same project settings to deploy your application and test it to see how it performs in the cloud and get recommendations. Amazon CodeGuru has a 90 day trial so you can do so completely free.