使用 AWS Lambda 部署 LINE 聊天機器人(上)
前言
今年因為疫情關係,許多公司會要求同仁們每天填健康回報表單,並指定小組長定時回報給小老闆。那麼問題來了,小組長要等到最後一位組員回覆後,才能回報給小老闆。然而我們記憶力有限,常常忘記需要回報給小老闆,身為一個程式設計師,能自動化,就不會手動。於是,用 AWS Lambda 部署的 LINE 聊天機器人就這麼誕生了!
我想用聽的:Spotify Podcast、Apple Podcast、Google Podcasts、KKBOX Podcast
情境
在本實驗中,我們會利用 AWS Lambda 及 API Gateway 來建構出一個無伺服器化的 LINE Echo 機器人,並在下一篇 Blog 中結合 DynamoDB 及 CloudWatch 等服務,建構進階功能。
步驟
步驟參考 reInvent 這個 session 中介紹,如何在 AWS Lambda 上部署一些影像偵測演算法,今年 AWS Lambda 也加大執行時可使用的記憶體容量到驚人的 10G,這個架構應該是目前最省錢的 AI 推理模型的部署方式。我們會找機會出一篇 Blog 教大家實作(其實步驟跟本篇也差不多,大家可試試著延伸看看)。
建置過程
首先,要在 LINE 申請一隻機器人,並得到機器人的 Channel secret 及 Channel access token,詳細操作因篇幅關係本篇部落格不多作介紹。
在創建之前我們要先來研究一下 LINE Bot 要怎麼做,可以參考 LINE 的官方教學,就可以發現基本程式架構流程,利用 Python flask 創建一個 Web API 去承接 POST 請求,再利用 LINE Bot 類別中的 handler 去處理後續動作。
相關程式完成 (如下) 後,再回到 LINE Bot 設定頁面的 Webhook URL 指定到你所建置的服務,並將 Use webhook 打勾即可完成一個 Echo Bot。
from flask import Flask, request, abort
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)
app = Flask(__name__)
line_bot_api = LineBotApi('YOUR_CHANNEL_ACCESS_TOKEN')
handler = WebhookHandler('YOUR_CHANNEL_SECRET')
@app.route("/callback", methods=['POST']) ## 接 POST 需求
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature) ## 丟到 Handler 去處理訊息
except InvalidSignatureError:
print("Invalid signature. Please check your channel access token/channel secret.")
abort(400)
return 'OK'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=event.message.text)) ## 回傳 User 剛剛所傳的訊息 (Echo Bot)
if __name__ == "__main__":
app.run()
在分析完上述程式原理之後,發現 Web API 這個關鍵字!不就代表,直接用 AWS Lambda + API Gateway 來取代 flask 並實作嗎?
思路是對的,但會面對一些問題 :
- 要怎麼在 AWS Lambda 部署 Python 函式庫
- 怎麼整合 API Gateway 成為 LineBot 的 webhook
利用 AWS Lambda Layers 在 AWS Lambda 環境中部署 Python 函式庫
當使用者在開發 Lambda Functions 時,往往會碰上需要使用到第三方函式庫,或需要共用某些程式碼的情形,這時候就可以將那些功用的開發包以 Lambda Layer 形式上傳,Lambda Function 便能直接引用這些函式庫。使用者也能管理這些 Lambda Layer 的版本。目前 AWS 的設定,Lambda 函數可以引用至多 5 個 Lambda Layer。
前面提到在 AWS Lambda 環境中部署 AI 推理推模型,也是使用到這個方法喔!
-
準備 LINE bot 函示庫
利用以下指令使用 pip 安裝 Line Bot SDK 到特定目錄
pip install line-bot-sdk -t C:/temp
-
打包 LINE bot 函示庫
將指定目錄中的檔案依照以下結構打包成 Zip 檔
📂python ┗ 📂lib ┣ 📂python3.7 // This is for python 3.7 ┃ ┗ 📂site-packages ┃ ┗ **All files in C:/temp** ┗ 📂python3.8 // This is for python 3.8 ┗ 📂site-packages ┗ **All files in C:/temp**
-
上傳成為一個 AWS Lambda Layer
登入 AWS Lambda 後台並選取 Layers
點 Create layer
輸入名稱、上傳 zip 並選擇 runtimes,因為我們部署了 python 3.7 跟 3.8 所以這樣選就好了,依據自己需求選擇,然後按下 Create
-
引入程式碼
這章節會先從 Echo Bot 為例子,先改寫好官方的範例,確保能運行,在修改成我們的目標 — 健康回報機器人。
回到 AWS Lamdba 後台點集 Create Function。
輸入函數名稱,並將 Runtime 選成 python 3.8,最後按下 Create Function。
點選 Layers 選項
下面 Layers 區塊選擇 Add a layer
選擇 Custom layers,並在 Custom layers 選擇剛剛所創建的 layer,並選取 Version。 設定好之後點擊 Add
貼上 import 的部分,並且按 Deploy
按 test 我們需要測試函數能不能正確被引入
測試成功的輸出
如果測試失敗的輸出,找不到函式庫,請回頭確認 zip 內部的檔案結構!
接下來將 LINE 官方 repo 的範例程式碼修改成 :
```python
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)
import os
import json
line_bot_api = LineBotApi(os.environ['Channel_access_token'])
handler = WebhookHandler(os.environ['Channel_secret'])
def lambda_handler(event, context):
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=event.message.text))
# get X-Line-Signature header value
signature = event['headers']['x-line-signature']
# get request body as text
body = event['body']
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
return {
'statusCode': 502,
'body': json.dumps("Invalid signature. Please check your channel access token/channel secret.")
}
return {
'statusCode': 200,
'body': json.dumps("Hello from Lambda!")
}
```
再按下上方 Deploy 即可部署上 AWS Lambda
整合 API Gateway 成為 LINE Bot 的 webhook
上面提到的思路:用 API Gateway 及 AWS Lambda 取代掉 Python flask 的功能。我們在上一步驟,已經將程式部署在 AWS Lambda 中了,現在要用 API Gateway 來取代掉 Python flask routing 的功能。
- 創建 API Gateway
首先先進到 API Gateway 後台按下 Create API
建置一個 REST API
輸入名稱,按下 Create API 即可
Create 一個 Method
選擇 POST
按下勾勾
選擇 Lambda Function、勾選 Use Lambda Proxy integration、將 要觸發的 Lambda 名稱輸入至 Lambda Function,就可以按下 Save 了。
> Use Lambda Proxy integration 勾選起來的話,會將其他 Http request Header 中的資訊也帶入 AWS Lambda,如果沒勾選 AWS Lambda 只會收到 Body tag 中的值。因為程式中需要用到 **x-line-signature** 他躲在 header 中,所以勾選此選項。
部署上 API Gateway,就可以拿到 Webhook 用的 Endpoint 了。
Endpoint get
-
整合至 LINE Bot
將 LINE Bot 的 Channel secret、Channel access token 填入 AWS Lambda 中的 Environment variables
將上面的 Invoke URL 填入 LINE Bot 管理頁面中
把機器人加入好友,就可以玩他了!!
結論
跟著本篇 Blog 的步驟,我們了解到 AWS Lambda 與 API Gateway 的連動關係,以及想要在 AWS Lambda 使用第三方函數庫的時候,可以使用 AWS Lambda Layer 將函數庫部署在 AWS 環境中,供日後使用。當然這個套路也因為 Lambda 可使用的記憶體大幅增加的關係,所以可以更彈性的部署一些需要較大量運算資源的 Function ,像是開頭提到的影像辨識相關功能函數。
本篇內容將學到 1. 利用 AWS Lambda Layers 部署 Python 額外函式庫,並且給 AWS Lambda Function 使用 2. 設定 API Gateway 作為 Line Bot 後端的 Webhook