# 🔁 CodePipeline, CodeDeploy và SAM: luồng triển khai chuẩn

## Mục tiêu bài học

Sau bài này, bạn sẽ:

- Mô tả được luồng chuẩn của **CodePipeline** (source → build → test → deploy) và vai trò của artifact store, service role, stage, action.
- Phân biệt các **deployment type** của CodeDeploy (in-place vs blue/green) và các deployment config (AllAtOnce, Canary, Linear).
- Đọc hiểu file **appspec.yml** và thứ tự **lifecycle hook** trên từng compute platform (EC2/On-premises, Lambda, ECS).
- Giải thích được **SAM** đóng gói và triển khai ứng dụng serverless như thế nào (`sam build`, `sam package`, `sam deploy`).
- Tránh được các bẫy hay gặp trong đề thi về rollback, hook và quyền IAM của pipeline.

## Ôn nhanh

Bài trước kết luận: IAM quyết định **ai được gọi API gì** (Deny luôn thắng), KMS quyết định **ai được dùng key nào**. Hai ý này quay lại ngay trong bài này: pipeline và CodeDeploy đều chạy bằng **service role**, còn artifact trong S3 bucket nếu mã hoá bằng KMS key thì role của CodePipeline, CodeBuild lẫn CodeDeploy đều phải được cấp quyền dùng key đó. Thiếu một trong hai là pipeline đứng giữa đường.

## Lý thuyết

### 1. CodePipeline: bộ khung điều phối

CodePipeline **không tự build, không tự deploy**. Nó là orchestrator: mỗi **stage** chứa một hoặc nhiều **action** chạy song song, kết quả trung gian lưu vào **artifact store** (một S3 bucket), rồi chuyển sang stage kế tiếp.

```mermaid
flowchart LR
    Dev["💻<br/>Developer"] --> Src["📥<br/>Source<br/>CodeCommit/GitHub"]
    Src --> Build["🔨<br/>Build<br/>CodeBuild"]
    Build --> Test["🧪<br/>Test<br/>CodeBuild"]
    Test --> Approve["✅<br/>Manual approval"]
    Approve --> Deploy["🚀<br/>Deploy<br/>CodeDeploy / CloudFormation"]
    Deploy --> Prod["🌐<br/>Production"]
```

Vài chi tiết đề thi rất hay hỏi:

- Action provider phổ biến: CodeCommit, CodeBuild, CodeDeploy, CloudFormation, S3, ECR, CodeStar Connections (GitHub).
- **Manual approval** là một action, thường đặt trước stage deploy production.
- Có thể **disable transition** giữa hai stage để chặn pipeline mà không xoá stage.
- Mọi thay đổi trạng thái đều phát event qua **EventBridge**, dùng để gắn SNS alert.
- Artifact phải cùng region với pipeline.

**CodeBuild** là nơi thực thi thật: `buildspec.yml` với các phase `install`, `pre_build`, `build`, `post_build` và mục `artifacts`, `cache`.

### 2. CodeDeploy: đưa code lên máy và kiểm soát traffic

CodeDeploy gồm ba khái niệm: **application**, **deployment group** (tập target + config + rollback), và **revision** (bundle code + `appspec.yml`).

| Nền tảng | Deployment type | Lifecycle hook theo thứ tự |
| --- | --- | --- |
| EC2/On-premises | in-place | BeforeInstall → Install → AfterInstall → ApplicationStart → ValidateService |
| EC2/On-premises | blue/green | BeforeBlockTraffic → BlockTraffic → AfterBlockTraffic → BeforeAllowTraffic → AllowTraffic → AfterAllowTraffic |
| Lambda | blue/green (bắt buộc) | BeforeAllowTraffic → AllowTraffic → AfterAllowTraffic |
| ECS | blue/green | BeforeInstall → Install → AfterInstall → AllowTestTraffic → AfterAllowTestTraffic → BeforeAllowTraffic → AllowTraffic → AfterAllowTraffic |

Deployment config đáng nhớ:

- EC2: `AllAtOnce`, `HalfAtATime`, `OneAtATime`.
- Lambda/ECS: `Canary10Percent5Minutes`, `Linear10PercentEvery1Minute`, `AllAtOnce`.

Rollback: CodeDeploy tự rollback khi deployment fail, hoặc khi **CloudWatch alarm** gắn trong deployment group chuyển sang ALARM (nếu bật rollback configuration).

### 3. SAM: CloudFormation viết cho serverless

SAM là lớp viết tắt của CloudFormation cho Lambda, API Gateway, DynamoDB. Template SAM có dòng `Transform: AWS::Serverless-2016-10-31`, dùng resource kiểu `AWS::Serverless::Function`, và khối `Globals` để chia sẻ cấu hình chung.

Luồng chuẩn:

```bash
sam build          # build dependency, tạo .aws-sam/build
sam package \
  --s3-bucket my-artifact-bucket \
  --output-template-file packaged.yaml   # upload artifact, viết lại template
sam deploy \
  --template-file packaged.yaml \
  --stack-name my-app \
  --capabilities CAPABILITY_IAM          # tạo change set rồi execute
```

Điểm cần nhớ: `sam package` **không deploy**, nó chỉ đẩy artifact lên S3 và thay đường dẫn local bằng S3 URI. `sam deploy` mới tạo **change set** và thực thi. Vì vậy câu "zero downtime khi deploy Lambda" thường đi kèm `DeploymentPreference` trong template: SAM sẽ tự tạo CodeDeploy application và deployment group để dịch chuyển traffic.

## Ví dụ minh hoạ

**Ví dụ 1: đọc appspec.yml EC2 in-place**

```yaml
version: 0.0
os: linux
files:
  - source: /
    destination: /var/www/html
hooks:
  BeforeInstall:
    - location: scripts/stop_server.sh
      timeout: 300
  AfterInstall:
    - location: scripts/install_deps.sh
  ApplicationStart:
    - location: scripts/start_server.sh
  ValidateService:
    - location: scripts/check_health.sh
```

Thứ tự chạy: `BeforeInstall` → (agent copy file vào `/var/www/html`) → `AfterInstall` → `ApplicationStart` → `ValidateService`. Nếu `ValidateService` trả exit code khác 0, deployment bị đánh dấu fail và rollback (nếu đã cấu hình). Lưu ý hook `Install` do agent tự chạy, mình không viết script cho nó.

**Ví dụ 2: chọn config cho Lambda**

Đề cho "chỉ 10% request vào version mới trong 5 phút đầu, sau đó 90% còn lại". Đáp án là `Canary10Percent5Minutes`, không phải `Linear10PercentEvery1Minute` (linear chia đều theo từng phút: 10, 20, 30...).

**Ví dụ 3: pipeline cho serverless**

Stage 1 Source (CodeCommit) → Stage 2 Build (`sam build && sam package`, output là `packaged.yaml`) → Stage 3 Deploy (CloudFormation action, chọn **create change set** rồi **execute change set**). Hai bước change set là điều kiện để CodeDeploy chen vào và chạy traffic shifting.

## Thảo luận và suy ngẫm

1. Ứng dụng của bạn chạy trên 4 EC2 sau ALB. Khi nào bạn chọn in-place, khi nào chọn blue/green, và chi phí khác nhau ra sao?
2. Nếu `ValidateService` luôn pass dù app chưa sẵn sàng, hậu quả với người dùng là gì? Bạn sẽ thêm kiểm tra gì?
3. Vì sao tách `sam package` và `sam deploy` lại có lợi trong pipeline nhiều môi trường (dev, staging, prod)?

## Góc chuyên gia

- **Blue/green không có nghĩa là miễn phí**: nó tạo môi trường thứ hai, chuẩn bị sẵn để trả lời câu hỏi "zero downtime" trong đề.
- **Agent là điểm chết người**: EC2 phải cài CodeDeploy agent, agent phải chạy, và instance profile phải có quyền gọi CodeDeploy. Câu hỏi "deployment fail ngay ở bước đầu" thường là do thiếu một trong ba thứ này.
- **Script phải có shebang và quyền thực thi**, đường dẫn trong `hooks` là tương đối so với thư mục app đã copy, không phải so với `/`.
- **Nhớ hook đặc trưng**: Lambda chỉ có 3 hook quanh traffic (`BeforeAllowTraffic`, `AllowTraffic`, `AfterAllowTraffic`). EC2 in-place có `ValidateService` ở cuối. Đề rất thích hỏi "hook nào chạy sau khi traffic đã chuyển".
- **Rollback cần alarm**: chỉ bật rollback thôi chưa đủ, phải gắn CloudWatch alarm vào deployment group thì mới rollback theo metric.
- **SAM `DeploymentPreference` chỉ có tác dụng khi deploy qua CodeDeploy**, tức pipeline phải dùng change set hai bước. Dùng "deploy trực tiếp" là mất traffic shifting.
- Câu thần chú khi làm bài: *zero downtime → blue/green; chuyển dần từng phần → Canary; chia đều theo phút → Linear; đơn giản, chấp nhận downtime → in-place AllAtOnce.*
- Artifact bucket mã hoá bằng KMS thì **cả ba role** (pipeline, CodeBuild, CloudFormation/CodeDeploy) đều cần quyền `kms:Decrypt` và `kms:GenerateDataKey`.

## Tóm tắt và bước tiếp theo

Bốn ý mang vào phòng thi:

1. CodePipeline điều phối, CodeBuild thực thi build, CodeDeploy (hoặc CloudFormation) thực thi deploy.
2. CodeDeploy có in-place (EC2) và blue/green (EC2, Lambda, ECS); mỗi nền tảng có bộ lifecycle hook riêng, học theo bảng ở trên.
3. Deployment config quyết định tốc độ dịch chuyển traffic: AllAtOnce, Canary, Linear.
4. SAM = CloudFormation + transform serverless; `sam package` đẩy artifact lên S3, `sam deploy` tạo và execute change set.

Kiểm tra nhanh bằng cloze:

- CodeDeploy đọc file {{c1::appspec.yml}} để biết cách copy file và chạy hook.
- Trên EC2 in-place, hook chạy cuối cùng là {{c2::ValidateService}}.
- Lệnh {{c3::sam package}} upload artifact lên S3 và viết lại template.
- Config {{c4::Canary10Percent5Minutes}} đẩy 10% traffic trong 5 phút rồi phần còn lại.
- Dòng `Transform: {{c5::AWS::Serverless-2016-10-31}}` cho CloudFormation biết đây là template SAM.

Bài tiếp theo đi sâu vào **CloudFormation: template, stack và change set**, nơi bạn sẽ thấy chính xác change set mà `sam deploy` tạo ra hoạt động thế nào, cách viết template thuần, và cách CloudFormation xử lý update, rollback, drift.

```flashcard
[
  { "front": "Vai trò thật sự của CodePipeline là gì?", "back": "Orchestrator điều phối các stage/action, không tự build hay tự deploy." },
  { "front": "Artifact trung gian của CodePipeline được lưu ở đâu?", "back": "Trong artifact store, thường là một S3 bucket cùng region với pipeline." },
  { "front": "Hai deployment type của CodeDeploy trên EC2 là gì?", "back": "In-place và blue/green." },
  { "front": "Lifecycle hook cuối cùng của EC2 in-place deployment?", "back": "ValidateService." },
  { "front": "Ba lifecycle hook của CodeDeploy khi deploy Lambda?", "back": "BeforeAllowTraffic, AllowTraffic, AfterAllowTraffic." },
  { "front": "Hook nào của EC2 blue/green chặn traffic vào bộ instance cũ?", "back": "BlockTraffic (sau BeforeBlockTraffic, trước AfterBlockTraffic)." },
  { "front": "Deployment config nào chuyển 10% traffic trong 5 phút rồi phần còn lại?", "back": "Canary10Percent5Minutes." },
  { "front": "Deployment config nào chia traffic đều theo từng phút?", "back": "Linear10PercentEvery1Minute." },
  { "front": "Điều kiện để CodeDeploy tự rollback theo metric?", "back": "Deployment group phải gắn CloudWatch alarm và bật rollback configuration." },
  { "front": "sam package làm gì?", "back": "Upload artifact lên S3 và viết lại template với S3 URI, chưa deploy." },
  { "front": "sam deploy làm gì?", "back": "Tạo change set từ template đã đóng gói rồi execute change set đó." },
  { "front": "Dòng Transform nào đánh dấu template SAM?", "back": "AWS::Serverless-2016-10-31." },
  { "front": "DeploymentPreference trong SAM chỉ hoạt động khi nào?", "back": "Khi deploy qua CodeDeploy, tức pipeline dùng change set hai bước (create rồi execute)." },
  { "front": "Ba thứ phải có trên EC2 để CodeDeploy chạy được?", "back": "CodeDeploy agent đang chạy, instance profile có quyền CodeDeploy, và appspec.yml trong bundle." }
]
```
