Docker Bundling

Have installed

With CDK Docker bundling you can deploy GO Lambda functions without having go locally installed. Usually it’s more efficient to build GO with the GO compiler. But in the case you dont want to install the GO tools, you can build the GO app inside a local Docker container.

We distinguish three different types of Container usage with lambda:

  1. Run lambda functions locally
  2. Deploy Packages as Docker images
  3. Build function packages

1) Run Lambda locally

Run locally with SAM

With sam local start-lambda you can run Lambda functions in local Containers. See SAM Developer Guide for usage.

Run locally with Lambci

Here you can find images to run Lambda functions

See the lambci project.

You find the images in the docker hub: lambdci lambda image

2) Deploy Lambda as Docker Images

See Chapter Lambda Container Deployment

3) Build Packages locally in Docker container

In Chapter Create a GO Lambda Function you see how to build a GO App.

If you don`t want to install GO locally, maybe you have top have a clean separation from development and deployment, you may use the CDK Docker bundling feature.

Images for building

You may use any container with GO included, or just use the prebuild AWS SAM images. The AWS SAM project pushed images to the AWS public ECR registry

In this example public.ecr.aws/sam/build-go1.x:latest is used.

Define Build cmds

 25   handler := lambda.NewFunction(this, aws.String("GoLangDockerECRImageBundle"),
 26     &lambda.FunctionProps{
 27       Handler:                      aws.String("main"),
 28       Runtime:                      lambda.Runtime_GO_1_X(),
 29       Code:                         lambda.AssetCode_FromAsset(aws.String("../app"), &asset.AssetOptions{
 30         Bundling:       &cdk.BundlingOptions{
 31           Image:            lambda.Runtime_GO_1_X().BundlingImage(),
 32           User:             aws.String("root"),
 33           Command:          &[]*string{
 34             aws.String("bash"),
 35             aws.String("-c"),
 36             aws.String("go version && go build -o /asset-output/main"),
 37           },
 38         },
 39       }),
 40     },
 41   )

You see in line 31 the used Docker image.

The app is build with normal go build command. The type of the deployment demands that you write the output file in the /asset-output directory. (Line 36). Please note, that you have to use a ZIP package when the Lambda function needs additional files. The “go version” command is just to mirror the example from the CDK Book. It’s not needed for the build.

Although it is not recommended from security perspective, this only works with user root (Line 32).

Run Build

The first time you execute

go test

In the infrastructure-as-go/cdk-go/lambda/deploy_bundler/infra directory you will see the output:

Bundling asset MyStack/GoLangDockerECRImageBundle/Code/Stage...
Unable to find image 'public.ecr.aws/sam/build-go1.x:latest' locally
latest: Pulling from sam/build-go1.x
...

That show you that the Docker images are pulled from the public.ecr.aws.

Deploy CDK

Suppose you change just one line of code in the Lambda app in infrastructure-as-go/cdk-go/lambda/deploy_bundler/app/main.go, the Deployment now has two phases, which takes some time (more than one minute):

1 - Buildung the app in local Container

cdk deploy
Bundling asset DockerbundlerStack/GoLangDockerECRImageBundle/Code/Stage...
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
go version go1.17.5 linux/amd64
go: downloading github.com/aws/aws-lambda-go v1.27.0

✨  Synthesis time: 9.68s

DockerbundlerStack: deploying...
[0%] start: Publishing 00ff15471bdba4032c0379cb904af48c1eae15ba923e7e3d012615656ce533c5:current_account-current_region
[50%] success: Published 00ff15471bdba4032c0379cb904af48c1eae15ba923e7e3d012615656ce533c5:current_account-current_region
[50%] start: Publishing 6bdc278bdbefc18398c6b43671e3364bcdb803f16fbaef4e4901cf4242c7efa7:current_account-current_region
[100%] success: Published 6bdc278bdbefc18398c6b43671e3364bcdb803f16fbaef4e4901cf4242c7efa7:current_account-current_region

2 - Deploy CloudFormation Change-Set

DockerbundlerStack: creating CloudFormation changeset...
...
 ✅  DockerbundlerStack

✨  Deployment time: 61.55s

Outputs:
DockerbundlerStack.lambdabundle = DockerbundlerStack-GoLangDockerECRImageBundle68C01-UevXVHkSLeks
Stack ARN:
arn:aws:cloudformation:eu-central-1:795048271754:stack/DockerbundlerStack/caa390a0-7142-11ec-8b1d-06a376f57136

✨  Total time: 71.23s

Deployment Speed comparism and Usage

If you deploy changed code in the infrastructure-as-go/cdk-go/lambda/deploy_bundler/app directory with:

 time task fastdeploy

the deployment process takes less then 4 seconds.

time task fastdeploy
task: [build] env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o ../dist/main main.go
task: [build] chmod +x ../dist/main
task: [build] cd ../dist && zip main.zip main
  adding: main (deflated 60%)
task: [fastdeploy] aws lambda update-function-code --function-name  DockerbundlerStack-GoLangDockerECRImageBundle68C01-UevXVHkSLeks --zip-file fileb://../dist/main.zip
{
    "FunctionName": "DockerbundlerStack-GoLangDockerECRImageBundle68C01-UevXVHkSLeks",
    "FunctionArn": "arn:aws:lambda:eu-central-1:795048271754:function:DockerbundlerStack-GoLangDockerECRImageBundle68C01-UevXVHkSLeks",
    "Runtime": "go1.x",
    ... 
}
task fastdeploy  1,34s user 0,57s system 52% cpu 3,601 total

So its 20 times faster than the bundling deployment.

On a developer workstation GO should be installed, so you dont need the bundling option.

In a CI/CD pipeline its no problem to install GO, see Chapter Architecture - Serverless DynamoDB, S3, Lambda with the CodeBuild buildspec.yml as an example.

So this deployment type is only useful if you want to deploy from non-developer workstations, for example if you usually develop in another language, dont want to deal with GO, but you have no CI/CD pipeline.

See the full source on github.

Sources