Visual Studio for Mac で AWS Lambda(C#)ファンクションを作成してみた
1 はじめに
LambdaファンクションをC#で作成する場合、Windows上のVisual Studioであれば、AWS SDK for .NETをインストールするだけで、プロジェクトの雛形生成、デブロイ、実行が、すべてVisual Studio上から超簡単に作業できます。
しかし、残念ながら、Macから作業する場合、このような便利なツールキットは提供されていません。
下記のリンクに .NET Core CLI を使用して、Lambdaファンクションをデプロイする方法が解説されていますが、やはり、ちょっと複雑な作業をしようとすると、CLIだけで物足りないのは確かです。
Creating a Deployment Package (C#) > .NET Core CLI
そこで、今回は、上記を基本としながら、Visual Studio for Mac でプロジェクトを作成し、デバッグしながらLambdaファンクションを作成してみようと思います。
なお、使用した、Visual Studio for Mac は、現時点(2017/01/01)でプレビュー版 Preview 2(7.0 build 560) です。
2 Visual Studio for Mac によるプロジェクト作成からデバッグまで
(1) プロジェクトの作成
メニューからファイル > 新しいソリューション > .NET Core > App と辿り、Console Applicatipon(.NET Core) を選択します。
プロジェクト名は、AWSLambdaSampleとしました。
作成されたソリューションは、次のようになっています。
(2) NuGetパッケージの追加
メニューから プロジェクト > NuGetパッケ時の追加 を選択し、次の3つのパッケージを追加しました。 (「Amazon.Lambda.」で検索すると、容易に見つけることが出来るでしょう。)
- Amazon.Lambda.Core(必須)
- Amazon.Lambda.Serialization.Json(オリジナルのデータをシリアライズする場合に必要)
- Amazon.Lambda.TestUtilities(テストに使用)
この他にも、使用するサービスによっては、他のパッケージが必要になるかもしれません。
(3) ファンクションの作成
プロジェクトを選択して右クリックメニューから 追加 > 新しいファイル > General > 空のクラスを選択し、プロジェクトにクラスを追加します。 (名前は、Functionとしました。)
そして、Fontionc.csを次の様に編集します。
using Amazon.Lambda.Core; [assembly: LambdaSerializerAttribute(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] namespace AWSLambdaSample { public class Function { public string FunctionHandler(SampleData sampleData, ILambdaContext context) { context.Logger.LogLine(sampleData.ToString()); return sampleData.ToString(); } } public class SampleData { public string Key1 { get; set; } public string Key2 { get; set; } public string Key3 { get; set; } override public string ToString() { return $"Key1={Key1}, Key2={Key2}, Key3={Key3}"; } } }
これは、下記のAWSコンソールからのLambdaのテストに使用される雛形の一つであるHello WorldをLambdaSerializerでSampleDataクラスにシリアライズして受け取り、それを文字列にしてログに書き出し、併せて戻り値として返しているだけのファンクションです。
(4) テスト・デバック
続いて、Main.csを以下のように書き換えます。
Mainメソッドの中では、先程のLambdaファンクションを実行しているだけです。
一応、Mainから、Lambdaファンクションをテスト実行出来るという意味で、単体テスト風に書いてみましたが、xunitやNUnitが利用できないため、Assertクラスを無理やり作ったりして、ちょっと見苦しくなってしまいました。(どなたか、指導してやって下さい。)
using System; using Amazon.Lambda.TestUtilities; using AWSLambdaSample; class Program { static void Main(string[] args) { // setup var sut = new Function(); var expected = "Key1=value1, Key2=value2, Key3=value3"; var sampleData = new SampleData() { Key1 = "value1", Key2 = "value2", Key3 = "value3" }; //exercise var acture = sut.FunctionHandler(sampleData, new TestLambdaContext()); //veryfy Assert.That(acture, expected); //tearDown } class Assert { static public void That(string acture, string expected) { if (acture == expected) { Console.WriteLine($"success"); } else { Console.WriteLine($"error {acture}!={expected}"); } } } }
テストに拘らなければ、このMain関数からは、単純にFunctionクラスをnewしてFunctionHandlerメソッドを呼ぶだけでいいと思います。
static void Main(string[] args) { var sampleData = new SampleData() { Key1 = "value1", Key2 = "value2", Key3 = "value3" }; (new Function).FunctionHandler(sampleData, new TestLambdaContext()); }
作成したプロジェクトは、ProgramクラスのMainメソッドをエントリーポイントとして実行を開始するアプリになっていますので、この状況で、デバック実行することでLambdaファンクションをローカルで実行できます。
そして、Lambdaファンクションの中で、ブレークポイントを置いたり、変数を確認したりすることもできます。
下記は、Ctrl+F5でデバッグ実行した様子です。(一応、ログ出力とテスト結果がコンソールに表示されています。)
3 project.json
projetc.jsonは、.NET Coreプロジェクトで利用される、メタデータ、コンパイル情報、依存関係の定義です。
Visual Studioでプロジェクト(ソリューション)を管理する場合、このファイルは使用されませんが、この後、dotnetコマンドで作業を進めるために作成することにします。
プロジェクトを選択して右クリックメニューから 追加 > 新しいファイル > Misc > 空のテキスト ファイル を選択し、プロジェクトフォルダ内にproject.jsonを追加します。
project.jsonの内容を次のように編集します。
{ "version": "1.0.0-*", "buildOptions": { }, "dependencies": { "Microsoft.NETCore.App": { "type": "platform", "version": "1.0.0" }, "Newtonsoft.Json": "9.0.1", "Amazon.Lambda.Core": "1.0.0*", "Amazon.Lambda.Serialization.Json": "1.0.0", "Amazon.Lambda.Tools" : { "type" :"build", "version":"1.0.4-preview1" } }, "tools": { "Amazon.Lambda.Tools" : "1.0.4-preview1" }, "frameworks": { "netcoreapp1.0": { "imports": "dnxcore50" } } }
Lambaファンクションで登録するアセンブリは、ライブラリなので、buildOptionsに、 "embedInteropTypes": trueは、必要ありません。そして、実行されるファンクションは、後述するHandlerで指定されます。
dependenciesには、サーバー上で、このアセンブリが動作するために必要なライブラリを列挙します。NuGetで追加したAmazon.Lambda.TestUtilitiesは、デバッグ時にProgram.Mainで使用しただけなので、ここでは、必要ありません。また、プロジェクト作成時に入っていたMicrosoft.NET.Sdkも必要ありません。
なお、dependencies及びtoolsのAmazon.Lambda.Toolsについては、実は、アップロードするアセンブリには必要ありません。
Amazon.Lambda.Toolsは、本記事では、「 (5) packageコマンド」を使用する場合のみ必要になります。 なお、Amazon.Lambda.Toolsは、publishでは対象外扱いとなっており、記述があっても作成されるパッケ−ジには、含まれません。
※現在(2017.01.01)、AWS Lambdaで使用できる.NETCoreは、1.0までです。
参考:Microsoft DorNET Document project.json
4 アップロードファイルの作成
C#によるLambdaファンクションは、Javaと同様に、実行ファイルと、必要なライブラリ等をzipで纏めてアップロードします。
ここからは、コマンドラインで作業を進めますが、使用するのは、先に、VisualStudioで作業した、Function.cs及び、project.json のみです。
(1) 作業フォルダの作成
現時点でのプロジェクトのフォルダ構成は次のようになっています。
Function.cs及び、projetc.jsonが存在するフォルダの階層下に、作業用フォルダ(プロジェクト名)を作成し、2つのファイルをコピーします。 フォルダ名をプロジェクト名にする理由は、dotnetコマンドが、デフォルトでフォルダ名をプロジェクト名と認識するからです。
$ mkdir AWSLambdaSample $ cd AWSLambdaSample $ cp ../Function.cs . $ cp ../project.json .
(2) ライブブラリの復元
project.jsonに基いて、開発プロセス中に変更された可能性のあるプロジェクトの依存関係への参照が復元されます。
$ dotnet restore
(3) デプロイパッケージ作成
dotnet publishで、アプリケーションをコンパイルし、ソースコードとすべての依存関係をフォルダにパッケージ化します。
$ dotnet publish --output publish Publishing AWSLambdaSample for .NETCoreApp,Version=v1.0 Project AWSLambdaSample (.NETCoreApp,Version=v1.0) will be compiled because expected outputs are missing Compiling AWSLambdaSample for .NETCoreApp,Version=v1.0 Compilation succeeded. 0 Warning(s) 0 Error(s) Time elapsed 00:00:01.8458286 publish: Published to publish Published 1/1 projects successfully
ここまでの作業で、プロジェクトのフォルダ構成は次のようになっています。 赤枠で示しているのが現在作業中の作業フォルダです。
そして、--output オプションで指定した publishフォルダの中に、デプロイに必要なファイルが生成されます。
(4) 圧縮
生成されたpublishの中のファイルを圧縮ファイルに固めます。 圧縮ファイルを作成する場合は、アセンブリがrootに配置されるようにする必要があります。
$ cd publish $ zip ../publish.zip * adding: AWSLambdaSample.dll (deflated 62%) adding: AWSLambdaSample.pdb (deflated 36%) adding: Amazon.Lambda.Core.dll (deflated 57%) adding: Amazon.Lambda.Serialization.Json.dll (deflated 55%) adding: Newtonsoft.Json.dll (deflated 60%) adding: System.Runtime.Serialization.Primitives.dll (deflated 48%)
ここで作成されたpublish.zipがデプロイされるファイルです。なお、ファイル名は、なんでも構いません。
(5) packageコマンド
実は、ここまで、分かりやすいように分解して作業しましたが、「(3)デプロイパッケージの作成」と「(4)圧縮」については、packageというコマンドで一発で可能です。( (2)ライブラリの復元は、行われません )
$ dotnet lambda package --configuration Release --framework netcoreapp1.0
この時、作成されるパッケージは、プロジェクト名.zipとなっています。
$ ls -la ./bin/Release/netcoreapp1.0/*.zip -rw-r--r-- 1 hirauchishinichi staff 214845 12 29 14:48 ./bin/Release/netcoreapp1.0/AWSLambdaSample.zip
5 デプロイ
マネージメントコンソールから新しいLambdaファンクションを登録します。
必須項目に指定したのは、次のとおりです。
- 名前は識別するだけのものなので何でも構いません。( ① )
- アップロードしたのは、先ほど作成したpublish.zipです。( ② )
- ハンドラー名は、AWSLambdaSample::AWSLambdaSample.Function::FunctionHandlerです。 ( ③ )
ハンドラ名の指定によって、アップロードしたアセンプリの中からLambdaが実行するべきメソッドを知ることになります。 ハンドラの命名規則は、次のとおりになっています。
アセンブリ名::名前空間.クラス名::メソッド名
ハンドラ名に指定したものが、アップロードしたアセンブリの中で見つからなかった場合、次のようなエラーになります。
例)Methodが見つからない場合
Execution result: failed(logs) The area below shows the result returned by your function execution. Learn more about returning results from your function. { "errorType": "LambdaException", "errorMessage": "Unable to find method 'FunctionHandler2' in type 'AWSLambdaSample.Function' from assembly 'AWSLambdaSample, Culture=neutral, PublicKeyToken=null': Found no methods matching method name 'FunctionHandler2'." }
6 テスト実行
テストボタンを押すと、デフォルトのペーロードとして、Hello WorldのJSONを入力として、Lambdaファンクションが実行されます。 そして、エラー無く、Execution result: successeedと表示されていれば、成功です。
まだ、ログも確認することができます。
7 最後に
今回は、Visual Studio for Macを使用して、Lambda(C#)を作成してみました。 残念ながら、現時点でMac用のツールキットは提供されていませんが、Visual Studio for Macを使用することで、デバッグしながら、そこそこ快適に作業できるかもしれません。
8 参考資料
Announcing C# Support for AWS Lambda
Creating a Deployment Package (C#) > .NET Core CLI
project.jsonは、.NET Coreプロジェクトで利用される、プロジェクトのメタデータ、コンパイル情報、依存関係の定義です。