DEV Community

Cover image for Using container image support for AWS Lambda with AWS SAM and Codebuild/Codepipeline
Enrico Vecchio
Enrico Vecchio

Posted on

Using container image support for AWS Lambda with AWS SAM and Codebuild/Codepipeline

Alt Text

Recently AWS introduced container image support for lambda functions.
I wanted to run a little test deploying a simple lambda function with Java8 using Codebuild+Codepipeline as CICD, but it was not more straightforward than expected.

I hope to save you some valuable time with this quick guide.
So, let's start!
 
 

SETUP CI/CD PIPELINE

In my example, I used all the AWS services to build my CI/CD: Codecommit, Codebuild, and Codepipeline.

The most convenient way to set up them is by executing the Cloudformation template in your target AWS account.
You'll find the template under my Github example.
 
 
This template will create:

  1. Codecommit repo for our example lambda
  2. Codebuild project will build our Lambda and push the related docker image into the ECR repo.
  3. Codepipeline will orchestrate the services above by catching any commit on our master branch in Codecommit and send it to Codebuild for building the App, push the Image and deploy the Lambda.
  4. An ECR repository to store our Docker images.    

LAMBDA

The main difference between the container image lambda and the old java is that now we have to copy all classes and libraries into the docker image instead upload your zip/jar file.
To do that, I set up the following Gradle tasks, which extract Lambda's runtime dependencies into the "build/dependencies" folder during the "build" task.

task copyRuntimeDependencies(type: Copy) {
    from configurations.runtimeClasspath
    into 'build/dependency'
}

build.dependsOn copyRuntimeDependencies
Enter fullscreen mode Exit fullscreen mode

 
 

DOCKERFILE

 
AWS has various public docker images you can extend.
In my example I used "public.ecr.aws/lambda/java:8".
I copied all my dependencies and setup my entry-point.

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

# Copy function code and runtime dependencies from Gradle layout
COPY build/classes/java/main ${LAMBDA_TASK_ROOT}
COPY build/dependency/* ${LAMBDA_TASK_ROOT}/lib/

# Set the CMD to your handler 
CMD [ "me.enryold.docker.lambda.Lambda::handleRequest" ]
Enter fullscreen mode Exit fullscreen mode

 
 

SAM (template.yml)

 
Here we have to specify three new fields under the "Metadata" section of our lambda definition.

Dockerfile: Dockerfile     # name of Dockerfile file
DockerContext: .           # Folder where Dockerfile has placed. Mine is under the project root. 
DockerTag: latest          # Tag of our image. 
Enter fullscreen mode Exit fullscreen mode

That's how looks the final result.

  Lambda:
    Type: AWS::Serverless::Function
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: .
      DockerTag: latest
    Properties:
      PackageType: Image
      FunctionName: docker-lambda
      MemorySize: 1536
      Timeout: 30
      Policies:
        - AWSLambdaBasicExecutionRole                    # enable lambda execution
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - cloudwatch:*
                - ec2:DescribeVpcs
                - ec2:DescribeSubnets
                - ec2:DescribeSecurityGroups
                - ec2:DescribeKeyPairs
              Resource: '*'
Enter fullscreen mode Exit fullscreen mode

 
 

Buildspec.yml

 
That's the part where I lost a couple of hours since the original article use the guided sam build while we need to use the standard one.

  • In the install phase, I downloaded and installed the latest sam cli from AWS.
  • Under the pre_build section, we need to get the ECR credentials to allow our Codebuild execution to push the updated docker image.
  • Finally, we build our java app and launch sam build/package/deploy commands in the build phase.
version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18
    commands:
      - wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
      - unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
      - sam --version
      - ./sam-installation/install --update
      - /usr/local/bin/sam --version
  pre_build:
    commands:
      - aws ecr get-login-password --region ${AWS_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com
  build:
    commands:
      - cd sources && gradle build
      - /usr/local/bin/sam build --template-file ${CODEBUILD_SRC_DIR}/sources/sam.yml --region ${AWS_REGION}
      - /usr/local/bin/sam package --template-file ${CODEBUILD_SRC_DIR}/sources/.aws-sam/build/template.yaml --output-template-file ${CODEBUILD_SRC_DIR}/sources/packaged.yaml --image-repository ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/me.enryold/docker-lambda
      - /usr/local/bin/sam deploy --template-file ${CODEBUILD_SRC_DIR}/sources/packaged.yaml --stack-name ${PROJECT_NAME}-sam --capabilities CAPABILITY_NAMED_IAM --region ${AWS_REGION} --image-repository ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/me.enryold/docker-lambda
Enter fullscreen mode Exit fullscreen mode

 
 

WORKING EXAMPLE

 
Feel free to download/fork the entire project from my Github page.

Top comments (5)

Collapse
 
ygorthomaz profile image
Ygor Thomaz • Edited

Quick question.

Did you face this error message in some point of your CICD project:

AWS CodeBuild output:
`Building codeuri: . runtime: None metadata: {'DockerTag': 'go1.14.x', 'DockerContext': './functions/ftp-publisher', 'Dockerfile': 'Dockerfile'} functions: ['Function1']
Building image for Function1 function

Build Failed
Error: Building image for Function1 requires Docker. is Docker running?`

SAM build looks like this:
sam build --template-file functions/xxxx/sn-xxxxx/sam-template.yml --base-dir /codebuild/output/src846189501/src --parameter-overrides 'some...'

CodeBuild:
...
Environment:
ComputeType: BUILD_GENERAL1_SMALL
Type: LINUX_CONTAINER
Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
...
runtime-versions: golang: 1.14

Thanks for the article! :)

Collapse
 
enryold profile image
Enrico Vecchio

Hi Ygor, it seems that your Codebuild is not running in "privileged mode", try to activate it and retry!

Collapse
 
ygorthomaz profile image
Ygor Thomaz

Thanks!! It worked.

Collapse
 
mohithuria2608 profile image
mohithuria2608

Hi, i am trying to automate the following scenario..
I am using lambda function as container image and now I want is Whenver there is anew container image pushed.. The lambda should trigger by itself and deploy the lambda with that..
I thought of using sed to replace the tag in my pipeline but that's kinda workaround. Can you please suggest on that.

Collapse
 
enryold profile image
Enrico Vecchio

Sorry for late reply on that..
Why you don't just use a codebuild/codepipeline setup to handle it?