crambda
Crystal AWS Lambda custom runtime
Installation
- Add the dependency to your
shard.yml
:
dependencies:
crambda:
github: lambci/crambda
targets:
bootstrap:
main: src/main.cr
- Run
shards install
Usage
(Assuming this is your src/main.cr
or similar from above)
require "json"
require "crambda"
def handler(event : JSON::Any, context : Crambda::Context)
pp context
JSON.parse("[1, 2]")
end
Crambda.run_handler(->handler(JSON::Any, Crambda::Context))
Where Crambda.run_handler
expects a handler that takes a JSON::Any
event
and a Context
, and returns a JSON::Any
response:
def self.run_handler(handler : Proc(JSON::Any, Context, JSON::Any))
# ...
end
And Context
is a class that looks like this:
class Context
getter function_name : String
getter function_version : String
getter memory_limit_in_mb : UInt32
getter log_group_name : String
getter log_stream_name : String
getter aws_request_id : String
getter invoked_function_arn : String
getter deadline_ms : Int64
getter identity : JSON::Any
getter client_context : JSON::Any
def get_remaining_time_in_millis
# ...
end
nd
Compiling and uploading to AWS Lambda
Static binary (easy, but larger and slower)
Creating a static binary is the easiest method, but will be larger and slower than using dynamic libraries.
If you're on Linux already, you can do:
shards install
shards build --release --no-debug --static
strip bin/bootstrap # optional, to reduce size
Then package up bootstrap
at the top level of a zipfile:
cd bin
zip lambda.zip bootstrap
And upload lambda.zip
to your custom runtime AWS Lambda.
If you're not on Linux, you can run the install step locally (if you have crystal – brew install crystal
on MacOS),
and then compile in a docker container:
shards install
docker run --rm -v "$PWD":/app -w /app crystallang/crystal sh -c \
'shards build --release --no-debug --static && strip bin/bootstrap'
(then zip up your bootstrap
executable as above)
Dynamically linked binary (more steps, but smaller and faster)
The only libs that need to be statically linked in your binary (ie, that don't
exist on AWS Lambda) are libevent
, libgc
and libcrystal
. By default, crystal
statically links the last two anyway, but libevent
doesn't exist on Lambda, so
either needs to be uploaded as a separate .so
alongside your bootstrap
, or
compiled in.
The most straightforward way to link these libs into your binary is to use the
ones supplied in the ext
directory in crambda
, as follows:
First build a cross-compiled version of your Lambda function (you can do this
on any machine that has crystal
, including MacOS):
shards install
PKG_CONFIG_PATH=lib/crambda/ext crystal build src/main.cr -o bin/bootstrap \
--release --no-debug --cross-compile --target x86_64-unknown-linux-gnu
# Ignore the command it outputs – it needs to be modified slightly as below
This will create a bin/bootstrap.o
object file that you can link in a
Lambda-like environment – eg, a machine running Amazon Linux, or in a Docker container:
docker run --rm -v "$PWD":/var/task lambci/lambda:build-provided cc bin/bootstrap.o -o bin/bootstrap -s \
-rdynamic -lz -lssl -lcrypto -lpcre -lm -lgc -lpthread -lcrystal -levent -lrt -ldl -Llib/crambda/ext
This will place the bootstrap
binary in bin
where you can zip it up and
upload to Lambda as shown above in the static binary instructions.
If you're using a different version of crystal
from the one supplied in ext
(currently 0.28.0
),
then you'll need to replace -lcrystal
with the path to the version of
libcrystal that matches your environment.