↓ 快轉到主要內容

AWS 基於 Target Group 和 ASG 的藍綠部署研究

·1531 字·4 分鐘
Denny Cheng / 月月冬瓜
作者
Denny Cheng / 月月冬瓜
獸控兼工程師兼鍵盤武術家

背景
#

公司用的是 AWS,目前主要服務是在 EC2 上,而不是 ECS 或 EKS。
發版的流程是會先將目前的程式碼和相關設定打包成一個 AMI,更新 Launch Template,再更新 Auto Scaling Group (ASG)。
即使上版前都會盡量測試,但公司的程式如果動到 legacy code,實在很難保證所有部件都沒問題。
之前甚至還有出現過 infra 異常,包出來的 ami 少安裝了 imagemagick,讓全站完全無法上傳圖片。

當發現線上程式異常時,EC2 rollback (主要是 ASG instance refresh) 一次需要五分鐘的時間,多發生幾次就覺得對心臟有點不好…
於是決定開始研究 EC2 要怎麼執行藍綠部署。

在 AWS 文件洋洋灑灑的列了一堆可用的藍綠部署。 不過去查發現 AWS 真的有做到全自動化的只有 CodeDeploy 這個選項,其他都是需要自己撰寫流程。原本想說有原生走原生就好,但 CodeDeploy 的坑實在太大,最後還是手動土炮自己的流程出來。

流程
#

這個作法是使用 Load balancer 進行流量切換,簡要流程如下

  1. 準備兩個 ASG,各自綁定在不同 Target Group 上。我們稱線上環境為 Blue,即將部署的環境為 Green。
  2. 開始時,Application Load balancer (ALB) 上的某條 Rule 同時綁定這兩個 Target Group。此時 Rule 上 Blue 的權重為 100,Green 的權重為 0,也就是全流量為 Blue。
  3. 調整 Green 環境版號,並將數量調節成跟 Blue 一致。等待 Green Healthy。
  4. 在 ALB 上,一口氣將所有流量從 Blue 切到 Green。此時 Rule 上 Blue 權重為 0,Green 權重為 100。也就是全流量為 Green。
  5. 等待猶豫期,若流量正常,關閉 Blue 環境。若流量不正常,將流量導回 Blue,並關閉 Green 環境。

若想要做金絲雀(Canary) 部署,只要在步驟 4 稍微更改流量比重即可。

有一個重點是防呆。這個流程中最嚴重的狀況有可能為:

  1. 將流量導向空的 Target Group,網站會直接回傳 5xx。
  2. 將目前的流量導向的 ASG 關閉,沒備援的情況下會直接停機,也是 5xx。

做藍綠部署就是避免錯誤部署導致的停機行為,如果在 CI 環節中可以因手誤就引發停機錯誤,那還不如用 instance refresh 安全性高一點。

CI 實作細節
#

由於流程較長,這邊只會提一些比較重要的 CLI,其餘用文字描述。

如何分辨此次部署,哪個 ASG 是 Blue,哪個 ASG 是 Green
#

Ans: 觀看目前的 Target Group。

使用 AWS 的 API 去查詢目前 ALB 上的 Listener Rule,理應只有兩個 Target Group,100權重的 Target Group 對應到的 ASG 就是 Blue 環境。

防呆:若出現意外情況,例如想查詢的 Target Group 不在該 Rule 中,請直接終止部署行為。

當查詢結束後,這一整個 pipeline 就要以此為基準。因此要將其存為 gitlab artifacts 供後續流程使用。

對應的 aws cli 如下,可以靠 describe-rules 來判斷目前的 Blue 和 Green。

BLUE_TG_ARN=$(aws elbv2 describe-rules --rule-arns "$ALB_LISTENER_RULE_ARN" --query "Rules[0].Actions[0].ForwardConfig.TargetGroups[?Weight==\`100\`].TargetGroupArn" --output text)

GREEN_TG_ARN=$(aws elbv2 describe-rules --rule-arns "$ALB_LISTENER_RULE_ARN" --query "Rules[0].Actions[0].ForwardConfig.TargetGroups[?Weight==\`0\`].TargetGroupArn" --output text)

Green 環境準備
#

每次部署時,會將新的 launch template 版本套用在 Green ASG 上。並將目前的 Blue 的數量 (min/max/desired),更新到 Green 環境。
這邊要特別注意:aws 在更新 launch template 時,會把 override 的選項給拿掉,這些選項包含了是否要使用 spot, 要用什麼 instance type 等。所以如果有 override 的必須想辦法處理,我是更新前讀取以前的結果再塞回去。

可以靠 describe-auto-scaling-groups 拿舊資料

BLUE_ASG_INFO=$(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names "$BLUE_ASG" --output json)
GREEN_ASG_INFO=$(aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names "$GREEN_ASG" --output json)
DESIRED_COUNT=$(echo "$BLUE_ASG_INFO" | jq -r '.AutoScalingGroups[0].DesiredCapacity')
MIN_SIZE=$(echo "$BLUE_ASG_INFO" | jq -r '.AutoScalingGroups[0].MinSize')
MAX_SIZE=$(echo "$BLUE_ASG_INFO" | jq -r '.AutoScalingGroups[0].MaxSize')
CURRENT_COUNT=$(echo "$GREEN_ASG_INFO" | jq -r '.AutoScalingGroups[0].DesiredCapacity')

防呆: 若在開啟 Green 環境時,發現 Green ASG 的當前數量不為 0。請直接終止部署行為,否則有可能會有多個版本交雜在同一個 ASG 的狀況。

再靠 update-auto-scaling-group 更新 Green ASG (省略了 UPDATED_POLICY 的生成流程)

aws autoscaling update-auto-scaling-group \
    --auto-scaling-group-name $GREEN_ASG \
    --mixed-instances-policy "$UPDATED_POLICY" \
    --min-size $MIN_SIZE --max-size $MAX_SIZE --desired-capacity $DESIRED_COUNT

最後則是使用 elbvv2 wait target-in-service 等待 Target Group 變成 healthy。全 healthy 後 Green 環境才算準備完畢。

aws elbv2 wait target-in-service --target-group-arn $GREEN_TG_ARN

切換流量
#

當 Green 環境準備完畢後,就可以準備切換流量。

aws elbv2 modify-rule --rule-arn $ALB_LISTENER_RULE_ARN --actions "$ACTIONS" --no-cli-pager

防呆:在任何切換流量的行為執行前,都應先確認即將切換的 Target Group 是否已經是非空且所有 instance 皆 healthy。

之後若要 rollback,指令基本上是一樣的。

關閉環境
#

最後一個步驟為關閉環境,可以將 desired / min / max 皆設定為 0。

防呆:在關閉環境之前,都應先確認當前環境對應的 Target Group,是否正在接受 100% 的流量。