The AWS Serverless feature you’re probably not using (yet)
We’ve all been there: you’re working on a fancy new feature, everything looks promising and works smoothly… and suddenly, you realize performance isn’t great. At first, you try to analyze the code and see where the problem is, but with complex microservices, it might be quite difficult to profile components on your machine, or even simulate a production-like environment.
This is where AWS CodeGuru Profiler comes in. As the utilization of serverless architecture continues to rise, the need for tools that can optimize the performance of these applications becomes increasingly important. CodeGuru Profiler is a powerful tool that collects runtime performance data from your live applications and provides data and recommendations that can help you fine-tune your application performance. It integrates seamlessly with Lambda functions, making the whole experience effortless.
In this blog post, I will provide an overview of the features of AWS CodeGuru Profiler for profiling Lambda functions and provide a step-by-step guide on how to set it up and use it to optimize the performance of your serverless applications.
Setting up CodeGuru Profiler
One of the best things about using CodeGuru Profiler is how easy it is to set up for supported Lambda runtimes — Python 3.6 to Python 3.9, and Java 8 (Corretto) and Java 11 (Corretto).
For those who prefer using the AWS Console, all you have to do is enable Code profiling under the Monitoring and operations tools in the Configuration tab of your function. This will add the CodeGuru Profiler layer to your function, set the necessary environment variables, and add the AmazonCodeGuruProfilerAgentAccess policy to your function’s role.
Alternatively, you can use the AWS CLI to set up CodeGuru Profiler. The following is an example of a snippet for a Python function:
#!/bin/bash
group_name=$1
lambda_arn=$2
# Create a new profiling group
aws codeguruprofiler create-profiling-group \
--compute-platform AWSLambda \
--profiling-group-name "$group_name"
AWS_LAMBDA_EXEC_WRAPPER=/opt/codeguru_profiler_lambda_exec
CODEGURU_VARIABLES="{
\"AWS_LAMBDA_EXEC_WRAPPER\": \"$AWS_LAMBDA_EXEC_WRAPPER\",
\"AWS_CODEGURU_PROFILER_TARGET_REGION\": \"us-west-2\",
\"AWS_CODEGURU_PROFILER_HEAP_SUMMARY_ENABLED\": \"true\",
\"AWS_CODEGURU_PROFILER_GROUP_NAME\": \"$group_name\"
}"
ROLE=$(aws lambda get-function --function-name FancyFunction | jq -r '.Configuration.Role' | awk -F '/' '{print $NF}')
# Grant permissions to Lambda to perform CodeGuru actions
aws iam attach-role-policy \
--role-name "$ROLE" \
--policy-arn arn:aws:iam::aws:policy/AmazonCodeGuruProfilerAgentAccess
# Update environment variables
aws lambda update-function-configuration \
--function-name "$lambda_arn" \
--environment "{ \"Variables\": $CODEGURU_VARIABLES }" \
--layers "arn:aws:lambda:us-west-2:157417159150:layer:AWSCodeGuruProfilerPythonAgentLambdaLayer:11"
It’s also integrated perfectly with AWS CDK:
export class LambdaStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
let region = Stack.of(this).region;
const LAYER_ARN = 'arn:aws:lambda:us-west-2:157417159150:layer:AWSCodeGuruProfilerPythonAgentLambdaLayer:11';
const CodeGuruLayer = LayerVersion.fromLayerVersionArn(this,
"CodeGuruLayer",
LAYER_ARN
);
const function = new lambda.Function(this, 'MyFunction', {
runtime: lambda.Runtime.PYTHON_3_9,
handler: 'lambda_handler.handler',
code: Code.fromAsset("./lambda_assets/"),
environment: {
'AWS_LAMBDA_EXEC_WRAPPER': '/opt/codeguru_profiler_lambda_exec',
},
layers: [CodeGuruLayer],
profiling: true,
});
function.add_to_role_policy(
iam.PolicyStatement(
effect=iam.Effect.ALLOW,
actions=[
"codeguru-profiler:ConfigureAgent",
"codeguru-profiler:CreateProfilingGroup",
"codeguru-profiler:PostAgentProfile",
],
resources=["arn:aws:codeguru-profiler:*:*:profilingGroup/*"],
)
);
}
}
It’s also worth noting, that CodeGuru Profiler can integrate with other runtime versions of Java or Python.
CodeGuru Profiler in action
To demonstrate the capabilities of CodeGuru Profiler, let’s create a new Lambda function with a simple handler that has a permission issue. The function includes several slow functions that simulate a performance problem:
import time
def slow_function():
time.sleep(2)
def another_slow_function():
time.sleep(3)
def even_slower_function():
time.sleep(4)
def innocent_looking_function():
even_slower_function()
another_slow_function()
def innocent_function():
time.sleep(0.2)
def lambda_handler(event, context):
innocent_function()
innocent_looking_function()
slow_function()
innocent_function()
another_slow_function()
even_slower_function()
innocent_looking_function()
innocent_function()
return {
'statusCode': 200,
'body': 'Hello, World!'
}
Using the instructions above, we’ll enable Code profiling from the Lambda console. Once the function is executed, we can view the results in the CodeGuru Profiler:
Obviously the example here is oversimplified, here is a sample demo provided by CodeGuru:
I really like the ability to to navigate between different frames in the CPU graph. This provides a detailed breakdown of what’s happening with the performance of your Lambda.
The heap summary graph also provides valuable insight into your application’s heap usage over time, although I would appreciate more granular information, such as heap usage for specific packages.
The recommendation panel is a great addition, as it offers actionable suggestions for mitigating performance issues. While I haven’t personally found the recommendations to be particularly helpful yet, I believe this feature has potential. I think that navigating through recommendations and CPU usage graph could be more user-friendly, and point to where in my codebase changes should be made.
It’s important to note that the data collected will be more reliable and accurate if you run your function multiple times over an extended period of time. Keep in mind that it may take a few minutes for the results to appear.
Overall, AWS CodeGuru Profiler is an excellent tool that can help you identify and fix performance issues, resulting in improved user experience and reduced costs. You should definitely give it a try.