Deno: Wanted: serverless deno configuration

Created on 3 Jan 2019  ·  57Comments  ·  Source: denoland/deno

Would like some scripts and documentation on how to deploy to AWS Lambda, Serverless, GCloud functions, Azure Functions, etc.

Most helpful comment

I've posted an article about AWS Lambda version https://dev.to/kt3k/write-aws-lambda-function-in-deno-4b20

The following scripts worked:

The custom runtime (bootstrap):

#!/bin/sh
set -euo pipefail

SCRIPT_DIR=$(cd $(dirname $0); pwd)
HANDLER_NAME=$(echo "$_HANDLER" | cut -d. -f2)
HANDLER_FILE=$(echo "$_HANDLER" | cut -d. -f1)

export DENO_DIR=/tmp/deno_dir

echo "
import { $HANDLER_NAME } from '$LAMBDA_TASK_ROOT/$HANDLER_FILE.ts';
const API_ROOT =
  'http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/';
(async () => {
  while (true) {
    const next = await fetch(API_ROOT + 'next');
    const reqId = next.headers.get('Lambda-Runtime-Aws-Request-Id');
    const res = await $HANDLER_NAME(await next.json());
    await (await fetch(
      API_ROOT + reqId + '/response',
      {
        method: 'POST',
        body: JSON.stringify(res)
      }
    )).blob();
  }
})();
" > /tmp/runtime.ts
$SCRIPT_DIR/deno run --allow-net --allow-read /tmp/runtime.ts

The lambda function (function.ts):

export async function handler(event) {
  return {
    statusCode: 200,
    body: JSON.stringify({
      version: Deno.version,
      build: Deno.build
    })
  };
}

All 57 comments

I was considering looking into what a lambda custom layer/runtime might look like.

In a serverless context I think https://github.com/denoland/deno/issues/1386 becomes a lot more important. You don't really want to be re-fetching dependencies on every cold start

I have a docker image I need to dust off and get available.

This seems to be an updated docker configuration: https://github.com/maxmcd/deno-docker

@maxmcd What's the status of that project? Is it working? Are there any major missing pieces?

The repo was originally intended to be a Dockerfile of the deno dev environment. That is now mostly unmaintained and it's just two simple Dockerfiles: jessie, jessie-slim . They auto-build the latest version every day so they're fairly up to date. I haven't run the test suite off of the build, so maybe there are missing pieces.

As for the context of this issue. I tried to get deno running in Lambda when it first switched over to Rust. Had issues getting it to link with the headers available in the Lambda runtime. Maybe recent changes in Deno or Lambda's new layers have made this a non-issue.

I have working dockerfiles for alpine (85mb)+ubuntu(260mb)+debian(150mb), installing from the linux binary rather than build.
I was thinking they could go to deno_install, but I am not sure where to publish them (note: they are each only a few lines each so perhaps it's not critical to publish them).

Edit: Perhaps I should clarify, "working" as in the deno console works and I tried a few things. I haven't run a vigorous test suite. It'd be cool to have some kind of deno test available to do that for the binary...

Edit2: @maxmcd just looked at your repo, would you accept alpine+ubuntu Dockerfiles there?

https://gist.github.com/hayd/ba4412a6c7ae58deb178348a4c6f37a3

@hayd you got it to work on alpine only? When I tried, there were some core lib requirements, I think from V8 that I couldn't get in alpine, so I had to revert to an ubuntu base.

@kitsonk Ya it works: See gist: https://gist.github.com/hayd/5360b3cd855bc7ba0eea0e20a8413c29
(all three are working, and i put them on dockerhub)

Edit: btw I took the denoland organization dockerhub, but am not using it. At some point in the future, we can move to it / happy to hand it over.

Edit2: See repo: https://github.com/hayd/deno_docker

Hi @hayd
You use frolvlad/alpine-glibc:alpine-3.8 as base container image.

Thank you so much for useful information!

Beware that https://hub.docker.com/u/denoland is not me...

(Please contact me if you've squatted it)

It has been transferred to me. I will look into deploying to docker hub as part of our release process.

It's me, I said that above. Happy to hand over.

Edit: Sorry, realized I only mentioned this as a later edit. Ooops.

Ah sorry didn’t see that - thanks

@hayd happy to accept alpine & ubuntu images

Is alpine-glibc the way to go here? Node docker images seem to get away with building without swapping out musl.

It was more expedient to use alpine-glibc... 😄

But I suspect musl is preferable, the proof will be in perf testing between the two options. The Dockerfile for node is helpful.
Edit: OTOH openjdk8 (and hence scala etc.) uses alpine-glibc so it can't be terrible.

There are certainly challenges to alpine without glibc, Deno appears to work fine with musl, at least I got pretty far with it, it was some non-exported symbols that tripped me up and I didn't know enough about how to deal with them. I don't think it was terminal, but I think it would impact the build process for Deno and I didn't have enough experience to work through it.

I had got to this:

$ /bin/deno  # alpine without glibc
Error relocating /bin/deno: __rawmemchr: symbol not found
Error relocating /bin/deno: strtoll_l: symbol not found
Error relocating /bin/deno: strtoull_l: symbol not found
Error relocating /bin/deno: __register_atfork: symbol not found
Error relocating /bin/deno: __res_init: symbol not found
Error relocating /bin/deno: __isnan: symbol not found
Error relocating /bin/deno: __vfprintf_chk: symbol not found
Error relocating /bin/deno: __vsnprintf_chk: symbol not found
Error relocating /bin/deno: __isinf: symbol not found
Error relocating /bin/deno: __isnanf: symbol not found
Error relocating /bin/deno: backtrace: symbol not found
Error relocating /bin/deno: backtrace_symbols: symbol not found

then found https://github.com/gliderlabs/docker-alpine/issues/11 and used glibc :)

I suspect we'd have to not use any prebuilts, e.g. build v8* etc., which are atm fetching from google storage glibc (?) executables IIUC. This has actually come up with the brew repo: they'd prefer to make all the executables rather than fetch any...

* actually prebuild v8 was removed in #1369 ...

@hayd yup that list of symbols looks really familiar! 😁

I guess Node.js doesn't run into the issue because they don't use GN? That does look a bit horrifying. Good reference though!

To go back to the original issue, at least with aws lambda it looks like they require musl (or at least this announcement suggests that - I'll try and make a musl binary following those instructions above.

I think the ideal would be to use libdeno in such a way you can pass event+context from rust to a typescript function/entrypoint. IIUC in that way you could start deno once (so that deno is not started upon every lambda invocation, only on the first for the underlying vm - this is kinda interesting, see chromeless where you can keep the chrome app running over multiple invocations).

If we could get "cargo package" working (https://github.com/denoland/deno/issues/1387), then we could release a crate which will contain a prebuilt libdeno. Using that and Lambda's Rust interface seems like an ideal way to deploy.

Annoyingly RE musl it seems like alpine ships with clang 5.0.1 rather than clang 8.0.0 that deno ships with, unclear how to resolve - it could be that there is some flags. I'll try and share where I got to... 🤕

Currently, It is not possible to run Deno in AWS Lambda.
Deno may require GLIBC_2.18, but Lambda container does not include it.

Check below
https://github.com/pueue/deno-custom-lambda
https://blog.pueue.com/2019/Failure-building-an-AWS-Lambda-custom-runtime-for-Typescript-with-Deno/

And guides for zeit's now.sh service?

@balupton is zeit just a wrapper around AWS Lambda?

In order to get a binary for Lambda you need to build deno on musl without glibc dep, I had made some progress following this gist but wasn't able to quite get there... though I'm confident it is feasible.

You could alternatively add glibc 2.18 to your Lambda zip too – /var/task (where your Lambda package unzips to) is included in LD_LIBRARY_PATH. Or package it up as a layer (will need to put in a lib directory so it unpacks at /opt/lib)

I've posted an article about AWS Lambda version https://dev.to/kt3k/write-aws-lambda-function-in-deno-4b20

The following scripts worked:

The custom runtime (bootstrap):

#!/bin/sh
set -euo pipefail

SCRIPT_DIR=$(cd $(dirname $0); pwd)
HANDLER_NAME=$(echo "$_HANDLER" | cut -d. -f2)
HANDLER_FILE=$(echo "$_HANDLER" | cut -d. -f1)

export DENO_DIR=/tmp/deno_dir

echo "
import { $HANDLER_NAME } from '$LAMBDA_TASK_ROOT/$HANDLER_FILE.ts';
const API_ROOT =
  'http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/';
(async () => {
  while (true) {
    const next = await fetch(API_ROOT + 'next');
    const reqId = next.headers.get('Lambda-Runtime-Aws-Request-Id');
    const res = await $HANDLER_NAME(await next.json());
    await (await fetch(
      API_ROOT + reqId + '/response',
      {
        method: 'POST',
        body: JSON.stringify(res)
      }
    )).blob();
  }
})();
" > /tmp/runtime.ts
$SCRIPT_DIR/deno run --allow-net --allow-read /tmp/runtime.ts

The lambda function (function.ts):

export async function handler(event) {
  return {
    statusCode: 200,
    body: JSON.stringify({
      version: Deno.version,
      build: Deno.build
    })
  };
}

I skipped error handling, lambda context handling, etc in the above work. These need to be addressed in later versions.

AWS Lambda supports Lambda Layer, which is a kind of shared resources among lambdas. If we (deno community) maintain the shared deno lambda runtime as a public layer, the users can use it as the base deno runtime and can create lambda functions based on it. I think that's very convenient for new deno users (because creating custom runtime is not so easy).

Very nice @kt3k - we got to get that glibc issue solved.

I’m happy to have the runtime in deno_std

@kt3k great article, great work!

If we will be able to run scripts with Deno out of the box, using AWS Lambda - this will be a total win

Similar to @kt3k's solution for Lambda, on GCP you can now use their Cloud Run product to use Docker images as serverless instances (Cloud Run does managing, autoscaling, etc.).

I wrote a little PoC, thinking I could get it to a custom runtime that can execute custom code (that's not precompiled in the Dockerfile!).

https://github.com/denoserverless/deno-serverless-poc

I think we can almost close this, but I think we could all benefit from a section in manual with links to https://github.com/denoserverless/deno-serverless-poc and maybe https://dev.to/kt3k/write-aws-lambda-function-in-deno-4b20 (if it isn't totally out of date).

Any volunteers want to take a stab at some official guide? https://github.com/denoland/deno/blob/master/website/manual.md

Your PR should contain "Fixes #1456" in the description :)

Ran into this. You can deploy serverless deno functions apparently. https://fly.io/docs/getting-started/deno/

(if it isn't totally out of date).

@ry The lambda guide is still working, however the binary is outdated. (deno 0.4.0 I believe)

I haven't got my hands on a recent build working on lambda yet. Probably needs to be compiled with glibc 2.17. Any ideas?

Edit: I tried compiling as musl, but that fails as well. Unfortunately don't know enough about that stuff to get it working :(

I was trying this again for alpine linux (w/o glibc), any help appreciated:
Dockerfile: https://gist.github.com/hayd/4e4dbe867cb11d7b579b82711b7da27d
failing output: https://gist.github.com/hayd/2c95fac12891ffe6458a87761eae28ae

with help of @chrmoritz here is a Dockerfile for Amazon Linux 1 which builds a binary which should be able to be used for custom runtimes from @kt3k's blogpost.

binary file: https://github.com/hayd/deno_docker/blob/f7996e94f69e6a2d59038742f3ba03d1607888a5/amz-deno-0.22.0

I think about how to add this to releases in hayd/deno_docker...

^Can confirm the example works on AWS Lambda (with some minor modifications):

Edit: posted in deno_docker: https://github.com/hayd/deno_docker/tree/master/aws-lambda

I published the binary as a release: https://github.com/hayd/deno_docker/releases/tag/v0.23.0
Also, includes a zip layer and zip example function. To try out in the AWS console:

  1. create a layer and upload deno-lambda-layer.zip. Note its ARN.
  2. create a lambda function from scratch with runtime "provide your own bootstrap".
  3. add a layer "Provide a layer version ARN" use the ARN above.
  4. upload deno-lambda-example.zip as function code ("Code entry type": "upload a .zip").
  5. "Save". "Test".

I'll try and write this up soon.

Note: There's now error handling and context, near feature complete.


I've moved the code to here:
https://github.com/hayd/deno_lambda
https://github.com/hayd/deno_lambda/releases

I've added a serverless example to the deno-lambda repo:
https://github.com/hayd/deno-lambda/tree/master/example

It's a port of the _Building a REST API in Node.js with AWS Lambda, API Gateway, DynamoDB, and Serverless Framework_ blogpost by Shekhar Gulat to deno. It uses (a patched version of) @chiefbiiko's dynamodb library to read/write to a dynamodb table.

Deploy in one step with serverless deploy (including the dynamodb table creation).
Note: compilation is done prior to upload: there's no init/runtime compilation (see serverless.yml).

@hayd you're a 🌟 for packaging that runtime thank you! I've added to the AWS SAR (Serverless Application Repository) and feel this should probably be an official thing from the Deno project eventually. In the meantime, it does make getting started with Deno on Lambda ridiculously simple.

I've created two sample apps to demo:

(You can see it running here https://a9gmmkcppj.execute-api.us-east-1.amazonaws.com)

Will be blogging and documenting more soon. If anyone has any questions don't hesitate to ask! Also! Noticing that Deno wants a fair bit of memory so defaulting anywhere around 1gb makes it happiest/fastest coldstart.

@brianleroux nice! Would it be possible to share the template to create/publish the application (layer)? I think it would make a lot of sense to add this to the deno-lambda CI (and then maybe in future that repo can move somewhere more official / we'll be able to use the same code).
I hadn't seen SAR before - that seems like a really nice way to publish (hides the complexity of publishing/managing the Deno layer yourself).

I'm not sure if I've seen this high memory issue - does init time out in this case? what happens? Is it pre-compiled (e.g.)? 1Gb seems relatively high.

Sent a PR! https://github.com/hayd/deno-lambda/pull/17

Def a nicer way to manage the runtime and should be pretty easy to get it all automated. 👍

Memory: didn't know that precompile hack and gonna try it out thx!

Draft of the blog post announcing the capability in Architect if anyone here is interested https://blog.begin.com/deno-runtime-support-for-architect-805fcbaa82c3

If anyone is interested here is a deno-lambda issue tracking all features, any feedback would be very helpful: https://github.com/hayd/deno-lambda/issues/20

RE closing this issue, has anyone tried using GCloud or Azure? It reads like these ought to be more straightforward... 🤷‍♂

I make heavy use of Azure Functions these days but I don't think Deno will work in this enviornment. I could use Azure Apps and deploy inside a container, but I have yet to get a Docker container working under Deno for some reason.

Found this package that lets you deploy with one click https://github.com/hayd/deno-lambda

@josermorales huh, thx... And this one looks particularly cool (haven't tried it yet though):

https://github.com/lucacasonato/now-deno

// now.json
{
  "functions": {
    "api/**/*.{j,t}s": {
      "runtime": "[email protected]"
    }
  }
}
// api/hello.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'https://deno.land/x/lambda/mod.ts';

export async function handler(
  event: APIGatewayProxyEvent,
  context: Context
): Promise<APIGatewayProxyResult> {
  return {
    statusCode: 200,
    body: `Welcome to deno ${Deno.version.deno} 🦕`,
    headers: {
      'content-type': 'text/html; charset=utf-8',
    },
  };
}

Thanks @JerryGreen was looking for an example with now!

Currently building the binary for amazonlinux is kinda arduous. If anyone can get the musl build working #3711 that would be incredibly helpful for deno-lambda - it feels close.

cc/@hayd

If anyone is interested, I have compiled DENO with V8 in Alpine Linux Docker container - without glibc, but completely using the Alpine muslc library.

https://gist.github.com/kesor/68df53a5d76784a235ca6b0e7efed4d9

Enjoy!

cc: @anthonychu

Azure Functions: https://github.com/anthonychu/azure-functions-deno-worker

GCloud functions is the only remaining.

... and adding something about these in the documentation.

~Is Cloud Run third party? It looks like it's Google: https://cloud.google.com/run~
Edit : that was in reply to a deleted comment.

I think the conclusion here is to add (or at least start) a "deploy" section to the manual.

Agree! Would appreciate if we put Begin.com in (tho its really more CI/CD to AWS) …also arc.codes for 'native' deployment to AWS!

I will write something up and submit a PR, likely on Sunday.

@hayd How did you go with your writing?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

xueqingxiao picture xueqingxiao  ·  3Comments

doutchnugget picture doutchnugget  ·  3Comments

ry picture ry  ·  3Comments

ry picture ry  ·  3Comments

davidbarratt picture davidbarratt  ·  3Comments