DialogflowモジュールがLambdaでエラーになった時のワークアラウンド – CodePipelineとCodeBuild
はじめに
こんにちは、中村です。
日本語のLINEチャットボット開発のために、Dialogflowを利用することがあります。他にはnode.jsやtypescriptで開発し、AWSではLambdaやAPI Gatewayを主に活用しています。npmにDialogflowのモジュールがあることがわかったので、ローカルにて利用し、Dialogflowの接続部分を開発しました。
開発・ローカルテストが完了しデプロイしました。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のSettings
のDeveloper settings
にあるPersonal access tokens
にてトークンを生成します。スコープにはrepo
とadmin: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内の、runtime
・handler
は修正しましょう。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」の導入を検討している方を対象とした無料相談会を毎週開催中です。
また音声を中心とした各種ソリューションの開発支援も行なっております。