Visual Studio for Mac で AWS Lambda(C#)ファンクションを作成してみた

2017.01.01

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

LambdaファンクションをC#で作成する場合、Windows上のVisual Studioであれば、AWS SDK for .NETをインストールするだけで、プロジェクトの雛形生成、デブロイ、実行が、すべてVisual Studio上から超簡単に作業できます。

AWS SDK for .NET

001

しかし、残念ながら、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) です。

Visual Studio for Mac PREVIEW

2 Visual Studio for Mac によるプロジェクト作成からデバッグまで

(1) プロジェクトの作成

メニューからファイル > 新しいソリューション > .NET Core > App と辿り、Console Applicatipon(.NET Core) を選択します。

008

プロジェクト名は、AWSLambdaSampleとしました。 002

作成されたソリューションは、次のようになっています。

201

(2) NuGetパッケージの追加

メニューから プロジェクト > NuGetパッケ時の追加 を選択し、次の3つのパッケージを追加しました。 (「Amazon.Lambda.」で検索すると、容易に見つけることが出来るでしょう。)

  • Amazon.Lambda.Core(必須)
  • Amazon.Lambda.Serialization.Json(オリジナルのデータをシリアライズする場合に必要)
  • Amazon.Lambda.TestUtilities(テストに使用)

この他にも、使用するサービスによっては、他のパッケージが必要になるかもしれません。

003

(3) ファンクションの作成

プロジェクトを選択して右クリックメニューから 追加 > 新しいファイル > General > 空のクラスを選択し、プロジェクトにクラスを追加します。 (名前は、Functionとしました。)

017

そして、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 WorldLambdaSerializerSampleDataクラスにシリアライズして受け取り、それを文字列にしてログに書き出し、併せて戻り値として返しているだけのファンクションです。

007

(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ファンクションの中で、ブレークポイントを置いたり、変数を確認したりすることもできます。

019

下記は、Ctrl+F5でデバッグ実行した様子です。(一応、ログ出力とテスト結果がコンソールに表示されています。)

018

3 project.json

projetc.jsonは、.NET Coreプロジェクトで利用される、メタデータ、コンパイル情報、依存関係の定義です。

Visual Studioでプロジェクト(ソリューション)を管理する場合、このファイルは使用されませんが、この後、dotnetコマンドで作業を進めるために作成することにします。

プロジェクトを選択して右クリックメニューから 追加 > 新しいファイル > Misc > 空のテキスト ファイル を選択し、プロジェクトフォルダ内にproject.jsonを追加します。

004

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及びtoolsAmazon.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) 作業フォルダの作成

現時点でのプロジェクトのフォルダ構成は次のようになっています。

020

Function.cs及び、projetc.jsonが存在するフォルダの階層下に、作業用フォルダ(プロジェクト名)を作成し、2つのファイルをコピーします。 フォルダ名をプロジェクト名にする理由は、dotnetコマンドが、デフォルトでフォルダ名をプロジェクト名と認識するからです。

$ mkdir AWSLambdaSample
$ cd AWSLambdaSample
$ cp ../Function.cs .
$ cp ../project.json .

021

(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フォルダの中に、デプロイに必要なファイルが生成されます。

022

(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です。 ( ③ )

023

ハンドラ名の指定によって、アップロードしたアセンプリの中から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と表示されていれば、成功です。

まだ、ログも確認することができます。

024

7 最後に

今回は、Visual Studio for Macを使用して、Lambda(C#)を作成してみました。 残念ながら、現時点でMac用のツールキットは提供されていませんが、Visual Studio for Macを使用することで、デバッグしながら、そこそこ快適に作業できるかもしれません。

8 参考資料

Announcing C# Support for AWS Lambda

Lambda Function Handler (C#)

Creating a Deployment Package (C#) > .NET Core CLI

project.jsonは、.NET Coreプロジェクトで利用される、プロジェクトのメタデータ、コンパイル情報、依存関係の定義です。