0

🚀 Chapter 2 — Automation: Phần A — Thiết lập CD với AWS CodeBuild + SSH

Ở Chapter 1, chúng ta đã deploy Todo List App lên Amazon EC2 bằng cách thủ công:

Developer
   ↓
SSH vào EC2
   ↓
git pull
   ↓
docker compose build
   ↓
docker compose up -d

Cách này hoạt động, nhưng mỗi lần có code mới chúng ta lại phải SSH vào server và chạy các command trên.

Trong Chapter này, chúng ta sẽ tự động hóa quá trình đó bằng:

  • GitHub
  • AWS CodeBuild
  • AWS Secrets Manager
  • SSH
  • Amazon EC2

Mục tiêu cuối cùng:

image.png

Không sử dụng GitHub Actions.


1. Chúng ta sẽ xây dựng gì?

Ở Chapter 1:

Developer
   ↓
SSH
   ↓
EC2
   ↓
Deploy thủ công

Sau Chapter này:

Developer
   ↓
git push
   ↓
GitHub
   ↓
CodeBuild
   ↓
SSH
   ↓
EC2
   ↓
Deploy tự động

Điểm quan trọng:

CodeBuild không trực tiếp chạy Docker Compose trên EC2.

CodeBuild chỉ đóng vai trò:

  1. Nhận source/event.
  2. Lấy SSH private key.
  3. SSH vào EC2.
  4. Ra lệnh cho EC2 deploy.

Còn việc:

git pull
docker compose build
docker compose up -d

vẫn được thực hiện trên EC2.


2. Kiến trúc

Kiến trúc của Chapter này:

image.png


3. Chuẩn bị EC2

Chúng ta tiếp tục sử dụng EC2 từ Chapter 1.

Kiểm tra:

ssh -i ./chapter1-key.pem ubuntu@<EC2_PUBLIC_IP>

Sau đó:

docker --version
docker compose version
git --version

Đi tới project:

cd ~/todo-list-app

Kiểm tra container:

docker compose ps

Nếu application đang chạy:

NAME                  STATUS
todo-list-app-app-1   Up
todo-list-app-db-1    Up

thì có thể tiếp tục.


Nên Tạo Github repo mới cho todo-app

Ở chapter 1, chúng ta đang sử dụng repo mã nguồn mở của người khác. Từ chương này, nên tạo một repo mới của riêng mình để có quyền kiểm soát với repo.

Ví dụ như:

  • Push code
  • Merge PR

Vì CI/CD sẽ được thực hiện tự động bởi action push code, merge PR hoặc điều gì đó tương tự


Nên dùng Elastic IP cho EC2

CodeBuild sẽ SSH vào EC2.

Nếu EC2 sử dụng Public IPv4 thông thường, IP có thể thay đổi khi instance được stop/start.

Vì vậy, cho lab này nên sử dụng:

EC2
 ↓
Elastic IP

Trong AWS Console:

EC2
 ↓
Elastic IP addresses
 ↓
Allocate Elastic IP address
 ↓
Associate Elastic IP address
 ↓
Chọn EC2

Sau khi có Elastic IP, chúng ta có:

EC2_PUBLIC_IP=<ELASTIC_IP>

Ví dụ:

EC2_PUBLIC_IP=13.XXX.XXX.XXX

Từ đây trở đi sử dụng Elastic IP này.


Kiểm tra Security Group

CodeBuild sẽ SSH vào EC2 nên Security Group của EC2 cần cho phép SSH:

Type: SSH
Protocol: TCP
Port: 22

Để làm lab đơn giản, có thể:

Source: 0.0.0.0/0

Tuy nhiên, đây chỉ là cấu hình đơn giản cho lab.

Không cần mở:

3000
3306

Application vẫn truy cập thông qua:

HTTP : 80

4. Lưu SSH private key vào Secrets Manager

CodeBuild chạy trong AWS nên nó không có file:

chapter1-key.pem

trên máy local của chúng ta.

Vì vậy CodeBuild cần lấy private key từ AWS Secrets Manager.

Vào:

AWS Console
    ↓
Secrets Manager
    ↓
Store a new secret

Chọn:

Other type of secret

Quan trọng

Để đơn giản và tránh lỗi parse JSON, hãy lưu toàn bộ private key dưới dạng plaintext.

Ví dụ:

-----BEGIN OPENSSH PRIVATE KEY-----
...
...
-----END OPENSSH PRIVATE KEY-----

Đặt tên:

chapter2/ec2-ssh-key

Sau đó lưu secret.

Không commit file .pem vào Git repository.


5. Chuẩn bị trước khi tạo CodeBuild

Tạo IAM Role cho CodeBuild

CodeBuild cần quyền đọc secret.

Vào:

IAM
 ↓
Roles
 ↓
Create role

Chọn:

Trusted entity:
AWS service

Use case:
CodeBuild

Đặt tên:

chapter2-codebuild-role

Sau khi tạo role, thêm Inline Policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:<AWS_ACCOUNT_ID>:secret:chapter2/ec2-ssh-key-*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "codeconnections:UseConnection"
      ],
      "Resource": "arn:aws:codeconnections:us-east-1:<AWS_ACCOUNT_ID>:connection/<CONNECTION_ID>"
    }
  ]
}

Thay:

<AWS_ACCOUNT_ID>

bằng AWS Account ID của bạn.

Policy này có nghĩa:

CodeBuild ──────────────→ GitHub connection
   ↓
GetSecretValue
   ↓
chapter2/ec2-ssh-key

CodeBuild chỉ được đọc secret cần thiết.


Chuẩn bị repository

Trong repository:

todo-list-app/
├── buildspec.yml (new)
├── compose.yaml
├── Dockerfile
├── package.json
└── ...

Tạo file:

buildspec.yml

CodeBuild mặc định tìm buildspec.yml ở root của source repository.


Tạo buildspec.yml

Ban đầu chúng ta chưa deploy.

Chỉ kiểm tra:

CodeBuild
   ↓
SSH
   ↓
EC2

Nội dung:

version: 0.2

phases:
  install:
    commands:
      - yum install -y openssh-clients
      - mkdir -p ~/.ssh
      - aws secretsmanager get-secret-value --secret-id chapter2/ec2-ssh-key --query SecretString --output text > /tmp/ec2-key.pem
      - chmod 400 /tmp/ec2-key.pem

  build:
    commands:
      - echo "Preparing deployment..."

  post_build:
    commands:
      - echo "Deploying to EC2..."
      - ssh-keyscan -H <EC2_PUBLIC_IP> >> ~/.ssh/known_hosts
      - ssh -i /tmp/ec2-key.pem ubuntu@<EC2_PUBLIC_IP> "echo SSH connection successful"

Thay:

<EC2_PUBLIC_IP>

bằng Elastic IP của EC2.

Ví dụ:

- ssh-keyscan -H 13.XXX.XXX.XXX >> ~/.ssh/known_hosts
- ssh -i /tmp/ec2-key.pem ubuntu@13.XXX.XXX.XXX "echo SSH connection successful"

Commit buildspec.yml

Ở máy local:

git add buildspec.yml
git commit -m "add CodeBuild deployment"
git push origin main

6. Tạo CodeBuild Project

Vào:

AWS Console
 ↓
CodeBuild
 ↓
Build projects
 ↓
Create build project

Đặt:

Project name:

chapter2-todo-list-cd

Source

Chọn:

Source provider:
GitHub

Sau đó kết nối GitHub repository chứa:

todo-list-app

Branch:

main

CodeBuild hỗ trợ GitHub làm source provider và có thể nhận webhook từ repository.


Cấu hình Environment

Chọn:

Environment image:
Managed image

Operating system:
Amazon Linux

Runtime:
Standard

Chọn một image Standard hiện có trong Console.

Ví dụ:

aws/codebuild/amazonlinux-x86_64-standard:...

Service role:

chapter2-codebuild-role

Privileged mode

Không bật Privileged mode.

Trong Chapter này CodeBuild không build Docker image.

Docker được build trên EC2 thông qua SSH:

CodeBuild
    ↓
SSH
    ↓
EC2
    ↓
docker compose build

AWS chỉ cần privileged mode khi build project cần Docker daemon bên trong CodeBuild environment.


Cấu hình Buildspec

Trong phần Buildspec:

Use a buildspec file

Không cần nhập tên nếu file nằm ở root và có tên:

buildspec.yml

Artifacts:

No artifacts

Vì Chapter này chỉ thực hiện deployment.

Logs:

CloudWatch Logs

Sau đó:

Create build project

7. Chạy CodeBuild lần đầu

Vào:

CodeBuild
 ↓
Build projects
 ↓
chapter2-todo-list-cd
 ↓
Start build

CodeBuild sẽ thực hiện:

INSTALL
   ↓
Install SSH client
   ↓
Get SSH private key
   ↓
BUILD
   ↓
POST_BUILD
   ↓
SSH → EC2

Nếu thành công, log sẽ có:

SSH connection successful

8. Nếu SSH thành công thì chúng ta đã hoàn thành một nửa

Bây giờ flow đã hoạt động:

CodeBuild
    │
    │ SSH
    ▼
Amazon EC2

Điều này chứng minh:

  • IAM Role hoạt động.
  • CodeBuild đọc được Secrets Manager.
  • SSH key hợp lệ.
  • Security Group cho phép SSH.
  • CodeBuild có thể kết nối tới EC2.

Chúng ta chưa deploy application.

Đây là cách debug dễ hơn so với việc đưa tất cả command vào ngay từ đầu.


9. Thêm bước Deploy

Bây giờ sửa buildspec.yml.

Thay phần:

post_build:
  commands:
    - ssh-keyscan -H <EC2_PUBLIC_IP> >> ~/.ssh/known_hosts
    - ssh -i /tmp/ec2-key.pem ubuntu@<EC2_PUBLIC_IP> "echo SSH connection successful"

bằng:

post_build:
  commands:
    - echo "Deploying to EC2..."

    - ssh-keyscan -H <EC2_PUBLIC_IP> >> ~/.ssh/known_hosts

    - >
      ssh -i /tmp/ec2-key.pem
      ubuntu@<EC2_PUBLIC_IP>
      "cd ~/todo-list-app &&
       git pull origin main &&
       docker compose build &&
       docker compose up -d"

buildspec.yml hoàn chỉnh

File cuối cùng:

version: 0.2

phases:
  install:
    commands:
      - yum install -y openssh-clients
      - mkdir -p ~/.ssh
      - aws secretsmanager get-secret-value --secret-id chapter2/ec2-ssh-key --query SecretString --output text > /tmp/ec2-key.pem
      - chmod 400 /tmp/ec2-key.pem

  build:
    commands:
      - echo "Preparing deployment..."

  post_build:
    commands:
      - echo "Deploying to EC2..."
      - ssh-keyscan -H 34.201.104.212 >> ~/.ssh/known_hosts
      - >
        ssh -i /tmp/ec2-key.pem
        ubuntu@34.201.104.212
        "cd ~/todo-list-app &&
         git pull origin main &&
         docker compose build &&
         docker compose up -d"

Thay:

<EC2_PUBLIC_IP>

bằng Elastic IP thật.


Một điều kiện quan trọng: EC2 phải pull được GitHub

Trong deployment command có:

git pull origin main

Điều này có nghĩa EC2 cũng phải có quyền đọc repository.

Nếu repository là public, không cần cấu hình thêm.

Nếu repository là private, EC2 cần được cấu hình GitHub authentication, ví dụ:

GitHub Deploy Key

hoặc một cơ chế authentication phù hợp.

Trong lab này, để giữ kiến trúc đơn giản, có thể sử dụng repository public.


Commit và push

Sau khi sửa:

git add buildspec.yml
git commit -m "deploy todo app through CodeBuild"
git push origin main

Hiện tại chúng ta vẫn có thể chạy thủ công:

CodeBuild
 ↓
Start build

Chạy deployment

Trong CodeBuild:

chapter2-todo-list-cd
 ↓
Start build

Theo dõi log.

Flow sẽ là:

INSTALL
   ↓
Install SSH
   ↓
Get SSH key
   ↓
BUILD
   ↓
POST_BUILD
   ↓
SSH → EC2
   ↓
git pull
   ↓
docker compose build
   ↓
docker compose up -d

Nếu thành công:

BUILD SUCCEEDED

10. Xác nhận kết quả

Kiểm tra EC2

SSH vào EC2:

ssh -i ./chapter1-key.pem ubuntu@<EC2_PUBLIC_IP>

Đi tới project:

cd ~/todo-list-app

Kiểm tra commit:

git log -1 --oneline

Kiểm tra container:

docker compose ps

Kiểm tra image:

docker images

Nếu mọi thứ đúng, container mới đã được deploy.


Kiểm tra application

Mở:

http://<EC2_PUBLIC_IP>

Todo List App phải hoạt động.


11. Tự động trigger CodeBuild

Hiện tại chúng ta vẫn phải:

git push
   ↓
AWS Console
   ↓
Start build (thủ công)

Mục tiêu của CD là bỏ bước:

Start build

CodeBuild hỗ trợ webhook để tự động bắt đầu build khi source repository có thay đổi.

Vào:

CodeBuild
 ↓
chapter2-todo-list-cd
 ↓
Edit

Thiết lập webhook như ảnh:

Screenshot 2026-09-17 at 10.56.17.png

Ý nghĩa:

Hệ thống sẽ tự động khởi chạy một quy trình build mới mỗi khi có bất kỳ thay đổi mã nguồn nào (commit/push) được đẩy lên repository

Test Auto Deployment

Bây giờ chúng ta chỉ cần:

git add .
git commit -m "update todo app"
git push origin main

Không cần:

❌ GitHub Actions
❌ AWS Console → Start build
❌ SSH vào EC2
❌ git pull
❌ docker compose build
❌ docker compose up

Kiểm tra Build History

Vào:

CodeBuild
 ↓
chapter2-todo-list-cd
 ↓
Build history

Bạn sẽ thấy build mới.

Ví dụ:

Screenshot 2026-09-17 at 11.08.19.png

Sau đó kiểm tra EC2:

cd ~/todo-list-app
git log -1 --oneline
docker compose ps

Cuối cùng mở:

http://<EC2_PUBLIC_IP>

12. Kết quả cuối cùng

image.png

Điểm quan trọng nhất của Chapter này:

CodeBuild = Automation engine

EC2 = nơi application thực sự được deploy

Secrets Manager = nơi lưu SSH private key

GitHub = source repository + webhook

13. Kiến trúc này còn hạn chế gì?

Cách làm này rất phù hợp để học CD cơ bản, nhưng chưa phải kiến trúc production hoàn chỉnh.

Hiện tại chúng ta vẫn:

CodeBuild
   ↓
SSH
   ↓
EC2

và phải quản lý:

SSH private key
SSH port 22
EC2 Public IP
EC2 GitHub access

Ngoài ra, mỗi lần deploy EC2 vẫn phải:

git pull
docker compose build

Điều này có nghĩa server tự build application.

Ở các phần tiếp theo, chúng ta sẽ dần chuyển sang mô hình:

Source Code
    ↓
Build
    ↓
Docker Image
    ↓
Container Registry
    ↓
Deploy

Ví dụ:

GitHub
   ↓
CodeBuild
   ↓
Docker Build
   ↓
Amazon ECR
   ↓
EC2 / EKS

Khi đó chúng ta không cần để production server tự build Docker image nữa.


14. Chapter tiếp theo

Sau khi đã có:

Git push
   ↓
CodeBuild
   ↓
EC2
   ↓
Docker Compose

chúng ta có thể bắt đầu giải quyết vấn đề tiếp theo:

Làm thế nào để cùng một quy trình CI/CD có thể deploy nhiều service?

Đó chính là bước chuyển từ:

1 application

sang:

Multiple services

và bắt đầu bước vào:

Chapter 3 — Scale

🎯 Kết quả của Chapter 2A

Cuối bài, bạn cần đạt được:

[✓] EC2 chạy Todo List App
[✓] SSH private key được lưu trong Secrets Manager
[✓] CodeBuild có IAM Role
[✓] CodeBuild SSH được vào EC2
[✓] CodeBuild deploy được application
[✓] GitHub webhook trigger CodeBuild
[✓] git push → application tự động deploy

Nếu đạt đủ 7 mục trên, bạn đã hoàn thành CD cơ bản với AWS CodeBuild + SSH.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí