template.yaml
設定lambda_handler()
因為內容有些許連貫性,建議先閱讀昨天的文章喔([Day-07] 讓 AWS Lambda 定時執行 Python 爬蟲程式)
昨天 Day-07 的發文迴響屬實讓我感到非常驚訝與感動,凌晨 12 點壓線寫完收尾後,不過出門吃個宵夜的功夫,回來就發現有超過 30 次的觀看數,觸及率比先前的每篇發文都高,也帶動了其他發文的觀看。瞬間有種努力與堅持獲得回報的欣慰感,當然還是要繼續向板上那些幾百幾千觀看的個人挑戰發文看齊。
由於昨天的最後超級匆忙,在 AWS SAM CLI 使用的介紹略顯不夠完整,所以今天先以這部分的補充作為開場,後續則帶來使用 AWS SAM 部署 AWS Lambda、API Gateway 來建立新聞資料的 API 的範例,為之後從資料庫獲取前端網頁所需的資料做準備。
如果你只對「以 AWS Lambda 和 API Gateway 建立資料 API」有興趣,可以先跳過此段,有需要再回來查看。
先查看一下 AWS SAM CLI(Command line interface)的基本用法,在終端機輸入指令 sam --help
:
❯ sam --help
Usage: sam [OPTIONS] COMMAND [ARGS]...
AWS Serverless Application Model (SAM) CLI
The AWS Serverless Application Model extends AWS
CloudFormation to provide a simplified way of defining the
Amazon API Gateway APIs, AWS Lambda functions, and Amazon
DynamoDB tables needed by your serverless application. You
can find more in-depth guide about the SAM specification
here: https://github.com/awslabs/serverless-application-
model.
Options:
--debug Turn on debug logging to print debug message
generated by SAM CLI and display timestamps.
--version Show the version and exit.
--info
-h, --help Show this message and exit.
Commands:
init Init an AWS SAM application.
validate Validate an AWS SAM template.
build Build your Lambda function code
local Run your Serverless application locally for quick...
package Package an AWS SAM application.
deploy Deploy an AWS SAM application.
delete Delete an AWS SAM application and the artifacts
created by sam deploy.
logs Fetch logs for a function
publish Publish a packaged AWS SAM template to the AWS
Serverless Application Repository.
traces Fetch AWS X-Ray traces
sync Sync a project to AWS
pipeline Manage the continuous delivery of the application
下方整理部分擷取自 AWS SAM CLI command reference
sam [OPTIONS] COMMAND [ARGS]...
中[OPTIONS]
可選用下列選項(無選用也可)
--debug
--version
SAM CLI, version 1.55.0
的版本資訊。--info
{"version": "1.55.0"}
,包含版本資訊的 json。-h
或 --help
sam [OPTIONS] COMMAND [ARGS]...
中COMMAND
可選用下列選項
sam init [ARGS]...
用來依照 SAM template (e.g. template.ymal
)初始化無伺服器應用程式,建 Lambda 函數的資料夾結構,連接到 event source(例如 API、S3 Buckets 或 DynamoDB Table)。
我推薦使用 sam init
不加任何 [ARGS]...
,進行互動式的設定操作(Day-07 昨天有範例)。
[ARGS]...
的用法可以使用 sam init --help
查看。
sam validate [OPTIONS]
驗證 AWS SAM 模板檔案有效的同時,也會檢查 AWS config 和要部署檔案的區域(例如 us-east-1)等資訊。
沒特別需求使用 sam validate
就可以了。
[OPTIONS]
的用法可以參考 sam validate
sam build [OPTIONS] [RESOURCE_LOGICAL_ID]
使用此命令構建(build)的 AWS Lambda 函數程式碼,並產生針對 AWS Lambda 執行環境的工件。
可以針對指定的 Lambda 進行建構:
To build the 'MyFunction' resource
$ sam build MyFunction
To build the 'MyFunction' resource of the 'MyNestedStack' nested stack
$ sam build MyNestedStack/MyFunction
像是 Day-07 昨天範例中使用 Image 的 Lambda 函數,在建構時要先啟用 Docker,然後接著執行 sam build
就可以了。
[OPTIONS]
的用法可以參考 sam Build
sam local [OPTIONS] COMMAND [ARGS]...
用來在本地端執行無伺服器應用程式,給有快速部署其測試需求使用,又近一步細分為下面幾個主要功能:
sam local generate-event [OPTIONS] COMMAND [ARGS]...
從不同的事件源(如 Amazon S3、Amazon API Gateway 和 Amazon SNS)產生(在終端機印出來)有效負載範例。這些負載範例包含發送到 Lambda 函數的事件源信息(lambda_hander(context, event)
的 event
輸入)。
可用的事件源可以執行 sam local generate-event --help
查詢,
其餘細節可在 sam local generate-event 中查找。
sam local invoke [OPTIONS] [FUNCTION_LOGICAL_ID]
用來呼叫本地 AWS Lambda 函數一次,並在調用完成後退出。
搭配 -e, --event EVENT_JSON_PATH
可以採用 EVENT_JSON_PATH
這個位置的 json 檔案內容作為 lambda_hander(context, event)
的 event
輸入,相當實用。
其餘細節可在 sam local invoke 中查找。
sam local start-api [OPTIONS]
sam local start-lambda [OPTIONS]
# SETUP
# ------
# Start the local Lambda endpoint by running this command in the directory that contains your AWS SAM template.
sam local start-lambda
# USING AWS CLI
# -------------
# Then, you can invoke your Lambda function locally using the AWS CLI
aws lambda invoke --function-name "HelloWorldFunction" --endpoint-url "http://127.0.0.1:3001" --no-verify-ssl out.txt
sam package [OPTIONS] [ARGS]...
用以封裝 AWS SAM 應用程式。此命令會建立.zip檔案,然後將檔案上傳至 Amazon Simple Storage Service (Amazon S3)。
如果跟昨天的範例一樣選用 Image 而不是 zip 則不會用到。對我來說不常用。
其餘細節可在 sam package 中查找。
用來部署此 AWS SAM 應用程式.
在預設的情況下,當使用此命令時,AWS SAM CLI 會假定當前工作目錄是項目的根目錄。所以此 AWS SAM CLI 首先嘗試找到使用 sam build
命令時建立,位於 .aws-sam
資料夾,並命名為 template.yaml
或者template.yml
的檔案。接下來,AWS SAMCLI 嘗試找到名為 template.yaml
或者 template.yml
在目前的工作目錄。如果指定--template選項,AWS SAMCLI 的默認行為被覆蓋,並將加載 AWS SAM 模板及其指向的本地資源。最後依照指定的 template.yaml
進行資源部署。
首次部署建議使用 sam deploy --guided
的互動模式,過程中會有個選項將部署選項紀錄在 samconfig.toml
,第二次後的部署就可以直接採用 sam deploy
,部署設定將會自動從 samconfig.toml
導入。
其餘細節可在 sam deploy 中查找。
sam delete [OPTIONS]
用來移除所有此無伺服器應用在 AWS 上的資源。
如果需要輸入 --stack-name
可以在 AWS CloudFormation > 堆疊 的網頁管理頁面中找到堆疊名稱。
其餘細節可在 sam deploy 中查找。
sam logs [OPTIONS]
獲取由 Lambda 函數生成的日誌(logging)。
我建議直接在網頁版的 AWS 上查會比印在終端機中好閱讀。
其餘細節可在 sam log 中查找。
sam publish [OPTIONS]
指令會發佈 AWS SAM 應用程式,並添加到 AWS Serverless Application Repository。採用一個打包 AWS SAM 範本,並將應用程式發佈至指定的AWS區域。
我通常直接使用 sam deploy
其餘細節可在 sam publish 中查找。
sam traces [OPTIONS]
指令用來獲得 AWS 帳號所處區域的 AWS X-Ray 追蹤結果。
AWS X-Ray 服務可協助開發人員分析和偵錯生產、分散式應用程式,例如那些使用微型服務架構的建置成果。採用 X-Ray,可以了解應用程式及其基礎服務的執行方式,以識別和疑難排解效能問題與錯誤的根本原因。(參考自:AWS X-Ray)
其餘細節可在 sam traces 中查找。
sam sync [OPTIONS]
會將本地更改部署到 AWS 雲端。使用sync在您對應用程序進行構建、打包和部署更改到開發環境。
我通常直接使用 sam deploy
,將本地更改部署到 AWS 雲端。
其餘細節可在 sam sync 中查找。
sam pipeline init
建立 GitHub Action CI/CD 的範例:
❯ sam pipeline init
sam pipeline init generates a pipeline configuration file that your CI/CD system
can use to deploy serverless applications using AWS SAM.
We will guide you through the process to bootstrap resources for each stage,
then walk through the details necessary for creating the pipeline config file.
Please ensure you are in the root folder of your SAM application before you begin.
Select a pipeline template to get started:
1 - AWS Quick Start Pipeline Templates
2 - Custom Pipeline Template Location
Choice: 1
Cloning from https://github.com/aws/aws-sam-cli-pipeline-init-templates.git (process may take a moment)
Select CI/CD system
1 - Jenkins
2 - GitLab CI/CD
3 - GitHub Actions
4 - Bitbucket Pipelines
5 - AWS CodePipeline
Choice: 3
You are using the 2-stage pipeline template.
_________ _________
| | | |
| Stage 1 |->| Stage 2 |
|_________| |_________|
Checking for existing stages...
[!] None detected in this account.
To set up stage(s), please quit the process using Ctrl+C and use one of the following commands:
sam pipeline init --bootstrap To be guided through the stage and config file creation process.
sam pipeline bootstrap To specify details for an individual stage.
...
後續需要填寫一些資訊
...
許多網頁會透過呼叫 API 來獲得回傳的資訊,再將這些資訊經過適當分析處理後顯示在網頁上。舉例來說如果你呼叫以下 WorldTimeAPI(可以貼在瀏覽器網址欄位測試):
http://worldtimeapi.org/api/timezone/Asia/Taipei
就可以取得如下的台北時間資訊:
{"abbreviation":"CST","client_ip":"218.164.5.168","datetime":"2022-09-23T21:55:37.140822+08:00","day_of_week":5,"day_of_year":266,"dst":false,"dst_from":null,"dst_offset":0,"dst_until":null,"raw_offset":28800,"timezone":"Asia/Taipei","unixtime":1663941337,"utc_datetime":"2022-09-23T13:55:37.140822+00:00","utc_offset":"+08:00","week_number":38}
呼叫的 API(http://worldtimeapi.org/api/timezone/Asia/Taipei
)當中的路徑 /Asia/Taipei
代表以 /:area/:location
格式作為路徑的兩個參數,所以如果待換成 Europe/London
則可以獲取倫敦的時間。
現在要為我的「基於自然語言處理的新聞意見提取應用」建一個能從建立好的 PostgreSQL 資料庫取得新聞資料的 API。這裡先實作獲取新聞資料的 API,此 API 會具備以下格式的 URL:
https://.../PATH
而當中的 PATH 則需要自己根據功能來設計,我為獲取新聞資料這個功能設計了對應的路徑結構,如下方所示:
使用 https://.../list
(PATH=/list) 呼叫 API 時,會得到來自所有媒體的新聞資料。而使用 https://.../list/chinatimes
(PATH=/list/chinatimes)呼叫 API ,則只會得到來自中時新聞網的新聞資料,https://.../list/cna
(PATH=/list/cna)擇取得中央社新聞資料。
接下來將以上面「從建立好的 PostgreSQL 資料庫取得新聞資料」的想法,示範以 AWS Lambda 和 API Gateway 建立新聞資料 API。
由於會用到 AWS Lambda 和 API Gateway 兩項服務來建立新聞資料 API,下面整理了還沒介紹過的 AWS API Gateway 服務介紹:
擷取自 Amazon API Gateway
Amazon API Gateway 是一種全受管的服務,可讓開發人員輕鬆地建立、發佈、維護、監控和保護任何規模的 API。API 可作為應用程式的「前門」,以便從後端服務存取資料、商業邏輯或功能。使用 API Gateway 時,您可以建立 RESTful API 和 WebSocket API,以啟用即時雙向通訊應用程式。API Gateway 支援容器化、無伺服器工作負載和 Web 應用程式。
API Gateway 負責處理有關接受和處理多達數十萬個並行 API 呼叫的所有工作,包括流量管理、CORS 支援、授權和存取控制、調節、監控和 API 版本管理。
首先可以透過與昨天相同的做法,執行 sam init
,建立一個範例的檔案結構,再將其做修改,過程如下:
❯ sam init
You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Choose an AWS Quick Start application template
1 - Hello World Example
2 - Multi-step workflow
3 - Serverless API
4 - Scheduled task
5 - Standalone function
6 - Data processing
7 - Infrastructure event management
8 - Lambda EFS example
9 - Machine Learning
Template: 1
Use the most popular runtime and package type? (Python and zip) [y/N]: N
Which runtime would you like to use?
1 - dotnet6
2 - dotnet5.0
3 - dotnetcore3.1
4 - go1.x
5 - graalvm.java11 (provided.al2)
6 - graalvm.java17 (provided.al2)
7 - java11
8 - java8.al2
9 - java8
10 - nodejs16.x
11 - nodejs14.x
12 - nodejs12.x
13 - python3.9
14 - python3.8
15 - python3.7
16 - python3.6
17 - ruby2.7
18 - rust (provided.al2)
Runtime: 13
What package type would you like to use?
1 - Zip
2 - Image
Package type: 2
Based on your selections, the only dependency manager available is pip.
We will proceed copying the template using pip.
Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]: N
Project name [sam-app]:
注意:
Project name [sam-app]:
可以按 enter 使用預設的 sam-app 或是自行命名。
完成後獲得到如下的檔案結構:
❯ tree
.
└── sam-app
├── README.md
├── __init__.py
├── events
│ └── event.json
├── hello_world
│ ├── Dockerfile
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── template.yaml
└── tests
├── __init__.py
└── unit
├── __init__.py
└── test_handler.py
5 directories, 11 files
這次的 API 應用則要針對 template.yaml
、app.py
和 requirements.txt
進行更改。
template.yaml
設定將上面檔案結構中的 template.yaml
替換成下面的設定:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
python3.9
Sample SAM Template for database-api
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 60
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
Architectures:
- x86_64
Events:
GetAll:
Type: Api
Properties:
Path: /all
Method: get
GetChinatimes:
Type: Api # More info about API Event Source:
Properties:
Path: /chinatimes
Method: get
GetCna:
Type: Api # More info about API Event Source:
Properties:
Path: /cna
Method: get
Policies: AWSLambdaVPCAccessExecutionRole
VpcConfig:
SecurityGroupIds:
- sg-XXXX
SubnetIds:
- subnet-XXXX
Metadata:
Dockerfile: Dockerfile
DockerContext: ./hello_world
DockerTag: python3.9-v1
當中可以觀察到,我將觸發 Lambda 函數的事件(Events)換成多個 API,說明如下
Events: # 觸發Lambda 函數的事件(Events)
GetAll: # 用來獲取所有媒體新聞資料的 API
Type: Api
Properties:
Path: /list # 此函數被調用的 Uri 路徑
Method: get # 採用 GET HTTP 方法調用此函數
GetChinatime:s # 用來獲取中時新網新聞資料的 API
Type: Api
Properties:
Path: /list/chinatimes # 此函數被調用的 Uri 路徑
Method: get # 採用 GET HTTP 方法調用此函數
GetCna: # 用來獲取中央社新聞資料的 API
Type: Api
Properties:
Path: /list/cna # 此函數被調用的 Uri 路徑
Method: get # 採用 GET HTTP 方法調用此函數
上面是我針對示範的任務所建立的配置,如果要針對自己的應用進行調整,可以參考 Api
下方片段則按照 [Day-07] 讓 AWS Lambda 定時執行 Python 爬蟲程式中的介紹,填入與 PostgreSQL 資料庫相同的 VPC 設定。
Policies: AWSLambdaVPCAccessExecutionRole
VpcConfig:
SecurityGroupIds:
- sg-XXXX
SubnetIds:
- subnet-XXXX
lambda_handler()
由於這個 Lambda 應用程式需要使用 Python 連線到前幾天建立的 PostgreSQL 資料庫,在 [Day-06] 以 PostgreSQL 建立新聞資料庫(採用 AWS Amazon Aurora Serverless v2) 有提到會需要 psycopg 這個套件。所以先將 psycopg 紀錄在 requirements.txt
當中,依照 Dockerfile
(維持 sam init
後的 Dockerfile
)中所設定的流程, psycopg 會被安裝。
將 psycopg 紀錄在 requirements.txt
當中(注意要採用二進制版本):
psycopg[binary]
接著將 app.py
替換成下列程式碼:
import json
import psycopg
from psycopg.rows import dict_row
def lambda_handler(event, context):
path = event["path"]
conninfo = f"host={DB_INSTANCE_ENDPOINT} dbname={DB_NAME} port={PORT} user={MASTER_USER_NAME} password={PASSWORD}"
with psycopg.connect(conninfo) as conn:
# Open a cursor to perform database operations
with conn.cursor(row_factory=dict_row) as cur:
# Query the database and obtain data as Python objects.
if path == "/list":
cur.execute("SELECT * FROM newscollecttest ORDER BY time DESC LIMIT 20")
elif path == "/list/chinatimes":
cur.execute("SELECT * FROM newscollecttest WHERE source='中時新聞網' ORDER BY time DESC LIMIT 20")
elif path == "/list/cna":
cur.execute("SELECT * FROM newscollecttest WHERE source = '中央社' ORDER BY time DESC LIMIT 20")
result = cur.fetchall()
print(result)
return {
"statusCode": 200,
"headers": {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET'
},
"body": json.dumps(
{
"data": result,
},
default=str,
ensure_ascii=False
),
}
上面的程式碼中先透過 path = event["path"]
來取得被呼叫的 API 的路徑(e.g. /list/cna
),配合 if 流程去執行對應的 SQL 取得 20 筆新聞資料,再藉由 result = cur.fetchall()
或得回傳的每筆資料(注意有搭配 from psycopg.rows import dict_row
一併使用)。
另外,當中的 conninfo = f"host={DB_INSTANCE_ENDPOINT} dbname={DB_NAME} port={PORT} user={MASTER_USER_NAME} password={PASSWORD}"
需要依照 [Day-06] 以 PostgreSQL 建立新聞資料庫(採用 AWS Amazon Aurora Serverless v2) 中的說明,換成資料庫連線所需的資訊。
return {
"statusCode": 200,
"headers": {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET'
},
"body": json.dumps(
{
"data": result,
},
default=str,
ensure_ascii=False
),
}
這部分會作為 API 的 response,"statusCode": 200
表示成功,此處 headers
的設定能解決在呼叫 API 遇到的 CORS 錯誤(參考 如何對 API Gateway API 的 CORS 錯誤進行疑難排解?)。"body"
則包含了 json 格式的回傳資料,使用 json.dumps
搭配 ensure_ascii=False
是為了避免將中文轉成 ascii 的顯示方式。
將 sam-app/events/event.json
的內容改成下面的 json:
{
"path": "/list"
}
換成
/list/chinatimes
、/list/chinatimes
也可以測試。
運行(run)Docker。
執行 sam build
最後執行 sam local invoke "HelloWorldFunction" --event ./events/event.json
就可以看到 API 的回傳結果顯示在終端機中(因為有加 print(result)
在 app.py
)。
注意
"HelloWorldFunction"
是根據template.yaml
中的定義。
首次部署建議使用 sam deploy --guided
的互動模式,過程中會有個選項將部署選項紀錄在 samconfig.toml
,第二次後的部署就可以直接採用 sam deploy
,部署設定將會自動從 samconfig.toml
導入。
根據 在 Amazon API Gateway 中叫用 REST API 中的指示,獲得具備以下格式的 URL:
https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/
將 https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/
格式的末端加上你設計的 PATH (例如 /list/cna
),接著整串貼到瀏覽器網址欄,按 enter。
API 的回傳結果就會顯示在瀏覽器當中。
終於完成了!
寫的有些匆忙,如果文章有錯誤,歡迎指正~