昨天我們介紹了如何根據 Prometheus 中的 metrics 來自動擴展 Pod。今天,我們將使用 GCP 的訊息佇列服務 Pub/Sub,並實現當佇列中沒有訊息需要處理時,將 Pod 縮減至 0,以節省運算資源。
在 Kubernetes 中需要進行以下準備工作:
gcp-service-account-credential.json
憑證文件。gcp-service-account-credential.json
透過 Kubernetes Secrets 儲存至 Cluster。透過 UI 或 gcloud
command line tool 進行配置
# 需要先 login 與 指定 project
# gcloud auth login
# gcloud config set project PROJECT_ID
# 建立 topic
TOPIC_NAME=ithome2024.day11.topic
gcloud pubsub topics create $TOPIC_NAME
SUBSCRIPTION=ithome2024.day11.subscription
# 建立 subscription
gcloud pubsub subscriptions create --topic $TOPIC_NAME $SUBSCRIPTION
透過 UI 或 gcloud
command line tool 進行配置
PROJECT_ID=${you GCP project_id}
SERVICE_ACCOUNT_NAME=ithome-demo
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/pubsub.subscriber"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/pubsub.viewer"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
--role="roles/monitoring.viewer"
# Download service account key
# ⚠️注意:這個 key 不能外流,否則有 GCP 被外人操控的風險
gcloud iam service-accounts keys create ./gcp-service-account-credential.json \
--iam-account=$SERVICE_ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com
透過 kubectl 建立 secret
kubectl create secret generic pubsub-secret --from-file=gcp-service-account-credential.json=./gcp-service-account-credential.json
檢查是否成功建立
kubectl get secrets
NAME TYPE DATA AGE
pubsub-secret Opaque 1 110m
我們部署一個 listen GCP Pub/Sub 的 spring boot Application
# keda-pubsub-demo.yml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: keda-pubsub-demo
name: keda-pubsub-demo
namespace: ithome
spec:
replicas: 1
selector:
matchLabels:
app: keda-pubsub-demo
template:
metadata:
creationTimestamp: null
labels:
app: keda-pubsub-demo
scrape: spring-boot-prometheus-exporter
spec:
volumes:
- name: gcp-sa-credentials
secret:
secretName: pubsub-secret
items:
- key: gcp-service-account-credential.json
path: gcp-service-account-credential.json
containers:
- image: luciferstut/spring-boot-application-for-ithome2024:day12
imagePullPolicy: Always
name: spring-boot-application-for-ithome2024
env:
- name: GOOGLE_CLOUD_PROJECT
value: yihonggao-1548227860432
- name: PUBSUB_ITHOME2024_DAY11_SUBSCRIPTION
value: projects/yihonggao-1548227860432/subscriptions/ithome2024.day11.subscription
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /etc/gcp/gcp-service-account-credential.json
- name: GOOGLE_APPLICATION_CREDENTIALS_JSON
valueFrom:
secretKeyRef:
name: pubsub-secret
key: gcp-service-account-credential.json
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "512Mi"
cpu: "500m"
ports:
- name: web
containerPort: 8080
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 18
periodSeconds: 5
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 3
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
failureThreshold: 3
periodSeconds: 10
volumeMounts:
- name: gcp-sa-credentials
readOnly: true
mountPath: "/etc/gcp/"
部署該 Deployment ,並嘗試 Push 一個 message 看是否能成功消化。
# 推送 message to Pub/Sub Topic
gcloud pubsub topics publish $TOPIC_NAME --message='Hello, Pub/Sub!'
# 檢視 Pod log
kubectl logs -f $YOU_POD_NAME
看到這一行時,代表 Spring boot Application 成功與 GCP Pub/Sub 連上並能處理 message 了。
2024-09-03T16:12:26.052Z INFO 1 --- [iThome-2024] [sub-subscriber2] c.e.iThome_2024.config.PubSubConfig : Message arrived via an inbound channel adapter from sub-one! Payload: Hello, Pub/Sub!
接下來要來配置 KEDA 來自動擴展此 Deployment
為了讓 KEDA 能與 GCP 正常連線,我們需要配置 TriggerAuthentication,使 KEDA 知道如何攜帶憑證來進行認證與授權檢查。
# keda-scaled-auth.yml
apiVersion: keda.sh/v1alpha1
kind: TriggerAuthentication
metadata:
name: keda-trigger-auth-gcp-credentials
spec:
secretTargetRef:
- parameter: GoogleApplicationCredentials
name: pubsub-secret
key: gcp-service-account-credential.json
重點欄位解釋:
secretTargetRef
:從 Secrets 取得憑證
parameter
:憑證類型,GoogleApplicationCredentials
代表 GCP Service Account 的 json 憑證.name
:指定從哪個 Secrets 中取得憑證。key
:Secrets 中存放憑證的欄位名稱。稍後,我們將在 ScaledObject 配置中使用到這個 TriggerAuthentication 物件。
kubectl apply -f keda-scaled-auth.yml
# keda-scaled-object.yml
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: keda-pubsub-demo
namespace: ithome
spec:
fallback:
failureThreshold: 3
replicas: 1
pollingInterval: 5
cooldownPeriod: 60
idleReplicaCount: 0
minReplicaCount: 1
maxReplicaCount: 3
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: keda-pubsub-demo
triggers:
- type: gcp-pubsub
authenticationRef:
name: keda-trigger-auth-gcp-credentials
metadata:
mode: "SubscriptionSize"
value: "10"
activationValue: "5"
subscriptionNameFromEnv: PUBSUB_ITHOME2024_DAY11_SUBSCRIPTION
可以看到新增了許多欄位,首先從 triggers
開始說明:
type
: 設置為 gcp-pubsub
,代表監聽目標為 GCP Pub/Sub 服務
authenticationRef.name
: 使用上一步建立的 TriggerAuthentication
做為與 GCP 溝通的憑證mode
, value
: 這這兩個屬性組合表示,當 Pub/Sub 訂閱中的未處理訊息數超過 10 筆時,KEDA 將觸發擴展操作,增加 Pod 副本數。idleReplicaCount
, activationValue
: 這兩個屬性組合表示,當未處理訊息數低於 5 筆時,系統將進入 Inactive
狀態,並將 Pod 副本數縮減為 0。反之,進入 Active
狀態後,HPA 會接手擴容,並確保 Pod 副本數介於 minReplicaCount
和 maxReplicaCount
之間。
有關
Active
與Inactive
狀態,更多資訊,能參閱此官方文件
cooldownPeriod
: 當未處理訊息數持續低於 5 筆超過 60 秒後,才會進入 Inactive
狀態。其他欄位在之前的介紹中已有說明,不再重複。更詳細的介紹請參閱 官方文件
這個 ScaledObject 利用 GCP 的監控指標來檢查 Pub/Sub 訂閱的佇列長度,當佇列長度持續低於 5 筆超過 60 秒時,將 Pod 副本數縮減為 0,以節省資源;當佇列長度超過 5 筆時,則啟動 Pod 處理訊息。若佇列長度超過設定的值,系統會擴展更多的 Pod 來協同處理訊息。
配置 ScaledObject
kubectl apply -f keda-scaled-object.yml
過段時間應該能看到,以下資訊
kubectl get scaledobjects.keda.sh
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX TRIGGERS AUTHENTICATION READY ACTIVE FALLBACK PAUSED AGE
keda-pubsub-demo apps/v1.Deployment keda-pubsub-demo 1 3 gcp-pubsub keda-trigger-auth-gcp-credentials True False False Unknown 9m50s
此 demo 中
READY
欄位: 代表是否成功連上 GCPACTIVE
欄位: 代表 queue 長度是否超過 activationValue
配置能看到 ACTIVE
為 False 時,Deployment 的 Pod 會歸 0
kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
keda-pubsub-demo 0/0 0 0 162m
kubectl get pod
No resources found in ithome namespace.
讓我們打些 message 到 GCP Pub/Sub 試試看
for i in {1..40}; do gcloud pubsub topics publish $TOPIC_NAME --message='Hello, Pub/Sub'; done
⚠️ 因為 GCP monitoring 採樣數據頻率較慢,需要稍等一下
能看到 ScaledObject
ACTIVE 改為 True
並啟動 Pod 了
kubectl get scaledobjects.keda.sh
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX TRIGGERS AUTHENTICATION READY ACTIVE FALLBACK PAUSED AGE
keda-pubsub-demo apps/v1.Deployment keda-pubsub-demo 1 3 gcp-pubsub keda-trigger-auth-gcp-credentials True True False Unknown 19m
kubectl get pod
NAME READY STATUS RESTARTS AGE
keda-pubsub-demo-7f8944cf7f-9qfxb 1/1 Running 0 46s
keda-pubsub-demo-7f8944cf7f-h6br5 0/1 Running 0 31s
keda-pubsub-demo-7f8944cf7f-rphh5 0/1 Running 0 31s
因 ScaledObject
監聽 Pub/Sub
的方式是透過 GCP Monitoring 來感知 Queue 長度,所以驗證完成後,記得把 ScaledObject
移除,避免產生 GCP 費用。
kubectl delete scaledobjects.keda.sh keda-pubsub-demo
今天我們介紹了如何使用 KEDA 根據 Message Queue 的長度來進行自動擴/縮容。當 message consumer 資源消耗大且訊息頻率不穩定的情況下,這種策略能有效避免資源長時間被佔用和浪費。
明天會介紹本系列最後一個 KEDA 使用案例,若對更多使用案例有興趣,能參閱 官方文件/scalers 中許多能作為事件來源的選擇。