February 2017
Volume 32 Number 2
Azure - Azure Functions によるサーバーレス アーキテクチャ
Joseph Fultz | February 2017
ツール、仮想マシンからコンピューターまで、開発者は反復作業を自動化したり、作業時のコンテキストを標準化する方法を求めています。こうした方法があれば、仕事の完成や問題の解決に必要な作業に専念できます。同時に、IT 業界の進化につれて、CPU からサーバー ファームまで、システムの全レベルで高密度化が図られ、こうしたシステムから最大の効果が得られることを期待しているのも明らかです。サーバーレス アーキテクチャは、この 2 つの流れに対応する合流点になります。このアーキテクチャでは、開発者個人が専念するタスクが最大限に細分化され、システムの浪費が最小限に抑えられます。
サーバーレス環境では、開発者はインフラストラクチャではなくソリューションを作成し、環境の健全性ではなく実行を監視します。費用は利用した時間に対して発生し、大半がアイドル状態の仮想マシン (VM) ファームに費用を支払う必要はありません。Microsoft Azure には、利用可能なサービスが多数存在します。こうしたサービスをつなぎ合わせれば、ソリューション全体を形成できます。中でも新しく重要なコンポーネントが Azure Functions です。このコンポーネントは、完全なソリューション エコシステムにおいてサーバーレス コンピューティング機能を提供します。今回は、サーバーレス アーキテクチャの意義を紹介し、Azure Functions のツールを調査します。
Azure Functions によるサーバーレス アーキテクチャ
サーバーレス アーキテクチャには多くの定義があります。サーバーレスといっても、コードがサーバーを利用しないで動作するわけではありません。実際には、依然としてサーバーは重要な役割を果たします。ですが、サーバーについて考える必要はありません。これは新たな種類の PaaS のようにも思えますが、実際には似て非なるものです。では、具体的にはどのようなものでしょう。 基本的には、サーバーレス アーキテクチャとは、PaaS を土台とする次世代クラウド サービスです。このクラウド サービスは、バインドによって VM、アプリケーション フレームワーク、外部依存関係を抽象化し、開発者がビジネス ロジックを実装するコードだけに専念できるようにします。
念のため、Azure Functions とサーバーレス アーキテクチャの代表的な性質について説明しておきます。Azure Functions は、サーバーレス アーキテクチャのサーバーレス コンピューティング コンポーネントを提供します。図 1 に示すように、Azure Functions は Azure App Service と WebJobs SDK の上位に構築され、Azure Function のコードをホストして実行する機能や、実行時バインドなどの便利な機能を実現する追加要素をいくらか盛り込んでいます。
図 1 Azure Functions のアーキテクチャ
このため、Azure App Service を使用して得られるメリットはすべて利用でき、設定の [service] の直下で確認できます。つまり、WebJobs SDK をローカルで使用して、ローカル ランタイム環境を作成することも可能です。アーキテクチャのメリットではありませんが、Azure Functions は、Node.js、C#、PHP、Python、さらに Windows PowerShell など、さまざまな言語をサポートする多言語プラットフォームです。このプラットフォームによって、ランタイムと依存関係が処理されます。これにより、好みやスキルが異なるチームが同じプラットフォームを使用して機能をビルドおよび提供できようになります。混在環境で作業する開発者にとっては、この点がメリットになります。Azure Functions をサーバーレス アーキテクチャのサーバーレス コンピューティング コンポーネントとして使用すると、主に以下のメリットが得られます。
- 市場に投入するまでの時間の短縮: このプラットフォームによって基盤となるインフラストラクチャが管理されるため、開発者はインフラストラクチャを意識しないで、ビジネス ロジックを実装するアプリケーション コードに専念できます。Azure Functions は、マイクロサービスをさらにナノサービスへと細分化するものと考えてもかまいません。開発、テスト、配置が小さな機能単位に絞り込まれるため、どちらの枠組みも開発プロセスにとってメリットがあります。絞り込まれる小さな機能単位は、個別のサービスとして、他の機能単位とは別途管理されます。
- 総保有コストの低減: インフラストラクチャや OS のメンテナンスが行われるため、開発者はビジネスへの価値提供に専念できます。つまり、DevOps とメンテナンスに必要な費用が大幅に単純化され、削減されます。
- 実行に対する課金: 通常、使用するコンピューティング リソースの機能密度の増加に連動して、利用したサイクルのみに課金されることで、大きくコストが抑えられます。
Azure Functions を使用してビルドする際のアプローチとして、その価値を十分引き出すには、作業のスコープを 1 つだけに絞って最小単位のロジックのみを作成し、依存関係を最小限に抑えることが重要になります。Azure Functions を使用して作業するときは、以下のベスト プラクティスに従います。
- 事実上、Azure Function の目的を 1 つに絞ります。つまり、複雑な文章ではなく、短く簡潔な文として考えます。
- 事実上、操作をアイデムポテント (何度行っても結果が同じ) にします。つまり、同じパラメーターで連続して API エンドポイントを呼び出しても、システム状態は変わりません。
- 実行時間を短くします。入力を受け取り、目的の操作を実行して、結果を後工程の利用者に提供することを目指します。時間のかかる処理には、Azure WebJobs を検討するか、Azure Service Fabric でサービスをホストしてもかまいません。
- 全体の複雑さを抑えて実行速度を上げる場合は、内部の依存関係を最小限に抑えるのが最も効果的です。ランタイムに負荷をかけすぎると、最初の読み込み時間が長くなり、システムに複雑さが加わります。
- 外部統合には入力と出力のバインドを利用します。パフォーマンスの高いサイトにするための共通ガイダンスとして、ステートレスのサービスを作成します。ステートレスであれば、ランタイム状態の保持、シリアル化、シリアル化解除によって複雑になったり、速度低下を招くことがなくなります。また、起こっていることを確認するために状態を再現する方法を調べたり試したりする必要がなくなり、デバッグが簡単になります。ステートレスで重要なのは、パラメーター値の受け渡しのみです。
サーバーレス アーキテクチャには、次のような性質があります。
- サーバーレス アーキテクチャの作業単位は、イベントによって起動されるステートレス関数の形式をとります。
- スケール、容量、インフラストラクチャの管理はサービスとして提供されます。
- コードが実行された時間にのみ支払う実行ベースの課金モデルが採用されます。
サーバーレス アーキテクチャには次のような課題もあります。
- 複雑性: サーバーレス アーキテクチャは、開発者の多くの作業を簡略化しますが、プラットフォームの抽象化により、アプリケーションのビルド方法を考え直す必要があります。目的別の関数一式と関数間の依存関係を管理するよりも、モノリシック アプリケーションを 1 つの単位として管理する方が簡単です。この複雑性を緩和するのに、Azure API Management が提供する機能が便利です。この機能によって一貫した外部向けインターフェイスを備えた名前空間を作成し、個別に定義および管理される関数をすべて結び付けます。
- ツール: 公開されてから間がないため、関数を作成、デバッグ、テストするツールは現在開発中です。本稿執筆時点のツールはプレビュー版ですが、Visual Studio 2017 にはツールが最初から組み込まれる予定です。管理ツールも監視ツールも現在開発中ですが、Azure Functions の基本インターフェイスがあるため、そこから、要求、成功、エラー、要求/応答の詳細を確認できます。プラットフォームとアプリケーション監視ツールを結び付ける際にはやるべき作業が数多くあるため、Azure Functions チームは、そのようなサポートを拡張し、進化させるよう取り組んでいます。
- 組織的なサポート: サーバーレスという構想への移行は、簡単には検討できない場合もあります。多くの組織は、完全に自動化される継続的インテグレーション (CI)、継続的配信 (CD) パイプライン、マイクロサービス アーキテクチャへの移行に苦戦しています。サーバーレス設計への移行はそのときの標準と対立することが多く、さらに、利用可能な要素、それらを結びつける方法、管理方法を教えるリソースが必要になるため、難易度が上がる可能性があります。
- ランタイム最適化の欠如: 従来の設計では、ワークロードに対して実行環境を最適化し、RAM、スワップ、HDD、ネットワーク接続などの種類や量を変更してきました。Azure Functions では、使用するストレージ アカウントなど、最低限の変更しかできません。
従来のアーキテクチャとサーバーレス アーキテクチャの比較
システムの設計アーティファクトの代表的な組み合わせには、論理設計、技術設計、ソフトウェア アーキテクチャなどがあります。通常、論理設計ではシステムの機能と役割を定義し、技術設計ではシステムそのものを定義します。サーバーレス アーキテクチャに移行する場合は、システム自体ではなく、システムで何を行うかを考えます。多くの IT 企業では、図 2 のようなアーキテクチャ図が多く利用されています。
図 2 従来の技術アーキテクチャ
ホスティング、ネットワーク、DBA グループのメンバーはプロビジョニングや構成について把握する必要があるため、この種のアーティファクトは特に重要です。ただし、PaaS 環境では、機能の特性に注目し、プロビジョニングの詳細はプラットフォームに任せ、PaaS サービス自体の構成を定義するだけです。構成への入力詳細はテンプレートで保持し、RAM、CPU、HDD の最適な量を議論するのではなく、能力、統合、および機能に集中します。
最終的な図は、図 3 に示すような典型的な論理図に近くなります。このような図では、アプリケーション ホストの構成ではなく、最初から機能と各機能の結び付きに注目できるようになります。このようにメッセージを簡略し、目的を明確にすることで、目標や実装についての技術的な議論につながります。それだけではなく、論点がシステム自体からシステムが果たす内容に変わることから、技術部門以外の関係者にシステムの価値を伝えるのにも有効です。
図 3 サーバーレス アーキテクチャ
Azure Functions の利用
Azure IoT プラットフォームは、データの収集と分析をサポートするためのサービスのリッチなセットを提供します。今回の例は基本的な分析機能を備えた既存の IoT 実装で、車両のテレメトリを収集してクラウド内に保存するように構築されています。ここでは、このソリューションに新しい機能を追加し、データのクエリをリアルタイムに実行できるようにして、特定の場所に最も近い車両を特定します。この種の機能は、フリーフロート型カーシェアリング プログラムに使用したり、駐車場にある自分の車を見つけるのに使用することもできます。
今回の例の実装では、ツールとプラットフォームを説明するため意図的にいくつか省略していることがあるため、ここで注意点を示しておきます。まず、単一パーティションの DocumentDB を直接操作しています。より現実的な実装では、共有を最小限に抑えて、予想されるデータ量を基に DocumentDB を用意します。ただし、一部の読み取りパスを最適化する目的で、Azure Redis Cache や Azure での Elasticsearch を追加するなど、他の方法を選択することもできます。次に、現状の DocumentDB APIで比較するレコードを取得するにはクライアント側での処理が少し必要になるため、上位 100 件のレコードだけを要求するようにしています。パーティション機能や検索機能は、大きなセットで必要なレコードを見つけるために利用する代表的な方法です。いずれにせよ、関数を使用して可能性のあるレコードを検索してから、特定の位置と比較して、セットの中から最も近い車両を返します。
関数を作成する
本稿執筆時点で、開発プロセスの速度を上げるために利用できる Visual Studio ツールはありません。そのため、ポータル インターフェイスを使って Azure Function を作成することから始めます。Azure ポータルで Azure Function の作成に着手すると、関数の設定をいくつか調整するためのブレードが表示されます。App Service プランを選択するオプションである、 従量課金プランと App Service プランに注意します。どちらを選択してもかまわないように思えますが、実際には、旧式のリソース管理にするか、サーバーレス アーキテクチャを目指すかの 2 択が示されています。App Service プランを選択する場合、どの程度の処理能力が必要かを推測する必要があります。選んだ量が多すぎると、使用しないリソースに支払うことになりますが、少なすぎると、実装に問題が起こり、最終的には顧客や利益に影響を及ぼします。従量課金プランを選択すると、スケールアップやスケールダウンがシステムに任され、アプリが利用する分だけに課金されるため、こちらの方が好ましい選択です。
最後の手順として、関数自体を実際に作成します。まず、C# を使用して事前に作成されている WebHook 関数から始めます。この関数により、HTTP 要求の受け取りに応じて関数を実行するトリガーが事前に構成されることになります。左側のメニューから [統合] 項目を選ぶことにより、トリガー、入力、出力に対する構成方法としていくつかオプションを選択できます。トリガー選択ページには、WebHook のバインドに関する情報、API キーの使用方法に関する情報、C# と Node.js 両方から WebHook を呼び出す場合のサンプル コードなど、有益な情報がいくつか用意されています。ダイアログ ボックスが表示されたら、入力と出力のバインドを構成します (図 4 参照)。
図 4 入力バインドの構成
外部システムへの呼び出しを行うためにライブラリやアダプターをインクルードできますが、このシステムはバインドを使って機能し、EventHub や DocumentDB などの多くのデータ ソースに対する優れたサポートが用意されています。開発エクスペリエンスは、Azure Functions に含まれるバインド インフラストラクチャによって強化されています。バインドは実際にターゲットにするソフトウェア インフラストラクチャ (DocumentDB、EventHubs、Storage など) を抽象化するため、開発者はターゲットを表すパラメーターをコーディングするだけです。ただし、構成を変更すればターゲットを変更できるので柔軟性が確保されます。これで、ソース DocumentDB と対話するように構成されました。この構成により、関数内で DocumentDB クライアントに対して直接コードを作成できます。自身で接続を行う必要はありません。ドキュメント パラメーター名は inputDocument です。これは変数で、DocumentDB への呼び出しを行うために使用する関数に渡します。
ここで、出力オプションを確認し、ストレージ、キュー、他の外部システムなどを数多くを含めることができます。この UI で選択して構成可能なすべての項目は、後から [Function App の設定] UI でアクセスし、テンプレートの一部として、または、プログラミング可能なインターフェイスから構成できます。今回は、HTTP 経由で JSON の結果を呼び出し元に返すだけです。HTTP(res) は出力パラメーターを定義して既に出力に設定されているため、そのまま使用します。
コードを開発する
左側のメニューの [開発] を選択すると、オンライン エディターが表示されます。このエディターは変更を簡単に繰り返したり、リアルタイムでログを確認するのに適しています。また、WebHook 関数をトリガーするためのインターフェイスも用意されています。ツールは Visual Studio と Visual Studio Code (エクスペリエンスに優れている) の両方向けに作成中です。現時点の Azure Functions を操作していると、IDE を使いたくなります。Visual Studio なら、サーバー エクスプローラーから Function App に接続するのは簡単です。
ファイルの編集時に、いくつか作業が必要です。[コード] ウィンドウの左下に [ファイルの表示] リンクがあります。ファイル エクスプローラーは [コード] ウィンドウの右隣に開きます。まず必要なのは DocumentDB クライアントです。多くのライブラリがあります。ライブラリは、先頭で #r ディレクティブを使用して参照すると自動的に使用可能になります。利用可能なライブラリの一覧などの開発者向け情報の多くは、開発者向けのオンライン リファレンス (bit.ly/2gaWT9x)で参照できます。たとえば、DocumentDB に対する優れたサポートがあるため、DocumentDB クライアント オブジェクトへのアクセスが必要になります。そこで、ファイルの先頭でこのアセンブリに対する #r ディレクティブを追加します。必要なライブラリをインクルードしない場合、つまり、自身で保守して NuGet に公開する場合は、project.json ファイル (Node.js では package.json) に追加することができます。
この時点で、run.csx ファイルを編集する準備が整いました。このファイルでコードをすべて保持することになります。そのためには、Azure Functions 用のオンライン IDE で直接編集します (図 5 参照)。
図 5 関数コードの編集
テンプレート コードから始めます。まず、独自のカスタム外部ライブラリを関数に追加します。このライブラリが Haversine 関数のコードを含みます。あまり長くなく、関数に固有のカスタム クラスや関数がある場合は、run.csx に直接追加できます。ただし、再利用可能な機能がある場合は別の方法を使います。たとえば、コンパイル済みのバージョンを \bin フォルダー含めたり、project.json ファイルから NuGet パッケージとして参照し、#r でライブラリを参照します。あるいは、コードを別の .csx ファイルに配置して、#load ディレクティブを使用することもできます。
近さをチェックする車両と、関数に渡された場所との距離を判断できるように、2 つの関数が必要です。はるか昔に学校を卒業した今では、普段、半正矢 (haversine) の数式を必要としないことは明らかです。Wikipedia (bit.ly/2gCWrgb) にはこれに関して非常に参考になる記載があります。C# 関数は bit.ly/2gD26mK (英語) から借用していくつか変更を加えます。今回は haversine クラスの静的メンバーとして必要な関数を作成します。
namespace SphericalDistanceLib
{
public class Haversine
{
public static double CalculateDistance(
double currentLong, double currentLat,
double vehicleLong, double vehicleLat){…}
public static double ToRadians(double degrees){...}
}
}
コードがコンパイルされると、関数のルート フォルダーから相対位置にある bin フォルダーにアップロードされます。今回の場合、このパスは FindNearVehicle/bin です。ただし、bin フォルダーは既定で存在するわけではないため、作成する必要があります。ここまでの設定が終わったら、必要なコードに専念します。関数の先頭で必要なライブラリを参照していることを確認します。特に、DocumentDB クライアント オブジェクトの型、Newtonsoft、/bin フォルダーにアップロードしたカスタム ライブラリの参照が必要です。この参照を #r ディレクティブを使用してファイルの先頭に追加します。
#r "Microsoft.Azure.Documents.Client"
#r "Newtonsoft.Json"
#r "SphericalDistanceLib.dll"
前述のように、created_at タイムスタンプ フィールドに基づいて最新のレコード 100 件を取得します。DocumentDB には、これを非常に容易にする優れた SQL 構文があります。
IQueryable<Document> telemDocs = inputDocument.CreateDocumentQuery<Document>(
UriFactory.CreateDocumentCollectionUri(dbName, collectionName),
new SqlQuerySpec("SELECT TOP 100 c.vehicle.vin, c.vehicle.model,
c.location FROM c ORDER BY c.created_at DESC"), queryOptions);
Document 型を Dynamic 型にキャストすればオブジェクトにプロパティを簡単に追加できるため、作業が簡単になる Document 型を使用しています。SQL は SqlQuerySpec の形式で渡され、車両識別番号、モデル、位置情報のみをオブジェクトに提供します。ここで、ドキュメントの一覧を反復し、外部ライブラリの haversine 関数を使用して距離を計算し、最も近い車両を特定してそれを返す必要があります。ここから少し複雑になります。
車両識別番号ごとに最新位置情報レコードのみが必要なので、確認したすべて番号を追跡する必要があります。番号は降順に並べ替えているため、最初に受け取るドキュメントが最新のドキュメントです。車両識別番号 null は走行中の車両を示すため、null があれば、そのドキュメントを無効であると見なします。要素が null 値でなければ、その車両識別番号を単純に HashSet に追加します。その要素が一覧の中で一意であれば問題なく追加されます。ただし、一意でなければ追加に失敗し、その車両の最新レコードが既に一覧の中にあることがわかります (図 6 参照)。
図 6 個々の車両識別番号の既知の最新位置情報
HashSet<string> seen = new HashSet<string>();
foreach(dynamic doc in telemDocs){
if(seen.Add(doc.vin)){
// Calculate the distance
if (doc.location != null) {
doc.distance =
Haversine.CalculateDistance(double.Parse(currentLong),
double.Parse(currentLat), double.Parse(
doc.location.lon.ToString()),
double.Parse(doc.location.lat.ToString()));
lastKnownLocations.Add(doc);
}
else{
// Location is null, so we won't take this
// record as last known location
seen.Remove(doc.vin);
}
}
}
ドキュメント全体を lastKnownLocations の一覧に追加します。これにより、距離値の短い順に反転させ、先頭のドキュメントを簡単にクエリできるようになります。
var nearestVehicle = lastKnownLocations.OrderBy(x => ((dynamic)x).distance).First();
return currentLat == null || currentLong == null
? req.CreateResponse(HttpStatusCode.BadRequest,
"Please pass a lat and lon on the query string or in the request body")
: req.CreateResponse(HttpStatusCode.OK, nearestVehicle);
これで、並べ替えた一覧の先頭のドキュメントを返すことができます。最後の行では null パラメーターのチェックも処理しています。ドキュメントのシリアル化は自動的に処理されます。
サンプルを実行する
最後の手順は、動作の確認です。[開発] ビューの右上にはフラスコのアイコンの付いたリンクがあり、[テスト] というラベルが付いています。このリンクをクリックするとテスト UI が開きます (図 7 参照)。この UI では、HTTP メソッドの変更、パラメーターやヘッダーの追加、選択した HTTP メソッドに渡す本文の設定などが可能です。このような作業を数多く行うときは、通常、Postman を使用します。これにテストのライブラリを含めているためです。ただし、HTTP 関数用の組み込みテスト機能も優秀で、すぐにも反復作業に利用できます。
図 7 関数のテスト
今回は、緯度と経度を取得し、[実行] ウィンドウで目的の JSON 形式に変換しています。[実行] をクリックすると、呼び出しからログ オブジェクトと一般的なログ出力を [ログ] ウィンドウで確認できるようになります。[出力] ウィンドウの右下にはステータスも表示されます。ログ出力の時間を注意して見ると、関数の実行に約 250 ミリ秒かかっています。これは Azure Functions を使用する目標であった、目的を 1 つに絞った実行時間が比較的短い実行モデルという範疇に問題なく収まります。[出力] ウィンドウからコンテンツを取り出して書式を設定すると、車両、位置を記録したタイムスタンプ、位置、および距離が含まれていることがもっと明確になります。
{
"vin": "wmwrxxxxxxxx54251",
"model": "Cooper Hardtop",
"location": {
"lat": 30.4xxxxxx,
"lon": -97.8xxxxxx,
"accuracy_m": 22.505,
"ts": 1473116792970
},
"distance": 13.552438042837085
}
距離計算を行ったとき、地球の円周をキロメートルで表したため、応答で示される距離は約 13.6 キロメートルになっています。
まとめ
今回の例では、Azure Functions の開発と実行にオンライン ツールを組み合わせて使用しましたが、開発チーム向けのより現実的なアプローチとしては、ローカルで開発を行い、継続的インテグレーションをセットアップして、Git レポジトリから配置します。WebJobs.Script と WebJobs SDK を使用すると、Azure Functions をローカルで開発および実行する機能をセットアップできます。これを行う方法のチュートリアルについては、bit.ly/2hhUCt2 を参照してください。ここには、デプロイのソースとして構成できる多種多様なソースもあります。
Azure Functions は、Azure プラットフォームの新たな要素です。Azure Functions は、クラウド PaaS 実装のメリット実現するために必要なサーバーレス コンピューティングの重要な要素です。Azure Functions によりコードの価値に専念できるようになり、インフラストラクチャの管理を意識する必要がなくなります。プラットフォーム、ツールのサポート、および言語のサポートは進化を続けていますが、既に多くの言語をサポートしており、CI/CD パイプラインに結び付けられるようになっています。詳細については、bit.ly/2h7lo4C (英語) を参照してください。Azure Functions をまだ使用したことがない方は、是非お試しください。bit.ly/2glBJnC で、初歩的な説明を確認できます。
サーバーレス コンピューティングへの移行
サーバーレス コンピューティングによって、クラウド コンピューティングやクラウド向けのアプリケーションのビルドに対する考え方が根本的に変わります。サーバーレス コンピューティングにより、開発者は完全に抽象化され、無限にスケーリングが可能な環境でコードを実行し、コードを実行した分だけに課金されるモデルを利用できるようになります。
サーバーレス コンピューティングは、PaaS の次の段階への進化と捉えることができます。コードからアプリケーション インフラストラクチャを抽象化し、自動スケーリング機能を提供するという PaaS の誓約を満たしています。サーバーレスの差別化要因となるのは、コードがホストされている時間ではなく、実行時間に課金されるモデルと、制限なく瞬時にスケーリングされることです。サーバーレス コンピューティングにより、「完全に管理された」 (管理を必要としない) コンピューティング エクスペリエンス、「制限のない瞬時の」 (構成を必要としない) スケーリング、および 「イベントへの反応」 (リアルタイム処理) が提供されます。その結果、開発者は、設計に応じてスケールが拡大縮小し、最終的には回復性の高い新たなクラスのアプリケーションを開発できるようになります。
サーバーレス アプリケーションをビルドしている開発者は Azure Functions のようなサーバーレス コンピューティンだけでなく、着々と数が増えている完全管理型の Azure やサードパーティ製のサービスを使用して、機能することが確認されているエンドツーエンドのサーバーレス ソリューションから組み立てることになります。現在の開発者は、設計に応じて容易に低コストでスケーリングできる、完全なサーバーレス アーキテクチャを簡単にセットアップして実行できるようになっています。さらに、インフラストラクチャを管理、監視する負担から解放され、コードを実行するインフラストラクチャのメンテナンスではなく、ビジネス ロジックに専念して、ビジネスに関連する問題を解決できるようになります。
サーバーレスはアプリケーションのビルドについての考え方を変えます。そして、今後長い間用いられるようになるでしょう。bit.ly/2hhP41O や bit.ly/2gcbPzr (英語) での議論に是非参加してください。機能に関する要望は bit.ly/2hhY3jq (英語) から送信でき、Twitter の @Azure (英語) で #AzureFunctions というハッシュタグでツイートできます。 —Yochay Kiriaty
Darren Brust は Microsoft のクラウド ソリューション設計担当者であり、企業顧客が Microsoft Azure に移行する際の手助けに多くの時間を費やしています。時間が空くと、3 人の子供たちの誰かが参加しているスポーツ イベントを見たり、地元オースティンのコーヒー ショップで大量のコーヒーを飲んでいます。彼の連絡先は dbrust@microsoft.com (英語) で、Twitter は @DarrenBrust (英語) からフォローできます。
Joseph Fultz は、Microsoft のクラウド ソリューション設計担当者です。彼は、Microsoft Azure を活用してビジネス問題を解決するためのアーキテクチャを開発している Microsoft の顧客に協力しています。以前、GM のカーシェアリング プログラム (mavendrive.com、英語) の開発と設計を担当していました。彼の Twitter は @JosephRFultz (英語) で、jofultz@microsoft.com (英語) から連絡することもできます。
この記事のレビューに協力してくれたマイクロソフト技術スタッフの Fabio Calvacante に心より感謝いたします。