本記事は F# Advent Calendar 2016 に参加しています。
先日の AWS re:Invent 2016 で、 AWS Lambda で C# のサポートが発表されました。 C# で動くなら F# で動くはずだ、ということで、やってみました。
免責: 私の環境は macOS Sierra です。 Windows の方は適当に読み替えてください。
Lambda の C# ランタイムは、 .NET Core 1.0 runtime で動いています。つまり F# のコードも .NET Core 1.0 runtime で動かす設定にすれば動くはずです。ということで、 .NET Core 1.0 を公式サンプルのアナウンスのサンプルにしたがってやっていきます。
環境準備
.NET Core 1.0 SDK
.NET Core 1.0 SDK をインストールします。 Google で .NET Core を検索すると最新版の 1.1 がヒットしますが、これをインストールしても後で 1.0 をインストールしろと怒られますので、 .NET Core Downloads から 1.0 をダウンロードしてインストールします。
macOS 環境で .NET Core を動かしたい場合は、 Advent calendar 5 日目の『MacOSX+.NET Core SDK+VS CodeでF#開発』が参考になります。ただし、 .NET Core 1.0 SDK である点だけ注意します。
IAM Role
Lambda 関数に与えるロールを作成します。詳細は割愛します。後述しますが、デプロイ時に作成することもできます。
プロジェクト作成
project.json
dotnet
でプロジェクトテンプレートを作成します。
1 2 3 | mkdir fsharp-advent-calendar-2016 cd fsharp-advent-calendar-2016/ dotnet init --lang F # |
ディレクトリー内には Program.fs
と project.json
が作成されます。
AWS Lambda に必要な依存ライブラリーを追加するために project.json
をいじり、以下のように変更します[A]。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | { "version" : "1.0.0-*" , "buildOptions" : { "outputName" : "ASSEMBLY_NAME" , "compilerName" : "fsc" , "compile" : [ "Program.fs" ] }, "dependencies" : { "Amazon.Lambda.Core" : "1.0.0" , "Amazon.Lambda.Serialization.Json" : "1.0.0" , "Microsoft.FSharp.Core.netcore" : "1.0.0-alpha-*" , "Amazon.Lambda.Tools" : { "type" : "build" , "version" : "1.0.0-preview1" }, "dotnet-compile-fsc" : { "type" : "build" , "version" : "1.0.0-preview2-*" } }, "tools" : { "Amazon.Lambda.Tools" : "1.0.0-preview1" , "dotnet-compile-fsc" : "1.0.0-preview2-*" }, "frameworks" : { "netcoreapp1.0" : { "dependencies" : { "Microsoft.NETCore.App" : { "type" : "platform" , "version" : "1.0.0" } }, "imports" : "dnxcore50" } } } |
依存関係を修正したら、依存ライブラリーをインストールします[B]。
1 | dotnet restore |
aws-lambda-tools-defaults.json
aws-lambda-tools-defaults.json
は、 dotnet lambda
の設定値を定義します。作成しなくても対話的に入力できますが、入力項目が多く面倒なので、作成しておくと良いでしょう。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | { "Information" : [], "profile" : "" , "region" : "ap-northeast-1" , "configuration" : "Release" , "framework" : "netcoreapp1.0" , "function-runtime" : "dotnetcore1.0" , "function-name" : "FUNCTION_NAME" , "function-memory-size" : 128, "function-timeout" : 300, "function-handler" : "ASSEMBLY_NAME::CLASS_NAME::FUNCTION_NAME" , "function-role" : "arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME" } |
自分の環境に応じて適宜設定値を変更してください。
ここで微妙にハマったのですが、 Lambda 関数は初回実行時のみ時間が非常にかかるため、 function-timeout
に最小の 5(秒)を設定すると、タイムアウトしてしまいました。 2 回目以降の実行は高速に動きますし、 Lambda は実行時間で課金されるので、大きな値を設定していても特に問題ないと思います。
function-handler
はアセンブリー名、クラス名、関数名を ::
で区切ったものです。アセンブリー名は、 project.json
の buildOptions.outputName
を指定していればその名前になり、指定しなければディレクトリー名(本記事の例では fsharp-advent-calendar-2016)となります。
関数の作成
dotnet init
で作成された Program.fs
を以下のような感じに書き換えます。
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | module FSharpAdventCalendar2016.Day10.LambdaFunction open Amazon.Lambda.Core open Amazon.Lambda.Serialization.Json open Newtonsoft.Json [<assembly: LambdaSerializer(typeof<JsonSerializer>)>] type InputValue = { [<JsonProperty( "question" )>] Question : string } type ResultValue = { [<JsonProperty( "answer" )>] Answer : int } [<CompiledName( "FunctionHandler" )>] let handle (input:InputValue) (context:ILambdaContext) = match input.Question.ToLower () with | "the answer to the ultimate question of life the universe and everything" | "the answer to life the universe and everything" -> { Answer = 42 } | _ -> { Answer = 0 } |
いちばん重要なのは aws-lambda-tools-defaults.json
の function-handler
で指定する関数名で、この例では handle
関数です。この例では FunctionHandler
を指定しているので、 function-handler
は "FSharpAdventCalendar2016.Day10::FSharpAdventCalendar2016.Day10.LambdaFunction::FunctionHandler"
のようになります。
handle
関数は最初の引数が任意の型で、 LambdaSerializer
で指定されたシリアライザーによりシリアライズ・デシリアライズされます。この例では JSON 形式で。入力が {"question":""}
のような形式、出力が {"answer":0}
のような形式になります。
2 つめの引数が ILambdaContext
で、 Lambda に関する情報が含まれます。ちなみに Lambda 関数がリクエストにパラメーターを必要としない場合は、 1 つめの引数を省略して ILambdaContext
だけでも動きます。
デプロイ
最後に関数のデプロイを行います。以下のコマンドで、ビルドとデプロイが実行されます。
1 | dotnet lambda deploy- function |
function-role
を指定しないと、既存のロールから選択するか、新規に作成するかを選択されます。
Lambda 関数の実行
以下のコマンドで Lambda 関数リストが取得できます。正しくデプロイされていると、作成した関数が表示されます。
1 | dotnet lambda list-functions |
以下のように Lambda 関数を実行することができます。
1 2 3 | dotnet lambda invoke- function \ -- function -name <FUNCTION_NAME> \ --payload '{"question":"the answer to life the universe and everything"}' |
関数の削除
以下のコマンドで Lambda 関数を削除できます。
1 | dotnet lambda list-functions <FUNCTION_NAME> |
まとめ
AWS Lambda で F# が使えるようになったよ!
補足
dotnet lambda
の使い方は、 help
サブコマンドを利用します。
1 | dotnet lambda help [subcommand] |
おわりに
変更履歴
- [2016-12-10 09:45] .NET Core のインストール方法の参考文献を追記。
- [2016-12-10 09:45] パラメーターがない場合のハンドラーのパラメーターについて追記。
- [2016-12-10 09:45] 実行方法を追記。
- [2016-12-11 15:10] コードの誤りを修正。