早安,這是「第 55 屆全國技能競賽英雄榜暨第3屆亞洲技能競賽及第 48 屆國際技能競賽國手選拔賽青年組雲端運算職類」系列文章的第三篇,這次來分享科目三的題目和解題過程。
科目三: Unicorn Farm
共有兩大題,考驗的是懂 serverless 與 no-code 的概念與實作能力,題目如下:
- 無程式碼搬遷
- 給一既有 Lambda + DynamoDB 架構,透過 Step Functions 來取代 Lambda
- Serverless Application Model 部屬
- 給一 Python 程式碼,透過 Serverless Application Model(SAM) 來部署
無程式碼搬遷
這題給了兩個 Lambda 與兩個 DynamoDB table,要求透過 Step Functions 來取代 Lambda 的功能。
看懂既有架構
先看下方負責 feed unicorns 的 Lambda 程式碼,我們可以得知它會吃到一個 event,裡面包含了多個 unicorn 的 ID 以及 feed_amount,然後去 DynamoDB 更新每個 unicorn 對應的數值與 last_feed。
json
{"Unicorns": [{"id": "unicorn-1","amount": 10},{"id": "unicorn-2","amount": 20}],"TotalFeedAmount": 30,"TableName": "unicorn_status","FeedTableName": "feed_amount"}
id | name | hunger | last_feed |
---|---|---|---|
unicorn-1 | Twilight Sparkle | 50 | 2025-07-22T10:00:00Z |
unicorn-2 | Fluttershy | 30 | 2025-07-22T10:05:00Z |
上方的 Lambda 只會檢查 feed amount 是否小於 50,若是的話則補到 300。
id | amount |
---|---|
feed_amount | 45 -> 300 |
設計 Step Functions
對於第一次接觸 Step Functions 的我來說,第一眼看到時一臉懵逼,我要怎麼把 event 讀出來? Array 要怎麼分別處理?
幸好雲端運算職類可以看官方文檔,需要用到 Parallel 與 Map 兩個狀態,邏輯如下圖所示:
Step Functions Code: step-func-1.json
對於 Map state,我們需要設定 Provide items 為 {% $states.input.Unicorns %}
,這樣才會將每個元素傳入其中。
兩個更新 DynamoDB 的 PutItem 則需修改 Arguments,需要留意 DynamoDB 對於 Number 欄位的 API 呼叫需要傳遞字串,而不是數字。
json
{"TableName": "unicorn_status","Key": {"id": {"S": "{% $states.input.id %}"}},"UpdateExpression": "SET hunger = hunger + :feedRef","ExpressionAttributeValues": {":feedRef": {"N": "{% $string($states.input.amount) %}"}}}
對於上方的 Lambda,我們則須先從 DynamoDB 讀取剩餘的 feed_amount,檢查是否足夠,然後再更新。
Step Functions Code: step-func-2.json
要留意的是 GetItem 後需要將結果存在 Output,每個 state 的 $states.input
都是根據上一個 state 的輸出而來的。
此處的 $states.result
等同於 DynamoDB API response 的格式。
json
{"dbAmount": "{% $number($states.result.Item.feed_amount.N) %}"}
接下來透過 Choice state 與判斷式 {% $states.input.dbAmount < 50 %}
來決定是否執行後方 UpdateItem 來更新 feed_amount。
最後,透過 EventBridge 建立一個 cron job 來定時觸發 Step Functions,這樣這題就完成了。
Serverless Application Model 部屬
這題提供了一個 Cloud9 雲端 IDE 環境,以及一個 Python 程式碼,要求透過 Serverless Application Model(SAM) 來部署。
題目共有多個階段,第一步先叫你設計一個能跑的 template 出來,再來要求把 Lambda 拔掉但要維持功能…
看懂要求
文檔中隱喻提到會用到 API Gateway、Lambda 與 DynamoDB,這些都是 Serverless 架構中常見的組件,也是 SAM 這骨董唯數不多支援的服務。
看的出來這題設計時還沒有 Lambda Function URL,所以才會需要 API Gateway。
設計 SAM template
SAM template 需要包含以下幾個部分:
- AWS::Serverless::Api => API Gateway (RESTful)
- AWS::Serverless::Function => Lambda Function
- AWS::Serverless::SimpleTable => DynamoDB Table
基本這題上要做的事只有寫 yaml 檔,然後透過 sam deploy
部署上去。
考 Cloud Formation 我還願意學,考這 SAM 搞得我有點沒心態,下方範例就靠 Copilot 幫我想了 ❤️
yaml
AWSTemplateFormatVersion: "2010-09-09"Resources:UnicornApi: # API GatewayType: AWS::Serverless::ApiProperties:StageName: prodUnicornFunction: # Lambda FunctionType: AWS::Serverless::FunctionProperties:Handler: lambda_function.lambda_handlerRuntime: python3.11CodeUri: unicorn_function/Events:Request: # API Gateway EventType: ApiProperties:Path: /Method: POSTRestApiId: !Ref UnicornApiUnicornTable: # DynamoDB TableType: AWS::Serverless::SimpleTableProperties:PrimaryKey:Name: idType: String
能跑的 template 大概就是這樣,接著把 Python 程式碼放到 unicorn_function/
資料夾下,然後透過 sam deploy
就搞定了。
第二步要求把 Lambda Function 拔掉但又要維持功能,原本以為也是用 Step Functions 來取代,但搞好之後一直不給我分數 😡😡😡。
花錢買 hint 才知道要到 API Gateway 設定 Integration type 為 AWS service ,並透過 Integration Request 與 Integration Response 去修改請求與回應… 這是省 Lambda 費用的方法沒錯,但沒必要這麼噁心吧。
圖很醜懶得畫好看,我盡力了。總之就是 request body 會被轉換成 DynamoDB UpdateItem 的格式,呼叫到 service 後再把 API response 轉換成正常的格式。
看了半小時搞懂了原理但想不到 yaml 怎麼寫,文檔翻了三遍還是找不到哪裡可以設 integration type,最後有想到應該是要直接導入 OpenAPI schema 但剩下時間不多就放棄了。
科目心得
之前就有想學 Step Functions 的念頭,但一直沒有機會深入了解。謝謝這次題目(除了第二題最後一步,我不喜歡你)讓我有機會被迫學習。 這應該也是近幾年雲端運算職類第一次碰到 Infrastructure as Code 的題目,做起來其實還蠻有趣的,希望未來的我可以順便去學一下 Terraform。