Crystal AWS Lambda custom runtime
- Add the dependency to your
shard.yml
:
dependencies:
crambda:
github: lambci/crambda
targets:
bootstrap:
main: src/main.cr
- Run
shards install
(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
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)
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.