Skip to content

jarryDk/aws-cdk-demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWS CDK demo

Project introduction

This project is created to demonstrate how-to deploy to AWS using IaC (Java and CDK).

We start simpe by just deploying a simple lambda function. Going on to deploy an Quarks application with MicroProfile to get an REST endpoint.

As we like to persist data in our REST endpoint we add DynamoDb.

By default AWS do not support JDK 17 and we add a JDK 17 layer to be able to use JDK 17 in our application.

As we expect our application to really take of we add an ApplicationLoadBalancer in front and see how we now have done a "hot deployment" via the health endpoint.

All code in this demo will be in Java - IaC and the application.

Demo steeps

  • Introduction to AWS CDK

  • aws-cdk-demo-simple-lambda

  • aws-cdk-demo-hello-world-http

  • aws-cdk-demo-hello-world-rest

  • todo-app-h2

  • aws-cdk-demo-st

  • aws-cdk-demo

  • aws-cdk-demo-dynamodb-todo

  • aws-cdk-demo-dynamodb-todo-jdk17

  • aws-cdk-demo-dynamodb-todo-jdk17-alb

  • aws-cdk-demo-dynamodb-todo-jdk17-sam (only on request)

Introduction to AWS CDK

What is

The AWS Cloud Development Kit (AWS CDK) is an open-source software development framework to define your cloud application resources using familiar programming languages.

Key concepts

An AWS CDK app is an application written in TypeScript, JavaScript, Python, Java, C# or Go that uses the AWS CDK to define AWS infrastructure. An app defines one or more stacks. Stacks (equivalent to AWS CloudFormation stacks) contain constructs, each of which defines one or more concrete AWS resources, such as Amazon S3 buckets, Lambda functions, Amazon DynamoDB tables, and so on.

Supported programming languages

The AWS CDK has first-class support for TypeScript, JavaScript, Python, Java, C#, and Go. Other JVM and .NET CLR languages may also be used, at least in theory, but we are unable to offer support for them at this time.

][CDK
Figure 1. How it works
CDK bootstrapping

Deploying stacks with the AWS CDK requires dedicated Amazon S3 buckets and other containers to be available to AWS CloudFormation during deployment. Creating these is called bootstrapping.

cdk bootstrap --profile lambda_user

aws-cdk-demo-simple-lambda

  • Show the lambda funtion

  • Show lambda handler

  • Show the CKD code (including cdk.json)

  • Show cdk diff after change to function

  • Do live deployment to AWS

  • Use/Test lambda function

Lambda function
public String onEvent(Map<String, String> event) {
    System.out.println("received: "   event);
    return event
       .entrySet()
       .stream()
       .map(e -> e.getKey()   "->"   e.getValue())
       .collect(Collectors.joining(","));
}
Lambda handler
static String lambdaHandler = "dk.jarry.aws.lambda.greetings.boundary.Greetings::onEvent";
Build and deploy funtion with AWS CDK
#!/bin/sh
set -e

echo "building functions"
mvn clean package

echo "building and deploy - CDK"
cd cdk && mvn clean package && cdk deploy $PROFILE
Output of cdk diff after change to function (mvn clean package)
Stack aws-lambda-cdk-plain
Resources
[~] AWS::Lambda::Function dk_jarry_aws_lambda_greetings_boundary_Greetings dkjarryawslambdagreetingsboundaryGreetingsDCA7FDA8
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 4efad5ac647d6e70d8b21628811edfdb86f9042902d462bcb70ccd8bf4c1ab98.jar
 │       └─ [ ] 8a6de566201c0ce68f1b15fab8bad8ed5215fa850b74d7325af9bb8b1493691e.jar
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.4efad5ac647d6e70d8b21628811edfdb86f9042902d462bcb70ccd8bf4c1ab98.jar
         └─ [ ] asset.8a6de566201c0ce68f1b15fab8bad8ed5215fa850b74d7325af9bb8b1493691e.jar
Testing
Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo-simple-lambda && code -n .

aws-cdk-demo-hello-world-http

  • Show the Quarkus app with MicroProfile

  • Show how to test the app on localhost

  • Show the CKD code (http)

  • Show lambda handler

Create the application
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.2.Final:create \
    -DprojectGroupId=dk.jarry.aws \
    -DprojectArtifactId=aws-cdk-demo-hello-world-http \
    -DclassName="dk.jarry.aws.HelloResource" \
    -Dpath="/hello" \
    -Dextensions="quarkus-resteasy"
pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-amazon-lambda-http</artifactId>
</dependency>
Rest endpoint
@Path("/hello")
public class HelloResource {

    @Inject
    @ConfigProperty(defaultValue = "hello, quarkus on localhost", name="message")
    String message;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return message;
    }
}

Expected output : hello, quarkus on localhost

Expected output : Hello World - Quarkus as AWS Lambda

Lambda handler
static String lambdaHandler = "io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest";
Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo-hello-world-http && code -n .

aws-cdk-demo-hello-world-rest

  • Show the CKD code (rest)

Create the application
mvn io.quarkus.platform:quarkus-maven-plugin:2.11.2.Final:create \
    -DprojectGroupId=dk.jarry.aws \
    -DprojectArtifactId=aws-cdk-demo-hello-world-http \
    -DclassName="dk.jarry.aws.HelloResource" \
    -Dpath="/hello" \
    -Dextensions="quarkus-resteasy"
pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-amazon-lambda-rest</artifactId>
</dependency>
Rest endpoint
@Path("/hello")
public class HelloResource {

    @Inject
    @ConfigProperty(defaultValue = "hello, quarkus on localhost", name="message")
    String message;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return message;
    }
}
Add the endpoint
var apiGateway = LambdaRestApi.Builder
                    .create(this, "RestApiGateway")
                    .handler(function)
                    .build();
Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo-hello-world-rest && code -n .

todo-app-h2

  • Introduce the ToDo Quarkus application.

  • Start the ToDo Quarkus application on localhost.

Start app in dev mode
mvn compile quarkus:dev
Code pick
cd ~/git/jarrydk/aws-cdk-demo/todo-app-h2 && code -n .

aws-cdk-demo-st

Tip
todo-app-h2 app need to be up running before going forward.
  • Introduce the ToDo SystemTest.

Start app in dev mode
mvn compile quarkus:dev
Start app in dev mode - change endpoint
mvn compile quarkus:dev -Dquarkus.rest-client.extensions-api.url=http://localhost:8080
Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo-st && code -n .

aws-cdk-demo

Tip
Upload the java17layer.zip to the bucket aws-cdk-demo-lamda-layers with the script s3_upload_java17layers_to_aws-cdk-demo-lamda-layers.sh before creating the layer.
  • Show how to create a DynamoDB table (L1)

  • Show how to create a Role (L2)

  • Show how to create a Bucket (L1)

  • Show how to create a Layer (L1)

Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo && code -n .

aws-cdk-demo-dynamodb-todo

  • Show how the app is using DynamoDB

  • Show how to test the app on localhost

  • Show how to create the DynamoDB database and table for AWS

  • Show how to create a Role for AWS and use it

Start a DynamoDB container on "localhost"
podman run -it \
     --publish 8000:8000 \
     amazon/dynamodb-local:1.11.477 \
     -jar DynamoDBLocal.jar -inMemory -sharedDb
Create the table 'todos' in DynamoDB
var params = {
    TableName: 'ToDos',
    KeySchema: [{ AttributeName: 'uuid', KeyType: 'HASH' }],
    AttributeDefinitions: [{  AttributeName: 'uuid', AttributeType: 'S', }],
    ProvisionedThroughput: { ReadCapacityUnits: 1, WriteCapacityUnits: 1, }
};
dynamodb.createTable(params, function(err, data) {
    if (err) ppJson(err);
    else ppJson(data);

});
Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo-dynamodb-todo && code -n .

aws-cdk-demo-dynamodb-todo-jdk17

  • Show how to add a java17 layer

Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo-dynamodb-todo-jdk17 && code -n .

aws-cdk-demo-dynamodb-todo-jdk17-alb

Code pick
cd ~/git/jarrydk/aws-cdk-demo/aws-cdk-demo-dynamodb-todo-jdk17-alb && code -n .

aws-cdk-demo-dynamodb-todo-jdk17-sam (only on request)

Same as aws-cdk-demo-dynamodb-todo-jdk17 but deployed via SAM.

Java 17 layer

Build the java17layer layer from https://github.com/msailes/lambda-java17-layer or use the one in this project.

Tests from command line

Create a ToDo
curl -X POST http://localhost:8080/todos \
	-H 'Accept: application/json' \
	-H 'Content-Type: application/json' \
	-d '{"subject":"Hello from Quarkus","body":"Content"}'
./create_todo.sh http://localhost:8080/todos
Read a ToDo
curl http://localhost:8080/todos/db50ec51-99cf-4972-a435-50ea3701c78a
./read_todo.sh http://localhost:8080/todos db50ec51-99cf-4972-a435-50ea3701c78a

CDK commands

  • cdk bootstrap deploy AWS CDK into AWS into environment

  • cdk ls list all stacks in the app

  • cdk synth emits the synthesized CloudFormation template

  • cdk deploy deploy this stack to your default AWS account/region

  • cdk diff compare deployed stack with current state

  • cdk docs open CDK documentation

AWS Profiles

credentials in ~/.aws
[default]
aws_access_key_id = Tm90IGEgSm9rZSEh
aws_secret_access_key = QXJlIHlvdSBraWRkaW5nIG1l

[lambda_user]
aws_access_key_id = Tm90IGEgSm9rZSEh
aws_secret_access_key = QXJlIHlvdSBraWRkaW5nIG1l
config in ~/.aws
[default]
region = eu-central-1
output = json

Pulumi

import com.pulumi.Pulumi;
import com.pulumi.aws.s3.Bucket;

public class App {
    public static void main(String[] args) {
        Pulumi.run(ctx -> {

            // Create an AWS resource (S3 Bucket)
            var bucket = new Bucket("my-bucket");

            // Export the name of the bucket
            ctx.export("bucketName", bucket.getId());
        });
    }
}