快轉到主要內容

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% 的流量。