はじめに

今回は、さまざまイベントをきっかけにアプリケーションを実行できるAzure Functionsと、WebJobsという2つのサービスを使って、イベント駆動アプリケーションの作り方や双方の違いを2回に分けて見ていきたいと思います。今回はAzure Functionsについて説明し、アプリケーションを作成していきます。

ここで言うイベントとは、システムやアプリケーション上で日々発生している以下の様な出来事のことを指します。

・ファイルが作成・アップロードされた
・キューにメッセージが追加された
・別システムからHTTPリクエストを受信した
・IoT端末からデータを受信した
・ある日時になった

イベント駆動アプリケーションはこれらのイベントを処理開始のきっかけ(トリガー)として定義する形式のアプリケーションです。

検証環境
・Firefox 54
・Google Chrome 59

Azure Functionsとは

Azure Functionsとは、さまざまイベントによって駆動する処理を関数として定義して、Azureのクラウド環境上で実行するプラットフォームです。

イベントには、キューメッセージ、ファイルの作成、スケジュール、HTTP(webhook)などが含まれます。 Azure Functionsは、利用にあたり自分でサーバを構築したり管理したりする必要がないため「サーバレスアーキテクチャ」のサービスとして分類することができます(*)。

*)ただし、実際はPaaSのアプリケーションサーバであるApp Serviceが自動的に構築され、その上で「Function App」というアプリ形態でFunctionsが構築されているため、あくまで利用者視点で見るとサーバレスであるという意味合いになります。

Azure Functionsの構成要素

Azure Functionsで作成する処理のことを「関数」と呼びます。関数は「トリガー」と「バインド」と呼ばれる2つの概念を組み合わせて処理を作成します。

トリガーは関数の処理を開始するための条件となるもので、上述したイベントがこれに該当します。関数には、必ず1つのトリガーを定義する必要があります。また関数は、処理内部でトリガーに紐付いたデータを参照することができます。例えばファイル作成イベントをトリガーとした場合は、作成されたファイルのデータやファイル名を参照できます。

バインドには入力バインドと出力バインドがあり、データの入力元と出力先の接続情報をあらかじめ定義することで、関数のコード中で入力元や出力先との接続のための実装を記述することなくデータを入出力できるようになります。 入力バインドはトリガー以外で参照したいデータがある場合に使用します。バインドは複数定義することも、省略することも可能です。

Functionsの利用イメージ

関数は、C#、F#、Node.js、Python、PHP、Batch、Bashなどの言語から選んで記述できます。ブラウザ上でコーディングしてそのまま実行やテストまで行えるため、非常に簡単に素早くやりたいことを実現することができることも魅力の一つです。

Azure Functionsの料金体系

料金体系は2種類に分かれています。1つ目の「従量課金プラン」は、関数が実行される度に課金が発生するプランです。関数の実行回数と、実行した関数が使用したメモリと実行時間を掛けたものの合計で金額が決定します。また、従量課金プランでは、リソースのスケールが自動的に行われます。大量リクエスト発生時のスケールアップ・アウトやその後のスケールダウン・インを自分で定義せずともAzureが自動的に行ってくれます。詳細な価格に関しては、Azure Functionsの価格ページを参照してください。

もう1つの「App Service プラン」は、App Serviceのリソース上でFunctionsを実行することができるプランです。そのため、App Serviceで契約したリソース(CPUやメモリやインスタンス数)の上限までに制限して関数を実行します。リソースのスケールをコントーロールしたかったり料金を一定に保ちたい場合はこちらのプランを選択します。App Service プランの価格については、App Serviceの価格ページを参照してください。

Azure Functionsでタイマー起動の関数を作る

それではここからは、Functionsで関数を作成していきます。今回は5分間隔で起動して実行時刻をコンソールに出力する関数アプリを、Functionsのクイックスタート機能を使って簡単に作成・実行できるところまで行っていきます。

Function Appの作成

まずは関数の実行環境となるFunction Appを作成していきます。 Azureポータルの左側メニューから[新規(+マーク)] - [Compute] - [Function App]と進みます。Function Appの新規作成画面が表示されたら、必須項目を入力していきます。

Function Appの新規作成

アプリ名、サブスクリプション、リソースグループの設定

アプリ名は管理しやすいよう、わかりやすい名前を設定して下さい。サブスクリプションは無料試用版、従量課金など、Azureに設定しているものを選択します。

ホスティングプラン、場所の設定

ホスティングプランについては先程説明した「従量課金プラン」と「App Service プラン」から、用途にあったプランを選択します。本記事では、サンプルを作成して動作確認をするだけですので従量課金プランを選択します。既にApp Serviceをお使いになっている場合はApp Service プランを選択しても構いません。従量課金プランを選択した場合は、次に場所(アプリが配置されるデータセンターの場所)を選択します。App Service プランを選択した場合はApp Service プランの作成時に場所が決まっているため、ここには表示されません。

Storage、Application Insightsの設定

Storageは実行した関数のログ等を保存するのに使用します。従量課金プランの場合は、作成した関数のコードも、ここで選択したストレージに保存されます。既存のストレージがあればそれを、なければ新規作成します。Application Insightsはログやリソース情報を分析するためのAzureのサービスですが、今回はオフにします。

以上を設定できたら、[作成]ボタンをクリックします。しばらくするとFunctions Appが作成されます。

タイマー起動関数の作成

Function App作成後、そのままFunction Appの画面に遷移しているかと思います。もし表示されていなかったり、別の画面に遷移してしまった場合は、Azureポータルの左側メニューから「Function App」を選択します。

クイックスタートから関数を作成

画面左側に先程作成したFunction Appの名前が表示されているので、クリックするとメニューが展開されます。その中にある「関数」という項目の右側の+マークをクリックします。すると関数作成のためのクイックスタート画面が表示されます。シナリオ(webhook+API / タイマー / データ処理)と言語(CSharp / JavaScript / FSharp)を選んで[この関数を作成する]をクリックすると、すぐに関数が作成されます。

なお、クイックスタートに表示されているシナリオについては以下の様な用途で用います。

クイックスタートに表示されているシナリオの用途

シナリオ 用途
webhook+API HTTPリクエストやWebhookをトリガーとして処理を実行したい場合
タイマー 決まった時間に定期的に処理を実行したい場合
データ処理 メッセージキューイングをトリガーとして処理を実行したい場合

クイックスタート画面

今回はシナリオに「タイマー」、言語に「CSharp」を選択して関数を作成してみます。 作成が完了すると、画面中央にコードと、画面左側の「関数」メニューの下に新たな関数(TimerTriggerCSharp1)が表示されます。

タイマートリガーの関数作成後

表示されているコード(run.csx)を見てみると、現在時刻をコンソールに出力する処理が1行だけ書かれたメソッドが定義されています。FunctionsではRunメソッドを定義し、その中に実行したい処理を記述していくルールとなっています(C#の場合)。

今回作成したタイマートリガーの関数の場合は、メソッドの第1引数にTimerInfo型の引数を設定します。この引数には、いつ関数を起動するかのスケジュール情報が含まれており、それに従って関数が実行される仕組みとなっています。 第2引数のTraceWriterはFunctionsにおけるログ出力のためのオブジェクトです。Info等のメソッドを使うことでAzureポータル上でFunctionsのログを確認することができるようになります。

スケジュール情報の確認

それではスケジュール情報がどのように定義されているかを確認してみましょう。画面左側のサイドメニューから、[統合]をクリックします。すると「タイマーtrigger」という項目があり、「タイムスタンプ パラメータ名」と「スケジュール」が表示されています。

タイムスタンプ パラメータ名は、このスケジュール情報を関数のコードが使う際の名前(変数名)になるものです。先程のRunメソッドのTimerInfo型の引数の名前が、ここで表示されているものと一致していることが分かります。 スケジュールには、トリガーが起動するタイミングがcron式で記述されています。クイックスタートでタイマー起動関数を作成した場合は、5分間隔で起動するためのcron式となっています。

なお、構文は一般的なcron式のものと同様ですが、6つの項目は「秒 分 時 日 月 曜日」で定義されている点に注意して下さい。左端が秒の定義となっており、年を定義する項目は存在しません。

スケジュール情報

なお、この画面ではその他にも関数の入力と出力となるデータやイベントを定義することができます。

関数の実行

画面左側のサイドメニューの関数名(TimerTriggerCSharp1)をクリックし、コードが表示された画面に戻ります。コードの上部に[実行]ボタンがありますので、クリックして関数を実行してみましょう。関数が実行されると、画面下部の「ログ」のエリアに関数の開始ログなどが出力されます。その中に、Runメソッド内に記述しているメッセージも含まれていることが確認できます。

5分間隔でログが出力されている

Blob Storageにデータを出力してみる

タイマー起動の関数を作成し、実行できることを確認できたら、次はログに出力していた内容をストレージにも保存してみましょう。

出力先にBlob Storageを設定する

画面左側のサイドメニューから、[統合]をクリックします。画面右上に「出力」という項目があり、その下に[新しい出力]というボタンがあるのでクリックします。すると、出力先の選択肢が表示されるので「Azure BLOB ストレージ」をクリックし、[選択]ボタンをクリックします。

出力先の選択

次にAzure BLOB ストレージの接続情報を入力します。

「BLOB パラメーター名」はコード内で出力先を扱う際の名前(変数名)です。「パス」は実際に出力されるBLOBオブジェクトのAzure BLOB ストレージ上でのパスで、「コンテナ名/BLOBオブジェクト名」で表現されます。デフォルトで設定されている{rand-guid}はバインド式と呼ばれるものの一つで、ランダムに生成したGUIDがBLOBオブジェクト名に設定されます。バインド式にはこの他にも現在時刻(UTC)を表す{DateTime}などがあります。

「ストレージアカウント接続」ではAzure Storageの接続先を指定します。既に存在するものでも、新規で作成しても構いません。上記3点の設定を行ったら、[保存]ボタンをクリックして出力先の設定を完了させます。

出力先の設定

コードの修正

最後に関数コードを修正します。画面左側から関数名(TimerTriggerCSharp1)をクリックしてrun.csxを表示します。 以下のように、Runメソッドの引数にstring型で出力先のBLOBパラメーター名を追加し、メソッド内でそのBLOBパラメーター名の変数にログメッセージを代入します。

なお、追加した引数の先頭に付いているoutはC#の修飾子で、代入された値を参照渡しで呼び出し元に返すためのキーワードです。

修正したrun.csx

 using System;

 public static void Run(TimerInfo myTimer, out string outputBlob, TraceWriter log)
 {
     // ログメッセージ
     var message = $"C# Timer trigger function executed at: {DateTime.Now}";

     // ログ出力
     log.Info(message);

     // ログメッセージをBLOBストレージに出力する
     outputBlob = message;
 }

コードを修正すると、コード上部のボタンが[保存および実行]となっているはずですのでクリックします。コードが保存されたと同時に関数が実行されます。

画面下部のログエリアを確認して、コードのコンパイル成功を意味する「Compilation succeeded.」と関数の実行が成功したことを意味する「Function completed (Success ・・・」というログが出力されていることを確認してください。

コード修正後の実行結果

ファイル出力の確認

関数実行後のログで成功が確認できたら、BLOBストレージにBLOBオブジェクトが作成されていることも確認しましょう(本連載の第2回目でAzureストレージエミュレータをインストール済の場合はそちらをお使いになっても確認できます)。Azureポータル左側のサービス一覧から「ストレージ アカウント」を選択します。(一覧に無い場合は、左下の「More services」をクリックして選択してください。)

次に関数の出力先として設定したストレージ アカウントを選択し、ストレージ アカウントのメニューから[BLOB SERVICE] - [コンテナー]をクリックします。 コンテナーの一覧から「outcontainer」という名前のコンテナーを選ぶと、BLOBオブジェクトが保存されていることが確認できるかと思います。

BLOBコンテナーにデータが出力されている

BLOBオブジェクトをダウンロードしてテキストエディタ等で中身を見ると、ログに出力されている内容と同じメッセージが記載されていることが分かるかと思います。

関数の実行を止める

本記事で作成した関数は、5分間隔で起動する関数のため動作確認ができたら関数の実行を停止するか関数を削除して、予期せぬ課金額が請求されないようにしましょう。

関数の停止、削除

関数の実行を止めるには、関数を無効にする方法と関数を削除する方法があります。関数を無効にすると、トリガーで指定した関数の起動条件が無効化されて関数が実行されることがなくなります。関数の削除はFunction Appから関数を削除して使用不能にします。一時的に関数を止めたい場合は関数を無効に、関数が不要となった場合に削除をするといった使い分けになるかと思います。

無効化と削除のどちらも、画面左側の[管理]をクリックして表示した画面で操作することができます。

関数を無効にした場合

なお関数を無効にした場合でも、コードが表示されている画面で実行ボタンをクリックすることで関数を即時実行することができます。この場合、関数は一度だけ実行されますが無効状態は継続したままになります。

Function Appでの実行制限

この他にも、Function App単位で複数の関数の実行を制限することもできます。毎日の利用上限を決めて、それを超えた場合は関数の実行を翌日まで停止することで課金額を抑えられます。利用上限値はギガバイト秒(関数の実行時に使用されるメモリサイズ(GB) * 実行時間(秒))単位で設定します。画面では、Function Appを選択して[設定]タブ内の「日ごとの使用量クォータ(GB - 秒)」から設定することができます。

Functions Appで日ごとの利用上限を設定する

まとめ

Auzre Functionsを使うと、ブラウザ上から簡単にイベント駆動のアプリケーションが作れることが分かったかと思います。クラウドの利用が一般化し、サービス間の連携が必然となっている昨今のアーキテクチャではAzure Functionsの様な「つなぎ役」をこなせる仕組みが重宝します。

次回は、Azure Functionsと同様にイベント駆動のアプリケーションを構築することができるWebJobsについて取り上げ、Azure Functionsとの違いや使い分け方について紹介する予定です。

WINGSプロジェクト 秋葉龍一著/山田祥寛監修
<WINGSプロジェクトについて>テクニカル執筆プロジェクト(代表山田祥寛)。海外記事の翻訳から、主にWeb開発分野の書籍・雑誌/Web記事の執筆、講演等を幅広く手がける。一緒に執筆をできる有志を募集中。