Skip to content

Commit

Permalink
Merge pull request quarkusio#4777 from patriot1burke/aws-native
Browse files Browse the repository at this point in the history
Native
  • Loading branch information
stuartwdouglas authored Oct 25, 2019
2 parents fe5e5d6 + 536979a commit eb50076
Show file tree
Hide file tree
Showing 69 changed files with 2,657 additions and 1,220 deletions.
224 changes: 209 additions & 15 deletions docs/src/main/asciidoc/amazon-lambda-http-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,230 @@ This guide is maintained in the main Quarkus repository
and pull requests should be submitted there:
https://github.com/quarkusio/quarkus/tree/master/docs/src/main/asciidoc
////
= Quarkus - Amazon Lambda HTTP Applications 
= Quarkus - Amazon Lambda with Resteasy, Undertow, or Vert.x Web 

include::./attributes.adoc[]


The `quarkus-amazon-lambda-http` extension allows you to write microservices with Resteasy (JAX-RS),
Undertow (servlet), or Vert.x Web and make these microservices deployable to Amazon AWS using
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html[Amazon's SAM] framework.
Undertow (servlet), or Vert.x Web and make these microservices deployable as an Amazon Lambda
using https://aws.amazon.com/api-gateway/[Amazon's API Gateway] and https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html[Amazon's SAM framework].

You can deploy your Lambda as a pure Java jar, or you can compile your project to a native image (Graal) and deploy that for a smaller
memory footprint and startup time.

== Prerequisites

To complete this guide, you need:

* less than 15 minutes
* JDK 1.8 (Azure requires JDK 1.8)
* less than 30 minutes
* JDK 1.8 (AWS requires JDK 1.8)
* Apache Maven 3.5.3+
* https://aws.amazon.com[An Amazon AWS account]
* https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html#serverless-sam-cli[Amazon SAM CLI]
* https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html[AWS SAM CLI]

== Getting Started

This guide can be used on existing projects or new projects. Whether the project is a new green field project or you're
attempting to migrate an existing application, this guide will walk you through the steps necessary to use Quarkus powered
applications on Amazon's infrastructure.
This guide walks you through generating an example Java project via a maven archetype. Later on it walks through the structure
of the project so you can adapt any existing projects you have to use Amazon Lambda.

== Installing AWS bits

Installing all the AWS bits is probably the most difficult thing about this guide. Make sure that you follow all the steps
for installing AWS SAM CLI.

== Creating the Maven Deployment Project

Create the Quarkus AWS Lambda maven project using our Maven Archetype.


[source, subs=attributes+]
----
mvn archetype:generate \
-DarchetypeGroupId=io.quarkus \
-DarchetypeArtifactId=quarkus-amazon-lambda-http-archetype \
-DarchetypeVersion={quarkus-version}
----

== Build and Deploy

Build the project using maven.

[source, subs=attributes+]
----
mvn clean install
----

This will compile the code and run the unit tests included within the generated project.
Unit testing is all regular local Java and does not require running on Amazon. Quarkus dev-mode is also
usable.

If you want to build for native too, make sure you have Graal installed correctly and just add a `native` property
to the build

[source, subs=attributes+]
----
mvn clean install -Dnative
----

== Simulate Amazon Lambda Deployment

The AWS SAM CLI allows you to run your lambda's locally on your laptop in a simulated Lambda environment. This requires docker to be installed (see their install docs).
After you have built your maven project, execute this command

[source, subs=attributes+]
----
sam local start-api --template sam.jvm.yaml
----

This will start a docker container that mimic's Amazon's Lamba's deployment environment. Once the environment
is started, you can invoke the example lambda in your browser by going to

http://127.0.0.1:3000/hello

In the console you'll see startup messages of the example lambda. This particular deployment starts a JVM and loads your
lambda as pure Java.

If you want to deploy a native Graal executable of your lambda, use a different yaml template that is provided in your
generated project:

[source, subs=attributes+]
----
sam local start-api --template sam.native.yaml
----

== Deploy to AWS

There are a few steps to get your lambda running on AWS.

=== Package your deployment.

[source, subs=attributes+]
----
sam package --template-file sam.jvm.yaml --output-template-file packaged.yaml --s3-bucket <YOUR_S3_BUCKET>
----

Type the simple name of your S3 bucket you created during.

=== Deploy your package

[source, subs=attributes+]
----
sam deploy --template-file packaged.yaml --capabilities CAPABILITY_IAM --stack-name <YOUR_STACK_NAME>
----

The stack name can be anything you want.

=== Debugging AWS Deployment Problems

If `sam deploy`, run the `describe-stack-events` command
to get information about your deployment and what happened.

[source, subs=attributes+]
----
aws cloudformation describe-stack-events --stack-name <YOUR_STACK_NAME>
----

One common issue that you may run across is that your S3 bucket has to be in the same region as Amazon Lambda.
Look for this error from `describe-stack-events` output:

[source, subs=attributes+]
----
Error occurred while GetObject. S3 Error Code: AuthorizationHeaderMalformed. S3 Error Message:
The authorization header is malformed; the region 'us-east-1' is wrong; expecting 'us-east-2'
(Service: AWSLambdaInternal; Status Code: 400; Error Code: InvalidParameterValueException;
Request ID: aefcf978-ad2a-4b53-9ffe-cea3fcd0f868)
----

The above error is stating that my S3 bucket should be in `us-east-2`, not `us-east-1`.
To fix this error you'll need to create an S3 bucket in that region and redo steps 1 and 2 from above.

Another annoying this is that if there is an error in deployment, you also have to completely delete
it before trying to deploy again:

[source, subs=attributes+]
----
aws cloudformation delete-stack --stack-name <YOUR_STACK_NAME>
----

== Execute your REST Lambda on AWS

To get the root URL for your service, type the following command and see the following output:

[source, subs=attributes+]
----
aws cloudformation describe-stacks --stack-name <YOUR_STACK_NAME>
----

It should give you something like the following output:
[source, subs=attributes+]
----
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:502833056128:stack/QuarkusNativeRestExample2/b35b0200-f685-11e9-aaa0-0e8cd4caae34",
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
},
"Description": "AWS Serverless Quarkus HTTP - io.demo::rest-example",
"Tags": [],
"Outputs": [
{
"Description": "URL for application",
"ExportName": "RestExampleNativeApi",
"OutputKey": "RestExampleNativeApi",
"OutputValue": "https://234234234.execute-api.us-east-1.amazonaws.com/Prod/"
}
],
----

The `OutputValue` attribute is the root URL for your lambda. Copy it to your browser and add `hello` at the end.

== Examine the POM

If you want to adapt an existing Resteasy, Undertow, or Vert.x Web project to Amazon Lambda, there's a couple
of things you need to do. Take a look at the generate example project to get an example of what you need to adapt.

1. Include the `quarkus-amazon-lambda-http` extension as a pom dependency
2. Configure Quarkus build an `uber-jar`
3. If you are doing a native Graal build, Amazon requires you to rename your executable to `bootstrap` and zip it up.

== Examine sam.yaml

The `sam.yaml` syntax is beyond the scope of this document. There's a couple of things to note though that are particular
to the `quarkus-amazon-lambda-http` extension.

Amazon's API Gateway assumes that HTTP response bodies are text unless you explicitly tell it which media types are
binary through configuration. To make things easier, the Quarkus extension forces a binary (base 64) encoding of all
HTTP response messages and the `sam.yaml` file must configure the API Gateway to assume all media types are binary:

[source, subs=attributes+]
----
Globals:
Api:
EndpointConfiguration: REGIONAL
BinaryMediaTypes:
- "*/*"
----

Another thing to note is that for pure Java lambda deployments, do not change the Lambda handler name.

[source, subs=attributes+]
----
Properties:
Handler: io.quarkus.amazon.lambda.runtime.QuarkusStreamHandler::handleRequest
Runtime: java8
----

This particular handler handles all the intricacies of integrating with the Quarkus runtime. So you must use that
handler.








=== New Projects

The simplest scenario is when starting a completely new project. Once you've <<getting-started-guide.adoc,generated>> a new
Quarkus project, you'll need to configure your application for deployment to Amazon. The steps to achieve this are beyond
the scope of this document but you can find the full guide on
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-getting-started.html[Amazon's website].

For the purposes of this guide, we'll be using RESTEasy and maven.
10 changes: 9 additions & 1 deletion extensions/amazon-lambda-http/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,20 @@
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-web-deployment</artifactId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-amazon-lambda-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-amazon-lambda-http</artifactId>
</dependency>
<dependency>
<groupId>com.oracle.substratevm</groupId>
<artifactId>svm</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,53 @@
package io.quarkus.amazon.lambda.http.deployment;

import org.jboss.logging.Logger;

import io.quarkus.amazon.lambda.deployment.ProvidedAmazonLambdaHandlerBuildItem;
import io.quarkus.amazon.lambda.http.LambdaHttpHandler;
import io.quarkus.amazon.lambda.http.model.AlbContext;
import io.quarkus.amazon.lambda.http.model.ApiGatewayAuthorizerContext;
import io.quarkus.amazon.lambda.http.model.ApiGatewayRequestIdentity;
import io.quarkus.amazon.lambda.http.model.AwsProxyRequest;
import io.quarkus.amazon.lambda.http.model.AwsProxyRequestContext;
import io.quarkus.amazon.lambda.http.model.AwsProxyResponse;
import io.quarkus.amazon.lambda.http.model.CognitoAuthorizerClaims;
import io.quarkus.amazon.lambda.http.model.ErrorModel;
import io.quarkus.amazon.lambda.http.model.Headers;
import io.quarkus.amazon.lambda.http.model.MultiValuedTreeMap;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.substrate.ReflectiveClassBuildItem;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem;

public class AmazonLambdaHttpProcessor {
private static final Logger log = Logger.getLogger(AmazonLambdaHttpProcessor.class);

@BuildStep
public RequireVirtualHttpBuildItem requestVirtualHttp(LaunchModeBuildItem launchMode) {
return launchMode.getLaunchMode() == LaunchMode.NORMAL ? RequireVirtualHttpBuildItem.MARKER : null;
}

@BuildStep
public ProvidedAmazonLambdaHandlerBuildItem setHandler() {
return new ProvidedAmazonLambdaHandlerBuildItem(LambdaHttpHandler.class, "AWS Lambda HTTP");
}

@BuildStep
public void registerReflectionClasses(BuildProducer<ReflectiveClassBuildItem> reflectiveClassBuildItemBuildProducer) {
reflectiveClassBuildItemBuildProducer
.produce(new ReflectiveClassBuildItem(true, true, true,
AlbContext.class,
ApiGatewayAuthorizerContext.class,
ApiGatewayRequestIdentity.class,
AwsProxyRequest.class,
AwsProxyRequestContext.class,
AwsProxyResponse.class,
CognitoAuthorizerClaims.class,
ErrorModel.class,
Headers.class,
MultiValuedTreeMap.class));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS Serverless Quarkus - ${descriptionName}
Globals:
Api:
EndpointConfiguration: REGIONAL

Resources:
${resourceName}Function:
Type: AWS::Serverless::Function
Properties:
Handler: ${handler}
Runtime: java8
CodeUri: ${artifact}
MemorySize: 512
Policies: AWSLambdaBasicExecutionRole
Timeout: 15
Events:
GetResource:
Type: Api
Properties:
Path: /{proxy+}
Method: any
Loading

0 comments on commit eb50076

Please sign in to comment.