多分わかりやすいサーバーレスアーキテクチャ入門その3 〜 「Azure Functions」を使って、クラウドネイティブなLINE風チャットアプリを作ろう!! 〜

こんにちは、サイオステクノロジー技術部 武井です。シリーズ連載「多分わかりやすいサーバーレスアーキテクチャ入門」も今回で最後です。今回は、なんと現場ですぐ使える実践的なスキル(?)をご紹介します。

※全シリーズの記事一覧はこちら

LINE風なチャットアプリを作ってみよう

第1回目では、ハロワを返す簡単なAzure Functionsを作成してみましたが、実際のアプリケーション開発はもちろんそんな単純なものではありません。アプリケーションに最低限必要なものとして、以下の要素を挙げることが出来ます。

  • 認証
  • 認可
  • データの永続化

これらの要素を盛り込んだ「LINE風なチャットアプリ」を作ってみたいと思います。

外部仕様

チャットアプリの外部仕様は以下のとおりです。

まず、チャットアプリのURLにアクセスします。すると、Yahoo!の認証画面が表示されます。Yahoo!のIDとパスワードを入力します。

Screen Shot 2018-09-06 at 7.37.41

 

チャットの画面が表示されます。

Screen Shot 2018-09-06 at 7.40.41

画面下部にテキストボックスがあります。チャットに発言したい場合は、「メッセージ入れてね」の部分にメッセージを入れて「発言する」をクリックします。

「更新する」のボタンをクリックしない限り、相手の最新の発言が表示されません。

なんともレガシーな仕様ですみません。イマドキならWebSocketあたりでリアルタイムのチャットが出来るようにするんでしょうけど、今回はAzure Functionsの説明がメインなので、あまりチャットアプリの仕様を複雑にすると、本質から外れてしまうような気がしたので、こんな簡単な作りにさせて頂きました。

画面に表示されるアイコンはYahoo!のIDと紐づくアイコンを表示するようプログラム内でハードコーディングしてます。このあたり、実際は、別途ユーザー登録画面などがあり、プロフィール編集画面でアイコンを登録うんぬんという形になるのでしょうが、それも本質から外れると思いましたので、実装はしておりません。

ちなみこのアプリケーションの外部仕様は、私が一筆したためた記事「多分わかりやすいマイクロサービス入門 〜 マイクロサービスフレームワーク「Azure Service Fabric」でLINE風なチャットアプリを作ろう!! 〜 」(いいね!を忘れずにお願いします)で作成したアプリケーションと全く同じものでございます。

アーキテクチャ

チャットアプリのアーキテクチャをご説明致します。

Screen Shot 2018-09-06 at 7.46.56

認証は、Yahoo!のOpen ID Connect Providerの機能を利用します。Open ID Connectの詳細については、ブログ記事「多分わかりやすいOpenID Connect」(いいね!をお願いします)を参照して下さい。

画面のコンテンツについては、Angularによるシングルページアプリケーションとします。Angularによる静的コンテンツの格納先は、Azure App Serviceとしました。チャットのメッセージ取得や発言については、Angularで作成したクライアントアプリケーションから、Azure Functionsによって作成したRest APIをコールします。Angularについての詳細は、「JavaScriptフレームワーク「Angular」による掲示板システム構築 」(いいね!をしてくれると嬉しいな)をご参照下さい。

チャットのメッセージを取得したり、発言したりするAPIにはAzure Functionsを使います。

Yahoo!で認証して取得したIDトークンの検証は、AzureのAPI GatewayサービスであるAPI Managementを利用します。API Managementの詳細は、ブログ記事「AzureのAPI Gateway(API Management)を用いてOpenID Connect Providerより発行されたJWTを検証」(いいね!を・・・(´・ω・`))をご参照下さい。

データの永続化(チャットで発言したメッセージの格納)にはAzureが提供するNoSQLデータベース「CosmosDB」を使います。チャットアプリではAPIでやり取りするメッセージデータのフォーマットにJSONを用いておりますので、JOSNをそのまま登録できるCosmosDBと非常に相性がよいのです。

余談なのですが、Angularなどで作成された静的コンテンツ(jsとかHTMLとか)を格納するのは、やっぱりAzure Blob Storageかなと思いました。AWSでもS3とか使いますよね。でも、Azure Blob Storageはコンテナー配下にディレクトリが作成できませんでした。https://chat.example.com/assets/・・・というURLが実現出来ないのです。ファイル名をassets/auth.htmlというようにすれば、擬似的に実現できるとマイクロソフトのドキュメントに書いてあったのですが、仰せのとおりにすると、ファイル名がassets:auth.html(/が:(セミコロン))になってしまうんですよね(T_T)ワケ(´・ω・`)ワカ(´ε`;)リマ( ;∀;)セン(ノД`)なんだかんだしてもうまくいかなかったので、Azure App Serviceを立ち上げて、そこに静的コンテンツを格納しました。たったそれだけのためにAzure App Service立ち上げるのも重いなーと思ったのですが、解決方法わからなかったもので・・・。誰か教えて下さい(´・ω・`)

処理フロー

チャットアプリの処理フローについて説明します。

まず、チャットアプリのURLにアクセスします。静的コンテンツをホストしているAzure App Service内のコンテンツを取得することとなります。

Screen Shot 2018-09-06 at 8.26.26

 

Azure App Serviceから、Angularで作成された静的コンテンツ(チャットメッセージの一覧画面)を取得すると、チャットメッセージの一覧を取得するために、API Management経由でAzure Functionsで作成されたAPIにアクセスしようとします。しかし、未認証(=IDトークンを持っていない)ので、API Management側で401となり、Angularによって作成されたクライアントアプリケーション側で401をキャッチして、Yahoo!のAuthorizationエンドポイントへリダイレクトされます。

Screen Shot 2018-09-06 at 8.28.31

 

Yahoo!の認証画面が表示されるので、Yahoo!アカウントのIDとパスワードを入力します。

Screen Shot 2018-09-06 at 8.33.08

 

認証が完了すると、Implicitフローなので、クエリパラメータにIDトークンが付与されたコールバックURLにリダイレクトされます。

Screen Shot 2018-09-06 at 8.34.46

 

コールバックURLに記載されているJava Script内で、URLのクエリパラメターに付与されているIDトークンを取得し、クライアントPCのローカルストレージに保存します。そして、再びチャットアプリのURLにリダイレクトします。そのとき、HTTPリクエストのAuthorizationヘッダにIDトークンをセットします。

Screen Shot 2018-09-06 at 8.36.43

 

HTTPリクエストのAuthorizationヘッダに設定されたIDトークンをAPI Managementで検証します。検証を行うための鍵は、Yahoo!管理画面でアプリケーションを登録した際に取得したシークレットを、事前にAPI Managementに登録しておきます。

Screen Shot 2018-09-06 at 8.45.54

 

IDトークンの検証が完了したら、IDトークンのsubクレームに定義してあるYahoo!アカウントのユーザー識別子を、x-yahoo-uidヘッダに設定して、Azure FunctionsにHTTPリクエストを送信します。このとき、合わせて、Azure Functionsにアクセスするためのキーをcodeというクエリパラメータに付与します。

Screen Shot 2018-09-06 at 8.50.49

 

Azure Functions側でx-yahoo-uidを取得して、ユーザーの識別を行い、CosmosDBに保存されているチャットのメッセージ一覧を取得して、API Management経由でHTTPレスポンスを返します。

Screen Shot 2018-09-06 at 8.55.05

 

以降は、アクセスの都度、ローカルストレージに保存したIDトークンを取り出し、Authorizationヘッダに設定して、HTTPリクエストを行います。Screen Shot 2018-09-06 at 8.58.31

Yahoo!のOpen ID Connect Providerの設定

では、早速(てさっきから何度も言ってますが)アプリケーションの開発をしてみましょう。まずは、Yahoo!のOpenID Connect Providerの設定から始めます。

以下のURLにアクセスしてください。

https://e.developer.yahoo.co.jp/dashboard/

「新しいアプリケーションを開発」をクリックします。

Screen Shot 2018-08-02 at 23.02.01

 

「サーバーサイド」の方をチェックします。

Screen Shot 2018-08-02 at 23.02.19

 

「アプリケーション名」に任意のアプリケーション名を入力します。あとはデフォルトのままでOKです。

Screen Shot 2018-08-03 at 17.29.57

 

「同意する」をチェックして「確認」をクリックします。

Screen Shot 2018-08-02 at 23.03.07

 

入力内容に問題ないことを確認して「登録」をクリックします。

Screen Shot 2018-08-03 at 17.33.32

 

「Client ID」「シークレット」をメモっておいてください。後で使います。

Screen Shot 2018-08-03 at 17.38.09

CosmosDBの設定

チャットで発言したメッセージを格納するCosmosDBの設定をします。まず、Azureポータルにログインして下さい。

Cosmos DBには、データベース、コレクション、ドキュメントというものがあり、それぞれのものを一般的なデータベースに当てはめると以下の通りとなります。

  • データベース → データベース
  • コレクション → テーブル
  • ドキュメント → レコード

Azureのポータルを開いて、「すべてのサービス」をクリックして、「cosmos」と入力します。すると、サービスの一覧に「Azure Cosmos DB」が表示されますので、クリックします。

Screen Shot 2018-03-06 at 7.11.51

 

「+追加」をクリックします。

Screen Shot 2018-03-06 at 7.12.08

 

データベースを作成するための情報を入力します。

  • ID:任意の名称(後の接続先ホスト名となります)
  • API:SQL

後は必要に応じて、環境に適したものを入れて「作成」をクリックします。

Screen Shot 2018-09-06 at 18.07.29

 

しばらくすると、リソースグループ内にCosmosDBが出来上がります。クリックしましょう。

Screen Shot 2018-09-06 at 18.10.58

 

新しいデータベースとコレクションを作成します。「Data Explore」→「New Collection」の順にクリックします。

Screen Shot 2018-09-06 at 18.14.23

 

データベース名を「serverless-chat-db」、コレクション名を「messages」とします。その他の項目はとりあえず何も考えずに以下のように入力します。

Screen Shot 2018-09-06 at 18.16.03

これでとりあえずCosmosDBのセットアップは終わりです。

Azure Functionsの設定

次はあじゅーるふぁんくしょんずです。

Azureポータルにログインして、「リソースの作成」をクリックして、「function」と入力してフィルターすると「Function App」が出てきますので、クリックします。

Screen Shot 2018-08-03 at 8.10.40

 

「作成」をクリックします。

Screen Shot 2018-08-03 at 8.10.52

 

「アプリ名」は任意の名称、「サブスリプション」「リソースグループ」は環境に応じたものを、「Storage」はデフォルトで入っていたものそのまま、他は以下のように入力して、「作成」をクリックします。

Screen Shot 2018-09-06 at 18.24.14

 

しばらくして、リソースグループに移動すると、以下のようにAzure Functionsが出来上がっています。カミナリマークのアイコンをクリックして下さい。

Screen Shot 2018-09-06 at 18.27.42

 

「+」のアイコンをクリックして下さい。まずチャットのメッセージ一覧を取得するAPIを作成します。

Screen Shot 2018-09-06 at 18.32.12

 

「カスタム関数」をクリックします。

Screen Shot 2018-09-06 at 18.33.49

 

「HTTP trigger」の「Java Script」をクリックします。

Screen Shot 2018-09-06 at 18.34.51

 

「名前」の部分に「getMessageList」と入力し、その他の部分は以下の通りデフォルトのままで「作成」をクリックします。

Screen Shot 2018-09-06 at 18.36.05

 

CosmosDBからデータを取得出来るように設定します。「統合」→「+新しい入力」→「Azure Cosmos DB」→「選択」の順にクリックします。

Screen Shot 2018-09-06 at 18.47.41

 

「新規」をクリックします。

Screen Shot 2018-09-06 at 18.51.32

 

「Azure Cosmos DB アカウント」を選択して、先ほどCosmos DBを作成したサブスクリプションとデータベースアカウントを選択して、「選択」をクリックします。

Screen Shot 2018-09-06 at 18.52.27

 

データベース名及びコレクション名は、先程作成したCosmos DBのものを入力します。「SQL クエリ」は「SELECT * FROM c order by c.pubDate asc」と入力します。この設定をしますと、このAzure Functionsが呼ばれたときに、以下に定義したCosmos DBに対して「SELECT * FROM c order by c.pubDate asc」のクエリを発行した結果が、inputDocumentオブジェクトに入ってきますよと言うことを意味します。ということで、最後に「保存」をクリックします。

Screen Shot 2018-09-06 at 18.54.23

 

次に、「トリガー」の「HTTP(req)」をクリックします。「許可されているHTTPメソッド」をクリックして、「選択したHTTPメソッド」で「GET」のみをチェックして、「保存」をクリックします。リクエストをGETメソッドだけに絞ります。

Screen Shot 2018-09-06 at 19.00.15

 

「getMessageList」をクリックします。右側にコードが書いてあると思います。

Screen Shot 2018-09-06 at 19.03.31

 

以下のコードを入力して、「保存」をクリックします。

 

今度は、チャットのメッセージを保存するAPIを作成します。「+」のアイコンをクリックして下さい。

Screen Shot 2018-09-06 at 18.32.12

 

「HTTP trigger」の「Java Script」をクリックします。

Screen Shot 2018-09-06 at 18.34.51

 

「名前」の部分に「addMessage」と入力し、その他の部分は以下の通りデフォルトのままで「作成」をクリックします。

Screen Shot 2018-09-06 at 19.10.16

 

「addMessage」関数の「統合」→「トリガー」→「HTTP(req)」の順にクリックして、「許可されているHTTPメソッド」で「Selected methods」を選択します。「選択したHTTPメソッド」で「POST」のみをチェックして、「保存」をクリックします。リクエストをPOSTメソッドだけに絞ります。

Screen Shot 2018-09-06 at 19.11.47

 

次に、Azure FunctionsからDocument DBにデータを書き込めるようにします。「+新しい出力」→「Azure Cosmos DB」の順にクリックして、最後に「選択」をクリックします。

Screen Shot 2018-09-06 at 19.16.28

 

「新規」をクリックします。

Screen Shot 2018-09-06 at 19.18.41

 

「Azure Cosmos DB アカウント」を選択して、先ほどCosmos DBを作成したサブスクリプションとデータベースアカウントを選択して、「選択」をクリックします。

Screen Shot 2018-09-06 at 19.21.41

 

データベース名及びコレクション名は、先程作成したCosmos DBのものを入力します。他はそのままにして「保存」をクリックします。

Screen Shot 2018-09-06 at 19.23.31

 

「addMessage」をクリックします。右側にコードが書いてあると思います。

Screen Shot 2018-09-06 at 19.45.26

以下のコードを入力して、「保存」をクリックします。

以上で、Azure Functionsの設定はおしまいです。

API Managementの設定

Azureポータルにアクセスして、「すべてのサービス」をクリックして、下記のように「api」と入力すると、「API Managementサービス」が表示されますので、クリックします。

Screen Shot 2018-08-02 at 23.14.34

 

「追加」をクリックします。

Screen Shot 2018-08-02 at 23.15.03

 

「名前」「組織名」に任意の名称、「サブスリプション」「リソースグループ」「場所」は環境に応じた値を、「管理者のメールアドレス」はAPI Managementサービスの作成が完了したときに通知されるメールアドレスを、「価格レベル」は「開発者(いいえ SLA)」を選択して、「作成」をクリックします。しばらくするとAPI Managementのサービスが作成されます。

Screen Shot 2018-09-06 at 19.52.15

 

そして、これからこのAPI Gatewayの背後に配置するAPIを設定します。それが先程Azure Functionsで作成したものになるわけですが、その前にもう一つやることがあります。API ManagementにAPIを配置する前にAPIの定義を作成しなければなりません。これはOpen APIという仕様で定義されているのですが、このAPIがどのようなMethod(Post or Get or …)やどのようなパラメータを取るかを定義したものです。この定義を作成するために一旦、先程のAzure Functionsの画面に戻ります。以下の画面の右下の方にある「APIの定義」をクリックしてください。

Screen Shot 2018-09-06 at 19.56.43

 

関数(プレビュー)をクリックします。

Screen Shot 2018-09-06 at 19.58.00

 

「API定義テンプレートを生成する」→「保存」の順にクリックします。

Screen Shot 2018-09-06 at 19.59.20

 

では、API Managementの画面に戻りましょう。以下の画面で「API」をクリックします。

Screen Shot 2018-09-06 at 21.31.15

 

API Managementの背後に、先程作成したAzure Functionsを配置したいので「Function App」をクリックします。

Screen Shot 2018-08-03 at 18.12.55

 

「Browse」をクリックします。

Screen Shot 2018-09-06 at 21.33.28

 

「必要な設定の構成」をクリックします。

Screen Shot 2018-09-06 at 21.36.39

 

インポートしたいAzure Functionsを選択して、「選択」をクリックします。

Screen Shot 2018-09-06 at 21.37.43

 

「addMessage」「getMessageList」の2つがチェックが付いていることを確認した上で、「選択」をクリックします。

Screen Shot 2018-09-06 at 21.38.56

 

「Create」をクリックします。これでAzure Functionsが、API Managementのバックエンドとして配置されました。

Screen Shot 2018-09-06 at 21.40.03

 

これからは、細かいポリシーを制御していきます。「All APIs」の「Inbound Processing」の「</>」をクリックします。

Screen Shot 2018-09-06 at 21.41.41

 

以下のような画面が開きます。

Screen Shot 2018-09-06 at 22.03.27

 

先の画面で、以下のポリシーを入力して「Save」をクリックします。

 

最後にもう一つ手順があります。「af-chat」→「Settings」の順にクリックします。ここで、「Base URL」をメモしておきます。また「Products」に「Unlimited」を選択して、「Save」をクリックします。

Screen Shot 2018-09-06 at 22.07.44

 

次に「Test」のタブをクリックします。「Headers」のところに「Ocp-Apim-Subscription-Key」テキストボックスがあります。その隣のテキストボックスに目玉のマークみたいなものがあるのでそれをクリックすると、コードのようなものが表示されます。それを大事にメモしておきます。

Screen Shot 2018-09-06 at 22.10.51

Azure App Serviceの設定

次にAngularで作成したクライアントアプリケーションを配置するためのAzure App Serviceを設定します。Azureポータルにログインして、「すべてのサービス」をクリックし、検索テキストボックスに「app service」を入力すると以下のように表示されます。「App Service」をクリックします。

Screen Shot 2018-09-06 at 20.28.15

 

「追加」をクリックします。

Screen Shot 2018-09-06 at 20.30.18

 

「Web App」をクリックします。

Screen Shot 2018-09-06 at 20.31.10

 

「作成」をクリックします。

Screen Shot 2018-09-06 at 20.31.58

 

「アプリ名」は任意の名称、「サブスクリプション」「リソースグループ」は環境に応じたもの、「OS」は「Windows」、あとはデフォルトのままで「作成」をクリックします。

Screen Shot 2018-09-06 at 21.22.05

 

しばらくすると、リソースグループ内にApp Serviceが出来上がっております。地球のマークのアイコンをクリックして下さい。

Screen Shot 2018-09-06 at 20.53.03

 

「URL」を大切にメモして保存しておいて下さい。これは、後にこのApp Service内に保存する静的コンテンツにアクセスするためのURLになります。

Screen Shot 2018-09-06 at 20.54.37

 

クライアントアプリケーションをビルドします。ソースコードは以下のGitHubにありますので、Cloneして下さい。Angularで作成しております。もう何度言っているかわからなくなってしまいましたが、Angularについての詳細は、「JavaScriptフレームワーク「Angular」による掲示板システム構築 」(いいね!を忘れずに)をご参照下さい。

https://github.com/noriyukitakei/af-sample-chat

3箇所修正する箇所があります。src/app/chat.component.tsを開いて下さい。

まず上記コード11行目のLOGIN_URLです。こちらにはYahoo!のAuthorizationエンドポイントを以下のように設定します。

https://auth.login.yahoo.co.jp/yconnect/v2/authorization?client_id=[Yahoo!のアプリケーション登録時のクライアントID]&response_type=id_token&redirect_uri=[先程メモしたAzure App ServiceのURL]/assets/auth.html&scope=openid&state=hoge&nonce=hoge

※stateとnonceは適当です(´・ω・`)

[Yahoo!のアプリケーション登録時のクライアントID]は、「Yahoo!のOpen ID Connect Providerの設定」のところで、アプリケーションを登録したときに表示されたクライアントIDになります。 [先程メモしたAzure App ServiceのURL]は、先程大切にメモしたAzure App ServiceのURLになります。

次に、12行目のAPIのURLになります。こちらは、API Managementの設定でメモしたBase URLの値を設定します。

最後に13行目ですが、API Managementの設定でメモしたOcp-Apim-Subscription-Keyを定義します。

さて、ではソースコードをビルドします。その前にビルド環境を構築しましょう。ビルドにはnode.jsが必要になります。

まず、node.jsのバージョンを管理するためのツールnodebrewをインストールします。

$ curl -L git.o/nodebrew | perl - setup
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile

次に、node.jsをインストールします。

$ nodebrew install-binary stable

 

こうすると、最新の安定バージョンがインストールできます。インストールされたバージョンを確認してみます。

$ nodebrew ls
v9.2.1

 

今は9.2.1がstableのようです。では、このバージョンのnode.jsを使いたいので、以下のように入力します。

$ nodebrew use v9.2.1

 

これでバージョン9.2.1が使えるようになりました。次にAngularで作成したアプリケーションの実行やビルドを行うためのコマンドをインストールします。

$ npm install -g @angular/cli

 

Cloneしたソースコードのトップディレクトリに移動して、以下のコマンドを実行します。

$ ng build

するとdistというディレクトが出来上がり、中に何やら色々なファイルが入っています。これがビルドの成果物になります。後で使いますので、この場所を覚えておいて下さい。

 

Azure App Serviceの画面に戻ります。「高度なツール」→「移動」の順にクリックします。

Screen Shot 2018-09-06 at 21.24.17

 

「Debug Console」→「PowerShell」の順にクリックします。

Screen Shot 2018-09-06 at 21.25.08

 

D:\home\site\wwwrootに移動して、先程ビルドしたdist配下に出力されたファイルを全て、以下のコンソールにドラッグアンドドロップします。

Screen Shot 2018-09-06 at 21.26.22

コールバックURLの定義

Yahoo!で認証したが完了したあとに遷移するコールバックURLを定義します。AuthorizationエンドポイントのURLを以下のように定義したと思います。

https://auth.login.yahoo.co.jp/yconnect/v2/authorization?client_id=[Yahoo!のアプリケーション登録時のクライアントID]&response_type=id_token&redirect_uri=[先程メモしたAzure App ServiceのURL]/assets/auth.html&scope=openid&state=hoge&nonce=hoge

このURLのredirect_uriに定義しているURLがコールバックURLなのですが、これはYahoo!のアプリケーション管理画面であらかじめ定義したものと一致していないと、リダイレクトミスマッチでエラーとなってしまいます。

ということで設定しましょう。

以下のURLにアクセスしてください。

https://e.developer.yahoo.co.jp/dashboard/

アプリケーション一覧の中から、先程作成したアプリケーションをクリックして下さい。

Screen Shot 2018-09-07 at 7.37.14

 

コールバックURLに、以下のように入力して、「更新」をクリックします。

[先程メモしたAzure App ServiceのURL]/assets/auth.html

Screen Shot 2018-09-07 at 7.44.58

これでチャットアプリケーションを動かす準備は完了です。

動かしてみる

では、Azure App ServiceのURLにアクセスしてみましょう。

Yahoo!の認証画面が表示されます。Yahoo!のIDとパスワードを入力します。

Screen Shot 2018-09-06 at 7.37.41

 

チャットの画面が表示されます。おおー(๑•̀ㅂ•́)و✧

Screen Shot 2018-09-06 at 7.40.41

最後に

いかがでしたでしょうか?本シリーズの連載は今回で終了になります。本記事が、サーバーレスアーキテクチャ導入の際の参考になれば幸いです。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

3人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です