ServerlessFrameworkとLambdaでサーバーレスCrystalしてみた話改めCustomRuntimeでCrystal動かしてみた話
ServerlessFrameworkとLambdaでサーバーレスCrystalしてみた話改めCustomRuntimeでCrystal動かしてみた話:
この記事の執筆はCustom Runtimeの公開前でした
なので基本re:Invent2018以前の前提で書かれてます
一応Custom Runtimeについても追記しておきましたので「うるせぇ結論だけ聞かせろ」って方は追記部分だけお読みください
あとなんかこんなのがとかいつの間にか出来てるっぽいのでちゃんとした話を聞きたい人はそっちに行けばいいと思います(投げやり)
ハンズラボでエンジニアをしている回路(@qazx7412)です
普段はpostforのバックエンドの担当としてServerlessFrameworkやAWSやPythonと格闘したり、臨時で他のチームのテストを手伝ったりしたりしてます
今回はAWSでのサーバーレスで活躍するLambdaに関する話ですでした(過去形)
最近プライベートでの開発する量が増えました
それで考えるようになったことがインフラをどうやって無料で確保するかです
業務ならともかく個人なら財布のことを考えて無料か実質無料くらいのコストで済ませたいですね
実際自分の場合はGCPの無料枠のf1-microインスタンスや、みんな大好きherokuの無料枠や、さくらインターネットのArukasの無料枠でコンテナが一つ無料で動かせるのとかを活用してます
しかし本音としてはは普段触り慣れていてるAWSを使いたいところです
そこで出てくるのがLambdaです
AWS Lambdaだよ
コード書いてをアップロードするだけで実行してくれるすごいやつだよ
言わずとしれたAWSのFaaSです
関数のリクエスト数とメモリと実行時間による課金なので個人でちょっとしたリクエストが少ない感じの物を動かす程度ならばお財布に優しいサービスです
ですがこういったFaas(やPaaS等)の宿命として対応している言語しか使えないといった制約があります
実際執筆時現在使える言語はこんな感じ
RubyとPHP以外の主要な言語にはおおよそ対応してる感じですかね? Ruby来ちゃったんだよなぁ…
ですが私はプライベートではこれまでRubyを使って来ましたし最近Crystalを触り始めたのでこれで開発をしてみたい欲があります
ですがCrystalでサーバーレスをしようとしてもマイナー言語なので Lambdaでは対応していないので使えません
でも個人で使うならもっと自分の好きに言語を選びたい…
なのでどうにかする方法を考えます
公式で対応していない言語をLambdaで動かすにはいくつかの方法があります
例えばJavaが動くということはJVM言語が動くはずなのでScalaやKotlinを動かしてみるとか、TypeScriptやCoffeeScript、ElmにOpalみたいなAltJSをクロスコンパイルしてやるとか今更わざわざCoffeeScriptとか使いたいかどうかはともかく、対応している言語から他の言語を呼び出す(例えばPython3からrustを呼び出したり出来る)とかWebAssemblyとかやりようはいろいろあります
そしてこれらの手段に対応していないような言語でも最終手段があります
先程Lambdaのことを「コードをアップロードするといい感じに実行してくれるすごいやつ」みたいなことを書きましたが厳密にはこれは正確ではありません
正確には「コードをアップロードすると実行するためのコンテナを用意してくれて実行してくれるすごいやつ」です
何当たり前のことを言ってるんだって感じですが実際にはLambdaはコンテナの上で動作しています
なのでLambdaに使いたい言語を動かすのに必要な物を全部固めてアップロードしてnodeのexecのようなコマンドを実行する機能で無理やり叩いて実行してやれば理論上どんな言語でも動作するはずです
例えばRubyを動かすことを考えるなら、Traveling Rubyを突っ込んで実行してやれば無理やりですがサーバーレスRubyを実現することができます
(サーバーレスRubyに関する話はここやここなどにまとまっています)
具体例としてはこんな感じ
まとめると今動かしたいCrystalを動かすならLambdaで動くようにコンパイルしてやってアップロードし、それを叩いてやればサーバーレスCrystalできるんじゃないかということです
そのためには
そして実はこのやり方でサーバーレスCrystalを実現しているデプロイツールが存在します
Apexと同じ作者によるサーバーレス用のデプロイツールがUpです(日本語での紹介記事)
コンパイル用のコンテナ作るために思想錯誤している最中に偶然見つけました
なんとCrystalに対応しています
このUpというツールはPaaSのように普通に作ったAPIやWebアプリをデプロイできるのが特徴らしいです
普段使っているServerlessFrameworkとかはどちらかというとCFnのFaaSに寄った形での強化版みたいなサムシングだったのでまるっきり思想が違いますね
デプロイしてみます
コードはサンプルほぼそのままです
デプロイも簡単です
それでできたAPIGatewayのアドレスを叩いてみましょう
拍子抜けするほど簡単に動きました
とりあえずサーバーレスでCrystalするという当初の目的自体は達成できました
しかし個人的にはサーバーレスならyamlでいろいろなを定義したいなというお気持ちがあるのでこれをServerlessFrameworkで動かすことを考えていきたいと思います
これがlambda向けにコンパイルするときのコマンドのようですが、これを参考にすれば他のデプロイツールでも動かせるはずです
ということで本命のServerlessFrameworkでCrystalを動かしてみます
今回は入ってきたJSONをそのまま帰すだけの簡単なAPIを作ってみます
nodeのexecでコマンドを叩いてCrystalを呼び出します
標準入力でLambdaに渡ってきたデータをCrystalにぶち込んで同じく標準出力から結果を返してもらっています
いろいろ雑なのは許してほしい
あとはこれをコンテナでコンパイルしてServerlessFrameworkでデプロイしてやればいいですね
dockerの方はApex/Upと同じですがコンパイル元と先をそれぞれ
デプロイするのにいちいち2つのコマンドを両方打つのはだるいので今回は一つにまとめておきます
ということで早速デプロイしちゃいます
あとはこのエンドポイントにpostでバシバシ叩いてやればこれで今度こそLambdaでCrystalが動くはずですね
動きました
ということで無事LambdaでCrystalを動かしてサーバーレスCrystalすることに成功しました
でも正直冷静に考えるとこんなオーバーヘッドを気にしないやり方ならJVMで動くScalaとかいろいろ先人の知見がありそうなRustとかのほうがよかったのでは…?
まぁTraveling Rubyを入れて頑張るよりは良いとは思うしRubyな人がちょっとLambdaでなんか動かしたいとかなら使ってもいいのではって気はする
今回はたまたま自分が触ってたからCrystalを題材にしましたが(少なくともLambdaでは)他の非対応な言語でもサーバーレス出来るかもしれない可能性はあると思うので諦めきれない人は頑張ってみるといいと思います
でも本当は公式がherokuのビルドパックみたいな仕組みを作ってくれるのが一番なんじゃないかな(露骨な要求)はえーよホセ
というのがCustom Runtime発表前の話だったのです
でも来ちゃったんですよねぇ…悲しいなぁ
くよくよしててもしゃあないのでCustom RuntimeでもサーバーレスCrystalやってみます
最終的な物はこちらに置いてあります
まず公式ドキュメントを読む限りCustom RuntimeでLambdaを動かす場合は
このbootstrapはここではbashで書かれていますがどうやらこのファイルはbashである必要は無いようです
そしてこのコードを読む感じではループの中で
とりあえずLambda向けにCrystalをコンパイルできてはいるので同じようなコードをCrystalで書いてやりましょう
そうしたらコンパイルしてzipで固めてやります
出来たらコンソールからアップロードして実行してやりましょう
無事動きました
動かしたけど手でいちいちzipで上げるとか面倒くさくて死にそうになるのでServerlessFrameworkでもどうにかします
最終的な物はここにおいてあります
作るのにあたっては
というわけでまずbootstrap
こちらを参考にさせていただきました
内容は同じですので何も言うことなし次serverless.yml
今までの物とほぼ同じです
ただ今回はhandlerはディレクトリを指定しています
中に入っている
中身はいろいろイマイチですがなんとなくhandlerっぽくしました
context?知らない子ですね…
それでこっちが呼び出しているhandlerの本体です
前の例のbootstrapとほぼ同じです
あとは
そしたらまた動くか試してみましょう
こちらでもちゃんと動きましたね
(あ、ちなみにこれはあくまで自分用兼単に動かして見たかった事による産物なのでちゃんとライブラリ化してみたいなことは考えてないです…そういうのはもっと出来る人がやってどうぞ
というわけでCustom RuntimeでもサーバーレスCrystal出来ました
待望の機能が来た歓喜と結構な時間を書けて書いた記事が無に帰した絶望が入り混じった複雑な気分です
正直Rubyが無い前提で書いたのに対応してしまったのが一番痛かった…
もうアドベントカレンダーなんて二度と書きません(断言)
まえがきのまえがき
この記事の執筆はCustom Runtimeの公開前でしたなので基本re:Invent2018以前の前提で書かれてます
一応Custom Runtimeについても追記しておきましたので「うるせぇ結論だけ聞かせろ」って方は追記部分だけお読みください
あとなんかこんなのがとかいつの間にか出来てるっぽいのでちゃんとした話を聞きたい人はそっちに行けばいいと思います(投げやり)
以下本文
ハンズラボでエンジニアをしている回路(@qazx7412)です普段はpostforのバックエンドの担当としてServerlessFrameworkやAWSやPythonと格闘したり、臨時で他のチームのテストを手伝ったりしたりしてます
今回はAWSでのサーバーレスで活躍するLambdaに関する話
tl;dr
- nodeのexec等を使って叩いてやればLambdaで対応していない言語で強引にサーバーレスすることができる
-
Apex/UpではDocker上でLambdaで動くようにコンパイルしてCrystalに対応をしてる - 参考にしてServerlessFrameworkでデプロイしてみた
- (追記)Custom Runtimeでも動かしてみました
- (追記)ついでにServerlessFrameworkでそれっぽく動かして見ました
ことの始まり
最近プライベートでの開発する量が増えましたそれで考えるようになったことがインフラをどうやって無料で確保するかです
業務ならともかく個人なら財布のことを考えて無料か実質無料くらいのコストで済ませたいですね
実際自分の場合はGCPの無料枠のf1-microインスタンスや、みんな大好きherokuの無料枠や、さくらインターネットのArukasの無料枠でコンテナが一つ無料で動かせるのとかを活用してます
しかし本音としてはは普段触り慣れていてるAWSを使いたいところです
そこで出てくるのがLambdaです
AWS Lambdaだよ
コード書いてをアップロードするだけで実行してくれるすごいやつだよ
言わずとしれたAWSのFaaSです
関数のリクエスト数とメモリと実行時間による課金なので個人でちょっとしたリクエストが少ない感じの物を動かす程度ならばお財布に優しいサービスです
ですがこういったFaas(やPaaS等)の宿命として対応している言語しか使えないといった制約があります
実際執筆時現在使える言語はこんな感じ
- C#(.NET Core 1.0, 2.0, 2.1) - Go 1.x - Java8 - Node.js (6.10, 8.10) - Python(2.7, 3.6, 3.7) - Ruby 2.5 <- New!!
ですが私はプライベートではこれまでRubyを使って来ましたし最近Crystalを触り始めたのでこれで開発をしてみたい欲があります
ですがCrystalでサーバーレスをしようとしても
でも個人で使うならもっと自分の好きに言語を選びたい…
なのでどうにかする方法を考えます
Lambdaで非対応な言語を動かす
公式で対応していない言語をLambdaで動かすにはいくつかの方法があります例えばJavaが動くということはJVM言語が動くはずなのでScalaやKotlinを動かしてみるとか、TypeScriptやCoffeeScript、ElmにOpalみたいなAltJSをクロスコンパイルしてやるとか
そしてこれらの手段に対応していないような言語でも最終手段があります
先程Lambdaのことを「コードをアップロードするといい感じに実行してくれるすごいやつ」みたいなことを書きましたが厳密にはこれは正確ではありません
正確には「コードをアップロードすると実行するためのコンテナを用意してくれて実行してくれるすごいやつ」です
何当たり前のことを言ってるんだって感じですが実際にはLambdaはコンテナの上で動作しています
なのでLambdaに使いたい言語を動かすのに必要な物を全部固めてアップロードしてnodeのexecのようなコマンドを実行する機能で無理やり叩いて実行してやれば理論上どんな言語でも動作するはずです
例えばRubyを動かすことを考えるなら、Traveling Rubyを突っ込んで実行してやれば無理やりですがサーバーレスRubyを実現することができます
(サーバーレスRubyに関する話はここやここなどにまとまっています)
具体例としてはこんな感じ
handler.js
'use strict'; const exec = require('child_process').exec; module.exports.hello_crystal = (event, context, callback) => { const child = exec('/path/to/ruby ./path/to/script.rb'); child.stdout.on('data', (result) => { callback(null,result); }); child.stderr.on('data', (result) => { callback(result); }); };
そのためには
- コンパイルするための環境を用意しコンパイルする
- コンパイルした実行ファイルを一緒にしてデプロイしてやる
そして実はこのやり方でサーバーレスCrystalを実現しているデプロイツールが存在します
Apex/Up
Apexと同じ作者によるサーバーレス用のデプロイツールがUpです(日本語での紹介記事)コンパイル用のコンテナ作るために思想錯誤している最中に偶然見つけました
なんとCrystalに対応しています
このUpというツールはPaaSのように普通に作ったAPIやWebアプリをデプロイできるのが特徴らしいです
普段使っているServerlessFrameworkとかはどちらかというとCFnのFaaSに寄った形での強化版みたいなサムシングだったのでまるっきり思想が違いますね
デプロイしてみます
コードはサンプルほぼそのままです
up.json
{ "name": "up-crystal", "profile": "<プロファイル名>", "regions": [ "ap-northeast-1" ] }
src/main.cr
require "http/server" port = ENV["PORT"].to_i server = HTTP::Server.new(port) do |ctx| ctx.response.content_type = "text/plain" ctx.response.print "新たな光に会いに行こう" end server.listen
$ up build: 120 files, 8.3 MB (718ms) deploy: version 1 (9.454s) stack: complete (22.085s)
$ curl https://hagehage.execute-api.ap-northeast-1.amazonaws.com/production/ 新たな光に会いに行こう
とりあえずサーバーレスでCrystalするという当初の目的自体は達成できました
しかし個人的にはサーバーレスならyamlでいろいろなを定義したいなというお気持ちがあるのでこれをServerlessFrameworkで動かすことを考えていきたいと思います
これがlambda向けにコンパイルするときのコマンドのようですが、これを参考にすれば他のデプロイツールでも動かせるはずです
$ docker run --rm -v $(pwd):/src -w /src tjholowaychuk/up-crystal crystal build --link-flags -static -o server main.cr
ServerlessFramework
ということで本命のServerlessFrameworkでCrystalを動かしてみます今回は入ってきたJSONをそのまま帰すだけの簡単なAPIを作ってみます
ディレクトリ構成
. ├ buildfile/ │ └ main ├ src/ │ └ main.cr ├ deploy.sh ├ handler.js └ serverless.yml
handler.js
'use strict'; const exec = require('child_process').exec; module.exports.hello_crystal = (event, context, callback) => { const child = exec(`echo '${JSON.stringify(event)}' | ./buildfile/main`); child.stderr.on('data', (result) => { callback(JSON.parse(result)); }); child.stdout.on('data', (result) => { const stdout = result .replace(/\"{/g,'{') .replace(/}\"/g,'}') .replace(/\\\"/g,'"') .split('\n')[0]; callback(null,JSON.parse(stdout)); }); };
src/main.cr
require "json" stdin = JSON.parse(STDIN.gets_to_end) p stdin["body"].to_json
あとはこれをコンテナでコンパイルしてServerlessFrameworkでデプロイしてやればいいですね
dockerの方はApex/Upと同じですがコンパイル元と先をそれぞれ
src/main.cr
と buildfile/main
に変更していますデプロイするのにいちいち2つのコマンドを両方打つのはだるいので今回は一つにまとめておきます
deploy.sh
docker run --rm -v $(pwd):/src -w /src \ tjholowaychuk/up-crystal crystal build \ --link-flags -static -o buildfile/main src/main.cr &&\ sls deploy
serverless.yml
service: serverlesshellocrystal custom: defaultStage: dev api_version: v0 common: common provider: name: aws runtime: nodejs8.10 region: ap-northeast-1 stage: ${opt:stage, self:custom.defaultStage} profile: <プロファイル名> functions: hello_crystal: handler: handler.hello_crystal events: - http: path: test method: post integration: lambda
$ ./deploy.sh /opt/crystal/embedded/lib/../lib/libevent.a(evutil.o): In function `test_for_getaddrinfo_hacks': evutil.c:(.text+0x1488): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking /opt/crystal/embedded/lib/../lib/libevent.a(evutil.o): In function `evutil_unparse_protoname': evutil.c:(.text+0xf0d): warning: Using 'getprotobynumber' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service .zip file to S3 (1.96 MB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... .............. Serverless: Stack update finished... Service Information service: serverlesshellocrystal stage: dev region: ap-northeast-1 stack: serverlesshellocrystal-dev api keys: None endpoints: POST - https://hage.execute-api.ap-northeast-1.amazonaws.com/dev/test functions: hello_crystal: serverlesshellocrystal-dev-hello_crystal Serverless: Removing old service artifacts from S3...
動きました
最後に
ということで無事LambdaでCrystalを動かしてサーバーレスCrystalすることに成功しましたまぁTraveling Rubyを入れて頑張るよりは良いとは思うしRubyな人がちょっとLambdaでなんか動かしたいとかなら使ってもいいのではって気はする
今回はたまたま自分が触ってたからCrystalを題材にしましたが(少なくともLambdaでは)他の非対応な言語でもサーバーレス出来るかもしれない可能性はあると思うので諦めきれない人は頑張ってみるといいと思います
…という話だったのさ
というのがCustom Runtime発表前の話だったのですでも来ちゃったんですよねぇ…悲しいなぁ
くよくよしててもしゃあないのでCustom RuntimeでもサーバーレスCrystalやってみます
最終的な物はこちらに置いてあります
Custom Runtime
まず公式ドキュメントを読む限りCustom RuntimeでLambdaを動かす場合は bootstrap
を起点にして実行されるようですドキュメントでのディレクトリ構成の例
. ├ bootstrap └ function.sh
bootstrap
#!/bin/sh set -euo pipefail # Initialization - load function handler source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh" # Processing while true do HEADERS="$(mktemp)" # Get an event EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) # Execute the handler function from the script RESPONSE=$($(echo "$_HANDLER" | cut -d. -f2) "$EVENT_DATA") # Send the response curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" done
そしてこのコードを読む感じではループの中で
http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next
から入ってきたデータとリクエストIDを取得して http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/<リクエストID>/response
に返却する値を再び放り込んで上げれば動くということのようですとりあえずLambda向けにCrystalをコンパイルできてはいるので同じようなコードをCrystalで書いてやりましょう
src/main.cr
require "http/client" while true response = HTTP::Client.get "http://#{ENV["AWS_LAMBDA_RUNTIME_API"]}/2018-06-01/runtime/invocation/next" event_data = response.body request_id = response.headers["Lambda-Runtime-Aws-Request-Id"] url : String = "http://#{ENV["AWS_LAMBDA_RUNTIME_API"]}/2018-06-01/runtime/invocation/#{request_id}/response" HTTP::Client.post url, body: event_data end
$ docker run --rm -v $(pwd):/src -w /src tjholowaychuk/up-crystal crystal build --link-flags -static -o bootstrap src/main.cr $ zip lambda-crystal.zip bootstrap
無事動きました
真・ServerlessFramework
動かしたけど手でいちいちzipで上げるとか面倒くさくて死にそうになるのでServerlessFrameworkでもどうにかします最終的な物はここにおいてあります
作るのにあたっては
というわけでまずbootstrap
bootstrap
#!/bin/sh set -euo pipefail EXEC="$LAMBDA_TASK_ROOT/buildfile/$_HANDLER" if [ ! -x "$EXEC" ]; then ERROR="{\"errorMessage\" : \"$_HANDLER is not found.\", \"errorType\" : \"HandlerNotFoundException\"}" curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/init/error" -d "$ERROR" exit 1 fi $EXEC
内容は同じですので何も言うことなし次serverless.yml
serverless.yml
service: serverless-crystal-sls custom: defaultStage: dev api_version: v0 common: common provider: name: aws runtime: provided region: ap-northeast-1 stage: ${opt:stage, self:custom.defaultStage} profile: <プロファイル名> functions: hello: handler: hello events: - http: path: test method: post integration: lambda
ただ今回はhandlerはディレクトリを指定しています
中に入っている
main.cr
が各Lambda関数という感じですディレクトリ構成
. ├ buildfile/ │ └ hello ├ src/ │ ├ hello │ │ └ main.cr │ └ runtime │ └ handler.cr ├ bootstrap ├ build.sh ├ deploy.sh └ serverless.yml
hello/main.cr
require "./../runtime/handler" def hello(event) event end lambda_handler(hello)
前の例のbootstrapとほぼ同じです
runtime/handler.cr
require "json" require "http/client" macro lambda_handler(func) module Lambda extend self def run while true response = HTTP::Client.get "http://#{ENV["AWS_LAMBDA_RUNTIME_API"]}/2018-06-01/runtime/invocation/next" event = JSON.parse(response.body) request_id = response.headers["Lambda-Runtime-Aws-Request-Id"] body = {{ func }} event["body"] url : String = "http://#{ENV["AWS_LAMBDA_RUNTIME_API"]}/2018-06-01/runtime/invocation/#{request_id}/response" HTTP::Client.post url, body: body.to_json end end end end Lambda.run()
main.cr
がある物だけをコンパイルしますdeploy.sh
stg=$1 [ "$stg" = "" ] && stg="dev" ls $(pwd)/src/ | while read line; do $(pwd)/build.sh $line || exit 1 done && sls deploy -s $stg
build.sh
func=$1 [ -n $func ] || exit 1 # main.crが存在する物だけを対象にしたい [ -f $(pwd)/src/$func/main.cr ] || exit 0 docker run --rm -v $(pwd):/src -w /src \ tjholowaychuk/up-crystal crystal build \ --link-flags -static -o buildfile/$func src/$func/main.cr && \ chmod +x src/$func
$ ./deploy.sh /usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/libcrypto.a(dso_dlfcn.o): In function `dlfcn_globallookup': (.text+0x11): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking T-C-P-S-ocket.o: In function `initialize': /opt/crystal/src/socket/tcp_socket.cr:98: warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking /opt/crystal/embedded/lib/../lib/libevent.a(evutil.o): In function `evutil_unparse_protoname': evutil.c:(.text+0xf0d): warning: Using 'getprotobynumber' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking Serverless: Packaging service... Serverless: Excluding development dependencies... Serverless: WARNING: Function hello has timeout of 300 seconds, however, it's attached to API Gateway so it's automatically limited to 30 seconds. Serverless: Uploading CloudFormation file to S3... Serverless: Uploading artifacts... Serverless: Uploading service .zip file to S3 (2.04 MB)... Serverless: Validating template... Serverless: Updating Stack... Serverless: Checking Stack update progress... .............. Serverless: Stack update finished... Service Information service: serverless-crystal-sls stage: dev region: ap-northeast-1 stack: serverless-crystal-sls-dev api keys: None endpoints: POST - https://hagehage.execute-api.ap-northeast-1.amazonaws.com/dev/test functions: hello: serverless-crystal-sls-dev-hello layers: None Serverless: Removing old service artifacts from S3...
こちらでもちゃんと動きましたね
(あ、ちなみにこれはあくまで自分用兼単に動かして見たかった事による産物なのでちゃんとライブラリ化してみたいなことは考えてないです…そういうのはもっと出来る人がやってどうぞ
まとめ
というわけでCustom RuntimeでもサーバーレスCrystal出来ました待望の機能が来た歓喜と結構な時間を書けて書いた記事が無に帰した絶望が入り混じった複雑な気分です
正直Rubyが無い前提で書いたのに対応してしまったのが一番痛かった…
もうアドベントカレンダーなんて二度と書きません(断言)
コメント
コメントを投稿