0

🚀 Chapter 4 — Production: Phần G — Production Governance với GitHub Actions và Amazon ECR

Chapter 4F, chúng ta đã hoàn thành Artifact Promotion:

GitHub
   ↓
GitHub Actions
   ↓
Docker Build
   ↓
Amazon ECR
   ↓
Security Gate
   ↓
Dev
   ↓
Staging
   ↓
Production

Image được build một lần và sử dụng cùng một image digest xuyên suốt các môi trường.

Tuy nhiên, quy trình hiện tại vẫn còn một vấn đề:

Bất kỳ ai có quyền chạy workflow cũng có thể promote image lên Production.

Trong môi trường Production, chúng ta cần thêm:

  • Quyền truy cập rõ ràng.
  • Approval trước Production.
  • Không cho phép image chưa được kiểm tra đi tiếp.
  • Không cho phép ghi đè image tag quan trọng.
  • Kiểm tra image digest trước khi promotion.
  • Lưu lại lịch sử release.
  • Có thể rollback về artifact trước đó.

Trong phần này, chúng ta sẽ xây dựng Production Governance bằng:

GitHub Actions
GitHub Environments
Amazon ECR
AWS IAM
OIDC
Image Digest
Manual Approval

1. Mục tiêu

Sau khi hoàn thành phần này, chúng ta sẽ có flow:

Developer
    ↓
GitHub
    ↓
GitHub Actions
    ↓
Test
    ↓
Docker Build
    ↓
Push Image vào ECR
    ↓
Security Gate
    ↓
Deploy Dev
    ↓
Deploy Staging
    ↓
[Production Approval]
    ↓
Deploy Production

Các nguyên tắc chính:

✅ Không build lại image ở từng environment
✅ Production chỉ nhận image đã được kiểm tra
✅ Production cần approval
✅ Sử dụng image digest để xác định artifact
✅ Không sử dụng AWS Access Key dài hạn nếu có thể dùng OIDC
✅ Có lịch sử promotion trên GitHub Actions
✅ Có thể kiểm tra và rollback về image cũ

2. Kiến trúc của Chapter 4G

                         GitHub
                            │
                            ▼
                    GitHub Actions
                            │
                            ▼
                    Security Gate
                            │
                            ▼
                       Amazon ECR
                            │
                 ┌──────────┼──────────┐
                 ▼          ▼          ▼
                Dev      Staging   Production
                                      │
                                      ▼
                              Required Approval
                                      │
                                      ▼
                              Production Release

GitHub Environments sẽ kiểm soát từng môi trường:

dev
└── Không cần approval

staging
└── Có thể yêu cầu approval

production
└── Bắt buộc approval

3. Kiểm tra môi trường hiện tại

Di chuyển vào repository:

cd online-boutique-cicd

Kiểm tra Git:

git status

Kiểm tra các workflow:

find .github/workflows -type f

Ví dụ:

.github/workflows/
├── reusable-ci-cd.yml
├── scale-ci-cd.yml
├── security-gate.yml
├── artifact-promotion.yml
└── ...

Kiểm tra script promotion:

ls -l scripts/promote-ecr-image.sh

Kiểm tra AWS CLI:

aws --version

Kiểm tra AWS account:

aws sts get-caller-identity

Kiểm tra Region:

aws configure get region

Nếu chưa cấu hình Region:

aws configure set region ap-northeast-1

Đặt biến môi trường:

export AWS_REGION=ap-northeast-1
export ECR_REPOSITORY=online-boutique-frontend
export IMAGE_TAG=a81c92f

Thay a81c92f bằng Git SHA image tag thực tế trong ECR.


4. Kiểm tra image trong Amazon ECR

Kiểm tra image:

aws ecr describe-images \
  --repository-name "$ECR_REPOSITORY" \
  --image-ids imageTag="$IMAGE_TAG" \
  --region "$AWS_REGION" \
  --query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest,Pushed:imagePushedAt}' \
  --output table

Ví dụ:

----------------------------------------------------
|                 DescribeImages                   |
+----------+---------------------------------------+
| Digest   | sha256:abc123...                       |
| Pushed   | 2026-09-12T10:20:00+00:00              |
| Tags     | a81c92f                                |
+----------+---------------------------------------+

Lấy digest của image:

export IMAGE_DIGEST=$(aws ecr describe-images \
  --repository-name "$ECR_REPOSITORY" \
  --image-ids imageTag="$IMAGE_TAG" \
  --region "$AWS_REGION" \
  --query 'imageDetails[0].imageDigest' \
  --output text)

Kiểm tra:

echo "$IMAGE_DIGEST"

Kết quả ví dụ:

sha256:abc123456789...

Từ thời điểm này, chúng ta dùng:

Git SHA → xác định version
Digest  → xác định chính xác nội dung image

5. Tạo GitHub Environments

Mở GitHub repository:

Repository
   ↓
Settings
   ↓
Environments

Tạo ba Environment:

dev
staging
production

5.1. Environment dev

Tạo:

dev

Không cần cấu hình approval.

Mục đích:

Image đã vượt qua Security Gate
          ↓
       Deploy Dev

5.2. Environment staging

Tạo:

staging

Có thể cấu hình approval nếu muốn mô phỏng quy trình kiểm thử nghiêm ngặt hơn.

Mục đích:

Dev
 ↓
Staging
 ↓
Integration Test
 ↓
Acceptance Test

5.3. Environment production

Tạo:

production

Trong phần Environment protection rules, cấu hình:

Required reviewers

Thêm GitHub account được phép approve Production.

Ví dụ:

Required reviewers:
- maintainer-account

Nếu repository hỗ trợ thêm các rule khác, có thể cấu hình:

Deployment branches
Required reviewers
Wait timer
Environment secrets
Environment variables

Chọn branch được phép deploy Production, ví dụ:

main

Kết quả mong đợi:

production
└── Required reviewers: enabled

6. Tạo GitHub Variables

Vào:

Settings
   ↓
Secrets and variables
   ↓
Actions
   ↓
Variables

Tạo Repository Variable:

Name:
AWS_REGION

Value:
ap-northeast-1

Có thể tạo thêm:

ECR_REGISTRY

Ví dụ:

123456789012.dkr.ecr.ap-northeast-1.amazonaws.com

Tuy nhiên, không nên hard-code AWS Account ID trong workflow nếu có thể lấy thông tin registry từ AWS CLI.


7. Cấu hình AWS IAM cho GitHub Actions

Có hai phương án.

Option A — Sử dụng AWS Access Key

Workflow sử dụng:

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY

Phương án này dễ bắt đầu nhưng có nhược điểm:

❌ Phải quản lý secret dài hạn
❌ Có nguy cơ bị lộ
❌ Cần rotate key

Nếu bạn đã sử dụng phương án này ở Chapter 4F, workflow vẫn có thể chạy.


Option B — Sử dụng GitHub OIDC

Đây là phương án nên dùng cho Production.

Flow:

GitHub Actions
      ↓
GitHub OIDC Token
      ↓
AWS IAM Role
      ↓
Temporary AWS Credentials
      ↓
Amazon ECR

Ưu điểm:

✅ Không cần lưu AWS Secret Access Key
✅ Không sử dụng credential dài hạn
✅ Có thể giới hạn repository và branch
✅ Phù hợp với CI/CD Production

8. Tạo IAM OIDC Provider

Đăng nhập AWS Console:

IAM
   ↓
Identity providers
   ↓
Add provider

Chọn:

Provider type:
OpenID Connect

Nhập:

Provider URL:
https://token.actions.githubusercontent.com

Chọn:

Audience:
sts.amazonaws.com

Sau đó tạo provider.

Nếu tài khoản AWS đã có provider này thì không cần tạo lại.


9. Tạo IAM Policy cho ECR Promotion

Tạo policy mới trong IAM.

Tên policy:

GitHubActionsECRPromotionPolicy

Nội dung:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadECRImages",
      "Effect": "Allow",
      "Action": [
        "ecr:DescribeImages",
        "ecr:BatchGetImage"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-1:AWS_ACCOUNT_ID:repository/online-boutique-*"
    },
    {
      "Sid": "PromoteECRImages",
      "Effect": "Allow",
      "Action": [
        "ecr:PutImage"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-1:AWS_ACCOUNT_ID:repository/online-boutique-*"
    }
  ]
}

Thay:

AWS_ACCOUNT_ID

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

Policy này cho phép workflow:

DescribeImages
BatchGetImage
PutImage

Không cấp các quyền không cần thiết như:

ecr:DeleteRepository
ecr:DeleteRepositoryPolicy
ecr:SetRepositoryPolicy

10. Tạo IAM Role cho GitHub Actions

Vào:

IAM
   ↓
Roles
   ↓
Create role

Chọn Web identity hoặc cấu hình trust policy thủ công.

Tên role:

GitHubActionsECRPromotionRole

Trust policy mẫu:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::AWS_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:GITHUB_OWNER/GITHUB_REPOSITORY:environment:production"
        }
      }
    }
  ]
}

Thay:

AWS_ACCOUNT_ID
GITHUB_OWNER
GITHUB_REPOSITORY

Ví dụ:

repo:Juu-dev/online-boutique-cicd:environment:production

Gắn policy:

GitHubActionsECRPromotionPolicy

Trust policy nên giới hạn đúng repository và environment Production. Không nên cho phép toàn bộ GitHub repository assume role.


11. Thêm AWS Role ARN vào GitHub

Vào:

Settings
   ↓
Environments
   ↓
production
   ↓
Environment variables

Tạo variable:

Name:
AWS_ROLE_ARN

Value:
arn:aws:iam::AWS_ACCOUNT_ID:role/GitHubActionsECRPromotionRole

Nếu sử dụng cùng role cho devstaging, có thể tạo variables tương ứng ở từng Environment.


12. Cập nhật script promotion

Kiểm tra script hiện tại:

cat scripts/promote-ecr-image.sh

Script cần có các bước:

1. Nhận repository name
2. Nhận source tag
3. Nhận target tag
4. Lấy source digest
5. Lấy image manifest
6. Tạo target tag
7. Lấy target digest
8. So sánh source digest và target digest
9. Dừng nếu digest khác nhau

Một phiên bản hoàn chỉnh:

#!/usr/bin/env bash

set -euo pipefail

REPOSITORY_NAME="${1:?Repository name is required}"
SOURCE_TAG="${2:?Source tag is required}"
TARGET_TAG="${3:?Target tag is required}"
AWS_REGION="${AWS_REGION:-ap-northeast-1}"

echo "========================================"
echo "ECR Production Governance Promotion"
echo "========================================"
echo "Repository: $REPOSITORY_NAME"
echo "Source tag: $SOURCE_TAG"
echo "Target tag: $TARGET_TAG"
echo "Region:     $AWS_REGION"
echo "========================================"

SOURCE_DIGEST="$(
  aws ecr describe-images \
    --repository-name "$REPOSITORY_NAME" \
    --image-ids imageTag="$SOURCE_TAG" \
    --region "$AWS_REGION" \
    --query 'imageDetails[0].imageDigest' \
    --output text
)"

if [[ -z "$SOURCE_DIGEST" || "$SOURCE_DIGEST" == "None" ]]; then
  echo "Source image was not found."
  exit 1
fi

echo "Source digest:"
echo "$SOURCE_DIGEST"

MANIFEST="$(
  aws ecr batch-get-image \
    --repository-name "$REPOSITORY_NAME" \
    --image-ids imageTag="$SOURCE_TAG" \
    --region "$AWS_REGION" \
    --query 'images[0].imageManifest' \
    --output text
)"

if [[ -z "$MANIFEST" || "$MANIFEST" == "None" ]]; then
  echo "Image manifest was not found."
  exit 1
fi

echo "Applying target tag: $TARGET_TAG"

aws ecr put-image \
  --repository-name "$REPOSITORY_NAME" \
  --image-tag "$TARGET_TAG" \
  --image-manifest "$MANIFEST" \
  --region "$AWS_REGION" \
  > /dev/null

TARGET_DIGEST="$(
  aws ecr describe-images \
    --repository-name "$REPOSITORY_NAME" \
    --image-ids imageTag="$TARGET_TAG" \
    --region "$AWS_REGION" \
    --query 'imageDetails[0].imageDigest' \
    --output text
)"

echo "Target digest:"
echo "$TARGET_DIGEST"

if [[ "$SOURCE_DIGEST" != "$TARGET_DIGEST" ]]; then
  echo "Promotion failed: digest mismatch."
  exit 1
fi

echo "========================================"
echo "Promotion successful"
echo "========================================"
echo "Source tag: $SOURCE_TAG"
echo "Target tag: $TARGET_TAG"
echo "Digest:     $SOURCE_DIGEST"

Cấp quyền thực thi:

chmod +x scripts/promote-ecr-image.sh

13. Tạo workflow Production Governance

Tạo file:

nano .github/workflows/production-governance.yml

Thêm nội dung:

name: Production Governance

on:
  workflow_dispatch:
    inputs:
      service:
        description: "ECR repository name"
        required: true
        type: string

      image_tag:
        description: "Git SHA image tag"
        required: true
        type: string

      target_environment:
        description: "Target environment"
        required: true
        type: choice
        options:
          - dev
          - staging
          - production

permissions:
  contents: read
  id-token: write

jobs:
  promote:
    name: Promote artifact
    runs-on: ubuntu-latest

    environment:
      name: ${{ inputs.target_environment }}

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Configure AWS credentials with OIDC
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ${{ vars.AWS_REGION }}

      - name: Verify source image
        env:
          AWS_REGION: ${{ vars.AWS_REGION }}
          ECR_REPOSITORY: ${{ inputs.service }}
          IMAGE_TAG: ${{ inputs.image_tag }}
        run: |
          aws ecr describe-images \
            --repository-name "$ECR_REPOSITORY" \
            --image-ids imageTag="$IMAGE_TAG" \
            --region "$AWS_REGION" \
            --query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest}' \
            --output table

      - name: Promote artifact
        env:
          AWS_REGION: ${{ vars.AWS_REGION }}
          ECR_REPOSITORY: ${{ inputs.service }}
          SOURCE_TAG: ${{ inputs.image_tag }}
          TARGET_TAG: ${{ inputs.target_environment }}
        run: |
          chmod +x scripts/promote-ecr-image.sh

          ./scripts/promote-ecr-image.sh \
            "$ECR_REPOSITORY" \
            "$SOURCE_TAG" \
            "$TARGET_TAG"

Workflow này thực hiện:

Run workflow
     ↓
Chọn service
     ↓
Chọn image tag
     ↓
Chọn environment
     ↓
GitHub Environment kiểm tra rule
     ↓
Nếu được phép → promotion

14. Commit và push workflow

Kiểm tra file:

git status

Add:

git add \
  scripts/promote-ecr-image.sh \
  .github/workflows/production-governance.yml

Commit:

git commit -m "add production governance workflow"

Push:

git push origin main

15. Test promotion lên Dev

Mở GitHub:

Actions
   ↓
Production Governance
   ↓
Run workflow

Nhập:

Service:
online-boutique-frontend

Image tag:
a81c92f

Target environment:
dev

Chọn:

Run workflow

Kết quả mong đợi:

Checkout repository
        ↓
Configure AWS credentials
        ↓
Verify source image
        ↓
Promote artifact
        ↓
Promotion successful

Kiểm tra ECR:

aws ecr describe-images \
  --repository-name online-boutique-frontend \
  --image-ids imageTag=dev \
  --query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest}' \
  --output table

16. Test promotion lên Staging

Chạy workflow lần nữa.

Nhập:

Service:
online-boutique-frontend

Image tag:
a81c92f

Target environment:
staging

Chọn:

Run workflow

Kết quả:

a81c92f
    ↓
staging

Kiểm tra digest:

aws ecr describe-images \
  --repository-name online-boutique-frontend \
  --image-ids imageTag=staging \
  --query 'imageDetails[0].imageDigest' \
  --output text

Lấy digest gốc:

aws ecr describe-images \
  --repository-name online-boutique-frontend \
  --image-ids imageTag=a81c92f \
  --query 'imageDetails[0].imageDigest' \
  --output text

Hai kết quả phải giống nhau.


17. Test Production Approval

Chạy workflow lần nữa.

Nhập:

Service:
online-boutique-frontend

Image tag:
a81c92f

Target environment:
production

Chọn:

Run workflow

Workflow sẽ dừng tại GitHub Environment:

production
     ↓
Waiting for approval

Trên GitHub:

Actions
   ↓
Production Governance
   ↓
Review deployments

Kiểm tra:

Environment:
production

Image:
a81c92f

Repository:
online-boutique-frontend

Nếu thông tin chính xác, chọn:

Approve and deploy

Sau khi approve:

Production Approval
        ↓
Configure AWS credentials
        ↓
Verify source image
        ↓
Promote artifact
        ↓
Promotion successful

Nếu không approve:

Workflow
   ↓
Waiting

Image sẽ không được gắn tag production.


18. Kiểm tra toàn bộ digest

Đặt biến:

export ECR_REPOSITORY=online-boutique-frontend

Chạy:

for tag in a81c92f dev staging production
do
  echo "===== $tag ====="

  aws ecr describe-images \
    --repository-name "$ECR_REPOSITORY" \
    --image-ids imageTag="$tag" \
    --query 'imageDetails[0].{Tag:imageTags,Digest:imageDigest}' \
    --output table
done

Kết quả mong đợi:

a81c92f     → sha256:abc123...
dev         → sha256:abc123...
staging     → sha256:abc123...
production  → sha256:abc123...

Hoặc lấy digest trực tiếp:

for tag in a81c92f dev staging production
do
  echo "$tag"

  aws ecr describe-images \
    --repository-name "$ECR_REPOSITORY" \
    --image-ids imageTag="$tag" \
    --query 'imageDetails[0].imageDigest' \
    --output text
done

Kết quả cần đạt:

sha256:abc123...
sha256:abc123...
sha256:abc123...
sha256:abc123...

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

Build một lần
     ↓
Security Gate
     ↓
Dev
     ↓
Staging
     ↓
Production

Tất cả environment sử dụng cùng một artifact.


19. Thử nghiệm Security Gate thất bại

Để kiểm tra governance, hãy thử promote một image không đạt điều kiện.

Ví dụ:

Image tag:
unknown-tag

Chạy workflow:

Actions
   ↓
Production Governance
   ↓
Run workflow

Nhập:

Service:
online-boutique-frontend

Image tag:
unknown-tag

Target environment:
production

Workflow phải thất bại ở bước:

Verify source image

Kết quả:

Source image was not found

Image không được promote.

Điều này chứng minh workflow không thể promote một artifact không tồn tại trong ECR.


20. Thêm bước kiểm tra digest trước Production

Để tăng tính an toàn, có thể tạo một file manifest lưu artifact đã được approve.

Tạo thư mục:

mkdir -p release-manifests

Tạo file:

nano release-manifests/online-boutique-frontend.json

Nội dung ví dụ:

{
  "service": "online-boutique-frontend",
  "imageTag": "a81c92f",
  "imageDigest": "sha256:abc123456789...",
  "approvedForProduction": true
}

Trong môi trường thực tế, file này có thể được tạo tự động sau khi:

Security Gate
     ↓
Staging Test
     ↓
Manual Review

Sau đó Production chỉ được phép sử dụng image digest đã được ghi nhận.

Trong lab này, file manifest chỉ dùng để minh họa. Ở hệ thống lớn, nên quản lý release metadata bằng một hệ thống release hoặc GitOps repository riêng.


21. Rollback về artifact trước đó

Giả sử Production hiện tại đang sử dụng:

production → sha256:new-image

Artifact trước đó là:

previous-release → sha256:old-image

Kiểm tra các tag trong ECR:

aws ecr describe-images \
  --repository-name online-boutique-frontend \
  --query 'imageDetails[].{Tags:imageTags,Digest:imageDigest,Pushed:imagePushedAt}' \
  --output table

Nếu tag cũ vẫn tồn tại, có thể promote lại:

./scripts/promote-ecr-image.sh \
  online-boutique-frontend \
  previous-release \
  production

Kiểm tra:

aws ecr describe-images \
  --repository-name online-boutique-frontend \
  --image-ids imageTag=production \
  --query 'imageDetails[0].imageDigest' \
  --output text

Kết quả phải trùng với digest của artifact cũ.

Flow rollback:

Production hiện tại
        ↓
Phát hiện lỗi
        ↓
Chọn artifact trước đó
        ↓
Kiểm tra digest
        ↓
Approval
        ↓
Promote lại production

22. Áp dụng cho nhiều service

Online Boutique có nhiều service:

online-boutique-frontend
online-boutique-cartservice
online-boutique-paymentservice
online-boutique-checkoutservice
online-boutique-shippingservice
online-boutique-productcatalogservice
online-boutique-currencyservice
online-boutique-emailservice

Có thể sử dụng cùng một workflow:

Production Governance

Ví dụ promote cartservice:

Service:
online-boutique-cartservice

Image tag:
b72d81a

Target environment:
production

Ví dụ promote paymentservice:

Service:
online-boutique-paymentservice

Image tag:
c63e91b

Target environment:
production

Mỗi service có image tag riêng:

frontend:
  a81c92f

cartservice:
  b72d81a

paymentservice:
  c63e91b

Nhưng cùng sử dụng:

Reusable Promotion Logic

23. Kiểm tra lịch sử release

Trên GitHub:

Actions
   ↓
Production Governance

Có thể xem:

Workflow run
Commit
Actor
Environment
Input image tag
Target environment
Approval status
Execution log

Ví dụ:

Run #25
Service: online-boutique-frontend
Image: a81c92f
Environment: production
Approved by: maintainer-account
Status: Success

Đây là một phần quan trọng của Production Governance:

Không chỉ deploy được, mà còn phải biết ai đã promote artifact nào lên Production và vào thời điểm nào.


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

Sau khi hoàn thành Chapter 4G, hệ thống sẽ có kiến trúc:

Developer
    ↓
GitHub
    ↓
GitHub Actions
    ↓
Reusable Workflow
    ↓
Test
    ↓
Docker Build
    ↓
Amazon ECR
    ↓
Vulnerability Scan
    ↓
Security Gate
    ↓
Artifact Promotion
    ↓
Dev
    ↓
Staging
    ↓
Production Approval
    ↓
Production

Amazon ECR quản lý:

Docker Images
Image Tags
Image Digests
Lifecycle Policy
Image Scanning

GitHub quản lý:

Workflow
Environment
Required Reviewers
Approval
Release History

AWS IAM quản lý:

Who can access ECR
Which repository can be accessed
Which action is allowed
Which GitHub repository can assume the role

25. Tổng kết Chapter 4

Toàn bộ Chapter 4 có thể được nhìn như sau:

4A — Amazon ECR
      ↓
4B — Lifecycle Policy
      ↓
4C — Image Security
      ↓
4D — Immutable Tags
      ↓
4E — Security Gate
      ↓
4F — Artifact Promotion
      ↓
4G — Production Governance

Sự khác biệt giữa các phần:

Phần Câu hỏi cần giải quyết
4A Image được lưu ở đâu?
4B Image cũ được quản lý như thế nào?
4C Image có vulnerability hay không?
4D Image tag có bị ghi đè hay không?
4E Image không an toàn có bị chặn hay không?
4F Làm sao sử dụng cùng một image qua các environment?
4G Ai được phép đưa image lên Production?

Kết quả cuối cùng:

Build Once
    ↓
Scan Once
    ↓
Promote Same Artifact
    ↓
Approve Production
    ↓
Release with Governance

Đây là nền tảng cần thiết trước khi chuyển sang Chapter 5 — Kubernetes + GitOps với Amazon EKS và ArgoCD.


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í