TypeScript CDK 使用紀錄

記錄 TypeScript CDK 的部署步驟、bootstrap 與 CloudFormation stack lifecycle 的運作,並與 Terraform 比較。

發佈 ~4 分鐘 #cloudformation

Deploy Steps

  1. 獲得 AWS CLI 存取權,輸出環境變數
     aws-vault exec [profile]
     export AWS_ACCOUNT_ID=(你的 account id)
     export AWS_REGION=ap-northeast-1
  2. 初始化 CDK 使用前置 stack
    cdk bootstrap aws://$AWS_ACCOUNT_ID/$AWS_REGION
  3. 部署 CDK
    cdk deploy MainStack --require-approval never
  4. 上傳環境變數 @root of CDK repo:
    chmod +x ./put-secret-values.sh
    ./put-secret-values.sh
  5. 打包 frontend apps @root of chatbotmodule:
    chmod +x ./deploy_frontend.sh
    ./deploy_frontend.sh
  6. Backend migration
    1. 建立 ssh connection config (reference: internal wiki)
    2. 建立 RDS port forwarding
      ssh -f -N -L 15432:[rds-endpoint]:5432 bastion
    3. 執行 migration 指令
      npm run db:migrate
  7. 調整 lambda 環境變數 @ ApiLambda:
    ALLOWED_ORIGIN={WebsiteDistribution domain name}

CDK 的部署流程與 bootstrap

Terraform 是直接呼叫 AWS API 建立資源,而 CDK 的部署原理則完全不同:

  1. cdk synth
    • 把 TypeScript 程式碼轉換成 CloudFormation Template(JSON/YAML 格式)。
    • 你可以 cdk synth > template.yaml 來檢查結果。
  2. cdk bootstrap
    • 第一次用 CDK,需要先在 AWS 帳號 & region 裡建一組「舞台環境」。
    • bootstrap 會建立:
      • 一個 S3 bucket(放 CDK 生成的 CloudFormation template 和資產)
      • 一個 ECR repo(放 Docker image 資產)
      • 一些 IAM 角色(CloudFormation stack 執行時需要的權限)
    • 可以理解成:bootstrap 幫 CDK 準備好環境,讓之後的 deploy 可以順利跑。
  3. cdk deploy
    • 把 template 和資產上傳到 bootstrap 的 S3/ECR,再交給 CloudFormation 執行。
    • CloudFormation 處理 stack lifecycle(建立 / 更新 / rollback)。

所以,CDK 其實是 程式碼 → CloudFormation → AWS 資源,中間隔了一層 bootstrap。


CloudFormation Stack Lifecycle

創建流程:
CREATE_IN_PROGRESS → CREATE_COMPLETE (成功)
                   ↓
                CREATE_FAILED → ROLLBACK_IN_PROGRESS → ROLLBACK_COMPLETE
                                                     ↓
                                                  ROLLBACK_FAILED

更新流程:
UPDATE_IN_PROGRESS → UPDATE_COMPLETE_CLEANUP_IN_PROGRESS → UPDATE_COMPLETE (成功)
                   ↓
                UPDATE_FAILED → UPDATE_ROLLBACK_IN_PROGRESS → UPDATE_ROLLBACK_COMPLETE
                                                            ↓
                                                         UPDATE_ROLLBACK_FAILED

刪除流程:
DELETE_IN_PROGRESS → DELETE_COMPLETE (成功)
                   ↓
                DELETE_FAILED (失敗)

特殊狀態

  • REVIEW_IN_PROGRESS: 使用 Change Sets 時的狀態,Stack 等待審核和執行
  • ROLLBACK_COMPLETE: 只會發生在 create_failed 之後
    • Stack 創建失敗後回滾完成
    • 此狀態下 Stack 無法更新,只能刪除
    • 必須先刪除後重新創建
  • _FAILED 狀態: 當 Stack 處於任何 FAILED 狀態時:
    • 需要人工介入處理
    • 可以選擇繼續回滾或修復問題後重試
    • UPDATE_ROLLBACK_FAILED 可使用 ContinueUpdateRollback API

重要概念

  • 自動回滾機制
    • 創建或更新失敗時,CloudFormation 會自動回滾
    • 可以通過 -disable-rollback 參數關閉(用於調試)
    • 回滾會盡可能恢復到先前的穩定狀態
  • 終止保護 (Termination Protection)
    • 可以啟用以防止意外刪除
    • 必須先禁用才能刪除 Stack
  • Drift Detection
    • 檢測 Stack 資源是否與模板定義不一致
    • 不改變 Stack 狀態,僅提供偏移資訊

常見問題

  1. 有些資源在 rollback 時因為種種原因沒辦法正確被刪除,舉例情境像是:

    VPC stack 因後半截的資源配置有誤,在前半截已經部署的 resource 中含有 EIC endpoint。但因為 EIC endpoint 部署時間會比較長(約五分鐘左右),因此當 VPC stack 部署到錯誤配置時、CloudFormation 判斷 stack 建立失敗、準備 rollback。但在 CloudFormation Rollback 時,EIC endpoint 還在 pending 階段、而這個 resource 在 pending 階段是沒辦法接收 delete api 的,最終 EIC endpoint 刪除失敗,從而導致 rollback_failed。

    在這個情境下,我們能做的只有等待 EIC endpoint 建立完畢,手動把這些資源刪掉之後,再重新嘗試一次部署。(其他像是 ElastiCache 也會遇到類似的問題)

  2. 已經建好的 sub-stacks 在 rollback 時被拆光,情境描述如下:

    假設有一個 CloudFormation Stack 裡面有 10 個 sub-stacks,在第一次測試部署時、部署到第 9 個 stack 才遇到配置錯誤;根據上面提到的 Lifecycle 運作方式,CloudFormation 會 rollback 到上一次部署成功的狀態,也就是什麼資源都不會留下。

    我自己針對這個問題的解決方案:通常一個大架構下,我們會用許多 sub-stack 的架構來構建出一個大的 CloudFormation stack,而這些 sub-stacks 也通常會有部署的先後順序。因此我在測試部署時,我會在 main stack 中依序部署 sub-stacks。

    假設 main stack 中的架構如下:main { stack-A → stack-B → stack-C },我會先註解掉 stack-B & stack-C,確認 stack-A 可以正確部署沒有遇到問題後,再依序解除 stack-B 和 stack-C 的註解。

    這樣做的好處是,當我確認 stack-A 沒問題,要部署 stack-B 時、遇到問題了,CloudFormation 會「rollback 到前一個正確部署、沒有錯誤的版本」,因此雖然我的 stack-B 被拆光了,至少因為在前一次部署時、stack-A 是好的,也會在部署失敗後被保留下來。

搞懂這些邏輯真的會省掉 debug 非常多時間。CDK 部署真的是 extremely time consuming 的一件事。

Terraform vs CDK:不同維度的比較

  1. 狀態管理(State)

    • Terraform:需要 state 檔案(通常放 S3 + DynamoDB 做鎖),紀錄目前 infra 的狀態。
      • 優點:狀態清楚、可追蹤。
      • 缺點:需要額外管理 state,團隊協作時容易踩坑、鎖沒設好會衝突。
    • CDK:沒有 state 檔,所有狀態、rollback 都交給 CloudFormation。
      • 優點:省去 state 管理,部署過程簡單。
      • 缺點:debug CloudFormation Stack 錯誤、Rollback 發生時比較痛苦,ChangeSet 也不如 Terraform diff 精確。
  2. 建立資源的角色與權限

    • Terraform:由執行 Terraform 的人 / pipeline IAM 角色直接呼叫 AWS API 建立資源。
    • CDK:需要 bootstrap 預先建立的 IAM 角色,後續所有 deploy 都透過 CloudFormation 執行。

    換句話說,Terraform 是「我自己蓋房子」,CDK 是「我寫好設計圖,交給 CloudFormation 施工」。

  3. AWS 新手友善度

    • Terraform:學習曲線比較平滑,因為你寫什麼就是什麼,雖然 verbose 但邏輯清楚。
    • CDK:對 AWS 新手來說反而有點黑箱,因為一個 construct 背後可能藏了很多資源。
  4. 長期維護 vs 一次性專案

    • 長期維護:Terraform 更適合,因為資源和程式碼是一一對應,debug、audit、migrate 都比較可控。
    • 一次性專案 / 快速開發:CDK 很有優勢,construct 能夠省下很多相依資源建置的時間,特別是要快速拉起 demo、PoC 時。
  5. 語言與開發體驗

    • Terraform:HCL 是專為 IaC 設計的 DSL,簡潔但有限,複雜邏輯要靠 module 或外部工具。
    • CDK:用 TypeScript / Python 等語言,能直接用條件式、迴圈、function 抽象化資源定義。對程式背景的人來說更自然。