Skip to content

Build Lambda Layer and Sample app with Terraform support #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions lambda-layer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# AWS Distro Lambda Layer for NodeJS

This package includes the following three components

1. AWS Lambda Layer implementation for supporting Application Signals in AWS Lambda.
2. Lambda Sample App with AWS SDK to demonstrate Application Signals telemetry data generated by Lambda Layer.
3. Terraform script for deploy the sample app with Lambda layer built from the local package.

## Scripts to build everything

Run the following command in the root director to build both NodeJS Lambda Layer and Sample App.

**Note**: Make sure to install NodeJS 16+ to build the layer. Lower version of NodJS runtime is not supported by AWS Lambda
anymore.

1. `./lambda-layer/build.sh`

## Steps to Build AWS Lambda Layer

1. Run the following command in the root director
* `./scripts/build_and_install_distro.sh`
2. Run to the following steps in `${root}/lambda-layer/packages/layer` director

```any
npm install
npm run compile
```

## Steps to Deploy this Lambda function

Go to `${root}/lambda-layer/sample-apps/aws-sdk` director,

1. run `npm install`.
2. run `terraform init` and `terraform apply`.

The lambda function(`aws-opentelemetry-distro-nodejs`) will be initialized and the URL for an API Gateway invoking the Lambda
will be displayed at the end. Send a request to the URL in a browser or using curl to execute the function. Then,
navigate to Lambda function's Monitoring page.
You will see a log stream with an event time and Traces data corresponding to when you issued the request,
open it and you can find
information about the exported spans in the log stream.
26 changes: 26 additions & 0 deletions lambda-layer/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

set -x

SOURCEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
echo "Source directory: ${SOURCEDIR}"

# Navigate to the root project directory
cd "${SOURCEDIR}/.."

# Install dependencies and compile all projects in the repository
echo "Installing dependencies and compiling projects..."
npm install
npm run compile

# Build Lambda SDK layer
cd ${SOURCEDIR}/packages/layer || exit
rm -rf build
rm -rf node_modules
npm install || exit

# Build sample apps
cd ${SOURCEDIR}/sample-apps/aws-sdk || exit
rm -rf build
rm -rf node_modules
npm install || exit
1 change: 1 addition & 0 deletions lambda-layer/packages/layer/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
7 changes: 7 additions & 0 deletions lambda-layer/packages/layer/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
"env": {
"mocha": true,
"node": true
},
...require('../../eslint.config.js')
}
53 changes: 53 additions & 0 deletions lambda-layer/packages/layer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "@aws/aws-distro-opentelemetry-node-autoinstrumentatio-sdk-layer",
"version": "0.0.1",
"description": "Lambda Layer including AWS Distro OpenTelemetry SDK for supporting Amazon Application Signals",
"repository": "aws-observability/aws-otel-js-instrumentation",
"author": {
"name": "Amazon Web Services",
"url": "http://aws.amazon.com"
},
"homepage": "https://github.com/aws-observability/aws-otel-js-instrumentation/tree/main/aws-distro-opentelemetry-node-autoinstrumentation#readme",
"license": "Apache-2.0",
"engines": {
"node": ">=16"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"clean": "rimraf build/*",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"prepare": "npm run compile",
"compile": "tsc -p .",
"postcompile": "copyfiles -F -e 'node_modules/@aws/aws-distro-opentelemetry-node-autoinstrumentation/node_modules/**' 'node_modules/**' build/workspace/nodejs && copyfiles -f 'scripts/*' build/workspace && copyfiles -f 'build/src/*' build/workspace && cd build/workspace && bestzip ../layer.zip *"
},
"keywords": [
"awsdistroopentelemetry",
"opentelemetry",
"awslambda",
"nodejs",
"tracing",
"profiling",
"instrumentation"
],
"dependencies": {
"@aws/aws-distro-opentelemetry-node-autoinstrumentation": "file:../../../aws-distro-opentelemetry-node-autoinstrumentation",
"@opentelemetry/api": "1.9.0",
"@opentelemetry/auto-instrumentations-node": "0.48.0",
"@opentelemetry/auto-configuration-propagators": "0.2.0",
"@opentelemetry/exporter-metrics-otlp-grpc": "0.52.1",
"@opentelemetry/exporter-metrics-otlp-http": "0.52.1",
"@opentelemetry/instrumentation": "0.52.1",
"@opentelemetry/id-generator-aws-xray": "1.2.2",
"@opentelemetry/propagator-aws-xray": "1.25.1",
"@opentelemetry/core": "1.25.1",
"@opentelemetry/sdk-trace-base": "1.25.1",
"@opentelemetry/semantic-conventions": "1.25.1",
"@opentelemetry/resources": "1.25.1",
"@opentelemetry/exporter-trace-otlp-proto": "0.52.1",
"@opentelemetry/exporter-zipkin": "1.25.1",
"@opentelemetry/sdk-metrics": "1.25.1"
}
}
27 changes: 27 additions & 0 deletions lambda-layer/packages/layer/scripts/otel-instrument
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
export NODE_OPTIONS="${NODE_OPTIONS} --require /opt/wrapper.js"
export LAMBDA_RESOURCE_ATTRIBUTES="cloud.region=$AWS_REGION,cloud.provider=aws,faas.name=$AWS_LAMBDA_FUNCTION_NAME,faas.version=$AWS_LAMBDA_FUNCTION_VERSION,faas.instance=$AWS_LAMBDA_LOG_STREAM_NAME,aws.log.group.names=$AWS_LAMBDA_LOG_GROUP_NAME";
export OTEL_NODE_ENABLED_INSTRUMENTATIONS="http,aws-lambda,aws-sdk"

# - Set Application Signals configuration
if [ "${OTEL_AWS_APPLICATION_SIGNALS_ENABLED}" = "true" ]; then
export OTEL_METRICS_EXPORTER="none";
export OTEL_LOGS_EXPORTER="none";
fi

# - Append Lambda Resource Attributes to OTel Resource Attribute List
if [ -z "${OTEL_RESOURCE_ATTRIBUTES}" ]; then
export OTEL_RESOURCE_ATTRIBUTES=$LAMBDA_RESOURCE_ATTRIBUTES;
else
export OTEL_RESOURCE_ATTRIBUTES="$LAMBDA_RESOURCE_ATTRIBUTES,$OTEL_RESOURCE_ATTRIBUTES";
fi

# - Set the service name
if [ -z "${OTEL_SERVICE_NAME}" ]; then
export OTEL_SERVICE_NAME=$AWS_LAMBDA_FUNCTION_NAME;
fi
if [[ $OTEL_RESOURCE_ATTRIBUTES != *"service.name="* ]]; then
export OTEL_RESOURCE_ATTRIBUTES="service.name=${AWS_LAMBDA_FUNCTION_NAME},${OTEL_RESOURCE_ATTRIBUTES}"
fi

exec "$@"
4 changes: 4 additions & 0 deletions lambda-layer/packages/layer/src/wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

require('@aws/aws-distro-opentelemetry-node-autoinstrumentation/register')
11 changes: 11 additions & 0 deletions lambda-layer/packages/layer/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base",
"compilerOptions": {
"rootDir": ".",
"outDir": "build"
},
"include": [
"src/**/*.ts",
"test/**/*.ts"
]
}
1 change: 1 addition & 0 deletions lambda-layer/sample-apps/aws-sdk/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
7 changes: 7 additions & 0 deletions lambda-layer/sample-apps/aws-sdk/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
"env": {
"mocha": true,
"node": true
},
...require('../../eslint.config.js')
}
56 changes: 56 additions & 0 deletions lambda-layer/sample-apps/aws-sdk/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "@opentelemetry-samples/lambda-awssdk",
"description": "Sample application for AWS Lambda using AWS SDK v2.",
"version": "0.0.1",
"repository": "aws-observability/aws-otel-js-instrumentation",
"author": {
"name": "Amazon Web Services",
"url": "http://aws.amazon.com"
},
"homepage": "https://github.com/aws-observability/aws-otel-js-instrumentation/tree/main/aws-distro-opentelemetry-node-autoinstrumentation#readme",
"license": "Apache-2.0",
"engines": {
"node": ">=16"
},
"publishConfig": {
"access": "public"
},
"main": "build/src/index.js",
"types": "build/src/index.d.ts",
"scripts": {
"clean": "rimraf build/*",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"precompile": "tsc --version",
"prepare": "npm run compile",
"compile": "tsc -p .",
"postcompile": "copyfiles 'package*.json' build/src/ && npm install --production --ignore-scripts --prefix build/src/ && cd build/src && bestzip ../function.zip *"
},
"keywords": [
"opentelemetry",
"awslambda",
"nodejs",
"tracing",
"profiling",
"instrumentation"
],
"files": [
"build/src/**/*.js",
"build/src/**/*.d.ts",
"doc",
"LICENSE",
"README.md"
],
"devDependencies": {
"@types/aws-lambda": "8.10.110",
"@types/node": "18.14.6",
"bestzip": "2.2.0",
"copyfiles": "2.4.1",
"rimraf": "4.1.2",
"ts-node": "10.9.1",
"typescript": "4.9.5"
},
"dependencies": {
"aws-sdk": "2.1328.0"
}
}
21 changes: 21 additions & 0 deletions lambda-layer/sample-apps/aws-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
APIGatewayProxyEvent,
APIGatewayProxyResult,
Context,
} from 'aws-lambda';

import AWS from 'aws-sdk';

const s3 = new AWS.S3();

exports.handler = async (event: APIGatewayProxyEvent, context: Context) => {
console.info('Serving lambda request.');

const result = await s3.listBuckets().promise();

const response: APIGatewayProxyResult = {
statusCode: 200,
body: `Hello lambda - found ${result.Buckets?.length || 0} buckets`,
};
return response;
};
11 changes: 11 additions & 0 deletions lambda-layer/sample-apps/aws-sdk/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.base",
"compilerOptions": {
"rootDir": ".",
"outDir": "build"
},
"include": [
"src/**/*.ts",
"test/**/*.ts"
]
}
67 changes: 67 additions & 0 deletions lambda-layer/terraform/api-gateway-proxy/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
resource "aws_api_gateway_rest_api" "lambda_api_proxy" {
name = var.name
}

resource "aws_api_gateway_resource" "lambda_api_proxy" {
rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id
parent_id = aws_api_gateway_rest_api.lambda_api_proxy.root_resource_id
path_part = "{proxy+}"
}

resource "aws_api_gateway_method" "lambda_api_proxy" {
rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id
resource_id = aws_api_gateway_resource.lambda_api_proxy.id
http_method = "ANY"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda_api" {
rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id
resource_id = aws_api_gateway_method.lambda_api_proxy.resource_id
http_method = aws_api_gateway_method.lambda_api_proxy.http_method

integration_http_method = "POST"
type = "AWS_PROXY"
uri = var.function_invoke_arn
}

resource "aws_api_gateway_method" "lambda_api_proxy_root_nodejs" {
rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id
resource_id = aws_api_gateway_rest_api.lambda_api_proxy.root_resource_id
http_method = "ANY"
authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda_api_root_nodejs" {
rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id
resource_id = aws_api_gateway_method.lambda_api_proxy_root_nodejs.resource_id
http_method = aws_api_gateway_method.lambda_api_proxy_root_nodejs.http_method

integration_http_method = "POST"
type = "AWS_PROXY"
uri = var.function_invoke_arn
}

resource "aws_api_gateway_deployment" "lambda_api_proxy" {
depends_on = [
aws_api_gateway_integration.lambda_api,
aws_api_gateway_integration.lambda_api_root_nodejs,
]

rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id
}

resource "aws_api_gateway_stage" "test" {
stage_name = "default"
rest_api_id = aws_api_gateway_rest_api.lambda_api_proxy.id
deployment_id = aws_api_gateway_deployment.lambda_api_proxy.id
xray_tracing_enabled = var.enable_xray_tracing
}

resource "aws_lambda_permission" "lambda_api_allow_gateway_nodejs" {
action = "lambda:InvokeFunction"
function_name = var.function_name
qualifier = var.function_qualifier
principal = "apigateway.amazonaws.com"
source_arn = "${aws_api_gateway_rest_api.lambda_api_proxy.execution_arn}/*/*"
}
3 changes: 3 additions & 0 deletions lambda-layer/terraform/api-gateway-proxy/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "api_gateway_url" {
value = aws_api_gateway_stage.test.invoke_url
}
26 changes: 26 additions & 0 deletions lambda-layer/terraform/api-gateway-proxy/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
variable "name" {
type = string
description = "Name of API gateway to create"
}

variable "function_name" {
type = string
description = "Name of function to proxy to"
}

variable "function_qualifier" {
type = string
default = null
description = "Qualifier of function to proxy to"
}

variable "function_invoke_arn" {
type = string
description = "Invoke ARN of function to proxy to"
}

variable "enable_xray_tracing" {
type = bool
description = "Whether to enable xray tracing of the API gateway"
default = true
}
Loading
Loading