在 Cloud Functions 上部署有 Open Data 功能的 LINE Bot | 摘要王, 天氣, 紅外線

前言
此篇文章為延續與政治大學 & 臺北大學 GDSC 工作坊的文章,如果對於整合 LINE 官方帳號的相關資訊,可以參考本篇喔!
標籤

此篇文章為延續與政治大學 & 臺北大學 GDSC 工作坊的文章,如果對於整合 LINE 官方帳號的相關資訊,可以參考本篇喔!
最近因為有些專案需要有 local Volume,但在 Serverless 上要用偏麻煩,除機器上的本身功能以外,還需要另外開 VM 讓 Container 能夠 mount volume,因此需要類似 Heroku、Fly.io 這類的 SaaS 的服務,可以用很少量的費用去使用到 Database/Volume 的功能...

由於近期自己蓋了幾隻 side project,慢慢發現有些服務倒了我自己也不知道,但時間久了其實當下也會忘了要修哪...
也因公司內有 status page 可以看每個服務狀態,想想這需求我也需要,因此萌生了架 status page 來幫忙確認健康狀態,接下來看看怎麼操作吧

今年也是一個 AI 元年,Google IO 上也講了許多與 AI 的結合,當然 Firebase 也不意外,剛好近期在 Side Project 中使用了比較多 Firebase 相關的開發,因此本篇會先紀錄較多 Firebase,也同步把 Keynote 中聽到的內容給記下來。

Serverless 架構的冷啟動(cold start)是指當一個沒有被使用的函式需要被調用時,需要先啟動一個新的容器或虛擬機器來執行該函式,這個啟動的期間被稱為冷啟動時間。
但我們在寫應用的時候通常都會帶有一些 cronjob 在背景跑(備份、爬蟲...),但在 Serverless 上都會遇到 Cold Start 的問題,在這種情況下因為資源都會被釋放掉,如果這些 cronjob 是比較重的且 Severless RAM&CPU 又放比較少, cronjob 時邊爬蟲邊異地備份又讀寫資料庫(~~ 誇張了點 ~~),這情況下可能會因為資源瞬間不夠導致功能異常。

近年來,隨著雲端計算的普及和發展,越來越多的企業和開發者正在尋找一種更簡單、更高效的方式來佈署和運營應用程式。而 Severless 技術正是一種流行的解決方案。Google Cloud Run 是一個強大的 Severless 平台,可以幫助開發人員快速地佈署和運營。接著,本文將介紹 Google Cloud Run 的優點和如何在其中佈署。

📸 by WFranz
Kubernetes 在分散式系統中已經成為一個高可用性平台,並且有許多人都會將程式部署在上面,雖然部署在 Kubernetes 上很好,但本身的管理以及學習都不那麼簡單,因此也有許多人會使用 Serverless 來解決需求。
而今天介紹這個集節目就邀請到 Mux 的 Matt Ward,他們家的產品主要是在做 video streaming APIs,現今他們有自己的 Kubernetes 也曾經在 Serverless 中使用過,以下就分享我從這集 podcast 學到的東西以及心得囉!

約在一年前,當時公司的同事帶我玩過 Swagger,只是當時的我寫得不太習慣,直到寫這篇之前都還只是寫 markdown 來做 API 文件 😓,不過寫 API 到最後總是要開放給其他人需要測試以及用文件來描述功能,但只是寫 Readme.md 給其他人看感覺有點不負責任 (?),所以這個時候 Swagger 就發揮它的作用啦~
但是看著下面這張圖…
左邊那是怎樣!怎麼設定了一大堆右邊才出現一點點東西,估且不論還要設定 Model、Payload 什麼的,我想光寫左邊這份文件加測試的時間我都可以寫一份 Markdown 給其他同仁用 Postman 測試了…🤣🤣🤣
這時候套件就出動啦,這次要介紹的是 flask-restful-swagger-2.0,他是延續原版套件並加以更新的,不得不說再搭更新完後整體寫起來人性多了,像是文先寫法用 decorator 的寫法在路由之前、宣告 Schema 時只要用類別包起來之類的,讓寫文件可以用 python 的寫法去寫,除了特殊的設定需要比對 Swagger 官方文件外,在建一個 Swagger 格式的文件這樣已經超快了 💪
既然如此那就介紹一下他的相關設定並把它記錄下來吧!

在 AWS 的 API gateway 上就有一個項目(Authorizer)是支援 Cognito 以及客制認證的方法,好處就是在進我們寫的 API 之前有個可以做身份確認的 Lambda Function 幫我們好前處理,而本篇則會介紹如何在 Serverless framework 上設定 API gateway 的 Authorizer 以及成功讓 API 回應,串接 Cognito 做使用部分就留給之後吧!
最近 LINE 釋出了一個我很喜歡的功能 - Icon Switch🎚,從字面上的意思就是可以切換機器人的 Icon (名稱以及大頭貼),過往開發者在使用時都只能較死板的使用同一個頭像或暱稱來回應使用者,而現在只要使用這個這個功能就可以很輕易地切換你想要的大頭貼以及暱稱了 😇
據可靠消息指出這以前是需要收費的功能,而現在則是免費推出來給大家使用,讓大家可以有更多的彈性去開發新功能~

大家好,我是 Chatbot TW 的 NiJia
當產品開發完上線後難免會遇到可能是邏輯上的 Bug、例外錯誤或是想記錄使用上的錯誤,當沒有工具時都只能等錯誤出來時乖乖的去翻 Log 找問題,對於每次出錯要 Debug 的我就覺得很困擾 😭。
公司強者寫 APP 的同事他們都很開心的在使用 Crashlytics 幫他們將 APP 出問題時將錯誤訊息丟上去,看了實在時羨慕,但那們寫 Web 的朋友要怎麼辦!
最近就發現了Sentry這個玩意兒(應該是有段時間的產品了),支援了幾乎支持現在市場上主流的各種框架以及語言,且串接上也都很簡單就能實現,文件也都清楚解釋每個 API 的功能如何使用,以下就來介紹如何使用它吧 😎

這邊顯示較常用的語言,還有支援很多其他種類的框架及語言
首先先到 https://sentry.io/welcome/ Get start

這邊可以整合了GitHub或者Azure DevOps兩種 OAuth 的登入方式

接著就照著步驟來囉!

選擇自己所使用的語言,這邊我使用 Flask(python) 框架來實作

接著官方會有個簡單的文件告訴你要如何實作這塊,這邊就先使用pip在本地安裝套件
pip install --upgrade "sentry-sdk[flask]==0.13.2"
若是使用雲端服務的話需要將
sentry-sdk[flask]==0.13.2放入 requirements.txt 中
將以下 init code 放入 api.py 裡,在 app = Flask(__name__) 之前
這邊我將 Sentry 的 DSN 放入 .env 裡,若有參考這部分的朋友需要注意環境變數的地方喔!
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
sentry_sdk.init(
dsn=os.environ.get('SENTRY_DSN'),
integrations=[FlaskIntegration()]
)

接著就開始丟錯誤上去看看狀態,我的專案上是串著 LINE Bot,功能只是當個應聲蟲,在這情況下只需要丟個貼圖給它就會出錯。
當錯誤丟出來之後原本監聽的紅色燈號就會打勾囉 ✅
Sentry 預設都會寄信通知,所以若有錯誤記得看信箱哦!

點進去後就可以很清楚的看到程式是錯在第幾行,寫到這有種莫名的感動,平常都是在 LOG 海裡找看看到底報錯在哪邊,終於有個地方能幫我接下來了 🎉

到這邊基本串接已經完成了,但身為免費導向的工程師怎麼能讓 5000/月 限制我!接下來就要去過濾一下來的錯誤內容,若不是系統造成的錯誤就不上傳。
我選擇判斷 Log 達到Waring或是Internal就回報,並照著官方的指示新增以下內容:
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration
+ import logging
+ from http.client import HTTPException
+ from sentry_sdk.integrations.logging import LoggingIntegration
+ def strip_sensitive_data(event, hint):
+ if "exc_info" in hint:
+ instance = hint['exc_info'][1]
+ if isinstance(instance, HTTPException) and instance.code < 500:
+ return None
+ return event
+ sentry_logging = LoggingIntegration(
+ level=logging.INFO,
+ event_level=logging.WARNING
+ )
sentry_sdk.init(
dsn=os.environ.get('SENTRY_DSN'),
+ before_send=strip_sensitive_data,
+ integrations=[FlaskIntegration(), sentry_logging]
)
strip_sensitive_data: 過濾來自 Http 的 Exception 錯誤碼是否大於500,否則不回傳。sentry_logging: 使用 SDK 的 LoggingIntegration 來定義 log 回傳事件時的層級到哪層才上傳錯誤訊息,這邊我設定Waring等級,並加入到 init 的 integrations 裡。此時不管你打上都會將訊息送上 Sentry:
logging.waring("i am waring")
logging.error("i am error")
或者是
raise InternalServerError("Hi Error")
或者!!自己手殘讓程式崩潰也會送通知
到這基本上已經完成串接完成並能過濾錯誤訊息,接下來則為將服務串至 Slack 上。
首先來到Settings按下Integrations會看到以下畫面,並安裝一下Slack

接著就要跟 Slack 連動囉

連動完成後再按下configure來設定以下內容

到了Alerts頁面後就開始設定以下內容囉,這部分參考官方文件

可以指定透過哪個group去傳送至哪個tag,我就直接送到預設的#general

測試就直接送出貼圖來讓 bot 報錯,結果就收到key的錯誤啦~

最後就能透過sls deploy將專案部署到 AWS 上,透過這次的紀錄對 Sentry SDK 運作在框架上的問題更加了解,同時也找到了一個不僅可以紀錄錯誤還能通知我錯誤訊息的地方,可以在第一時間直接進去分析 log,若完成了可以直接在 slack 上按Reslove來完成這個 issue。
本來有在考慮要串接LINE Notify,不過看著 Sentry 對 Slack 的整合比較好,也就放棄了這個念頭了 🤣,但串接哪個平台其實就看公司的需求,Sentry 一樣可以透過 webhook 將錯誤訊息傳至 Server。
此外它一樣可以整合Github、GitLab、Azure DevOps...等等,若錯誤處理好的話可以讓 Sentry 幫你送 Issue 上去,不僅整個超炫砲 😎,若是 open source 的專案也可以大家一起去修這個問題,真的是一舉兩得啊!!!
Serverless 架構是一個基於 FaaS(Function as a Service) 實作的一個服務,讓開發者可以更專注在開發功能,將 yml 檔設定好其餘維運的問題都交給 AWS、Google、Azure 這些服務商去處理,只要把信用卡準備好就好(?),讓開發者在寫完程式之後不用煩惱部署得問題,減少的不少的麻煩事。 那因為現任公司的服務都是基於 AWS,如此這般我就接觸到 Serverless(以下簡稱 sls) 這個框架啦 (想更深入了解 FaaS 架構可以參考 AWS) [2019/07/04 更新] 若已經暸解的朋友可以參考我的 python 版本: louis70109/aws-line-echo-bot Ruby 的範例版本: louis70109/aws-ruby-line-echo-bot
[2019/06/29 更新] 以用一個 Ruby on Rails 寫的機器人為例,在寫完個 webhook function 後,設定路由然後部署,一般主機或是虛擬機就很麻煩,要安裝佈署環境以及一安裝堆相關套件,完了之後設定 Domain 什麼的有夠花時間。但若是使用 heroku 部署就很方便,Login — Create-Deploy 馬上就結束,機器人馬上就上線了,實在是俗又大碗呀~ 但我覺得像是 LINE Message 這種 stateless 的服務,且只要一個 function + route 就能實作的程式最適合 Serverless 了,讓專案可以簡潔有力,只要寫一個 function+yml 設定並打個指令就部署了,而且 domain 還附贈 SSL,將服務交給 AWS 也不需要擔心,整個就是超級方便啊! Cold start time 問題,依照我目前使用的結果下來,在 heroku 以及 AWS Lambda 同時睡著的情況下,AWS 起床的回應速度大概 1 秒左右,而 heroku 則大概落在 10~15 秒(參考)。如下圖所示的免費方案,基本上開發階段應該是不至於到 100 萬/月 個請求吧 😆,所以這部分就別擔新大力地給他用下去~ (感謝 petertc 幫忙找這方面的問題 💪)
AWS Lambda 免費方案
接下來就讓我介紹如何使用 sls + LINE Message API 建立一個 Echo bot 吧!

npm 6.9.0 python 3.7.3 serverless 1.45.1
首先要先有個 AWS 帳號(廢話) ,如果沒有綁定信用卡記得去綁定才能繼續,接下來就可以去就可以去建立鑰匙囉!
如上圖,按下紅色框框的部分,接著按箭頭指的按鈕,就會建立一個我們要的金鑰囉!

如上圖所示,可以下載 key,AWS 不會幫忙保存 Secret key,若這視窗關掉的話 key 就不見了,所以使用者可以自行下載檔案保存,以免哪天要部署的時候找不到 key。 接著就將這兩個參數加入環境變數中:
export AWS_ACCESS_KEY_ID=<your-key-here>
export AWS_SECRET_ACCESS_KEY=<your-secret-key-here>
首先先透過以下指令讓 npm 幫忙安裝 sls 的指令。
npm install serverless -g
安裝完之後就透過 sls 指令來建立專案嚕!
serverless create — template aws-python — path aws-line-echo-bot
severless 指令可以縮寫成 sls template = 想用什麼平台以及語言就在此模板了 (參考) path = 檔案名稱

透過 severless 建立專案指令 依照不同的 template 可能會建立 N 個檔案,但主要還是以下三個檔案
.gitignore
handler.py = 主程式的地方
serverless.yml = 所有設定都在這裡,funtion 參數會對應到指定的路由
在部署前須至 serverless.yml 裡,把 runtime 的 python2.7 改至 3.7,否則會因版本問題導致錯誤。
serverless deploy # sls deploy

接著到 AWS Lambda 以及 S3 就可以看到第一次部署完的程式以及檔案囉!
既然是寫 python,那一定是要用 line-python-sdk 照著官方用法應該是要執行以下指令安裝套件
pip install line-bot-sdk
不過我們是要部署到 AWS 的 Lambda 上,且因為他是無伺服器運算的服務,所以他沒辦法跑 pip 去幫我安裝套件。 此時就要出動 requirements.txt 來管理相依套件 ,但只有加入它 sls 根本不認識,因此我們需要安裝 pulgin 來讓 sls 認得(requirements.txt)。
sls plugin install -n serverless-python-requirements


安裝指令 & package.json 如圖,安裝過程中會自動建立 package.json,且會幫忙在 serverless.yml 裡面加入 pulgin,這樣 sls 在部署時就認得 requirements.txt 囉! 接著將 LINE SDK 加入 requirements.txt, AWS 就會幫忙安裝套件了。
echo "line-bot-sdk==1.12.1" > requirements.txt
然後新增一個 setup.cfg 並加入以下內容:
[install]
prefix=
由於 AWS Lambda 上都會將套件安裝在本地端(Lambda 上),因此在執行 pip install -t . -r requirements.txt 時會需要 setup.cfg。
至於詳細原因嘛~還有請各位大大幫小弟解答 🙏
最後就處理 handler.py 以及 serverless.yml 囉!
將上述兩個 gist 分別貼到自己的 handler.py & serverless.yml,在 hanlder.py 裡的 CHANNEL KEY & TOKEN 記得換成自己的機器人 👾 哦!
接著在執行一次 sls deploy,將程式部署上去,如上圖所示,在 endpoints 那行會有一個網址,將那網址放到 LINE Webhook URL 上並 Verify 看看有沒有成功,如下圖所示。
最後對著自己的機器人發個話,看他有沒有回應你一模一樣的文字,若成功了就能往下一步應用層繼續開發囉!若失敗了就要檢查一下是不是有哪個步驟做錯了~
因為公司的緣故讓我能夠接觸到這神奇的 Serverless,經過這次的分享加深我對於 FaaS 的架構更加了熟悉,由於我也是初碰,我認為使用上可以把它當作是一個 Docker 的容器或許概念上會比較好理解,後續還有很多的設定檔都還沒詳細研究,最近讓我先研究研究一下再上來分享。
文章中有任何勘誤歡迎指正
如果覺得這篇文章有幫助的話請給我個掌聲鼓勵我一下 👋 下列是我的這次實作的專案,如果有幫助也來顆星星吧! louis70109/aws-line-echo-bot

大家好,我是 NiJia
以下是我最近參加 IT 邦幫忙 30 天鐵人賽的文章列表,主要是紀錄從我進入新公司後學到的Serverless,並透過結合 LINE API 實現在 AWS 上,歡迎大家參考取用 😃
不管是建立虛擬機(EC2),又或是像我們建立 Serverless 的服務(Lambda),總會挑選離自己家近的節點(東京),又或者可能因為價錢的關係選擇相對便宜的地點,但不管機器在哪,對於在台灣的我們來說都是一樣跨海啊!但好險台灣擁有 CloudFront 的節點,透過快取讓使用者可以更快的取得服務器上的資源,可能第一次都會比較慢,但只要讓 CloudFront 看過一次就會幫忙快取起來,下次使用者在用的時候就不會那麼慢囉!
首先我們先進去 CloudFront 的頁面,按下左欄的Distribution後並 Create的藍色按鈕
因為我們是做 Web API 相關的,這邊當然就是選 Web 囉!
到了這邊,AWS 很有心的在這些輸入框大多都有下拉式選單,這邊就選建立完的那個專案名稱
選完之後下面三個就照著這樣點選,Comment 會幫你自動填入
CNAMEs 的部分這邊我填入我在 serverless.yml裡 create_domain 所設定的 line.nijia.lin 字串,SSL 就選擇上一篇所建立的 Certificate
建立完成之後就會看到他 In progress,CloudFront 就開始撒點告訴其他節點有建立這個名為 line.nijia.lin,有看到他的時候記得幫他做個 Cache
等個大概 15 分鐘後他就部署完成啦~
點進來可以確認自己剛剛設定的資訊對不對,若有設定錯誤在按 Edit 來修正

現在每個服務都很流行 CDN,若有在寫前端的朋友常常就會用到 jQuery、Bootstrap、Vue 等等可以再標頭檔就引入的 CDN 路徑,CDN 不僅加速我們平常在抓伺服器資料的速度,也便利了我們有時候需要寫一些簡單的靜態頁面時可以透過抓取 CDN 路徑來快速取得需要的資源,但是像是 AWS 他們都是收取流量費,用得很爽時別忘了看一下自己的帳單是不是在哀嚎 🤣
繼上一篇 warm-up 機制之後來了解一下如何使用 AWS 的 CDN 服務 - cloudfont,CDN 顧名思義就是要讓使用者可以到最接近的主機上去拉到對應的資料,透過撒點的方式讓主機去挑選離自己近的服務據點,藉此加速服務。
像我之前部署在 ohio,想想光網路的時間 + Cold start 的時間就已經去了一大半,即便我們有熱開機,服務沒有 Timeout 我該慶幸了,既然如此就不得不認識一下 CDN 服務,而 AWS 的 CDN 服務就是 cloudfont 啦!
只是說原本的 domain 已經有 SSL 了,但是代理沒有這個東西,所以我們要 ban 出一個 Certificate,那在在建立 cloudfont 以前需要先到 Certificate Manager,接著會用圖片帶大家一步一步做

我已經有先在 Route53 註冊了一個 nijialin.com 的 domain,.com大概 10 美金左右,若是有在其他地方註冊域名的話要找一下相關文章把它導進 Route53 哦!





最後就等他完成嚕!將將
接著我們到 API Gateway 找到左邊的 Custom Domain Name,我們要來建立屬於這個 API 的 Domain 了
按下藍色的按鈕之後,輸入Domain name以及選擇剛剛註冊的 Certificate 後按下 Save
他就會開始初始化剛剛的設定,這邊大概需要等 15 分鐘左右
在此同時我們就去新增專案裡的套件 Domain-Manager
npm install serverless-domain-manager --save-dev
並且在plugin下加入套件
plugins:
- serverless-domain-manager
在custom底下加入
custom:
domainName:
default:
domainName: line.nijialin.com
certificateName: "*.nijialin.com"
createRoute53Record: true
endpointType: edge
等待前面初始化成功之後部署這個專案sls deploy之後就會看到剛剛註冊的域名啟動囉!

在建立 Certificate 那邊倒沒什麼問題,畢竟需要一個 SSL,只是到了建立 domain 這邊遇到了很怪的問題,一般來說使用 serverless 框架來跑的話只要照的文件裡說的 serverless create_domain就會幫忙註冊,而且可以依照自己開發環境去自動設定,在公司的專案中這樣使用是沒問題的,在是在寫這篇文章時卻只能使用以上的方法來替代使用,雖然結果是一樣的,但是用起來實在是很不符合邏輯,google 也沒找到類似的問題,或許這個問題還要多實測幾次才夠...😭
本篇會介紹 npm 套件庫裡的 Serverless-plugin-warmup,裡面介紹了很多參數可以使用,以下會簡介如何引入它。
透過npm來安裝套件
npm install --save-dev serverless-plugin-warmup
在serverless.yml的plugin加入套件
plugins:
- serverless-plugin-warmup
在custom加入warm up的設定參數
custom:
warmup:
enabled: true
cleanFolder: false
memorySize: 128
name: "LINE-warmup-pop"
events:
- schedule: "cron(0/5 0-12 ? * MON-FRI *)"
timeout: 10
.gitignore加入以下兩個_warmup/以及.requirements.zip,他在部署前建立一個_warp的資料夾並放一個 Lambda 來打已建立的 api 以及 Lambda 們,部署後會建立.requirements.zip,所以這邊就加入這兩行來防止被 git 推上去。
或許會有疑問不是
cleanFolder設定 true 就可以,這邊我自己實測時在部署會因為 Size 過大而無法上傳,把這參數設定掉他才會乖乖的上傳成功,但這部分有待驗證。
這邊我在lib/下新增一個decorator.py,加入以下的 code
from functools import wraps
from logging import Logger
def lambda_warm_up(func):
@wraps(func)
def wrapper(*args, **kwargs):
source = args[0].get('source')
if (source == 'serverless-plugin-warmup'):
Logger.info('WarmUp - Lambda is warm!')
return {}
return func(*args, **kwargs)
return wrapper
主要功能是透過 python decorator 的特性讓我們在 function 之前用個前綴符號就可以引用它,很像 po 文在 tag 別人一樣 🤣
warm-up 建立的 Lambda 會固定對其他的 Lambda 打serverless-plugin-warmup的字串,這邊就寫個判斷式判斷掉後直接 return
你以為這樣就結束了?還有 IAM role 的問題呢,詳細問題已不可考,想試看看的可以把 IAM 拔掉再部署一次,問題就會出現了,以下為設定檔:
iamRoleStatements:
- Effect: "Allow"
Action:
- "lambda:InvokeFunction"
Resource:
- Fn::Join:
- ":"
- - arn:aws:lambda
- Ref: AWS::Region
- Ref: AWS::AccountId
- function:*
如果你的目錄結構跟我一樣,或是跟到今天的實作,加入以下的 code,這邊主要是告訴你的 Lambda 要在每次的 warm-up 字串來的時候呼叫這個 decorator 來幫忙處理一下
from lib.decorator import lambda_warm_up
@lambda_warm_up
def xxx():
pass
我的 Lambda 都加了,那原本的 flask 建立的 api 我要加在哪?
看看這個網址,serverless-wsgi 在實作的時候已經有考慮到這件事了,所以 api 這邊就不用管他,他自己會去把它處理掉!

Warm-up 的機制就是透過建立一個 Lambda 來打一個 flag 到其他 Lambda 來他們別進入睡眠模式,但總是會有人考慮費用的問題,可以參考下圖,若是你建立了一個簡單的服務然後上線了,AWS 給你了一百萬個請求的量,我覺得一個月的請求可以到 100 萬應該是服務有點規模的情況,一個月能到這個量相信你也有一定的能力養它 🤣,爾後每一百萬個請求才六塊台幣,可能比 GCP 還貴,但其實以我來說這樣已經很夠用,而且還扛得住

過往寫了這麼多的 api,總是會有特定的功能會需要輪詢資料庫或是監聽某些事件,以前還要到弄個虛擬機寫腳本用 crontab,這時候 Lambda 的用處就來啦!不只可以處理 api 的事情,還可以設定排程做事,太舒服啦,以下就來介紹一下~
首先在 consumer 資料夾下line_bot_alert.py,再寫一段需要定時跑的程式,這邊我去抓中央氣象局的資料,大概簡單判斷一下區域以及算出他的範圍:
import json
import requests
def alert_handler(event, context):
air = requests.get(
'http://opendata.epa.gov.tw/webapi/Data/REWIQA/?$orderby=SiteName&$skip=0&$top=1000&format=json')
air_body = json.loads(air.text)
air_status = ["良好", "普通", "對敏感族群不健康", "對所有族群不健康", "非常不健康", "危害", "資料有誤"]
total, count = 0, 0
for fetch in air_body:
if (fetch['County'] == "臺中市" and (fetch['SiteName'] == "西屯" or fetch['SiteName'] == "沙鹿")):
count += 1
if fetch['AQI'] != 0:
total = total + int(fetch['AQI'])
switch = 0
pm = int(total / count)
print(pm)
if pm in range(0, 50):
switch = 0
elif pm in range(51, 100):
switch = 1
elif pm in range(101, 150):
switch = 2
elif pm in range(151, 200):
switch = 3
elif pm in range(201, 250):
switch = 4
elif pm in range(251, 300):
switch = 5
else:
switch = 6
payload = f"\n空氣品質: {air_status[switch]}"
r = requests.post('https://1uv2o723o4.execute-api.us-east-1.amazonaws.com/dev/notify/sqs',
data={'message': payload})
print(r)
在serverless.yml的 function 下面加入 Lambda,handler 就是 資料夾/檔案.類別,設定事件(event)排程一分鐘一次
function:
line_bot_alert:
handler: consumer/line_bot_alert_handler.alert_handler
events:
- schedule: rate(1 minute)
AWS Lambda 有兩種設定排程的方法,
rate以及cron,若是比較固定時建議用rate,比較複雜的排程就可以讓cron來,下面兩張圖分別是來自 AWS >

下sls deploy部署之後就會看到剛剛做line_bot_alert的function,他每分鐘都在跑,若玩完之後不想用了可以下sls remove把上面有的東西刪掉嚕!

其實還有很多應用可以實作排程,像是有朋友就用 GCP 的來定時發文 🤣,而且可以把腳本寫得像是 api 一樣,讓整個靈活性都增加了不少,下一篇就帶大家來做 LINE Login 嚕!
用 Serverless 用得這麼爽,也是時候來看一下他的資源在哪,距離我上次自己手動上傳 python 檔已經是 N 個月之前的事了(遠望),當時也沒用過什麼Api Gateway相關的服務,以下就帶大家來介紹一下嚕!
CloudFormation 是一個 AWS 所提供的 JSON-base 服務,主要是用來讓使用者能夠快速建立 AWS 的服務。
這邊也不用擔心設定的問題,Serverless 再 deploy 的過程中會幫忙 CloudFormation 的 json 檔
透過 CloudFormation 建立 JSON-based 的 template 你可以快速建立所需要的資源堆疊(stack)。
按下Template後如下圖所示,他會顯示你已經有建立的服務 Map,整個線牽起來看起來就很爽 XD

Serverless 建立過程中也會透過 CloudFormation 幫忙建立 Api Gateway,因為我們是用 WSGI,所以只會看到兩個ANY的路由。
點進去後就可以看到 Client 是怎麼進出的,最右邊是打到 AWS Lambda 上,可以點選上面的字進入 Lambda 所在地。

點選進來之後可以看到 Lambda 接了什麼服務,以我到目前的程式就會有API Gateway、SQS以及預設的CloudWatch(都是透過這個來看 log)
接著往下會看到之前設定的環境變數以及STAGE,這些都是托Serverless的福免去這些麻煩的設定

Serverless 在過程中會在 S3 幫忙建立一個 Bucket,並在後面加個 hash key 以免 bucket 重複。

在開始測試後 AWS 會幫忙在這個地方輸出 Log,

Serverless deploy --verbose 就可以看到所有部署中的基本資訊了首先因為我們有使用.env,所以在一開始時會先設定環境變數,接著就是把相依套件的東西都放進去,再去啟動 CloudFormation

接著會建立一個 S3 的 Bucket,將程式透過 CloudFormation 處理成 zip的檔案格式並上傳到 S3 上面

接著就會部署到 Lambda 以及 Api Gateway 上,並做對應 CloudFormation JSON 檔的設定

在最後部署完之後就可以看到所有的基本資訊,物件名稱、部署階段、區域、網域以及 Function 們(因為 Lambda 是 Function-base 的服務)等等的基本資訊都會在此

以目前的架構基本上到這麼基本資訊都顯示出來了,若是有使用像是Route 53、CloudFront等等的服務它就會顯示在這邊了。雖然說 CloudFormation 在設定上已經很方便了,但不得不說 Serverless 真的幫忙做了很多事,讓開發者又更能專注在打 Code 上了~
看著前幾天的文章,像我們 notify 驗證的 API 有使用到 REDIRECT_URI、CLIENT_ID 以及 CLIENT_SECRET,或是像 PostgreSQL 的帳號密碼,
只是說若今天當程式碼變多的時候,抑或是這個參數有給其他 API 使用,那在尋找的時候不僅費工又浪費時間
那接下來就帶著大家在 serverless.yml 一步步加入變數值,並更改 code。
用 npm 安裝 serverless 的 dotenv 套件
npm i -D serverless-dotenv-plugin
接著加入新的套件到serverless.yml
plugins:
- serverless-dotenv-plugin
新增dotenv到 requirements.txt
python-dotenv==0.10.3
到 api.py 加入下面內容
from dotenv import load_dotenv
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
接著新增.env並輸入對應的內容
NOTIFY_REDIRECT_URI=
NOTIFY_CLIENT_ID=
NOTIFY_CLIENT_SECRET=
REGION=us-east-2
SQS_URL=
SQS_ARN=
PG_DB=
PG_HOST=
PG_NAME=
PG_PWD=
PG_PORT=
接著修改有使用到他們的地方,範例如下
import os
os.getenv("SQS_ARN")
既然有安裝 serverless 的套件了,那 yml 檔也可以使用哦!
provider:
name: aws
runtime: python3.7
region: ${env:REGION}
有時候把專案抓下來的時候要找到這些輸入的地方很容易找不到(我有點癡呆),一般 open source 也都會有一個.env的,用serverless-dotenv-plugin來幫忙弄就方便許多了,後續有需要再繼續往裡面新增就好了~
最後在搭配 python 的 dot env 套件使用就讓整個好用多了 🤣,只是說 html 因為目前還不是透過 serverless 來幫忙部署,所以這邊的參數就沒辦法吃設定檔了 😓
Amazon Simple Storage Service(簡稱 AWS S3)是 AWS 的一個線上儲存服務,用戶能夠輕易把檔案儲存到網路伺服器上,可能有些朋友會把它當成雲端硬碟看待,但其實還能像是傳靜態檔案上去,若有寫前端框架的朋友通常都會需要把程式碼打包成靜態檔案再部署,然而這邊把他打包送上去後,S3 會給一個名為 Object Url 的網址,可以透過這個網址去執行網頁裡面的內容(如 AJAX、Axios...),並可以透過 cloudfont 去弄成 CDN(扯遠了),簡單說他還有很多不一樣的功能應用等著大家去用~
因為 serverless 只能放關於 API 的程式碼,因此我們需要把 html 靜態檔案放在 S3 裡,
接下來就來建立一個公開的 S3 容器,這邊就不會提到權限如何控管哦!
建立一個名為2019-it-30-bucket的容器,地區就選與 Lambda 同樣的地區,直接按下左下角的create來建立。

建立完會像這樣
接著點進去後在點到第三個選項會看到 Block of public access 是啟動的狀態,因為我們接下來需要讓我們的頁面可以外部使用
就把 check box 的按掉,在按下 Save
要輸入confirm就修改完成嚕
接著一樣點到第三個個選項,選到Access Control List,看到Access for bucket owner這個選項的Canonical ID,選項都全部打勾之後再來調整,這樣之後的 HTML 檔案外面就看得到了。

接著上傳一個index.html的檔案來測試
<html>
<h1>Hello world</h1>
</html>
上傳後下拉式選單選擇public read的選項,因為一個檔案在 S3 就算是一個 Object,所以一定要讓它能對外,然後按下左下的 Upload

成功後點進這個檔案,並按下方的Object URL,看到 Hello World 的話就代表成功囉,這時可以開個無痕測一次,確認一下沒有錯誤 😃

透過 npm 全域安裝 serverless 指令
npm install serverless -g
因為我是用 python3 在 AWS 上實作
--template可以選擇你想要的模板 (參考)name service 的名稱--path 專案路徑,若找不到資料夾時會自動新增serverless create --template aws-python3 --name aws-wsgi-flask --path aws-wsgi-flask

接著後面會需要使用套件,而 python 這邊會認得 requirements.txt 這個檔案,因此我們需要用 serverless 的 plugin 幫我們處理,讓專案在 deploy 上去之後會透過 requirements.txt 去安裝套件。
而接下來我們會需要一個介面幫我們處理一些麻煩事,這邊就選用 wsgi 來處理。詳細參考
sls plugin install -n serverless-python-requirements
sls plugin install -n serverless-wsgi
接著去看 serverless.yml 檔案最後出現下面這樣就成功安裝了。
plugins:
- serverless-python-requirements
- serverless-wsgi
然後新增一個 setup.cfg 並加入以下內容:
[install]
prefix=
由於 AWS Lambda 上都會將套件安裝在本地端(Lambda 上),因此在執行
pip install -t . -r requirements.txt時會需要 setup.cfg。
接著選用 Flask 框架作我們開發的
echo "Flask==1.0.2" > requirements.txt
pip install -r requirements.txt -—user
serverless-python-requirements 同樣也支持 pipenv 哦,如果想完全擋掉的話可以參考
置換以下內容至 serverless.yml,讓 WSGI 去處理路由。
service: aws-wsgi-flask
provider:
name: aws
runtime: python3.7
functions:
api:
handler: wsgi_handler.handler
events:
- http: ANY /
- http: ANY {proxy+}
custom:
wsgi:
app: api.app
plugins:
- serverless-python-requirements
- serverless-wsgi
因為我們不是單純只用 python 架設,所以要將 handler.py 替換成 api.py,並加入以下內容
from flask import Flask
app = Flask(__name__)
@app.route("/", methods=['GET'])
def handler():
return 'hello world'
if __name__ == '__main__':
app.run(debug=True)
到這邊基礎設施都弄好了,但總不能部署才測試吧,所以這時候我們就出動
serverless wsgi serve
透過 wsgi 去幫忙起一個 local server 供使用者可以測試,而且每次儲存都重新整理一次哦!詳細用法請參考

接著用 postman 來測試一下

燈燈,hello world 出來了~
最後也是最重要的一個步驟『部署』 只是說部署前需要有 AWS 的 secret key & token
首先要先有個 AWS 帳號(廢話) ,如果沒有綁定信用卡記得去綁定才能繼續,接下來就可以去就可以去建立鑰匙囉!
如上圖,按下紅色框框的部分,接著按箭頭指的按鈕,就會建立一個我們要的金鑰囉!
如上圖所示,可以下載 key,AWS 不會幫忙保存 Secret key,若這視窗關掉的話 key 就不見了,所以使用者可以自行下載檔案保存,以免哪天要部署的時候找不到 key。
接著就將這兩個參數加入環境變數中:
export AWS_ACCESS_KEY_ID=<your-key-here>
export AWS_SECRET_ACCESS_KEY=<your-secret-key-here>
或是藉由 serverless 指令來幫忙加入參數:
serverless config credentials --provider aws --key <your-key-here> --secret <your-secret-key-here>
這步驟很重要哦,不然 AWS 不認識你就不給你上傳了 ?
最後就是下 serverlesss deploy進行部署

這邊 AWS 幫忙建立一個含有 SSL 的 domain,可以直接對這個網址下
endpoints: ANY - https://4omvn4z7re.execute-api.us-east-1.amazonaws.com/dev
ANY - https://4omvn4z7re.execute-api.us-east-1.amazonaws.com/dev/{proxy+}
接著把網址丟到瀏覽器上來試試看
出現 hello world就成功囉!

那因為現任公司的服務都是基於 AWS,如此這般我就接觸到 Serverless(以下簡稱 sls) 這個框架啦 (想更深入了解 FaaS 架構可以參考 AWS)
Serverless 架構是一個基於 FaaS(Function as a Service) 實作的一個服務,讓開發者可以更專注在開發功能,將 yml 檔設定好其餘維運的問題都交給 AWS、Google、Azure 這些服務商去處理,只要把信用卡準備好就好(?),讓開發者在寫完程式之後不用煩惱部署得問題,減少的不少的麻煩事。
以用一個 Ruby on Rails 寫的機器人為例,在寫完個 webhook function 後,設定路由然後部署,一般主機或是虛擬機就很麻煩,要安裝佈署環境以及一安裝堆相關套件,完了之後設定 Domain 什麼的有夠花時間。但若是使用 heroku 部署就很方便,Login-Create-Deploy 馬上就結束,機器人馬上就上線了,實在是俗又大碗呀~
但我覺得像是 LINE Message 這種 stateless 的服務,且只要一個 function + route 就能實作的程式最適合 Serverless 了,讓專案可以簡潔有力,只要寫一個 function+yml 設定並打個指令就部署了,而且 domain 還附贈 SSL,將服務交給 AWS 也不需要擔心,整個就是超級方便啊!
serverless.yaml 是該專案的設定檔,可以把它想像成是 CloudFormation 的 wrapper,事實上也的確是這樣,serverless 背後會把他轉成 CloudFormation 的 template 去發佈。這個設定檔是 serverless 的精髓所在,一切有關 API Gateway 和 Lambda 的設定都在這邊,而底層所需要的資源,他都幫你配置好了,不需要操心。 ref
依照我目前使用的結果下來,在 heroku 以及 AWS Lambda 同時睡著的情況下,AWS 起床的回應速度大概 1 秒左右,而 heroku 則大概落在 10~15 秒(參考)。
如下圖所示的免費方案,基本上開發階段應該是不至於到 100 萬/月 個請求吧 ?,所以這部分就別擔新大力地給他用下去!


serverless.yaml 的設定參考 serverless.yaml 的變數傳遞
接下來會是使用 Serverless 這個框架去實作的,使用的語言為 Python,至於為什麼會選它呢,最主要原因還是在官網上有很多 plugin 可以用 ( NodeJS & Python ),開發時資源相對多,接著是 AWS Lambda 的 Code-start 最快的就屬於 NodeJS & Python 了,而我原本就是寫 Ruby 來的,自然就選一個寫法相近的語言啦(好像有點牽強),如果說你想了解 runtimes 相關的問題的話可以參考這篇。