無伺服器架構算是在應用程式開發上滿多採用的方式,而 Google Cloud 提供了這款 API Gateway,可讓 Google Cloud 無伺服器後端(包括 Cloud Functions、Cloud Run 和 App Engine)建立、保護和監控 API。讓用戶開發團隊能夠創建、管理和監控用於無伺服器工作負載的 API。
包括整合多支 API 到同一個 endpoint,透過 API Gateway 可以整到同一個端點底下來方便管理。其次是服務規模大到提供商業用途的 API,這時可能會有不同服務等級等級的區分或依照價格來控制每秒請求的流量,這時便可以透過 API Gateway 來統一管理。安全性上也可以設定要求所有流入呼叫都必須提供合法 API Key 進行驗證和鍵驗證,並可以設定配額和限制,以防止 API 被濫用。透過 API Gateway,算是為開發人員提供了一種更簡單、更安全的方式來管理和共享無伺服器功能。
背後的基礎架構包括有運用到了 Extensible Service Proxy(ESPv2),一個高效能的反向代理, 運行在 Google 的全球網路基礎架構上,用於將請求轉送到後端服務。ESPv2,並提供了負載平衡、身份驗證、監視和日誌記錄等功能。其次是Google Cloud Endpoints 提供了管理和託管API 的功能,並允許開發人員在Google Cloud Platform 上輕鬆建立和管理 API。 Cloud Endpoints 支援RESTful API 和 gRPC API,並提供了與 ESPv2 和 Service Control 整合的功能。
- 讓應用程式開發者可以輕鬆使用服務
- 能夠更改後端服務實現,而不會影響公共 API
- 能夠利用 Google Cloud Platform 內建的擴充、監控和安全功能
本次實作流程
1. 建立 API Gateway
在Google Cloud Console 中建立一個 API Gateway,指定所需的端點、路徑、HTTP 方法等。你可以定義GCF 的端點作為後端服務。確保你配置了適當的身份驗證和授權,以及適當的安全性原則。
2. 設定 API Gateway 呼叫 GCF
在 API Gateway 的配置中,將 GCF 配置為後端服務。這包括指定 GCF 的端點、HTTP 方法和其他必要的參數。確保你的 GCF 已經配置了適當的權限,以允許 API Gateway 呼叫 GCF。
3. 設定認證和授權
在 API Gateway 中,你可以設定各種認證和授權機制,例如 API 金鑰、OAuth 2.0、Service Account 等。確保你設定了正確的身份驗證方法,並且配置了相應的權限以允許呼叫 GCF。
👣Step01. 部署 API 後端
API Gateway 位於已部署的後端服務前面,並處理所有傳入請求。在本快速入門中,API Gateway 將傳入的呼叫路由至名為 hello-world
的 Cloud Functions 後端,該後端包含如下所示的函數
建立 API Gateway 說明
API Gateway 由兩個主要元件組成,流程上要建立 API Config 與 Gateway
- API Config:上傳 API 定義時所建立的配置。API 定義以 OpenAPI 規格為基礎,定義API 的結構和行為。開發人員可以使用YAML 或JSON 格式的OpenAPI 規範文件來描述API 的路徑、操作、參數、請求和回應等內容。每次上傳 API 定義時,API Gateway 都會建立一個新的 API 配置。我們可以建立 API Config,但比較麻煩的是無法事後修改。如果之後需在 OpenAPI 規格中調整 API 定義,需要重新建立一個新的 API Config 並更新 API Gateway 方式來進行(如何更新 API Gateway 請參照最下方補充)。
- Gateway: 基於 Envoy 的高效能、可擴展代理,託管已部署的 API 配置。網關部署到特定的 GCP 區域(區域是我們可以部署資源的特定地理區域)。每個 Gateway 必須託管 API Config,所有 Gateway 都需要一個 API Config。然而,在建立 Gateway 後,我們可以更新 Gateway 以將一個 API Config 替換為另一個 API Config。
👣Step02. 啟用 API Gateway
API Gateway 要求您啟用以下Google 服務
gcloud services enable apigateway.googleapis.com
gcloud services enable servicemanagement.googleapis.com
gcloud services enable servicecontrol.googleapis.com
👣Step03. 建立 Gateway 和 API Config
Web UI 把這兩部份都整合在一起了,操作起來還滿快的。
# 1.創建 API Gateway 的 API -> 2.創建 API Config -> 3. 建立 Gateway
# 步驟 1.
gcloud api-gateway apis create API_ID --project=PROJECT_ID
# 成功完成後,您可以使用以下命令查看有關新API 的詳細資訊
gcloud api-gateway apis describe API_ID --project=PROJECT_ID
# 輸出範本參考
createTime: '2024-04-12T09:42:32.237107924Z'
displayName: test-function-api
managedService: test-function-api-xxx.apigateway.{PROJECT_ID}.cloud.goog
name: projects/{PROJECT_ID}/locations/global/apis/test-function-api
state: ACTIVE
updateTime: '2024-04-12T09:44:09.677763051Z'
2️⃣ 建立API Config:這邊就要使用包含專用註解的 OpenAPI 規範來建立 API 配置,以定義所需的 API Gateway 行為
swagger: "2.0"
info:
title: test-function-api
description: Call cloud function to Hello World
version: "1.0.0"
schemes:
- "https"
paths:
/hello:
post:
summary: Call cloud function to Hello World
consumes:
- application/json
operationId: hello_world
x-google-backend:
address: https://GCP_REGION-PROJECT_ID.cloudfunctions.net/hello-world
security:
- api_key: []
parameters:
- name: "message"
in: "body"
description: "post message"
required: true
schema:
type: object
properties:
message:
type: string
responses:
'200':
description: Successful response
schema:
type: string
securityDefinitions:
api_key:
type: "apiKey"
name: "key"
in: "query"
接續後進行設定,設定作業可能需要幾分鐘才能完成,因為 API 配置會需要傳播。複雜API 配置的建立可能需要 10 分鐘才能完成。
# 1.創建 API Gateway 的 API -> 2.創建 API Config -> 3. 建立 Gateway
# 步驟 2.
gcloud api-gateway api-configs create CONFIG_ID \
--api=API_ID --openapi-spec=API_DEFINITION \
--project=PROJECT_ID --backend-auth-service-account=SERVICE_ACCOUNT_EMAIL
# 創建 API Config 指令參考
gcloud api-gateway api-configs create my-config \
--api=my-api --openapi-spec=openapi2-functions.yaml \
--project=my-project --backend-auth-service-account=0000000000000-compute@developer.gserviceaccount.com
查看一下設定後的結果
# 查詢 api-configs 指令
gcloud api-gateway api-configs describe CONFIG_ID \
--api=API_ID --project=PROJECT_ID
# 輸出範本參考
createTime: '2024-04-12T09:44:15.666448513Z'
displayName: test-function-api-config
gatewayServiceAccount: projects/-/serviceAccounts/{PROJECT_ID}@appspot.gserviceaccount.com
name: projects/xxxxxxxx/locations/global/apis/test-function-api/configs/test-function-api-config
serviceConfigId: test-function-api-xxx
state: ACTIVE
updateTime: '2024-04-12T09:46:56.203370368Z'
3️⃣ 建立 Gateway
# 1.創建 API Gateway 的 API -> 2.創建 API Config -> 3. 建立 Gateway
# 步驟 3.
gcloud api-gateway gateways create GATEWAY_ID \
--api=API_ID --api-config=CONFIG_ID \
--location=GCP_REGION --project=PROJECT_ID
# 查詢 Gateway 指令
gcloud api-gateway gateways describe GATEWAY_ID \
--location=GCP_REGION --project=PROJECT_ID
# 輸出範本參考
apiConfig: projects/xxxxxxx/locations/global/apis/test-function-api/configs/test-function-api-config
createTime: '2024-04-12T09:47:02.892839066Z'
defaultHostname: test-function-api-config-xxx.an.gateway.dev
displayName: test-function-api-config
name: projects/PROJECT_ID/locations/asia-northeast1/gateways/test-function-api-config
state: ACTIVE
updateTime: '2024-04-12T09:50:43.634529997Z'
記下 defaultHostname
屬性的值。這是下一個步驟中用於測試部署的網關網址的主機名稱部分,此外地區別也有一些限制!
👣Step04. Enable your API
上面都啟用後就能在 APIs & Services
中找到自訂的 API,接著把他 ENABLE 起來。
並留意我們在 API Config 定義了 API Gateway 的安全性配置以及 API Key 的安全定義。
回顧一下 yaml 設定
security:
- api_key: []
...
securityDefinitions:
api_key:
type: "apiKey"
name: "key"
in: "query"
security
- api_key: []
: 這表示 API 使用了基於 API Key 的安全機制。在這個配置中,API 的每個請求都需要提供有效的 API Key 才能被授權訪問。這個列表中可以包含其他安全配置,但在例子中我只使用了 API Key。
securityDefinitions
api_key:
: 這裡定義了名為 "api_key" 的安全定義type: "apiKey"
: 這表示使用了 API Key 類型的安全機制name: "key"
: 這是 API Key 的名稱,在請求中使用時必須與此匹配in: "query"
: 這指定了 API Key 是通過查詢參數的形式傳遞。因此,在發送 API 請求時,API Key 將作為查詢參數的一部分,例如https://example.com/api?key=your_api_key
若下階段在測試 API 時如果沒有帶 credential 及符合 query 的 pattern 話,將會發生 UNAUTHENTICATED
如下的警訊
curl -X POST https://test-function-api-config-xxx.an.gateway.dev/hello
{"message":"UNAUTHENTICATED: Method doesn't allow unregistered callers (callers without established identity). Please use API Key or other form of API consumer identity to call this API.","code":401}
👣Step05. 測試 API
$ export GATEWAY_URL=$(gcloud api-gateway gateways describe test-function-api-gateway --location "asia-northeast1" --format json | jq -r .defaultHostname)
$ export API_KEY=XXX
$ curl -sL -w "\n" -X POST https://$GATEWAY_URL/hello?key=$API_KEY \
-H "Content-Type: application/json" \
-d '{"message": "Hello Funk World"}'
# Result
Hello Funk World
# 或是
curl -X POST https://test-function-api-gateway-XXX.an.gateway.dev/hello?key=YOUR_API_KEY \
-H "Content-Type: application/json" \
-d '{"message": "Hello Funk World"}'
# 簡單測試
for i in {1..6}; do
curl -X POST https://test-function-quota-xxx.an.gateway.dev/hello?key=xxx \
-H "Content-Type: application/json" \
-d '{"message": "Hello Funk World"}'
sleep 1
done
另外, 也提供了豐富的遙測功能,可以透過 Dashboard 來監控 API 的使用量。可以至 API & Services Dashboard 查閱相關呼叫的資訊如 Error Rate, Latency, Response 等 API Status 統計!
API Gateway Dashboard 這邊也可以查看 4xx, 5xx 等資訊
👣Step06. 將 API Gateway 進行限流功能
根據 Google API Gateway 文檔完全沒有提供此實作,可能是免費套餐或是鼓勵轉往 APIGEE 這類的 Gateway 等級。實際上 GCP 也有一些 Quota 或限流的限制,例如從 IAM & Admin
的頁面登入至 Quotas & System Limits
會很好奇到底 Quotas and limits 可以作到什麼程度!但官方文檔大概就只有以下內容
不過還是參考了一些資訊,將 configure Quotas 給刻出來,主要留意以下這三個部份
- 命名 quotas
- 設定 default rate(query per minute and per project)
- 定義 cost of each paths call
開始設定
x-google-management:
metrics:
- name: "hello-world-request"
displayName: "HelloWorld Quota"
valueType: INT64
metricKind: DELTA
特別注意是 x-google-management
是一個用於定義特定於 Google Cloud 的管理配置的 root level。它通常用於定義與 API 配額和指標相關的管理屬性。
在這個配置中,我們將 x-google-management
添加到 YAML 文件的 root level,並定義了一個指標:
hello-world-limit
name
:指標的名稱displayName
:指標的顯示名稱,用於在介面上顯示valueType
:指標值的數據類型,此處設置為 INT64,表示整數metricKind
:指標的類型,此處設置為 DELTA,表示增量
quota:
limits:
- name: "hello-world-limit"
metric: "hello-world-request"
unit: "1/min/{project}"
values:
STANDARD: 5
在這個配置中,我們定義了一個名為 hello-world-limit
的配額限制。該配額限制與一個特定的指標(hello-world-request
)相關聯,並設置了限制的計量單位為每分鐘 {project}
。這意味著在一分鐘內,每個專案都可以進行最多 5 次請求。
name
:配額限制的名稱,此處設置為hello-world-limit
metric
:配額限制所基於的指標的名稱,此處設置為hello-world-request
unit
:配額限制的計量單位,此處設置為1/min/{project}
,表示每分鐘每個專案的配額values
:對於不同配額等級(例如STANDARD
),定義了配額限制的具體值。在這個例子中,STANDARD
配額限制為 5
尚可以運作的 Quota OpenAPI spec. 就長出來了
swagger: "2.0"
info:
title: test-function-quota
description: Call cloud function to Hello World with Quota
version: "1.0.0"
schemes:
- "https"
produces:
- application/json
x-google-management:
metrics:
- name: "hello-world-request"
displayName: "HelloWorld Quota"
valueType: INT64
metricKind: DELTA
quota:
limits:
- name: "hello-world-limit"
metric: "hello-world-request"
unit: "1/min/{project}"
values:
STANDARD: 5
paths:
/hello:
post:
summary: Call cloud function to Hello World
consumes:
- application/json
operationId: hello_world
x-google-backend:
address: https://asia-east1-xxx.cloudfunctions.net/hello-world
security:
- api_key: []
x-google-quota:
metricCosts:
"hello-world-request": 1
parameters:
- name: "message"
in: "body"
description: "post message"
required: true
schema:
type: object
properties:
message:
type: string
responses:
'200':
description: Successful response
schema:
type: string
securityDefinitions:
api_key:
type: "apiKey"
name: "key"
in: "query"
再從 Step03 ~ 05 重新定義一個新的 API Gateway,並命名為 test-function-quota 背後的 Cloud Function 則先繼續延用
但實測下來,會覺得產品的精準度上不是很優,會有一些統計上的延遲,導致雖然已符合了限流條件,但還可以持續提供 API 服務運作,可能之後在查找或驗證這段。
👣Step07. 刪除 API Gateway
- Disable API
- 分別 Delete Gateways -> Configs 再執行以下指令
gcloud api-gateway apis delete GatewayAPIName --project=PROJECT-ID
還沒時間研究一下為什麼直接 delete 會無法刪除,目前使用依序作業的方式是可以將 API Gateway 給完全刪除。
補充 — 叫用 GCF Gen1 & 2 的差異
- Cloud Functions(第一代):對於第一代函數執行以下命令,呼叫者角色是 Cloud Functions Invoker (
roles/cloudfunctions.invoker
)
需要向接收函數上的呼叫函數的服務帳戶授予適當的呼叫者角色。用於存取 Cloud Functions 的使用者帳戶指派了該cloudfunctions.functions.invoke
權限角色。
gcloud functions add-iam-policy-binding RECEIVING_FUNCTION \
--member='serviceAccount:CALLING_FUNCTION_IDENTITY' \
--role='roles/cloudfunctions.invoker'
- Cloud Functions(第二代):對於第二代函數執行以下命令,呼叫者角色是 Cloud Run Invoker (
roles/run.invoker
),必須在底層服務上授予。
gcloud functions add-invoker-policy-binding RECEIVING_FUNCTION \
--member='serviceAccount:CALLING_FUNCTION_IDENTITY'
常見錯誤
補充:排障
若有以下異常,是因為 API Gateway 預設超時設定為 15 秒,但有時後面的 backend Function 或 Cloud Run 可能會處理超過這個時間,建議調整 API Gateway 逾時時間即可!
{
"code": 504,
"message": "upstream request timeout"
}
先看一下文件
太棒了,成功了,另需注意 function gen1, gen2 timeout 秒數不同。
補充:API Gateway 如何更新
官方文件中有提供 API Config 只能更改顯示名稱和標籤,那我的 API spec 有更動怎麼辦?
- 只能透過指令的方式作業,Web UI 只能全部砍掉重新創建( Web UI 就只提供刪除功能)
# 原先步驟:1.創建 API Gateway 的 API -> 2.創建 API Config -> 3. 建立 Gateway
# 更新步驟:1.創建 API Config -> 2. 更新Gateway
# 1. 首先使用更新的 openapi spec 後創建 API Config
gcloud api-gateway api-configs create ★NEW_CONFIG_ID \
--api=API_ID \
--openapi-spec=openapi-spec-new.yaml \
--project=PROJECT_ID \
--backend-auth-service-account=SERVICE_ACCOUNT_EMAIL
# 2. 更新Gateway
gcloud api-gateway gateways update GATEWAY_ID \
--api=API_ID \
--api-config=★NEW_CONFIG_ID \
--location=GCP_REGION
# 選項:刪除舊的 OLD_CONFIG_ID
gcloud api-gateway api-configs delete OLD_CONFIG_ID --api=API_ID
【問】GCP Web UI 不能直接調整後套用嗎?
【答】感覺滿好的功能,但不知道是不是免費套餐這個被放到 freeze backlog 中。
Reference
- 使用 Google Api Gateway 為 Google Cloud Functions 加上 API Key 保護
- Rate limit Google Cloud Functions with API Gateway
- Exploring Google Cloud API Gateway with Google Cloud Functions
- Learn how to invoke authenticated Cloud Functions
- https://support.okta.com/help/s/article/Invoke-Function-in-Google-Cloud-Functions-causes-a-403-Forbidden-error?language=en_US