Deploy a Serverless Application using Crossplane on EKS

TUTORIAL

Overview

In this tutorial, we will walk you through deploying the Serverless architecture depicted in the diagram using Crossplane on Amazon EKS. Crossplane is a CNCF open source project that extends the capabilities of Kubernetes to include infrastructure resource management. If you are not familiar with the fundamentals of Crossplane, we recommend first completing the official Crossplane getting started guide.

What You Will Learn

In this guide, you will learn how to:

  • Create a Docker image and push it to ECR.
  • Set Crossplane underlying Compositions and XRDs, and use Claims.
  • Validate results with basic commands using kubectl and the AWS CLI.

Prerequisites

Before starting this guide, you will need:

  • An AWS account: if you don't already have one follow
    the Setup Your Environment (Steps 1, 2, and 3)
    getting started guide for a quick overview.
  • The latest version of the AWS CLI: Visit our guide to get started installing or updating
    the AWS CLI.
  • Kubectl: Visit Amazon EKS guide to get started installing or updating
    kubectl.
  • EKS cluster and Crossplane: follow this Readme to create with Terraform >= 1.0.0 
    • EKS cluster >= 1.27
    • Crossplane helm chart >= 1.13
    • Crossplane official aws-provider configured to access AWS API >= 0.38 
  • Go: Find the installation instructions here.
  • Docker: Find the installation instructions here.

Modules

This tutorial is divided into the following short modules. You must complete each module before moving to the next one.

Build a docker image and push to ECR (5 mins): Build a Docker image of a sample application, create an ECR repository, and upload the image to it.

Deploy the serverless application (15 mins): Using the Kubernetes command line, deploy Crossplane Resource Definitions, Compositions, and Claims in order to create your resources for your serverless application.

Test (5 mins): Send a message to your newly created SQS queue and observe it triggering your Lambda function and publishing the result in your S3 bucket.

Clean Up Resources (5 mins): In this last part of the guide you learn how to clean after you are done.

 AWS experience

Advanced

 Time to complete

30 minutes

 Cost to complete

See AWS Lambda, Amazon EKS pricing to estimate cost for this tutorial

 Requires

You must be logged in to an AWS account


 Services used

AWS Lambda, Amazon EKS

 Last updated

February 13, 2024

Module 1: Build a docker image and push to ECR

Introduction

In this module, we will be building a Docker image with a sample application, creating an ECR repository, and uploading the image to it. This container image contains the source code for the Lambda function that is part of the serverless application we will be deploying later on in this tutorial.

What you will learn

  • Building a Docker image from a given application
  • Creating an ECR repo and uploading an image to it using the AWS CLI

Implementation

Clone the crossplane-on-eks repository to your local machine.

git clone https://github.com/awslabs/crossplane-on-eks.git

Navigate to the function code folder

cd crossplane-on-eks/examples/upbound-aws-provider/composite-resources/serverless-examples/object-processor-app

Set AWS_REGION, ECR_URL, and IMAGE_NAME as environment variables.

export AWS_REGION=<replace-me-with-aws-region> # this should make aws cli pointing to the region without explicitly passing --region
export ECR_URL=$(aws sts get-caller-identity --output json | jq -r ".Account" | tr -d '[:space:]').dkr.ecr.$AWS_REGION.amazonaws.com
export IMAGE_NAME=<replace-me-with-image-name>

Log in to ECR

aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URL

Create ECR repository

aws ecr create-repository --repository-name $IMAGE_NAME

Build the Go binary

GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o main main.go

Set the DOCKER_IMAGE env var. This step is necessary to properly escape the ':' for the subsequent docker commands

export DOCKER_IMAGE=$ECR_URL/$IMAGE_NAME\:latest

Build the docker image

docker build -t $DOCKER_IMAGE .

Expected output

[+] Building 0.5s (7/7) FINISHED
 => [internal] load build definition from Dockerfile                                                             0.0s
 => => transferring dockerfile: 37B                                                                              0.0s
 => [internal] load .dockerignore                                                                                0.0s
 => => transferring context: 2B                                                                                  0.0s
 => [internal] load metadata for public.ecr.aws/lambda/go:1                                                      0.3s
 => [internal] load build context                                                                                0.1s
 => => transferring context: 13.18MB                                                                             0.1s
 => [1/2] FROM public.ecr.aws/lambda/go:1@sha256:ea8389f1a5e7e9d29c4d65008da143985bef131cf901db63ab57e4124f7f66  0.0s
 => CACHED [2/2] COPY main /var/task                                                                             0.0s
 => exporting to image                                                                                           0.0s
 => => exporting layers                                                                                          0.0s
 => => writing image sha256:0f3ea389fa4f3761f25b2b666a8f49b458e825f1180b9c2181ecadd55fd2cc9a                     0.0s
 => => naming to 111122223333.dkr.ecr.us-east-1.amazonaws.com/lambda-test:latest                                 0.0s

Upload the docker image to ECR

docker push $DOCKER_IMAGE

Expected output

The push refers to repository [111122223333.dkr.ecr.us-east-1.amazonaws.com/lambda-test]
8ce4b9b80622: Pushed
02c5912fad21: Pushed
2d244e0816c6: Pushed
2b58bd9b1feb: Pushed
d79a81893db4: Pushed
2ad9f6dfcb67: Pushed
latest: digest: sha256:86bb30cbf14d9ac538946cdd9565f09b7a4a0b9846e28a73cd27116280c0a58e size: 1579

Conclusion

In this module, we covered how to build a Docker image, create an ECR repo, and upload an image to it. In the next module, we will learn how to deploy the serverless application.

Module 2: Deploy the serverless application

Introduction

In this module, we will be using kubectl to deploy Crossplane Resource Definitions (XRDs), Compositions, and a Claim in order to create and deploy the resources for the serverless application.

What you will learn

  • Deploying Crossplane XRDs and Compositions
  • Deploying a Claim to create serverless application

Implementation

Deploy XRDs and Compositions

Navigate to the following directory:

cd examples/upbound-aws-provider/composite-resources/serverless-examples/sqs-lambda-s3/

Examine and apply the kustomization.yaml file to create Crossplane XRDs and Compositions using the following command:

kubectl apply -k .

Verify the XRDs

kubectl get xrds

Expected output

NAME                                   ESTABLISHED   OFFERED   AGE
eventsourcemappings.awsblueprints.io   True                    5m
iampolicies.awsblueprints.io           True                    5m
xlambdafunctions.awsblueprints.io      True          True      5m
xobjectstorages.awsblueprints.io       True          True      5m
xqueues.awsblueprints.io               True          True      5m
xserverlessapp.awsblueprints.io        True          True      5m

Verify the Compositions

kubectl get compositions

Expected output. Note: the output might contain more compositions but these are the ones uses by the claim in the next step

NAME                                                  XR-KIND              XR-APIVERSION               AGE
container.lambda.aws.upbound.awsblueprints.io         XLambdaFunction      awsblueprints.io/v1alpha1   5m
read-s3.iampolicy.awsblueprints.io                    IAMPolicy            awsblueprints.io/v1alpha1   5m
read-sqs.iampolicy.awsblueprints.io                   IAMPolicy            awsblueprints.io/v1alpha1   5m
s3bucket.awsblueprints.io                             XObjectStorage       awsblueprints.io/v1alpha1   5m
sqs.esm.awsblueprints.io                              EventSourceMapping   awsblueprints.io/v1alpha1   5m
sqs.queue.aws.upbound.awsblueprints.io                XQueue               awsblueprints.io/v1alpha1   5m
write-s3.iampolicy.awsblueprints.io                   IAMPolicy            awsblueprints.io/v1alpha1   5m
xsqslambdas3.awsblueprints.io                         XServerlessApp       awsblueprints.io/v1alpha1   5m

Update and apply the claim

Set the image name and AWS region in the claim with the ones set in Module 1 where the docker image is uploaded to ECR.

export AWS_REGION=<replace-with-aws-region> # example `us-east-1`
export IMAGE_NAME=<replace-with-image-name> # example `lambda-test`

Change the default value for CLAIM_NAME with any name you choose.

export CLAIM_NAME=<replace-with-claim-name> # example `test-sqs-lambda-s3`

Run the below command to use the template file sqs-lambda-s3-claim-tmpl.yaml in the claim folder to create the claim file with the variables CLAIM_NAME, IMAGE_NAME, and AWS_REGION substituted.

envsubst < "claim/sqs-lambda-s3-claim-tmpl.yaml" > "claim/sqs-lambda-s3-claim.yaml"

Check that the claim populated with values

cat claim/sqs-lambda-s3-claim.yaml

Apply the claim

kubectl apply -f claim/sqs-lambda-s3-claim.yaml

Validate the claim

kubectl get serverlessapps

Expected result (it might take sometime before READY=True)

NAME                 SYNCED   READY   CONNECTION-SECRET   AGE
test-sqs-lambda-s3   True     True                        20m

The claim will use the XRDs and Compositions created in the previous section to create the following resources:

Each XR in the diagram contains the underlying resource refs:

kubectl describe xserverlessapp | grep "Resource Refs" -A 18

Expected output:

Resource Refs:
    API Version:  awsblueprints.io/v1alpha1
    Kind:         XQueue
    Name:         test-sqs-lambda-s3-hc2m5-7qwnb
    API Version:  awsblueprints.io/v1alpha1
    Kind:         EventSourceMapping
    Name:         test-sqs-lambda-s3-hc2m5-9q2kf
    API Version:  awsblueprints.io/v1alpha1
    Kind:         XLambdaFunction
    Name:         test-sqs-lambda-s3-hc2m5-processor
    API Version:  awsblueprints.io/v1alpha1
    Kind:         XObjectStorage
    Name:         test-sqs-lambda-s3-hc2m5-mbqcc
    API Version:  awsblueprints.io/v1alpha1
    Kind:         IAMPolicy
    Name:         test-sqs-lambda-s3-hc2m5-jspzj
    API Version:  awsblueprints.io/v1alpha1
    Kind:         IAMPolicy
    Name:         test-sqs-lambda-s3-hc2m5-2qzfl

Conclusion

In this module, we defined and grouped resources using Crossplane’s XRDs and Compositions. We then used a Claim to create those resources in order to deploy a serverless application.

Module 3: Test

Introduction

In this module we will test the serverless application by sending a message to the newly created SQS queue so we can observe it triggering the Lambda function and publishing the result into the S3 bucket.

What you will learn

  • Pushing messages to SQS using the AWS CLI.
  • Exploring the AWS SQS, Lambda, and S3 consoles.

Implementation

Use the following command to get the SQS URL and store it in $SQS_URL environment variable

SQS_URL=$(aws sqs list-queues --output json | jq -r '.QueueUrls|map(select(contains("test-sqs-lambda-s3"))) | .[0]' | tr -d '[:space:]')

The command will only store the first url that contains test-sqs-lambda-s3 in the name. Validate you have the correct url:

echo $SQS_URL

Send a message to the queue.

aws sqs send-message --queue-url $SQS_URL --message-body abc

Or send 100 messages to the queue.

for i in {1..100}; do aws sqs send-message --queue-url $SQS_URL --message-body abc ; done

Navigate to the AWS console and observe the SQS triggering the Lambda, and publishing the result in the S3 bucket.

Conclusion

In this module, we tested the serverless application by sending messages to the SQS queue to verify that it triggers the Lambda function and publishes the result to the S3 bucket.

Module 4: Clean up resources

Introduction

You have now completed this guide, but you still need to clean up the resources created during this guide.

What you will learn

  • How to remove Crossplane resources
  • How to destroy EKS cluster if created via this terraform code.

Implementation

Remove Crossplane Resources

Delete the repository

aws ecr delete-repository $IMAGE_NAME

Navigate to the following directory

cd examples/upbound-aws-provider/composite-resources/serverless-examples/sqs-lambda-s3/

Remove the claim

kubectl delete -f claim/sqs-lambda-s3-claim.yaml

Validate the claim was removed

kubectl get serverlessapp

Should return no claims.

Navigate to the AWS Console and check that the SQS, Lambda function, and S3 bucket are deleted.

Once deleted, remove the XRDs and Compositions:

kubectl delete -k .

Remove the EKS cluster

If you created EKS cluster with the instructions from this Readme, use the included destroy.sh script to remove it.

Navigate to the following directory:

cd bootstrap/terraform/

Execute the destroy.sh script

./destroy.sh

Conclusion

Congratulations! You have finished the Deploy a Serverless Application using Crossplane on EKS getting started guide. Continue your journey with AWS by following other examples in the Crossplane on EKS repository.

Was this page helpful?

Next steps