Bundling Go Lambda Functions with the AWS CDK

mbacchi

Matt Bacchi

Posted on December 22, 2023

Bundling Go Lambda Functions with the AWS CDK

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:

Source code layout

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,
    });
...
Enter fullscreen mode Exit fullscreen mode

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:
...
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"}]
Enter fullscreen mode Exit fullscreen mode

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

💖 💪 🙅 🚩
mbacchi
Matt Bacchi

Posted on December 22, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related