himanago

Azure・C#などのMS系技術やLINE関連技術など、好きな技術について書くブログ

Azureサーバーレス&LINE API フル活用のシステム事例紹介!というタイトルで登壇しました&しゃべりきれなかった部分を補足します

はじめに

5/28(木)に、「クラウドが得意なLINE API Expert集合!サーバレス×LINEでアプリ開発してみたLT」というイベントでお話ししました。

linedevelopercommunity.connpass.com

時間 15 分で Azure サーバーレス × LINE API のおもしろさを、実例を交えてお伝えするという内容でしたが、時間も短く話したりない部分もあったので、補足込みで記事にします。

概要

「Azureサーバーレス&LINE APIフル活用のシステム事例紹介!」というタイトルでお話ししました。

ちょっと盛りすぎなタイトルですが、実際に業務で開発しているシステムの中で Azure サーバーレステクノロジーを活かしている機能がいくつかあったので、(ちょっとデフォルメして)紹介するという内容です。

Azure のサーバーレス

Azure の中でサーバーレスというと、たくさんのサービスが該当しますが、今回はこの 3 つを中心にまとめました。

f:id:himanago:20200530030545p:plain

サーバーレスとは「サーバー管理を意識する必要がない」という意味で、イベント駆動で動き、動いた分だけの課金体系という特徴を持ちます。Azure でこの特徴を持つ代表格は Azure Functions です。

Azure Functions

Azure Functions はサーバーレスなコード実行環境で、関数コードをデプロイ(または Web のポータル上で直接記述)するとすぐにイベント駆動で動作する関数が手に入ります。

さまざまな言語に対応し、デフォルトでも C# / JavaScript (TypeScript) / F# / Java / PowerShell / Python という多くの言語がサポートされているほか、(現在プレビューですが)カスタムハンドラーという仕組みを使えば、任意の言語で開発したプログラムを裏に置いて使うことができるので、事実上どんな言語でも Azure Functions を利用することができます。

Azure Functions でサポートされている言語 | Microsoft Docs

Azure Functions のカスタム ハンドラー (プレビュー) | Microsoft Docs

そのほか、豊富なトリガーと入出力バインディングが魅力的です。

HTTPトリガーや BLOB トリガーなど、イベントに応じて起動する設定がいろいろとできます。LINE API と組み合わせて使用することの多い HTTP トリガーも、Azure Functions 単体で Webhook をさばくことができるのもお手軽で使いやすい点です。

それと同時に、DB からの読み取り(入力)や DB への書き込み(出力)などの入出力バインディングを簡単に設定することができ、関数を使ってさまざまなデータのやりとりを行うことができます。

サーバーレスなコード実行環境としての Azure Functions は、通常「従量課金プラン」と呼ばれ、使われていない間は停止しています。そこから関数を呼び出して動作が始まるまでに時間がかかってしまうというコールドスタートの問題があるため、LINE Bot のリプライなど、即返信する必要のあるものでは向かないことがあります。

これに対する対策としては、常時稼働するインスタンスを用いる「App Service プラン」や、イベントに応じたスケールに対応する従量課金プラン同様のサーバーレスらしさを持ちながらコールドスタートのない「Premium プラン」を使うこともできます。料金はそれなりにかかってしまいますが、ユースケースによっては絶大な威力を発揮します。

Azure Cosmos DB

Azure における NoSQL の DB である Cosmos DB は、惑星規模のアプリケーションにも対応する大規模向けな DB として登場しましたが、さまざまな機能追加、特に今年登場した Free Tier (無料枠)の登場で小規模サービスや個人開発でも使いやすくなりました。

API (DB の形式)も選ぶことができ、とても使いやすいサービスです(自分は SQLを使ってデータ検索できるドキュメント型の「コア (SQL)」をよく使います)。

サーバーレスへの対応も進んでいて、2020年5月に「Serverless pricing」が発表されています。

Azure Cosmos DB で任意のサイズまたはスケールのアプリを構築 | Azure のブログと更新プログラム | Microsoft Azure

Azure SignalR Service

こちらはクライアントをリアルタイム更新する WebSocket のためのサービスです。出力バインディングを使うことで Functions とも簡単に統合できます。

WebSocket は、Web ページなどの更新をクライアント側の再取得操作に頼らずに、サーバー側のアクションを起点としてクライアント側の画面を更新できる技術です。もともと ASP.NET に SignalR という WebSocket 対応の機能があり、それの Azure 版といったところでしょうか。

Functions と組み合わせて使えば、簡単にサーバーレスでリアルタイムにクライアントに更新を通知できる機能を実現できます。

Azureサーバーレスフル活用のシステム事例 with LINE API

今回紹介したのは、以下の特徴をもったシステムです。

f:id:himanago:20200530030601p:plain

  • 自動応答(自然言語処理によりユーザーの意図を読み取り、数往復の対話フローで自動で処理)
  • 手動応答(運営担当者のマニュアル返信による個別トーク

の 2 つの応答モードを持ち、それをユーザーごとに切り替えることができます。

応答モード

応答モードといえば、LINE Bot / LINE 公式アカウントの標準でもサポートされています。

f:id:himanago:20200530030627p:plain

ユーザー(Bot の友だち)とチャットをするか、Bot による自動応答をさせるか選べる機能ですが、この機能では Bot / 公式アカウント単位での 切り替えしかできず、ある一人のユーザーと「チャットモード」で会話したい、と思って切り替えると、その他のユーザーが自動返信による Bot の機能が使えなくなってしまいます。

この弱点を克服するため、今回のシステムではユーザーごとの応答モードの切り替えを実装しています。

リアルタイムチャット

まず先に、マニュアル返信による手動応答(個別トーク)の実現方法を説明します。

運営・管理側には以下のような Web 画面を用意します。

f:id:himanago:20200530030656p:plain

この画面でユーザーとチャットを行いますが、チャットなので画面はリアルタイムに更新されてほしいところ。SignalR の出番です。

さらに、組み合わせるのは Cosmos DB の Change Feed と呼ばれる機能です。この機能は、Cosmos DB へのデータの変更を検知し、それを順番に次のイベントに伝えてくれる機能です。

以下の図のように、Change Feed は Cosmos DB トリガーとして Functions を起動するイベントとしてつながり、その Functions に出力バインドされた SignalR Service がクライアントにいあるタイムで更新を通知します。

f:id:himanago:20200530030719p:plain

今回のシステムでいえば、Bot からのメッセージが Cosmos DB に格納されると、Change Feed により Functions が動き、SignalR で Web サイトをリアルタイム更新します。

f:id:himanago:20200530030731p:plain

また、Web 画面から LINE へも同じ Change Feed / Cosmos DB トリガーを共通で利用しており、起動した関数から Messaging API の Push API でメッセージを送信します(Reply API ではなく Push API なのは、返信時にはリプライトークンの有効期限が切れてしまっているため)。

f:id:himanago:20200530030744p:plain

Web → LINE では、個別の担当者からのメッセージということがわかりやすくなるよう、Messaging API の icon / nickname switch API を使い、担当者のアイコンと名前を表示するようにしています。

ニュース: アイコンおよび表示名が変更できるようになりました | LINE Developers

自動応答部分

自動応答部分は自作のAzure Functions + C# の LINE Botフレームワークを使っています。bot-expressBot Framework のように会話コンテキストを記憶したうえでの対話フローを通してユーザーの課題解決を行う機能を「スキル」という単位で追加していける仕組みを作りました。

f:id:himanago:20200530030824p:plain

Azure には Bot Framework が動作するマネージドな Bot Service というのがあるのですが、今回のシステムでは Functions ベースで組みたかったので、「スキル」単位で対話フローの処理を開発していける簡易版のフレームワークを自作した、という経緯です。

ちなみに、実装がちょっと気に食わない部分があるので、個人として OSS で作り直そうかなと思っていたりします(時期など予定は全く決まっていませんが)。

Messaging API と LIFF の連携

対話フローの中で LIFF を併用したいことは多くあると思います。

チャットボットでは、通常対話を通して Bot 側に必要な情報を与えますが、チャットのやりとりなのでたくさんの種類の情報をインプットするのは一苦労です。

その点、通常の Web 画面のフォームなら、一気に入力欄を埋めて送信するだけで済むので、チャットボットにおいてもフォーム入力の併用が求められる場面は多くなります。

LINE Bot においては、LINE 内 に埋め込むことのできる Web アプリである LIFF(LINE Front-end Framework)を組み合わせることになりますが、チャットボット部分の Messaging API と LIFF をあわせて使う際、タイミングや情報の連携ために工夫が必要なときがあります。

たとえば、Messaging API で対話をしている途中で LIFF を使った入力を挟み、同じ対話フローでその LIFF の入力内容を使った対話を続けていく、というような場面です。

ユーザーID (Messaging API と LIFF では、同一プロバイダ内であれば同じ LINE ユーザー ID が振られます)をキーにして DB に登録したりしていけばよいのですが、ここを、Durable Functions を使って簡略化するアイディアを紹介します。

Durable Functions とは、「スターター(クライアント)」「オーケストレーター」「アクティビティ」といった関数を組み合わせ、サーバーレスでありながらステートフルな処理を実現する、Azure Functions の拡張機能です。

f:id:himanago:20200530030922p:plain

今回使うのは Durable Functions で利用できる「外部イベントの待機」機能です。

オーケストレーター関数は、外部イベントを待機して処理を中断することができます(関数としては実行されたままになるわけではなく、何度も再実行されてイベントの発生を監視している)。その間に、別の関数からイベントを発生させると中断していたオーケストレーターが再開します。

これを、Messaging API と LIFF の情報連携・待ち合わせに利用します。

f:id:himanago:20200530031239p:plain

  1. ユーザーから Bot に対して話しかけ、対話フローを開始
  2. HTTPトリガーの関数が起動。オーケストレーターをLINEユーザーIDをインスタンスIDとして起動し、イベントを待機
  3. 同じHTTPトリガーの関数からユーザーへLIFFアプリへのリンクを返信
  4. ユーザーはLIFFのフォームにデータを入力して送信(別のHTTPトリガーの関数にPOST)
  5. DBへの登録処理などを行い、イベントを発生させる(LINEユーザーIDとイベント名を指定)
  6. 同一LINEユーザーIDで待機していたオーケストレーターがイベントの発生を検知し、処理が再開される
  7. 再開したオーケストレーターがアクティビティ関数を呼び、LIFFの入力を前提にした処理を実行

ポイントはオーケストレーターの「インスタンスID」で、これに LINE ユーザー ID を使うことで、Messaging API と LIFF という異なる API 間での連携を容易にしている点です。

同一ユーザーであれば、LINE API では同じユーザー ID 文字列が振られているため、Durable Functions との相性がとてもいいのです。

もちろん、Durable Functions を使わなくても Messaging API と LIFF の連携は実装はできるので、無理して Durable Functions を使う必要も実はないと思います。実運用するうえでは扱いがちょっと難しいですし、関数を再実行ありきで設計するのも難しいです(何度も再実行される関数なので、意図しない再実行による二重処理などを防ぐため、冪等性を意識した関数設計が大切です。オーケストレーターは必須で、アクティビティはできれば。)。

また、CI/CD などをする場合にも、オーケストレーション実行中のデプロイには気を配る必要があります(以下のドキュメントが参考になります)。

Durable Functions のためのゼロダウンタイムのデプロイ | Microsoft Docs

なお、Durable Functions は現時点で C# / JavaScript / F# のみの対応ですが、Python のサポートがもう少しで登場しそう…?な予感です(以下参照)。

GitHub - Azure/azure-functions-durable-python: Python library for using the Durable Functions bindings.

(追記)
でました。
Durable Functions で Python がサポート対象に | Azure の更新情報 | Microsoft Azure

ユーザーごとの応答モード

ユーザーごとに応答モードを切り替える部分にも、Durable Functions を使っています。2.0 で追加された新機能、エンティティ関数(Durable Entities)です。

f:id:himanago:20200530031429p:plain

ここでも、エンティティ ID に LINE ユーザー ID を用いることで応答モードを使いやすくしています(Messaging API からでも LIFF からでも応答モードを参照・更新できる)。

セッションでは触れられませんでしたが、エンティティ関数はただ状態を持つだけではなく、それをもとにした操作も簡単に定義できるところが魅力です。

.NET での持続エンティティに関する開発者ガイド - Azure Functions | Microsoft Docs

応答モードのような単純なフラグ値なら DB に書いても別にいいのでは?と思うかもしれませんが、その状態をもとにした操作を定義したい場合など、エンティティ関数で書くと驚くほどシンプルに済むことがあります。

先に述べたように注意点も多く使い過ぎは禁物だと思いますが、Durable Functions はコードのみでステートフルな処理を実現できる点が非常に強力です。

その他

運営・管理側のチャット画面や LIFF など、静的 Web サイトを使っている部分がありますが、Azure の場合は大きく 3 つの実現方法があります。

  • Blob Storage:静的サイトホスティング機能を利用
  • Web Apps:Node.js アプリとして配備
  • Static Web Apps:2020年5月の Build で発表された新サービス。プレビューですが GitHub から簡単に静的サイトがホストできる&Functions の統合も可能

Static Web Apps が非常に強力で、今後かなり使われるようになるのではと思っています。

Azure Static Web Apps – App service | Microsoft Azure

ちなみに、今回のシステムでは通常の Web App として動かしています。

まとめ

Azure Functionsを軸にイベント駆動でサービスが簡単につながる

Cosmos DB → Functions → SignalR を例にお話ししましたが、イベント駆動がサーバーレスの醍醐味。

簡単な設定をするだけでサービスがつながって画面表示に反映されるのは感動ものです。

ステートフルな Durable Functions は LINE API と相性抜群

Durable Functions を使った機能を 2 つ紹介しましたが、LINE ユーザー ID が全 LINE API で共通であるという強みがとても活かしやすく、相性がいいです。

この特徴をうまく使えば、Azure × LINE でもっとおもしろいサービスを作ることができるのでは…と思っています。

Azure サーバーレス × LINE API はいいぞ

Azure サーバーレスと LINE API の相性はよいですし、サーバーレスも LINE API もどちらも手軽に始められるよさがあります。

小規模・個人でも始めやすく、大規模にも対応できるものなので、いろいろな人に本当におすすめです。

Azure を使うと、今回紹介したような機能を用いることで、新しいことができる可能性がたくさんあり、特におすすめです。ぜひ、触ってみてほしいなと思います。

スライドはこちら

www.slideshare.net

感想など

コロナでばたばたしててすっかり忘れてましたが、そういえば LINE API Expert になってから初登壇。

オンライン登壇も初だったのですが、オンラインのみの登壇は難しいですね。。反応がないのでわかりにくく、アドリブもしにくい。

開発者/非開発者の比率もわかっておらず、話しはじめてから準備段階でオーディエンスが意識できなかったのを思い出し、話しながら戸惑ってしまいました。アドリブを効かせていいかんじに乗り切る…ができないなと思ったので、話すことはきちんと事前に用意したほうがいいなと思いました。

おまけ

なんと、今回紹介したシステムを、Hiro さん (@mofumofu_dance) がすぐさま真似して自動応答と手動応答の Bot を作ってくれました!しかも、Power Platform で!

Power Apps で LINE とやりとりしてる…感動。

Twitter にも書いたけど、当日中にすぐ形にしてもらえるなんて、本当にうれしすぎます。。。ありがとうございます。。

その他、セッション中もたくさん Twitter で反応いただきました。

クラウドが得意なLINE API Expert集合!サーバレス×LINEでアプリ開発してみたLT まとめ #linedc - Togetter

Azure と LINE API の魅力が少しでも伝わっていたらうれしいです。ありがとうございました!