Skip to content

Commit 4471945

Browse files
Adding ruby lambda layer (aws-observability#918)
* add ruby lambda layer draft * update zip_ruby_layer script for passing name * revision * revision and add more info on readme * add github action to build ruby lambda layer * use https instead of http for rubygems.org * add dependent bot * Update .github/dependabot.yml Co-authored-by: Tristan Sloughter <[email protected]> * add sample handler as default runtime wrapper * Update ruby/README.md Co-authored-by: Tristan Sloughter <[email protected]> * Update ruby/README.md Co-authored-by: Tristan Sloughter <[email protected]> * fix workflow --------- Co-authored-by: Tristan Sloughter <[email protected]>
1 parent c146248 commit 4471945

File tree

12 files changed

+367
-0
lines changed

12 files changed

+367
-0
lines changed

.github/dependabot.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ updates:
6969
opentelemetry-deps-python:
7070
patterns:
7171
- "opentelemetry-*"
72+
- package-ecosystem: "bundler"
73+
directory: "/ruby/src/layer"
74+
schedule:
75+
interval: "weekly"
76+
groups:
77+
opentelemetry-deps-ruby:
78+
patterns:
79+
- "opentelemetry-*"
7280
other:
7381
patterns:
7482
- "*"

.github/workflows/codeql.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ jobs:
4848
directory: 'nodejs'
4949
- language: 'python'
5050
directory: 'python'
51+
- language: 'ruby'
52+
directory: 'ruby'
5153
- language: 'java'
5254
directory: 'java'
5355
- language: 'csharp'
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: "Release Ruby Lambda Layer"
2+
3+
on:
4+
# (Using tag push instead of release to allow filtering by tag prefix.)
5+
push:
6+
tags:
7+
- layer-ruby/**
8+
9+
permissions:
10+
id-token: write
11+
contents: read
12+
13+
jobs:
14+
build-layer:
15+
runs-on: ubuntu-latest
16+
outputs:
17+
RUBY_SDK_VERSION: ${{ steps.save-ruby-sdk-version.outputs.RUBY_SDK_VERSION}}
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: ruby/setup-ruby@v1
22+
with:
23+
ruby-version: '3.2.0'
24+
25+
- name: Install SAM
26+
run: |
27+
apt-get update && apt-get install wget unzip make -y
28+
wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
29+
unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
30+
./sam-installation/install
31+
32+
- name: Build
33+
id: save-ruby-sdk-version
34+
run: |
35+
sam build -u -t template.yml
36+
export GEM_PATH=$PWD/.aws-sam/build/OTelLayer/ruby/gems/3.2.0/
37+
RUBY_SDK_VERSION=$(ruby -e 'require "opentelemetry-sdk"; puts OpenTelemetry::SDK::VERSION')
38+
echo "RUBY_SDK_VERSION=$RUBY_SDK_VERSION" >> $GITHUB_OUTPUT
39+
working-directory: ruby/src
40+
41+
- name: Zip the layer file
42+
run: |
43+
echo ${{ steps.save-ruby-sdk-version.outputs.RUBY_SDK_VERSION}}
44+
./zip_ruby_layer.sh
45+
working-directory: ruby/src
46+
shell: bash
47+
48+
- uses: actions/upload-artifact@v3
49+
name: Save assembled layer to build
50+
with:
51+
name: opentelemetry-ruby-layer.zip
52+
path: ruby/src/opentelemetry-ruby-layer.zip
53+
54+
publish-layer:
55+
uses: ./.github/workflows/layer-publish.yml
56+
needs: build-layer
57+
strategy:
58+
matrix:
59+
aws_region:
60+
- ap-northeast-1
61+
- ap-northeast-2
62+
- ap-south-1
63+
- ap-southeast-1
64+
- ap-southeast-2
65+
- ca-central-1
66+
- eu-central-1
67+
- eu-north-1
68+
- eu-west-1
69+
- eu-west-2
70+
- eu-west-3
71+
- sa-east-1
72+
- us-east-1
73+
- us-east-2
74+
- us-west-1
75+
- us-west-2
76+
with:
77+
artifact-name: opentelemetry-ruby-layer.zip
78+
layer-name: opentelemetry-ruby
79+
component-version: ${{needs.build-layer.outputs.RUBY_SDK_VERSION}}
80+
runtimes: ruby3.2
81+
release-group: prod
82+
aws_region: ${{ matrix.aws_region }}
83+
secrets: inherit

ruby/README.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# OpenTelemetry Lambda Ruby
2+
3+
Scripts and files used to build AWS Lambda Layers for running OpenTelemetry on AWS Lambda for Ruby.
4+
5+
Requirement:
6+
* [Ruby 3.2.0](https://www.ruby-lang.org/en/news/2022/12/25/ruby-3-2-0-released/)
7+
* [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)
8+
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)
9+
* [Go](https://go.dev/doc/install)
10+
* [Docker](https://docs.docker.com/get-docker)
11+
12+
13+
## Building Lambda Ruby Layer With OpenTelemetry Ruby Dependencies
14+
15+
Build
16+
```bash
17+
sam build -u -t template.yml
18+
```
19+
20+
Make sure the layer structure like below with your zip
21+
```
22+
ruby
23+
└── gems
24+
└── 3.2.0
25+
├── build_info
26+
├── doc
27+
├── extensions
28+
├── gems
29+
├── plugins
30+
└── specifications
31+
```
32+
33+
Zip the file for uploading to ruby lambda layer
34+
35+
```bash
36+
cd .aws-sam/build/OTelLayer/
37+
zip -qr ../../../<your_layer_name>.zip ruby/
38+
cd -
39+
40+
# or run following script
41+
zip_ruby_layer.sh -n <your_layer_name>
42+
```
43+
44+
### Why build layer this way
45+
46+
The default GEM_PATH for Lambda Ruby is $LAMBDA_TASK_ROOT/vendor/bundle/ruby/2.5.0:/opt/ruby/gems/2.5.0. Therefore, it's important to set the Ruby path in the format `/opt/ruby/gems/<ruby_version>`.
47+
48+
Reference for build ruby lambda layer
49+
https://docs.aws.amazon.com/lambda/latest/dg/ruby-package.html
50+
51+
52+
### Define the AWS_LAMBDA_EXEC_WRAPPER
53+
54+
There are two ways to define the AWS_LAMBDA_EXEC_WRAPPER that point to either binary executable or script (normally bash).
55+
56+
#### Method 1: define the AWS_LAMBDA_EXEC_WRAPPER in function from template.yml
57+
```yaml
58+
AWSTemplateFormatVersion: '2010-09-09'
59+
Transform: 'AWS::Serverless-2016-10-31'
60+
Description: OpenTelemetry Ruby Lambda layer for Ruby
61+
Parameters:
62+
LayerName:
63+
...
64+
Resources:
65+
OTelLayer:
66+
...
67+
api:
68+
...
69+
function:
70+
Type: AWS::Serverless::Function
71+
Properties:
72+
...
73+
Environment:
74+
Variables:
75+
AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-handler # this is an example of the path
76+
77+
```
78+
79+
#### Method 2: directly update the environmental variable in lambda console: Configuration -> Environemntal variables
80+
81+
For more information about aws lambda wrapper and wrapper layer, check [aws lambda runtime-wrapper](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-modify.html#runtime-wrapper). We provide a sample wrapper file in `src/layer/otel-handler` as reference.
82+
83+
### Tracing and export trace to collector
84+
85+
To enable the default opentelemetry tracing, you can either initialize the opentelemetry ruby sdk in function code. To receive the trace, you can define the OTEL-related (e.g. OTEL_EXPORTER_OTLP_ENDPOINT) environmental variable and setup the opentelemetry collector accordingly.
86+
87+
For example:
88+
```ruby
89+
require 'opentelemetry/sdk'
90+
require 'opentelemetry/exporter/otlp'
91+
require 'opentelemetry/instrumentation/all'
92+
OpenTelemetry::SDK.configure do |c|
93+
c.service_name = '<YOUR_SERVICE_NAME>'
94+
c.use_all() # enables all instrumentation!
95+
end
96+
97+
def lambda_handler(event:, context:)
98+
# ... your code
99+
end
100+
```
101+
102+
Sample handler can be found here:
103+
1. [splunk-otel-lambda](https://github.com/signalfx/splunk-otel-lambda/tree/main/ruby)
104+
105+
## Sample App
106+
107+
1. Make sure the requirements are met (e.g. sam, aws, docker, ruby version.)
108+
2. Navigate to the path `cd ruby/sample-apps`
109+
3. Build the layer and function based on template.yml. You will see .aws-sam folder after executed the command
110+
```bash
111+
sam build -u -t template.yml
112+
# for different arch, define it in properties from template.yml
113+
# Architectures:
114+
# - arm64
115+
```
116+
4. Test with local simulation
117+
```bash
118+
sam local start-api --skip-pull-image
119+
120+
# curl the lambda function
121+
curl http://127.0.0.1:3000
122+
# Hello 1.3.0%
123+
```
124+
125+
NOTE:
126+
In `ruby/sample-apps/template.yml`, the OTelLayer -> Properties -> ContentUri is pointing to `ruby/src/layer/`. This is for local testing purpose. If you wish to deploy (e.g. `sam deploy`), please point it to correct location or zip file.
127+
128+
In this sample-apps, we use `src/layer/otel-handler` as default `AWS_LAMBDA_EXEC_WRAPPER`; to change it, please edit in `sample-apps/template.yml`
129+
130+

ruby/sample-apps/function/Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source 'https://rubygems.org'
2+
3+
# need this empty gemfile for success build
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
require 'json'
2+
require 'opentelemetry-sdk'
3+
4+
def lambda_handler(event:, context:)
5+
if defined?(::OpenTelemetry::SDK)
6+
{ statusCode: 200, body: "Hello #{::OpenTelemetry::SDK::VERSION}" }
7+
else
8+
{ statusCode: 200, body: "Missing OpenTelemetry" }
9+
end
10+
end

ruby/sample-apps/template.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: 'AWS::Serverless-2016-10-31'
3+
Description: OpenTelemetry Ruby Lambda layer for Ruby
4+
Parameters:
5+
LayerName:
6+
Type: String
7+
Description: Lambda layer name to be published
8+
Default: opentelemetry-ruby
9+
Resources:
10+
OTelLayer:
11+
Type: AWS::Serverless::LayerVersion
12+
Properties:
13+
LayerName: !Ref LayerName
14+
Description: Opentelemetry Ruby layer
15+
ContentUri: ./../src/layer
16+
CompatibleRuntimes:
17+
- ruby3.2
18+
Metadata:
19+
BuildMethod: makefile
20+
api:
21+
Type: AWS::Serverless::Api
22+
Properties:
23+
StageName: api
24+
OpenApiVersion: 3.0.2
25+
function:
26+
Type: AWS::Serverless::Function
27+
Properties:
28+
CodeUri: ./function
29+
Handler: lambda_function.lambda_handler
30+
Runtime: ruby3.2
31+
Description: Build OTel Ruby Lambda layer and sample app from scratch
32+
Policies:
33+
- AmazonS3ReadOnlyAccess
34+
Layers:
35+
- !Ref OTelLayer
36+
Events:
37+
getEndpoint:
38+
Type: Api
39+
Properties:
40+
RestApiId: !Ref api
41+
Path: /
42+
Method: GET
43+
Environment:
44+
Variables:
45+
AWS_LAMBDA_EXEC_WRAPPER: /opt/otel-handler

ruby/src/layer/Gemfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
source 'https://rubygems.org'
2+
3+
gem 'opentelemetry-sdk'
4+
gem 'opentelemetry-exporter-otlp'
5+
gem 'opentelemetry-instrumentation-all'

ruby/src/layer/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
build-OTelLayer:
3+
mkdir -p ruby
4+
mkdir -p $(ARTIFACTS_DIR)/ruby/gems/3.2.0
5+
bundler install --path ruby
6+
cp -r ruby/ruby/3.2.0/* $(ARTIFACTS_DIR)/ruby/gems/3.2.0
7+
cp otel-handler $(ARTIFACTS_DIR)/otel-handler
8+
rm -rf $(ARTIFACTS_DIR)/ruby/gems/3.2.0/cache

ruby/src/layer/otel-handler

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
3+
if [ -z "${OTEL_SERVICE_NAME}" ]; then
4+
export OTEL_SERVICE_NAME="$AWS_LAMBDA_FUNCTION_NAME";
5+
fi
6+
7+
export LAMBDA_RESOURCE_ATTRIBUTES="cloud.region=$AWS_REGION,cloud.provider=aws,faas.name=$AWS_LAMBDA_FUNCTION_NAME,faas.version=$AWS_LAMBDA_FUNCTION_VERSION";
8+
if [ -z "${OTEL_RESOURCE_ATTRIBUTES}" ]; then
9+
export OTEL_RESOURCE_ATTRIBUTES="$LAMBDA_RESOURCE_ATTRIBUTES";
10+
else
11+
export OTEL_RESOURCE_ATTRIBUTES="$LAMBDA_RESOURCE_ATTRIBUTES,$OTEL_RESOURCE_ATTRIBUTES";
12+
fi
13+
14+
exec "$@"

0 commit comments

Comments
 (0)