DialogflowモジュールがLambdaでエラーになった時のワークアラウンド – CodePipelineとCodeBuild

はじめに

こんにちは、中村です。

日本語のLINEチャットボット開発のために、Dialogflowを利用することがあります。他にはnode.jsやtypescriptで開発し、AWSではLambdaやAPI Gatewayを主に活用しています。npmにDialogflowのモジュールがあることがわかったので、ローカルにて利用し、Dialogflowの接続部分を開発しました。

dialogflow - npm

開発・ローカルテストが完了しデプロイしました。Lambdaのテストを試すとどうもDialogflowに関連するモジュールがエラーになっているようです。

Unable to import module 'index': Error Expected directory: node-v57-linux-x64-glibc Found: [node-v67-darwin-x64-unknown] This problem can often be fixed by running "npm rebuild" on the current system Original error: Cannot find module '/opt/nodejs/node_modules/grpc/src/node/extension_binary/node-v57-linux-x64-glibc/grpc_node.node'

エラー内に、npm rebuildをすると直る場合もあるようですが、うまくいきませんでした。ワークアラウンドとしてLambda内で動いているものと同様のAmazon Linuxを利用したコンテナ内でモジュールインストールしデプロイをすることで解決できるのではないかと思ったので、AWS CodePipeline・CodeBuildを利用したデプロイの仕組みを構築したのでまとめておこうと思います。

ワークアラウンド

今回の構成図はこのような形になります。Cloudformationで構築します。

GithubのSettingsDeveloper settingsにあるPersonal access tokensにてトークンを生成します。スコープにはrepoadmin:repo_hookを選択します。生成されたトークンをメモしておいて、今回利用するリポジトリを作成します。

Cloudformationでスタックを作成します。Cloudformationはこちら
テンプレートをAmazon S3にアップロードでファイルをアップロードします。次のページで、スタック名・Githubのトークン・オーナー・リポジトリを入力します。オプションは指定なしで確認でCAPABILITYにあるAWS CloudFormationによってカスタム名のついたIAMリソースが作成される場合があることを承認します。にチェックを入れて作成をします。

リソースを作成が開始し、完了するとCREATE_COMPLETEとなります。これでAWSリソースは完成です。 次にECRに利用するDocker imageをローカルからプッシュします。

まずはDockerfileをビルドします。Dockerfileはこちら

$ docker build -t chatbot .

ビルドが完了したら、イメージを確認します。

$ docker images
REPOSITORY                                                TAG                 IMAGE ID            CREATED             SIZE
chatbot                                                   latest              89ee8bf4a7e3        6 minutes ago       786MB

chatbotが確認できました。ローカルで作成できたので、ECRにプッシュします。

まずはAWS-CLIで、ECRへのログインを方法取得します。

$ aws ecr get-login --no-include-email --region ap-northeast-1
docker login -u AWS -p ****** https://xxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com

取得したログイン方法で、ローカルのイメージとタグ付けしプッシュします。

$ docker tag chatbot:latest XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/chatbot:latest
$ docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/chatbot:latest

ECRのchatbotリポジトリでプッシュされた日時が更新されていると思います。

先ほど作成したリポジトリからクローンし、ローカルでapex initします。project.json内の、runtimehandlerは修正しましょう。buildspec.ymlはこのような形にしておきます。

---
version: 0.2
phases:
  pre_build:
    commands:
      - cd functions/hello
      - npm install
      - cd ../../
  build:
    commands:
      - echo Build started on `date`
      - "/usr/local/bin/apex deploy"
  post_build:
    commands:
      - echo Build completed on `date`

Lambdaのindex.jsにはこのような形で記述しておきます。

const dialogflow = require('dialogflow');

let client_email = 'xxxxxx';
let private_key = 'xxxxxxx';

let credentials= {
    'credentials': {
        'client_email': client_email,
        'private_key': private_key
    }
}
const client = new dialogflow.SessionsClient(credentials);

exports.handler = async(event) => {
  console.log(JSON.stringify(event));

  :
  :
  :
  :
  :

  let result = await client.detectIntent(request);
  console.log(result);

  return { statusCode: 200 };
}

これでmasterにプッシュすると、AWS CodePipelineが起動します。

CodePipelineでの処理が完了すると、Lambdaが作成されているのでテストを実行してみます。LINEから下記のJSONが、Webhookリクエストされる想定です。

{
  "events": [
    {
      "replyToken": "00000000000000000000000000000000",
      "type": "message",
      "timestamp": 1548681979873,
      "source": {
        "type": "user",
        "userId": "Udeadbeefdeadbeefdeadbseefdeadbeef"
      },
      "message": {
        "id": "100001",
        "type": "text",
        "text": "こんにちは"
      }
    }
  ]
}

前回まではテストが呼び出されるとエラーになりましたが、無事通るようになりました。

まとめ

いかがでしたでしょうか。
今回は、CodePipeline・CodeBuildを利用して解決しましたが、実際の問題はまだ解決できていないので検証を進めていこうと思います。

弊社では、「Amazon Connect」の導入を検討している方を対象とした無料相談会を毎週開催中です。

また音声を中心とした各種ソリューションの開発支援も行なっております。