amazon-web-servicesdockergoaws-lambdaaws-cloudformation

Building and deploying multiple Go lambdas via images in AWS with differing Go packages from one Dockerfile


I'm trying to build and deploy multiple Go based lambdas using 1 Dockerfile (as the instructions don't differ between lambdas) using different go packages for each lambda.

My file structure is as follows:

test-lambda
|------ go.mod
|------ go.sum
|------ main.go
test-lambda-2
|------ go.mod
|------ go.sum
|------ main.go
Dockerfile
...other repo files

My Dockerfile looks like the following:

FROM golang:1.24.3 AS builder

ARG LAMBDA_DIR

RUN echo $LAMBDA_DIR

WORKDIR ${LAMBDA_DIR}
COPY * /lambda/
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -C /lambda -a -installsuffix cgo -o lambda
RUN chmod +x /lambda/lambda

FROM public.ecr.aws/lambda/provided:al2023-arm64

COPY --from=builder lambda/lambda /usr/local/bin/lambda

ENTRYPOINT [ "lambda" ]

I am using AWS CloudFormation and SAM to build and deploy the lambdas. My template.yaml looks like this

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Architectures: [arm64]
    Timeout: 900
    MemorySize: 1024

Parameters:
  CommitHash:
    Type: String
    Description: Commit hash of the change

Resources:
  TestLambda:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: .
      DockerTag: !Ref CommitHash
      DockerBuildArgs:
        LAMBDA_DIR: /test-lambda

  TestLambda2:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: .
      DockerTag: !Ref CommitHash
      DockerBuildArgs:
        LAMBDA_DIR: /test-lambda-2

I am then using a justfile to make running the commands easier in CI (GitLab). The commands are as follows:

build:
    sam build \
    --use-container \
    -t template.yaml \
    --parameter-overrides CommitHash=$CI_COMMIT_SHORT_SHA \
    --debug

deploy:
    sam deploy \
    --stack-name $CI_PROJECT_NAME--$CI_COMMIT_BRANCH \
    --region eu-west-2 \
    --profile $CI_PROJECT_NAME \
    --resolve-s3 \
    --resolve-image-repos \
    --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
    --parameter-overrides CommitHash=$CI_COMMIT_SHORT_SHA \
    --tags Service=$CI_PROJECT_NAME Repo=$CI_PROJECT_URL Version=$CI_COMMIT_SHORT_SHA

My main.go's look like this:

// main.go
package main

import (
    "github.com/aws/aws-lambda-go/lambda"
)

func hello() (string, error) {
    return "Hello λ!", nil
}

func main() {
    // Make the handler available for Remote Procedure Call by AWS Lambda
    lambda.Start(hello)
}

The only difference between the 2 is the returned string differs between each lambda package so that I can test they're different. The test-lambda-2 returns return "Hello λ2!", nil for example.

This builds fine, but the issue I am having is that both lambdas are seemingly using the same image, and returning the output of the test-lambda-2 package, and not the respective packages.

I can provide the CI output if necessary, but from the logs I can see 2 different images being built and 2 repos with 2 separate images in. Both lambdas point to the each repo. So it seems to be an issue with the build and not the pushing of the image.

Am I missing something obvious?

Tried adjusting the building to use container or not, and adjusted the build arg. Both result in the same thing


Solution

  • There are few issues with the setup, You have to fix those before trying again,

    1. In your Dockerfile, the COPY instruction is incorrect. You're trying to copy files to

      /lambda/
      

      but your WORKDIR is set to

      ${LAMBDA_DIR}
      

      Also, you need to copy the specific lambda directory contents.
      Here's the corrected Dockerfile:

    FROM golang:1.24.3 AS builder
    
    ARG LAMBDA_DIR
    
    WORKDIR /lambda
    COPY ${LAMBDA_DIR} .
    RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -a -installsuffix cgo -o lambda
    RUN chmod +x lambda
    
    FROM public.ecr.aws/lambda/provided:al2023-arm64
    
    COPY --from=builder /lambda/lambda /usr/local/bin/lambda
    
    ENTRYPOINT [ "lambda" ]
    
    1. In your Dockerfile, the COPY instruction is incorrect.

    2. In your SAM template, you should specify the function handler and other required properties[1]

    3. Make sure each lambda directory has the required AWS Lambda dependencies in its go.mod: [2]

      require github.com/aws/aws-lambda-go v1.41.0
      
      
    4. Each main.go should implement the Lambda handler interface

      package main
      
      import (
          "context"
          "github.com/aws/aws-lambda-go/lambda"
      )
      
      func handleRequest(ctx context.Context, event interface{}) (string, error) {
          // Your lambda logic here
          return "Hello from Lambda!", nil
      }
      
      func main() {
          lambda.Start(handleRequest)
      }
      
      
      
      go
      To build and deploy using SAM:
      
      # Build
      sam build
      
      # Deploy
      sam deploy --stack-name your-stack-name --parameter-overrides CommitHash=your-commit-hash
      
      
      

      Sources

      [1] SAM to deploy Image-Based Lambda

      [2] Deploy Lambda functions with container images - AWS Prescriptive Guidance