GCP を使って名刺管理ボットを本気で作ってみた

2023-08-15

アプリケーションの Google Kubernetes Engine への移行方法

Cloud Functions と Web App Bot から GKE へそれぞれアプリを移行する方法を説明します。

アプリを GKE 上で動かすためアプリをコンテナ化する必要がありますが、それは一般的な手順ですのでその説明は省きます。ここではプログラムの変更方法について説明します。

Cloud Functions からの移行

Cloud Functions で動いていたプログラムを Node.js のフレームワークである Express を使った Web サービスに作り替えます。

環境変数の設定

プログラムを修正する前に Web App Bot の実装のところで説明した GCP のサービスアカウントキーファイルをコンテナ内に配置し、環境変数 GOOGLE_APPLICATION_CREDENTIALS にそのパスを設定する必要があります。サービスアカウントキーファイルを実際にコンテナに格納するよりは GKE のシークレットにサービスアカウントキーファイルの中身を定義してボリュームにマウントして使用するとその後のメンテナンスは楽だと思います。

GOOGLE_APPLICATION_CREDENTIALS を設定することで GKE 上に配置したアプリから GCP のサービスを Cloud Functions から呼び出すのと同様に呼び出せるようになります。

なお、使用するサービスアカウントはアプリの中で使用している GCP のサービスによってロールを設定する必要がある事に留意してください。

プログラムの修正

Dialogflow の Fullfillment にあるインラインエディタのソースコードをベースに Webhook を作成した場合、プログラムは以下の様になっていると思います。

const functions = require('firebase-functions');
 …
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
 …
 [ロジック]
 …
});

Dialogflow Webhook は Firebase Functions として生成されていますが、Firebase である必要はないので firebase-functions は npm uninstall してしまいます。
代わりに express を npm install して以下の様に書き換えます。

const express = require('express');
const app = express();
app.use(express.urlencoded({extended: true}));
app.use(express.json());
 …
app.get('/', function(req, res) {
    res.send("I'm Mr.Mee!");
});
app.post('/', (request, response) => {
 …
 [ロジック]
 …
});

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`App listening on port ${PORT}`);
  console.log('Press Ctrl+C to quit.');
});
module.exports = app;

Express アプリケーションとしてリクエストを受け取る為の設定を行います。Cloud Functions に実装されていたロジックはそのまま POST メソッド内に記述します。GET メソッドは GKE 上でのヘルスチェック用です。

次に package.json の scripts から firebase 用の記述を取り除き以下の様に修正します。

"scripts": {
    "start": "firebase serve --only functions:dialogflowFirebaseFulfillment",
    "deploy": "firebase deploy --only functions:dialogflowFirebaseFulfillment"
  },
"scripts": {
    "start": "node index.js"
  },

また、GKE に移行した際に Basic 認証を行うようにしました。Dialogflow の Fullfillment の設定でユーザ名とパスワードを設定できます。

Dialogflow Fulfillment

プログラムの実装は以下の通りです。

const auth = require('basic-auth');
const admins = {
    [ユーザ名]: { password: [パスワード] }
};
 ・
 ・
 ・
const user = auth(request);
if (!user || !admins[user.name] || admins[user.name].password !== user.pass) {
    response.set('WWW-Authenticate', 'Basic realm="[アプリ名等]"');
    return response.status(401).send();
}

Web App Bot からの移行

Web App Bot で動いていたプログラムを GKE に移行します。

ボットソースコードのダウンロード

先ずは Azure 側のボットのソースコードをダウンロードします。
これは 「ボット」 > 「ビルド」 > 「ボットのソース コードをダウンロードする」 から zip 形式で取得できます。ボットのアプリ設定を含めるかと聞かれますが、 これはどちらでも良さそうです。今回の名刺管理ボットではダウンロードされたものに差異はありませんでした。

zip を適当なフォルダーに展開して、実行に必要な Node.js のモジュールを取得します。
ソースを展開したフォルダーで以下を実行してください。

npm install

実行するとライブラリが古くて脆弱性があるというメッセージが出力されると思うので以下を実行します。

npm audit fix

プログラムの修正

プログラムはほぼ修正は不要です。必須の修正ではないですが、ポートと URI を変更した程度です。後は GKE 上でのヘルスチェック用に GET メソッドを追加しました。

  ・
  ・
  ・
// Create HTTP server.
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function() {
    console.log(`\n${ server.name } listening to ${ server.url }`);
    console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
    console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});

// Listen for incoming requests.
server.post('/api/messages', (req, res) => {
    adapter.processActivity(req, res, async (context) => {
        await bot.run(context);
    });
});
  ・
  ・
  ・
// Create HTTP server.
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 8080, function() {
    console.log(`\n${ server.name } listening to ${ server.url }`);
});

// Listen for incoming requests.
server.get('*', (req, res) => {
    res.send('Hello Mee-Shi Bot!');
});

server.post('*', (req, res) => {
    adapter.processActivity(req, res, async (context) => {
        await bot.run(context);
    });
});

Azure 「ボットチャンネル登録(Bot Channel Registration)」を作成

ボットチャンネル登録(以降、チャンネル)は独自に作成したボットを Azure AD と認証する為に必要です。今回作成した名刺管理ボットは Office 365 のリソースにアクセスする機能もある為、このチャンネルを経由して呼び出します。

この手順の中で、GKE に移行したチャットボットアプリの設定で必要となる MicrosoftAppId、MicrosoftAppPassword、ConnectionName を生成します。

チャンネルの作成

Azure ポータルのホームから「 リソースの作成 > Bot Channels Registration 」と進みます。
以下を参考にチャンネルを登録します。

 ボット ハンドル:任意
 サブスクリプション:契約しているサブスクリプション
 リソースグループ:任意
 場所:日本
 価格レベル:F0(無料プラン)
 メッセージングエンドポイント: GKE へ移行したチャットボットアプリの関数の URL
 Application Insights:任意
 Microsoft アプリ ID とパスワード:アプリ ID とパスワードの自動作成

アプリ ID は作成したチャンネルの「設定」で確認出来ます。
またパスワードは、その「設定」のアプリ ID の脇にある「管理」リンクをクリックして「証明書とシークレット」へ移動して生成することが出来ます。移動先で「新しいクライアント シークレット」をクリックしてパスワードを生成します。

ここで作成したアプリ ID とパスワードはボット側の設定に使用します。

Id プロバイダの作成

Azure ポータルのホームから「 Azure Active Directory > アプリの登録 」と進みます。
以下を参考にアプリを登録します。

 名前:任意
 サポートされているアカウントの種類:「任意の組織ディレクトリ内のアカウント (任意の Azure AD ディレクトリ – マルチテナント) と個人の Microsoft アカウント (Skype、Xbox など)」
 リダイレクト URI:https://token.botframework.com/.auth/web/redirect

作成した id プロバイダのパスワード(シークレット)を作成し、アプリ ID、パスワード、テナント ID をメモします。次の接続設定で使用します。

Id プロバイダー接続を構成する

先程作成したチャンネルに戻り「設定」から「 OAuth 接続設定 > 設定の追加」をクリックします。
以下を参考に接続設定を行います。

 名前:任意 ※ ボット側の設定に使用します。ここでは BotTeamsAuthADv1 とします。
 サービス プロバイダ:Azure Active Directory
 Client id:先程作成した id プロバイダのアプリケーション (クライアント) ID
 Client Secret:先程作成した id プロバイダのシークレット
 Grant Type:authorization_code
 Login URL:https://login.microsoftonline.com
 Tenant ID:先程作成した id プロバイダのテナント ID
 Resource URL:https://graph.microsoft.com/
 スコープ:未入力

環境変数 .env ファイルの修正

環境変数は GKE 側に設定もできますがダウンロードしたボットのソースコードに含まれている .env もそのまま使用できます。今回はそれを修正します。

MicrosoftAppId、MicrosoftAppPassword、ConnectionName を書き換えます。

ConnectionName: "--- id プロバイダの接続設定の名前 ※ここでは BotTeamsAuthADv1 ---"
MicrosoftAppId: "--- Azure 接続チャンネルの アプリ ID ---"
MicrosoftAppPassword: "--- Azure 接続チャンネルの パスワード ---"

不要ファイルの削除

Azure ボットのソースをダウンロードすると Azure 側で必要な設定ファイル等が含まれてきます。今回は以下のものを削除しました。

web.config
.deployment
.gitignore
deploy.cmd
iisnode.yml
publish.cmd
README.MD
deploymentTemplates ディレクトリ
PostDeployScripts ディレクトリ
Properties ディレクトリ

Microsoft Teams にボットを公開する

作成したチャネルを Teams 用に公開します。チャネルの 「チャネル」 – 「チャネルに接続」 に「Microsoft Teams」を追加します。

次に PC 版の Teams アプリで、左側のメニューの「・・・」 から「App Studio」を選択します。

App Studio が開いたら「Manifest editor」から「Create a new app」を選択します。

アプリ生成画面が出てくるので、アプリの公開情報を入力してください。ここで重要なの「App ID」です。ここには先に作成したチャネルの「Microsoft App ID」を設定します。これはチャネルの「設定」画面から取得できます。

アプリの設定が終わったら 「Finish」 – 「Test and distribute」 – 「Download」 からアプリ配布用の Zip ファイルがダウンロードできます。

このファイルをボットを使用したい人の Teams の 「アプリ」 – 「カスタム アプリをアップロード」 から追加することで設定が出来ます。PC 側で設定するとスマホ側にも反映されます。

さいごに

この名刺管理ボットを作ったことで長年どうにかしたいと思っていた名刺管理問題が解決しました。それというのも、クラウドを利用することで革新的な技術を気軽に利用出来て、且つ、取り合えず作ってみて公開してみる、といったことのハードルが下がったことによると思います。

また単純に OCR で読み込んでそれを検索するだけのアプリですが、それを実用レベルにするにはそれなりに工夫が必要なことも分かったかと思います。AI と言ってもまだまだ人間レベルに全てが行えるわけではないので、適切にアーキテクチャを組み合わせながら工夫をしていくことが重要と感じます。

この記事では実装のポイントに絞って解説をしましたが思った以上に長文になってしまいました。これ以外にも実装時には細かな課題や問題がありましたのでそれらはまた別記事でご紹介できればと思います。

また、この記事を見てクラウドのビジネス利用にご興味を持ちましたらお気軽にお問い合わせいただければ幸いです。