Skip to content

Commit f93a089

Browse files
authored
Merge pull request #1 from JayJamieson/feat/upgrade-deploy-artifact-to-container-image
Updated lambda terraform to use container image deployment artifact
2 parents ef83769 + bdc5315 commit f93a089

File tree

13 files changed

+321
-102
lines changed

13 files changed

+321
-102
lines changed

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@ bin/main.local
1818
function.zip
1919
bootstrap
2020

21-
.idea
21+
.idea
22+
infrastructure/.terraform
23+
infrastructure/.terraform.lock.hcl
24+
infrastructure/.terraform/providers/registry.terraform.io/hashicorp/aws/5.13.1/linux_amd64/terraform-provider-aws_v5.13.1_x5
25+
infrastructure/terraform.tfstate
26+
infrastructure/terraform.tfstate.backup

Dockerfile

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
FROM amazonlinux:2.0.20230727.0
1+
FROM golang:1.22 AS build
22

3-
RUN yum install -y tar xz gzip gcc
3+
WORKDIR /project
44

5-
RUN curl -L -o golang.tar.gz https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
5+
COPY go.mod go.sum ./
6+
RUN go mod download -x
67

7-
RUN rm -rf /usr/local/go && tar -C /usr/local -xzf golang.tar.gz
8+
COPY handlers/ handlers/
9+
COPY main.go .
810

9-
WORKDIR /project
11+
RUN CGO_ENABLED=0 go build -tags lambda.norpc -o main main.go
1012

11-
COPY go.mod go.sum ./
12-
RUN /usr/local/go/bin/go mod download -x
13+
FROM public.ecr.aws/lambda/provided:al2
14+
15+
COPY --from=build /project/main ./main
1316

14-
RUN /usr/local/go/bin/go install github.com/mattn/go-sqlite3
15-
ENV PATH="${PATH}:/usr/local/go/bin"
17+
ENTRYPOINT [ "./main" ]

README.md

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,56 @@
22

33
**AWS has [deprecated go1.x](https://aws.amazon.com/blogs/compute/migrating-aws-lambda-functions-from-the-go1-x-runtime-to-the-custom-runtime-on-amazon-linux-2/) runtime.**
44

5-
This repository contains example lambda handler integration with API gateway. Terraform is used to deploy infrastructure including DNS, SSL Certificates.
5+
This repository contains example lambda handler integration with API Gateway. Terraform is used to deploy infrastructure including DNS, SSL Certificates.
66

77
## Requirements
88

9-
- go 1.20
9+
- go 1.22
1010
- zip
1111
- docker
1212
- terraform
1313
- aws cli
14+
- jq
1415

15-
## Setup (optional)
16+
## Setup (Optional)
1617

17-
### Docker
18+
For now an existing AWS ECR is required to exist already or created using `./create_ecr.sh`. Unfortunately it's not possible yet to create an ECR, build and push an image, then reference the image_uri needed for Lambda resource.
1819

19-
Although not require specifically to run go in lambdas, can be useful to avoid compatibility issues
20-
when using cgo bindings or third party libraries using cgo such as sqlite3.
20+
If you an existing ECR you can run `./create_ecr.sh` with the `--auth-only` flag to skip creating a new ECR. This will pull credentials and inject into Docker engine.
2121

22-
- build docker container for building lambda function binary fully compatible with aws lambda runtime
23-
- `docker build -t lambda-build .`
22+
```shell
23+
Usage: ./create-ecr.sh --repo-name NAME --region REGION --account-id ID [--auth-only]
24+
--repo-name ECR repository name (required)
25+
--region AWS region (required)
26+
--account-id AWS account ID (required)
27+
--auth-only Only login Docker to ECR and exit
28+
```
2429

2530
## Build
2631

27-
AWS `provided.al2` requires executables to be named bootstrap.
32+
The deploy artifact is a container image instead of a zip file with the built binary. AWS has optimized container images for Lambda and results in faster cold starts.
2833

29-
- `GOARCH=amd64 GOOS=linux go build -tags lambda.norpc -o ./infrastructure/bootstrap main.go`
30-
- `provided.al2` provide a single process architecture and allow running without RPC dependency using `-tags lambda.norpc`
31-
- Build without RPC using `GOARCH=amd64 GOOS=linux go build -tags lambda.norpc -o ./infrastructure/bootstrap main.go`
34+
To build an image and tag using `tag --points-at HEAD --sort=-version:refname` run `./build_image.sh`. This script will built a Lambda compatible container image and push to the configured AWS ECR. This script expects `./create_ecr.sh` to have run or at the minimum run with `--auth-only` for an existing ECR to get login credentials for Docker engine.
3235

33-
- `go build -o ./bin/main.local ./cmd/handler/main.go` for locally running lambda handler as a cli application.
34-
- requires manually providing event data from file or hard coded into the main.go file
35-
- it can be very easy to write a cli interface to read event from path or stdin or wrap in a http server
36+
```shell
37+
Usage: ./build_image.sh --account-id ID --region REGION --repo-name NAME [--tag TAG]
38+
--account-id AWS account ID (required)
39+
--region AWS region (required)
40+
--repo-name ECR repository name (required)
41+
--tag Image tag override; if omitted, use latest Git tag on HEAD
42+
```
3643
3744
## Deploy
3845
3946
Create a `terraform.tfvars` file inside infrastructure folder and fill out required variables.
4047
41-
- Manually deploy function code changes with cli `aws lambda update-function-code --function-name go-lambda --zip-file fileb://bootstrap.zip`
4248
- Use terraform to deploy infrastructure changes and function changes `terraform apply --auto-approve`
43-
- `main.aws_lambda_function.go_lambda.source_code_hash = filebase64sha256("bootstrap.zip")` will ensure new builds to redeploy with infrastructure changes.
49+
50+
To enable auto container image builds set `with_docker_build` to `true`. This will run the `./build_image.sh` script and output the image URI for use in the Lambda resource. Default is disabled.
4451
4552
## Optional
4653
47-
Currently, the lambda handler takes in any request method and path. For full utilization of api gateway proxy functionality you can make use of <https://github.com/awslabs/aws-lambda-go-api-proxy> to run a standard Go http server and adapt api gateway requests to Go requests and vice versa Go responses to api gateway responses
54+
Currently, the lambda handler takes in any request method and path. For full utilization of API Gateway proxy functionality you can make use of <https://github.com/awslabs/aws-lambda-go-api-proxy> to run a standard Go http server and adapt API Gateway requests to Go requests and vice versa Go responses to API Gateway responses
4855
4956
- Be aware to write lambda aware handlers using the adapters.
5057
- Optimize for fast startups

build.sh

Lines changed: 0 additions & 18 deletions
This file was deleted.

build_image.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
5+
GREEN="\e[32m"
6+
RED="\e[31m"
7+
YELLOW="\e[33m"
8+
NC="\e[0m"
9+
10+
trap 'echo -e "${RED}Error on line ${LINENO}. Aborting.${NC}" >&2; exit 1' ERR
11+
12+
usage() {
13+
cat <<EOF >&2
14+
Usage: $0 --account-id ID --region REGION --repo-name NAME [--tag TAG]
15+
--account-id AWS account ID (required)
16+
--region AWS region (required)
17+
--repo-name ECR repository name (required)
18+
--tag Image tag override; if omitted, use latest Git tag on HEAD
19+
EOF
20+
exit 1
21+
}
22+
23+
ACCOUNT_ID=""
24+
REGION=""
25+
REPO_NAME=""
26+
TAG=""
27+
28+
while [[ $# -gt 0 ]]; do
29+
case "$1" in
30+
--account-id) ACCOUNT_ID="$2"; shift 2 ;;
31+
--region) REGION="$2"; shift 2 ;;
32+
--repo-name) REPO_NAME="$2"; shift 2 ;;
33+
--tag) TAG="$2"; shift 2 ;;
34+
-h|--help) usage ;;
35+
*) echo -e "${RED}Unknown option: $1${NC}" >&2; usage ;;
36+
esac
37+
done
38+
39+
[[ -n "$ACCOUNT_ID" && -n "$REGION" && -n "$REPO_NAME" ]] || usage
40+
41+
if [[ -n "$TAG" ]]; then
42+
IMAGE_TAG="$TAG"
43+
echo -e "${YELLOW}Using override tag: ${IMAGE_TAG}${NC}" >&2
44+
else
45+
IMAGE_TAG=$(git tag --points-at HEAD --sort=-version:refname | head -n1 || true)
46+
if [[ -z "$IMAGE_TAG" ]]; then
47+
echo -e "${YELLOW}No tag found on HEAD; falling back to commit hash${NC}" >&2
48+
IMAGE_TAG=$(git rev-parse --short HEAD)
49+
else
50+
echo -e "${GREEN}Found Git tag on HEAD: ${IMAGE_TAG}${NC}" >&2
51+
fi
52+
fi
53+
54+
IMAGE_REPO="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO_NAME}"
55+
IMAGE_URI="${IMAGE_REPO}:${IMAGE_TAG}"
56+
57+
echo -e "${YELLOW}Building Docker image as ${IMAGE_REPO}:${IMAGE_TAG}${NC}" >&2
58+
docker buildx build \
59+
--platform linux/amd64 \
60+
--provenance=false \
61+
-t "${IMAGE_REPO}" \
62+
. >&2
63+
echo -e "${GREEN}Build succeeded.${NC}" >&2
64+
65+
echo -e "${YELLOW}Tagging image…${NC}" >&2
66+
docker tag "${IMAGE_REPO}" "${IMAGE_URI}" >&2
67+
docker tag "${IMAGE_REPO}" "${IMAGE_REPO}:latest" >&2
68+
echo -e "${GREEN}Tagging succeeded.${NC}" >&2
69+
70+
echo -e "${YELLOW}Pushing ${IMAGE_URI}${NC}" >&2
71+
docker push "${IMAGE_URI}" >&2
72+
echo -e "${GREEN}Pushed ${IMAGE_URI}.${NC}" >&2
73+
74+
echo -e "${YELLOW}Pushing latest tag…${NC}" >&2
75+
docker push "${IMAGE_REPO}:latest" >&2
76+
echo -e "${GREEN}Pushed latest tag.${NC}" >&2
77+
78+
jq -n --arg uri "$IMAGE_URI" '{"image_uri":$uri}'

create-ecr.sh

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
5+
GREEN="\e[32m"
6+
RED="\e[31m"
7+
YELLOW="\e[33m"
8+
NC="\e[0m"
9+
10+
trap 'echo -e "${RED}Error on line ${LINENO}. Aborting.${NC}" >&2; exit 1' ERR
11+
12+
usage() {
13+
cat <<EOF >&2
14+
Usage: $0 --repo-name NAME --region REGION --account-id ID [--auth-only]
15+
--repo-name ECR repository name (required)
16+
--region AWS region (required)
17+
--account-id AWS account ID (required)
18+
--auth-only Only login Docker to ECR and exit
19+
EOF
20+
exit 1
21+
}
22+
23+
REPO_NAME=""
24+
REGION=""
25+
ACCOUNT_ID=""
26+
AUTH_ONLY=false
27+
28+
while [[ $# -gt 0 ]]; do
29+
case "$1" in
30+
--repo-name) REPO_NAME="$2"; shift 2 ;;
31+
--region) REGION="$2"; shift 2 ;;
32+
--account-id) ACCOUNT_ID="$2"; shift 2 ;;
33+
--auth-only) AUTH_ONLY=true; shift ;;
34+
-h|--help) usage ;;
35+
*) echo -e "${RED}Unknown option: $1${NC}" >&2; usage ;;
36+
esac
37+
done
38+
39+
[[ -n "$REPO_NAME" && -n "$REGION" && -n "$ACCOUNT_ID" ]] || usage
40+
41+
ECR_URI="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com"
42+
43+
echo -e "${YELLOW}Logging in to ECR registry ${ECR_URI}${NC}" >&2
44+
aws ecr get-login-password --region "$REGION" \
45+
| docker login --username AWS --password-stdin "${ECR_URI}" \
46+
&& echo -e "${GREEN}Docker login succeeded.${NC}" >&2
47+
48+
if $AUTH_ONLY; then
49+
echo -e "${YELLOW}Auth-only flag set; exiting after login.${NC}" >&2
50+
exit 0
51+
fi
52+
53+
echo -e "${YELLOW}Creating ECR repository '${REPO_NAME}'…${NC}" >&2
54+
CREATE_OUTPUT=$(aws ecr create-repository \
55+
--repository-name "$REPO_NAME" \
56+
--region "$REGION" \
57+
--image-scanning-configuration scanOnPush=false \
58+
--image-tag-mutability MUTABLE)
59+
echo -e "${GREEN}Repository creation succeeded.${NC}" >&2
60+
61+
REPO_URI=$(echo "$CREATE_OUTPUT" | jq -r '.repository.repositoryUri')
62+
echo -e "Repository URI: $REPO_URI" >&2
63+
64+
printf '%s\n' "$REPO_URI"

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module github.com/JayJamieson/go-lambda
22

3-
go 1.21
3+
go 1.22
44

5-
require github.com/aws/aws-lambda-go v1.41.0
5+
require github.com/aws/aws-lambda-go v1.48.0

go.sum

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
github.com/aws/aws-lambda-go v1.41.0 h1:l/5fyVb6Ud9uYd411xdHZzSf2n86TakxzpvIoz7l+3Y=
22
github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8qK17ewzbQMM=
3+
github.com/aws/aws-lambda-go v1.48.0 h1:1aZUYsrJu0yo5fC4z+Rba1KhNImXcJcvHu763BxoyIo=
4+
github.com/aws/aws-lambda-go v1.48.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A=
35
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
47
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
8+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
59
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
10+
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
611
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
12+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

0 commit comments

Comments
 (0)