Matt Bacchi
Posted on December 22, 2023
Recently the Lambda Go runtime has changed from using the Go 1.x managed runtime to using the provided runtimes which have been historically used for custom runtimes (i.e. Rust.) The former go1.x
runtime is being deprecated on January 8, 2024 (quite soon) and the new runtimes provided.al2023
or provided.al2
are expected to be used.
With the introduction of these new runtimes, all of our Go binaries must now be called bootstrap
and be located at the root of the zip file used to deploy the function.
If we have two functions built by separate function code, in the go1.x
runtime the naming convention would allow us to name them differently (i.e. create
vs. list
). Now we must name both function handlers bootstrap
, potentially causing confusion during the bundling process.
In today's blog post, we'll describe how to bundle two different Go functions with the same name in a single CDK stack.
AWS CDK Deployment Using Typescript
I'll be using Typescript as the language for the AWS CDK deployment. It might be strange to mix languages for CDK deployment (Typescript) and the Lambda function (Go), but CDK uses Typescript as its main language and most examples are written in that. The reason for using Go in this example is because many deployments need to use compiled languages for task duration and performance (not necessarily to improve cold start time.)
Source Organization
As described earlier, in the past we could have kept the names of the Go function source files in the same directory and compiled them separately to different file names. Now that the handler file name has to be the same (bootstrap
), they can't be output to the same directory any longer.
The layout of the files now looks like the following, with each function in its own subdirectory in the lib/functions
directory:
Bundling Go Functions
Now that we have the function code in separate directories, we can bundle them using the Alpha CDK construct for a Go Function, shown below.
export class BasicGoLambdaStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const table = new TableV2(this, 'Table', {
partitionKey: { name: 'id', type: AttributeType.STRING },
removalPolicy: RemovalPolicy.DESTROY,
});
const createFunction = new GoFunction(this, 'CreateFunction', {
entry: 'lib/functions/create',
environment: {
"DYNAMODB_TABLE": table.tableName
},
logRetention: RetentionDays.ONE_WEEK,
});
const listFunction = new GoFunction(this, 'ListFunction', {
entry: 'lib/functions/list',
environment: {
"DYNAMODB_TABLE": table.tableName
},
logRetention: RetentionDays.ONE_WEEK,
});
...
The above configuration is obviously fairly straightforward. But before I found the aws-lambda-go-alpha module, I attempted (mostly unsuccessfully) to use the aws-lambda-function Function construct which required using the Code.fromAsset method with the ILocalBundling interface. This was extremely uncooperative, I found multiple blogs and suggestions on how to configure this, and in the end I did get it working, but the solution I came up with was ugly and actually packaged the Go source file in the zip file with the bootstrap handler. Not cool.
It's great that the CDK now supports Go Lambda functions as a full blown construct. While NodeJS functions have been supported for much longer it makes sense to have Go supported the same way, because the aggravation I endured packaging these Go handlers with the standard Lambda Function construct was a serious pain in the ass.
Verifying the Output
Now that we have these defined, we can package the artifacts using cdk synth
and verify that the bootstrap
binary was created for both functions.
We can see here that the first messages output from the cdk synth
command are the two functions being bundled:
$ cdk synth
Bundling asset BasicGoLambdaStack/CreateFunction/Code/Stage...
Bundling asset BasicGoLambdaStack/ListFunction/Code/Stage...
Resources:
...
And when we look in the cdk.out
directory which is where our intermediate CDK files are placed during packaging, we see that the two Lambda Function output directories itemized in the stack asset JSON file (called BasicGoLambdaStack.assets.json
) have binaries in them (ignore the third .js asset, that's for the cloudwatch logs):
$ tree cdk.out/
cdk.out/
├── asset.4e26bf2d0a26f2097fb2b261f22bb51e3f6b4b52635777b1e54edbd8e2d58c35
│ └── index.js
├── asset.b0bae29696f98febe5e6a655c4f61466ad71ebfa0071b46a15e917a1d307333c
│ └── bootstrap
├── asset.f153e459ffb70033083e7507aaa06c00f4b13a792fad71433c11b5f050078e74
│ └── bootstrap
├── BasicGoLambdaStack.assets.json
├── BasicGoLambdaStack.template.json
├── cdk.out
├── manifest.json
└── tree.json
4 directories, 8 files
After running cdk synth
to verify things, we can deploy with cdk deploy
.
Success
Now we can execute the functions and see them working as expected:
$ curl -X POST -H "Content-Type: application/json" https://someurl.execute-api.us-west-2.amazonaws.com/todos --data '{ "title": "stuff", "details": "really some stuff"}'
{"id":"80cf566a-937b-4aef-98ef-48b84ee75c01","title":"stuff","details":"really some stuff"}
$ curl -X GET https://someurl.execute-api.us-west-2.amazonaws.com/todos
[{"id":"80cf566a-937b-4aef-98ef-48b84ee75c01","title":"stuff","details":"really some stuff"}]
Summary
The new requirements for using the provided runtimes for Go Lambda Functions poses some minor challenges (especially if you have to migrate already running Go 1.x Lambda Functions) but thankfully the aws-lambda-go-alpha module makes it much more straightforward than bundling these manually.
I hope you've gotten a good understanding of how to bundle Go Lambda functions by reading this. I know I learned a lot, and this will come in handy as I use Go for Lambdas going forward.
Cover photo by Jesse De Meulenaere on Unsplash
Posted on December 22, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.