LIGHT

  • News
  • Docs
  • Community
  • Reddit
  • GitHub

Native Image

The number one complaint users have when building AWS Lambdas with JVM languages is the cold start latency issue.

Fatjar

GraalVM needs to run against a fat jar file with the main entry point provided by the custom-runtime.

In the build.gradle, the following lines are important to generate the fat jar.

plugins {
    ...
    id "com.github.johnrengelman.shadow" version "5.2.0"
}

dependencies {
	...	
    implementation 'com.networknt:custom-runtime:2.0.23-SNAPSHOT'
    ...
}

jar {
  manifest {
    attributes 'Main-Class': 'com.networknt.aws.lambda.Runtime'
  }
}

GraalVM Build

GraalVM works by taking the fat jar file and creating a Linux executable file that AWS Lambda can run.

reflect.json

GraalVM does not support refection, so we need to provide a reflect.json to specify all the initialized classes based on the Java Reflection.

Here is an example.


[
  {
    "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent",
    "methods": [
      { "name": "<init>", "parameterTypes": [] }
    ],
    "allPublicMethods" : true
  },
  {
    "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$ProxyRequestContext",
    "methods": [
      { "name": "<init>", "parameterTypes": [] }
    ],
    "allPublicMethods" : true
  },
  {
    "name": "com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent$RequestIdentity",
    "methods": [
      { "name": "<init>", "parameterTypes": [] }
    ],
    "allPublicMethods" : true
  },
  {
    "name": "com.networknt.aws.lambda.AuthPolicy",
    "allPublicMethods" : true
  },
  {
    "name": "com.networknt.aws.lambda.DefaultResponse",
    "allPublicMethods" : true
  },
  {
    "name": "com.networknt.aws.lambda.InvocationResponse",
    "allPublicMethods" : true
  },
  {
    "name": "com.networknt.aws.lambda.LambdaContext",
    "allPublicMethods" : true
  },
  {
    "name": "com.networknt.petstore.handler.App",
    "allDeclaredConstructors": true,
    "allPublicConstructors": true,
    "allDeclaredMethods": true,
    "allPublicMethods": true
  },
  {
    "name": "ch.qos.logback.core.ConsoleAppender",
    "allPublicMethods":true,
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.core.FileAppender",
    "allPublicMethods":true,
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.encoder.PatternLayoutEncoder",
    "allPublicMethods":true,
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.LineOfCallerConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.DateConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.ExtendedThrowableProxyConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.LevelConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.LineSeparatorConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.LoggerConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.MessageConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.ThreadConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.MDCConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.ClassOfCallerConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  },
  {
    "name": "ch.qos.logback.classic.pattern.MethodOfCallerConverter",
    "methods":[{"name":"<init>","parameterTypes":[] }]
  }
]

reource-config.json

We also need to specify all the resources used by the lambda function.

resource-config.json

{
  "resources": {
    "includes": [
      {"pattern": "app.yml"},
      {"pattern": "logback.xml"}
    ],
    "excludes": [
    ]
  }
}
bootstrap

A bootstrap file is required to invoke the binary native image as AWS assumes this file is the entry point.

bootstrap

#!/bin/sh
set -euo pipefail
./server

Makefile

When using custom runtime for the lambda function, the sam build will try to locate a Makefile in the lambda function folder to start the build.

Here is the example in the template.yaml with Runtime: provided

  PetsGetFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      
      CodeUri: PetsGetFunction
      Handler: com.networknt.petstore.handler.App::handleRequest
      Runtime: provided
      MemorySize: 512
      FunctionName: PetsGetFunction
      Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
        Variables:
          PARAM1: VALUE
      
      Events:
        PetsGet:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /v1/pets
            Method: GET
            
            RestApiId:
              Ref: ApiGateway

Here is the Makefile


CUR_DIR := $(abspath $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST))))))

build-PetsGetFunction:
	cd $(CUR_DIR) && ./gradlew build
	cp $(CUR_DIR)/build/graalvm/server $(ARTIFACTS_DIR)
	cp $(CUR_DIR)/bootstrap $(ARTIFACTS_DIR)
build.gradle

The above Makefile calls the gradlew build to build the native image. The build.grale has a section.

task buildGraalvmImage {
 inputs.files("${project.projectDir}/src/main", configurations.compileClasspath)
 outputs.upToDateWhen {file("${buildDir}/graalvm/server").exists()}
 outputs.file file("${buildDir}/graalvm/server")

 doLast {
    exec {
      commandLine "bash", "-c", "chmod +x build_graalvm.sh; chmod +x bootstrap; ./build_graalvm.sh"
    }
  }
}

buildGraalvmImage.dependsOn shadowJar, test
build.dependsOn buildGraalvmImage

build_graalvm.sh

The build.gradle calls the build_graalvm.sh to build the native image with docker.


#!/bin/bash

docker run --rm --name graal -v $(pwd):/working springci/graalvm-ce:master-java11 \
    /bin/bash -c "native-image \
                    --enable-url-protocols=http,https \
                    --no-fallback \
                    --allow-incomplete-classpath \
                    --enable-all-security-services \
                    -H:ReflectionConfigurationFiles=/working/reflect.json \
                    -H:ResourceConfigurationFiles=/working/resource-config.json \
                    -H:+ReportExceptionStackTraces \
                    -jar /working/build/libs/PetsGetFunction-all.jar \
                    ; \
                    cp PetsGetFunction-all /working/build/graalvm/server"

mkdir -p build/graalvm
if [ ! -f "build/graalvm/server" ]; then
    echo "there was an error building graalvm image"
    exit 1
fi

With the above config files and build scripts, we can invoke the build from the parent fold with AWS CLI to build multiple Lambda functions.

Run the following command in the folder with template.yanl file.

sam build
  • About Light
    • Overview
    • Testimonials
    • What is Light
    • Features
    • Principles
    • Benefits
    • Roadmap
    • Community
    • Articles
    • Videos
    • License
    • Why Light Platform
  • Getting Started
    • Get Started Overview
    • Environment
    • Light Codegen Tool
    • Light Rest 4j
    • Light Tram 4j
    • Light Graphql 4j
    • Light Hybrid 4j
    • Light Eventuate 4j
    • Light Oauth2
    • Light Portal Service
    • Light Proxy Server
    • Light Router Server
    • Light Config Server
    • Light Saga 4j
    • Light Session 4j
    • Webserver
    • Websocket
    • Spring Boot Servlet
  • Architecture
    • Architecture Overview
    • API Category
    • API Gateway
    • Architecture Patterns
    • CQRS
    • Eco System
    • Event Sourcing
    • Fail Fast vs Fail Slow
    • Integration Patterns
    • JavaEE declining
    • Key Distribution
    • Microservices Architecture
    • Microservices Monitoring
    • Microservices Security
    • Microservices Traceability
    • Modular Monolith
    • Platform Ecosystem
    • Plugin Architecture
    • Scalability and Performance
    • Serverless
    • Service Collaboration
    • Service Mesh
    • SOA
    • Spring is bloated
    • Stages of API Adoption
    • Transaction Management
    • Microservices Cross-cutting Concerns Options
    • Service Mesh Plus
    • Service Discovery
  • Design
    • Design Overview
    • Design First vs Code First
    • Desgin Pattern
    • Service Evolution
    • Consumer Contract and Consumer Driven Contract
    • Handling Partial Failure
    • Idempotency
    • Server Life Cycle
    • Environment Segregation
    • Database
    • Decomposition Patterns
    • Http2
    • Test Driven
    • Multi-Tenancy
    • Why check token expiration
    • WebServices to Microservices
  • Cross-Cutting Concerns
    • Concerns Overview
  • API Styles
    • Light-4j for absolute performance
    • Style Overview
    • Distributed session on IMDG
    • Hybrid Serverless Modularized Monolithic
    • Kafka - Event Sourcing and CQRS
    • REST - Representational state transfer
    • Web Server with Light
    • Websocket with Light
    • Spring Boot Integration
    • Single Page Application
    • GraphQL - A query language for your API
    • Light IBM MQ
    • Light AWS Lambda
    • Chaos Monkey
  • Infrastructure Services
    • Service Overview
    • Light Proxy
    • Light Mesh
    • Light Router
    • Light Portal
    • Messaging Infrastructure
    • Centralized Logging
    • COVID-19
    • Light OAuth2
    • Metrics and Alerts
    • Config Server
    • Tokenization
    • Light Controller
  • Tool Chain
    • Tool Chain Overview
  • Utility Library
  • Service Consumer
    • Service Consumer
  • Development
    • Development Overview
  • Deployment
    • Deployment Overview
    • Frontend Backend
    • Linux Service
    • Windows Service
    • Install Eventuate on Windows
    • Secure API
    • Client vs light-router
    • Memory Limit
    • Deploy to Kubernetes
  • Benchmark
    • Benchmark Overview
  • Tutorial
    • Tutorial Overview
  • Troubleshooting
    • Troubleshoot
  • FAQ
    • FAQ Overview
  • Milestones
  • Contribute
    • Contribute to Light
    • Development
    • Documentation
    • Example
    • Tutorial
“Native Image” was last updated: January 26, 2021: fixes #239 add more documents for light-aws-lambda (4a7e6af)
Improve this page
  • News
  • Docs
  • Community
  • Reddit
  • GitHub
  • About Light
  • Getting Started
  • Architecture
  • Design
  • Cross-Cutting Concerns
  • API Styles
  • Infrastructure Services
  • Tool Chain
  • Utility Library
  • Service Consumer
  • Development
  • Deployment
  • Benchmark
  • Tutorial
  • Troubleshooting
  • FAQ
  • Milestones
  • Contribute