前言

在現代軟體開發中,手動部署已經成為過去式。自動化部署不僅能減少人為錯誤,還能加快交付速度,讓開發團隊專注於功能開發而非維運瑣事。

AWS 提供了一套完整的 DevOps 工具鏈,從程式碼託管、建置、測試到部署,以及基礎設施即程式碼(IaC)服務。本篇將深入探討:

  • AWS Code 系列服務:CodeCommit, CodeBuild, CodeDeploy, CodePipeline
  • CloudFormation:AWS 的 IaC 基石
  • AWS CDK:用程式語言定義基礎設施
  • 實戰:構建一個完整的 CI/CD 流水線

AWS Code 系列服務全景

graph LR
    subgraph "Source"
        A[CodeCommit] -->|Git Push| B[CodePipeline]
        G[GitHub] -->|Webhook| B
    end
    
    subgraph "Build"
        B -->|Source Artifact| C[CodeBuild]
    end
    
    subgraph "Deploy"
        C -->|Build Artifact| D[CodeDeploy]
    end
    
    subgraph "Target"
        D --> E[EC2 / On-Premises]
        D --> F[Lambda / ECS]
    end
服務 功能 對應工具
CodeCommit Git 儲存庫託管 GitHub, GitLab
CodeBuild 編譯、測試、打包 Jenkins, GitHub Actions
CodeDeploy 自動化部署到運算資源 Octopus Deploy
CodePipeline 協調發布流程 Jenkins Pipeline
CodeArtifact 套件管理 Nexus, Artifactory

CodePipeline:自動化流水線

CodePipeline 是整個 CI/CD 的指揮官,負責串聯各個階段。

流水線階段設計

一個標準的生產環境流水線通常包含以下階段:

  1. Source:監聽 Git 分支變更
  2. Build:編譯程式碼、執行單元測試、建置 Docker Image
  3. Test (Staging):部署到測試環境、執行整合測試
  4. Approval:人工審核(生產環境部署前)
  5. Deploy (Prod):部署到生產環境

CodeBuild 配置 (buildspec.yml)

CodeBuild 使用 buildspec.yml 定義建置步驟。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
version: 0.2

phases:
install:
runtime-versions:
golang: 1.21
commands:
- echo Installing dependencies...
- go mod download

pre_build:
commands:
- echo Logging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com

build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG

post_build:
commands:
- echo Build completed on `date`
- echo Pushing the Docker image...
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- printf '[{"name":"my-app","imageUri":"%s"}]' $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG > imagedefinitions.json

artifacts:
files: imagedefinitions.json

CloudFormation:基礎設施即程式碼 (IaC)

CloudFormation 是 AWS 原生的 IaC 服務,使用 JSON 或 YAML 描述資源。

核心概念

  • Template:描述資源的 JSON/YAML 檔案
  • Stack:根據 Template 建立的一組資源集合
  • Change Set:預覽 Stack 更新會造成的影響

Template 結構範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
AWSTemplateFormatVersion: '2010-09-09'
Description: A simple S3 bucket and DynamoDB table

Parameters:
EnvironmentName:
Type: String
Default: dev
AllowedValues: [dev, staging, prod]

Resources:
MyBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "${EnvironmentName}-my-app-assets"
AccessControl: Private

MyTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${EnvironmentName}-Orders"
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: PK
AttributeType: S
KeySchema:
- AttributeName: PK
KeyType: HASH

Outputs:
BucketName:
Value: !Ref MyBucket
Export:
Name: !Sub "${EnvironmentName}-BucketName"

AWS CDK:用程式碼定義基礎設施

雖然 CloudFormation 很強大,但 YAML 寫起來冗長且缺乏邏輯能力。AWS CDK (Cloud Development Kit) 允許你使用熟悉的程式語言(TypeScript, Python, Go, Java)來定義基礎設施。

為什麼選擇 CDK?

  1. 真正的程式語言:可以使用迴圈、條件判斷、繼承等特性。
  2. 高階抽象 (Constructs):L2/L3 Construct 封裝了最佳實踐,幾行程式碼就能建立複雜架構(如 VPC + ALB + Fargate)。
  3. 型別安全:IDE 自動補全與編譯時檢查。

CDK 實戰:Go 語言範例

假設我們要建立一個包含 VPC、ECS Fargate Service 和 ALB 的架構。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package main

import (
"github.com/aws/aws-cdk-go/awscdk/v2"
"github.com/aws/aws-cdk-go/awscdk/v2/awsec2"
"github.com/aws/aws-cdk-go/awscdk/v2/awsecs"
"github.com/aws/aws-cdk-go/awscdk/v2/awsecspatterns"
"github.com/aws/constructs-go/constructs/v10"
"github.com/aws/jsii-runtime-go"
)

type MyStackProps struct {
awscdk.StackProps
}

func NewMyStack(scope constructs.Construct, id string, props *MyStackProps) awscdk.Stack {
var sprops awscdk.StackProps
if props != nil {
sprops = props.StackProps
}
stack := awscdk.NewStack(scope, &id, &sprops)

// 1. 建立 VPC (自動包含 Public/Private Subnets, NAT Gateways)
vpc := awsec2.NewVpc(stack, jsii.String("MyVpc"), &awsec2.VpcProps{
MaxAzs: jsii.Number(2),
})

// 2. 建立 ECS Cluster
cluster := awsecs.NewCluster(stack, jsii.String("MyCluster"), &awsecs.ClusterProps{
Vpc: vpc,
})

// 3. 使用 L3 Construct 建立 ALB + Fargate Service
// 這會自動建立:ALB, Target Group, Listener, Security Groups, Task Definition, Service
awsecspatterns.NewApplicationLoadBalancedFargateService(stack, jsii.String("MyService"), &awsecspatterns.ApplicationLoadBalancedFargateServiceProps{
Cluster: cluster,
Cpu: jsii.Number(256),
MemoryLimitMiB: jsii.Number(512),
DesiredCount: jsii.Number(2),
TaskImageOptions: &awsecspatterns.ApplicationLoadBalancedTaskImageOptions{
Image: awsecs.ContainerImage_FromRegistry(jsii.String("amazon/amazon-ecs-sample")),
},
PublicLoadBalancer: jsii.Bool(true),
})

return stack
}

func main() {
app := awscdk.NewApp(nil)

NewMyStack(app, "MyStack", &MyStackProps{
awscdk.StackProps{
Env: env(),
},
})

app.Synth(nil)
}

func env() *awscdk.Environment {
return nil
}

這段不到 50 行的 Go 程式碼,如果轉成 CloudFormation YAML,可能需要超過 500 行!

CDK 常用指令

  • cdk init app --language go:初始化專案
  • cdk synth:生成 CloudFormation Template
  • cdk diff:比較目前程式碼與已部署資源的差異
  • cdk deploy:部署 Stack
  • cdk destroy:刪除 Stack

實戰:構建完整的 CI/CD 流水線

我們將使用 CDK 來定義一個自我更新的 CI/CD 流水線(Pipeline that updates itself)。

架構設計

graph TD
    Git[GitHub Repo] -->|Webhook| Pipeline[CodePipeline]
    
    subgraph "Pipeline Stages"
        Source[Source Stage] --> Build[Build Stage]
        Build --> Update[Self-Mutate]
        Update --> DeployDev[Deploy Dev]
        DeployDev --> Approval[Manual Approval]
        Approval --> DeployProd[Deploy Prod]
    end
    
    Update -->|更新 Pipeline 定義| Pipeline

CDK Pipelines 實作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
func NewPipelineStack(scope constructs.Construct, id string, props *awscdk.StackProps) awscdk.Stack {
stack := awscdk.NewStack(scope, &id, props)

// 定義 GitHub Source
source := pipelines.CodePipelineSource_Connection(
jsii.String("my-org/my-repo"),
jsii.String("main"),
&pipelines.ConnectionSourceOptions{
ConnectionArn: jsii.String("arn:aws:codestar-connections:..."),
},
)

// 建立 Pipeline
pipeline := pipelines.NewCodePipeline(stack, jsii.String("Pipeline"), &pipelines.CodePipelineProps{
Synth: pipelines.NewShellStep(jsii.String("Synth"), &pipelines.ShellStepProps{
Input: source,
Commands: jsii.Strings(
"go mod download",
"cdk synth",
),
}),
})

// 加入 Dev 階段
pipeline.AddStage(NewAppStage(stack, "Dev", &awscdk.StageProps{
Env: &awscdk.Environment{Account: jsii.String("111111111111"), Region: jsii.String("ap-northeast-1")},
}), nil)

// 加入 Prod 階段(包含人工審核)
prodStage := pipeline.AddStage(NewAppStage(stack, "Prod", &awscdk.StageProps{
Env: &awscdk.Environment{Account: jsii.String("222222222222"), Region: jsii.String("ap-northeast-1")},
}), &pipelines.AddStageOpts{
Pre: []pipelines.Step{
pipelines.NewManualApprovalStep(jsii.String("PromoteToProd"), nil),
},
})

return stack
}

DevOps 最佳實踐

  1. 基礎設施即程式碼 (IaC)

    • 所有基礎設施變更都必須透過程式碼(CDK/CloudFormation)進行,禁止手動在 Console 修改。
    • 這樣可以確保環境的一致性與可複製性。
  2. 不可變基礎設施 (Immutable Infrastructure)

    • 不要 SSH 進去伺服器修補。
    • 每次部署都建立新的 AMI 或 Container Image,替換舊的資源。
  3. 小步快跑

    • 頻繁的小規模部署優於偶爾的大規模部署。
    • 配合 Feature Flags 控制功能開關。
  4. 環境隔離

    • Dev, Staging, Prod 應該在不同的 AWS 帳號中,避免資源誤用或權限過大。

本章重點回顧

關鍵要點

  1. CodePipeline

    • 串聯 Source, Build, Deploy 的自動化工具
    • 支援跨帳號、跨 Region 部署
  2. CloudFormation

    • AWS 的 IaC 基礎
    • 使用 Stack 管理資源生命週期
  3. AWS CDK

    • 推薦使用的 IaC 工具
    • 使用程式語言定義架構,具備高階抽象能力
    • cdk diff 是部署前的好幫手
  4. CI/CD 策略

    • 建立自我更新的 Pipeline
    • 實施人工審核機制保護生產環境
    • 嚴格遵守 IaC 原則

下一篇預告

在系列的第七篇文章中,我們將探討 監控與可觀測性

  • CloudWatch Metrics 與 Alarms
  • CloudWatch Logs Insights 查詢技巧
  • X-Ray 分散式追蹤
  • 構建全方位的監控儀表板

系列文章導覽


參考資源