Home > Software engineering >  How to deploy AWS Lambdas with Docker and CDK?
How to deploy AWS Lambdas with Docker and CDK?

Time:11-21

Context

I'm able to deploy to AWS lambda using Docker and CDK.

Here is what I did:

I have a Java package TestLambda. I use docker build -t test-lambda . there to build the docker image.

FROM public.ecr.aws/lambda/java:11

# /var/task should only contains *.class files
#COPY build/classes/java/main ${LAMBDA_TASK_ROOT}

# Always use /var/task/lib for dependencies
COPY build/dependency/* ${LAMBDA_TASK_ROOT}/lib/

COPY src/main/resources/log4j2.xml ${LAMBDA_TASK_ROOT}

ARG jarFile=TestLambda-0.0.1.jar

# Looks
COPY build/libs/${jarFile} ${LAMBDA_TASK_ROOT}/lib/

RUN cd ${LAMBDA_TASK_ROOT} && jar -xf ${LAMBDA_TASK_ROOT}/lib/${jarFile}

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "com.mywebsite.TestLambda::handle" ]

I have a CDK package where I defined Lambda Stack:

export interface LambdaStackProps extends cdk.StackProps {
  readonly env : cdk.Environment
}

export class LambdaStack extends cdk.Stack {

  // Make it public so that can be binded to Api Gateway
  public readonly testLambda : lambda.Function;

  constructor(scope: cdk.App, id: string, props: LambdaStackProps) {
      super(scope, id, props);

      this.testLambda = this.createLambda();
  }


  private createLambda() {
      // Configure path to Dockerfile
      const dockerfile = path.join(__dirname, "../../TestLambda/");

      // Because here it uses Asset,
      // need run cdk bootstrap aws://<AwsAccountId>/us-east-1
      return new lambda.DockerImageFunction(this, 'TestLambdaHandler', {
          functionName: 'TestLambdaHandler',
          description: 'Lambda that render presigned url for S3 bucket.',
          code: lambda.DockerImageCode.fromImageAsset(dockerfile),
          timeout: cdk.Duration.minutes(2),
          memorySize: 512,
          role: this.createLambdaExecutionRole(),
          tracing: lambda.Tracing.ACTIVE,
      });
  }

  private createLambdaExecutionRole() {
    return new iam.Role(this, "TestLambdaHandlerRole", {
      description: "Execution role for TestLambdaHandler.",
      assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
      inlinePolicies: {
        "TestLambdaHandlerRoleInlinePolicies" : new iam.PolicyDocument(
          {
            statements : [
              new iam.PolicyStatement({
                effect: iam.Effect.ALLOW,
                resources: ["*"],
                actions: [
                  "s3:Create*",
                  "s3:List*",
                  "s3:Set*",
                  "s3:Put*",
                  "s3:Get*",
                ]
              }),
              new iam.PolicyStatement({
                effect: iam.Effect.ALLOW,
                resources: ["*"],
                actions: [
                  "logs:Get*", "logs:Describe*", "logs:Create*", "logs:Put*"
                ]
              })
            ]
          }
        )
      }
    });
  }
}

With the above code, I'm able to upload the docker image and deploy to AWS Lambda with commandcdk deploy LambdaStack.

My question

Above code works for one Java Lambda function. And now I want add more Java functions to my Java package, and use the CDK package to deploy the docker image as a whole.

How can I do that?

As you can see in my Dockerfile, there is a line CMD [ "com.mywebsite.TestLambda::handle" ] specified the function that should be run, and Dockerfile can only have one CMD.

I tried to add another CMD for my new function and make the Dockerfile looks like:

CMD [ "com.mywebsite.GetS3PresignedUrl::handleRequest" ]
CMD [ "com.mywebsite.activity.Test::handleRequest" ]

Of course, I also updated the TypeScript CDK package to add corresponding code for the second lambda function. Basically a copy of my above code.

After deployment, I see two AWS lambda functions. But both lambda functions call com.mywebsite.activity.Test::handleRequest. As mentioned here, "If you list more than one CMD then only the last CMD will take effect."

I want the single docker image to hold all my Lambda functions, how can I do that?

CodePudding user response:

Use the lambda.Function construct insead. It allows you to reuse a docker asset and override the handler. Remove the CMD entries from the Dockerfile.

  const my_code = lambda.DockerImageCode.fromImageAsset(dockerfile);
  const my_lambda = new lambda.Function(this, 'TestLambdaHandler', {
      functionName: 'TestLambdaHandler',
      description: 'Lambda that render presigned url for S3 bucket.',
      code: my_code,
      handler: 'com.mywebsite.activity.Test::handleRequest',
      runtime: lambda.Runtime.FROM_IMAGE,
      timeout: cdk.Duration.minutes(2),
      memorySize: 512,
      role: this.createLambdaExecutionRole(),
      tracing: lambda.Tracing.ACTIVE,
  });
  • Related