🚀 Chapter 4 — Production: Phần F — Artifact Promotion với Amazon ECR
Ở Chapter 4E, chúng ta đã hoàn thành Security Gate:
GitHub
↓
GitHub Actions
↓
Docker Build
↓
Amazon ECR
↓
Vulnerability Scan
↓
Security Gate
├── FAIL → Dừng pipeline
└── PASS → Cho phép tiếp tục
Tuy nhiên, sau khi image vượt qua Security Gate, chúng ta vẫn cần giải quyết một vấn đề quan trọng:
Làm thế nào để đưa chính xác image đã được kiểm tra từ Dev lên Staging rồi Production?
Một cách làm không tốt là build lại image cho từng môi trường:
Build Dev
↓
image-dev
Build Staging
↓
image-staging
Build Production
↓
image-production
Cách này có thể tạo ra những image khác nhau giữa các môi trường.
Trong phần này, chúng ta sẽ sử dụng mô hình:
Build Once
↓
Security Scan
↓
Promote Same Artifact
↓
Dev
↓
Staging
↓
Production
1. Mục tiêu
Sau khi hoàn thành phần này, chúng ta sẽ có:
✅ Build image một lần
✅ Image được tag bằng Git SHA
✅ Image được push vào Amazon ECR
✅ Image vượt qua Security Gate
✅ Promote cùng một image qua Dev → Staging → Production
✅ Không build lại image ở mỗi môi trường
✅ Có thể kiểm tra image digest giữa các môi trường
✅ Có bước approval trước Production
✅ Có thể rollback về image đã được sử dụng trước đó
Kiến trúc cuối cùng:
Developer
↓
git push
↓
GitHub Actions
↓
Docker Build
↓
Push Image vào ECR
↓
Security Gate
↓
Promote to Dev
↓
Promote to Staging
↓
[Approval]
↓
Promote to Production
2. Kiểm tra môi trường
Trên máy local, di chuyển vào repository:
cd online-boutique-cicd
Kiểm tra Git:
git status
Kiểm tra workflow:
find .github/workflows -type f
Ví dụ:
.github/workflows/
├── reusable-ci-cd.yml
├── scale-ci-cd.yml
└── security-gate-test.yml
Kiểm tra script Security Gate:
ls -l scripts/ecr-security-gate.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ó Region, sử dụng Tokyo:
aws configure set region ap-northeast-1
3. Kiểm tra image đã vượt qua Security Gate
Ở phần trước, chúng ta đã sử dụng image tag theo Git SHA:
a81c92f
Ví dụ repository:
online-boutique-frontend
Đặt biến:
export AWS_REGION=ap-northeast-1
export ECR_REPOSITORY=online-boutique-frontend
export IMAGE_TAG=a81c92f
Thay a81c92f bằng tag thực tế trong ECR.
Kiểm tra image:
aws ecr describe-images \
--repository-name "$ECR_REPOSITORY" \
--image-ids imageTag="$IMAGE_TAG" \
--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 image digest:
export IMAGE_DIGEST=$(aws ecr describe-images \
--repository-name "$ECR_REPOSITORY" \
--image-ids imageTag="$IMAGE_TAG" \
--query 'imageDetails[0].imageDigest' \
--output text)
Kiểm tra:
echo "$IMAGE_DIGEST"
Ví dụ:
sha256:abc123456789...
Từ thời điểm này, chúng ta sẽ dùng image digest để xác định artifact. Tag chỉ là tên tham chiếu, còn digest xác định chính xác nội dung image.
4. Thiết kế tag cho từng môi trường
Chúng ta sẽ giữ lại tag Git SHA gốc:
a81c92f
Sau đó thêm các tag môi trường:
a81c92f
dev
staging
production
Ví dụ:
ECR Repository
└── online-boutique-frontend
├── a81c92f
├── dev
├── staging
└── production
Các tag này cùng trỏ tới một image digest:
a81c92f ─────┐
dev ─────────┤
staging ─────┼──→ sha256:abc123...
production ──┘
Khi promote image:
a81c92f
↓
dev
↓
staging
↓
production
Chúng ta chỉ thay đổi tag tham chiếu, không build lại image.
5. Tạo script promote image
Tạo thư mục scripts nếu chưa có:
mkdir -p scripts
Tạo file:
nano scripts/promote-ecr-image.sh
Thêm nội dung:
#!/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 Artifact 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"
Lưu file:
CTRL + O
ENTER
CTRL + X
Cấp quyền thực thi:
chmod +x scripts/promote-ecr-image.sh
6. Kiểm tra script promote
Sử dụng image đã tồn tại trong ECR:
./scripts/promote-ecr-image.sh \
online-boutique-frontend \
a81c92f \
dev
Thay a81c92f bằng tag thực tế.
Kết quả mong đợi:
========================================
ECR Artifact Promotion
========================================
Repository: online-boutique-frontend
Source tag: a81c92f
Target tag: dev
Region: ap-northeast-1
========================================
Source digest:
sha256:abc123456789...
Applying target tag: dev
Target digest:
sha256:abc123456789...
========================================
Promotion successful
========================================
Source tag: a81c92f
Target tag: dev
Digest: sha256:abc123456789...
Điểm quan trọng:
Source digest == Target digest
Điều này chứng minh rằng dev đang trỏ tới đúng image đã được build và scan.
7. Kiểm tra tag Dev
Chạy:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=dev \
--query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest}' \
--output table
Kiểm tra digest của Git SHA:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=a81c92f \
--query 'imageDetails[0].imageDigest' \
--output text
Kiểm tra digest của dev:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=dev \
--query 'imageDetails[0].imageDigest' \
--output text
So sánh:
SOURCE_DIGEST=$(aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=a81c92f \
--query 'imageDetails[0].imageDigest' \
--output text)
DEV_DIGEST=$(aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=dev \
--query 'imageDetails[0].imageDigest' \
--output text)
if [[ "$SOURCE_DIGEST" == "$DEV_DIGEST" ]]; then
echo "Same artifact"
else
echo "Different artifact"
fi
Kết quả:
Same artifact
8. Promote từ Dev sang Staging
Sau khi image được kiểm tra ở Dev, promote image sang Staging:
./scripts/promote-ecr-image.sh \
online-boutique-frontend \
dev \
staging
Kiểm tra:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=staging \
--query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest}' \
--output table
Kiểm tra cả ba tag:
for tag in a81c92f dev staging
do
echo "===== $tag ====="
aws ecr describe-images \
--repository-name online-boutique-frontend \
--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...
9. Promote từ Staging sang Production
Sau khi kiểm tra Staging thành công, promote image sang Production:
./scripts/promote-ecr-image.sh \
online-boutique-frontend \
staging \
production
Kiểm tra:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=production \
--query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest}' \
--output table
Kiểm tra toàn bộ artifact:
for tag in a81c92f dev staging production
do
echo "===== $tag ====="
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag="$tag" \
--query 'imageDetails[0].imageDigest' \
--output text
done
Kết quả mong đợi:
sha256:abc123...
sha256:abc123...
sha256:abc123...
sha256:abc123...
Như vậy:
Git SHA
↓
Dev
↓
Staging
↓
Production
đều sử dụng cùng một image digest.
10. Tạo workflow promotion trên GitHub Actions
Tạo file:
nano .github/workflows/artifact-promotion.yml
Thêm nội dung:
name: Artifact Promotion
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
jobs:
promote:
name: Promote artifact
runs-on: ubuntu-latest
environment: ${{ inputs.target_environment }}
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ${{ vars.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Promote image
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 cho phép chạy thủ công từ GitHub Actions.
Ví dụ:
Service:
online-boutique-frontend
Image tag:
a81c92f
Target environment:
dev
GitHub Actions sẽ thực hiện:
a81c92f
↓
dev
11. Thiết lập GitHub Environment
Vào GitHub repository:
Settings
↓
Environments
Tạo ba Environment:
dev
staging
production
Cấu hình:
dev
└── Không cần approval
staging
└── Có thể yêu cầu approval nếu muốn
production
└── Bắt buộc approval
Đối với production:
Settings
↓
Environments
↓
production
↓
Required reviewers
Thêm GitHub account được phép approve.
Khi workflow target production, GitHub Actions sẽ dừng ở bước Environment:
Workflow
↓
Production Environment
↓
[Waiting for approval]
↓
Approve
↓
Promote image
12. Thiết lập GitHub Variables và Secrets
Vào:
Settings
↓
Secrets and variables
↓
Actions
Tạo Repository Variable:
Name:
AWS_REGION
Value:
ap-northeast-1
Tạo hoặc kiểm tra các Secrets:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
Nếu các giá trị này đã được tạo ở Chapter 4D hoặc Chapter 4E thì không cần tạo lại.
13. Chạy promotion bằng GitHub Actions
Commit workflow:
git add \
scripts/promote-ecr-image.sh \
.github/workflows/artifact-promotion.yml
Commit:
git commit -m "add ECR artifact promotion"
Push:
git push origin main
Mở GitHub:
Actions
↓
Artifact Promotion
↓
Run workflow
Nhập:
Service:
online-boutique-frontend
Image tag:
a81c92f
Target environment:
dev
Chọn:
Run workflow
Kết quả:
Checkout source
↓
Configure AWS credentials
↓
Promote image
↓
Promotion successful
14. Promote lên Staging
Chạy lại workflow:
Actions
↓
Artifact Promotion
↓
Run workflow
Nhập:
Service:
online-boutique-frontend
Image tag:
a81c92f
Target environment:
staging
Workflow sẽ thực hiện:
a81c92f
↓
staging
Lưu ý: trong ví dụ này, staging được tạo trực tiếp từ Git SHA gốc. Điều này giúp tránh phụ thuộc vào tag dev.
15. Promote lên Production
Chạy workflow lần nữa:
Actions
↓
Artifact Promotion
↓
Run workflow
Nhập:
Service:
online-boutique-frontend
Image tag:
a81c92f
Target environment:
production
Flow:
Run workflow
↓
GitHub Environment: production
↓
[Approval required]
↓
Approve
↓
Promote image
↓
production tag created
Nếu chưa approve:
Waiting
Nếu approve thành công:
Promotion successful
16. Áp dụng cho nhiều service
Online Boutique có nhiều service, vì vậy chúng ta có thể áp dụng cùng một script cho từng ECR repository.
Ví dụ:
online-boutique-frontend
online-boutique-cartservice
online-boutique-paymentservice
online-boutique-checkoutservice
online-boutique-shippingservice
online-boutique-productcatalogservice
Promote cartservice:
./scripts/promote-ecr-image.sh \
online-boutique-cartservice \
a81c92f \
dev
Promote paymentservice:
./scripts/promote-ecr-image.sh \
online-boutique-paymentservice \
b72d81a \
dev
Promote checkoutservice:
./scripts/promote-ecr-image.sh \
online-boutique-checkoutservice \
c63e91b \
staging
Mỗi service có image tag riêng:
frontend:
a81c92f
cartservice:
b72d81a
paymentservice:
c63e91b
Nhưng tất cả đều dùng chung logic:
scripts/promote-ecr-image.sh
17. Kiểm tra artifact bằng digest
Tạo file:
nano scripts/verify-artifact-digest.sh
Thêm:
#!/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}"
SOURCE_DIGEST="$(
aws ecr describe-images \
--repository-name "$REPOSITORY_NAME" \
--image-ids imageTag="$SOURCE_TAG" \
--region "$AWS_REGION" \
--query 'imageDetails[0].imageDigest' \
--output text
)"
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 "Source tag: $SOURCE_TAG"
echo "Source digest: $SOURCE_DIGEST"
echo "Target tag: $TARGET_TAG"
echo "Target digest: $TARGET_DIGEST"
if [[ "$SOURCE_DIGEST" != "$TARGET_DIGEST" ]]; then
echo "Artifact verification failed."
exit 1
fi
echo "Artifact verification passed."
Cấp quyền:
chmod +x scripts/verify-artifact-digest.sh
Kiểm tra:
./scripts/verify-artifact-digest.sh \
online-boutique-frontend \
a81c92f \
production
Kết quả:
Source tag: a81c92f
Source digest: sha256:abc123...
Target tag: production
Target digest: sha256:abc123...
Artifact verification passed.
18. Thêm bước verify vào workflow
Mở workflow:
nano .github/workflows/artifact-promotion.yml
Sau bước Promote image, thêm:
- name: Verify promoted 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/verify-artifact-digest.sh
./scripts/verify-artifact-digest.sh \
"$ECR_REPOSITORY" \
"$SOURCE_TAG" \
"$TARGET_TAG"
Flow hoàn chỉnh:
Checkout source
↓
Configure AWS credentials
↓
Promote image
↓
Verify source digest
↓
Verify target digest
↓
Compare digest
↓
PASS
Nếu digest khác nhau:
Artifact verification failed
Workflow sẽ fail.
19. Kiểm tra bằng AWS CLI
Liệt kê các tag của image:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--query 'imageDetails[].{Tags:imageTags,Digest:imageDigest}' \
--output table
Ví dụ:
--------------------------------------------------
| DescribeImages |
+-------------------+----------------------------+
| Tags | Digest |
+-------------------+----------------------------+
| a81c92f, dev, | sha256:abc123... |
| staging, | |
| production | |
+-------------------+----------------------------+
Kết quả cho thấy các tag môi trường cùng trỏ tới một digest.
20. Rollback về artifact trước đó
Giả sử Production hiện tại đang sử dụng:
production → sha256:new123...
Image cũ vẫn còn trong ECR:
old-release
Kiểm tra image cũ:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=old-release \
--query 'imageDetails[0].{Tags:imageTags,Digest:imageDigest}' \
--output table
Rollback bằng cách promote image cũ thành production:
./scripts/promote-ecr-image.sh \
online-boutique-frontend \
old-release \
production
Kiểm tra:
aws ecr describe-images \
--repository-name online-boutique-frontend \
--image-ids imageTag=production \
--query 'imageDetails[0].imageDigest' \
--output text
Flow rollback:
Previous Artifact
↓
ECR Image
↓
Promote to production
↓
Production
Không cần build lại image cũ.
21. Lưu ý về tag môi trường
Trong lab này, chúng ta sử dụng:
dev
staging
production
để dễ quan sát quá trình promotion.
Tuy nhiên, trong production thực tế, nên ưu tiên:
Git SHA
hoặc:
Image Digest
làm định danh chính.
Ví dụ:
online-boutique-frontend@sha256:abc123...
Các tag như dev, staging, production chỉ nên được xem là các pointer trỏ tới artifact cụ thể.
Không nên dùng:
latest
làm định danh duy nhất cho production vì tag này có thể thay đổi.
22. Kết quả cuối cùng
Sau Chapter 4F, pipeline sẽ có dạng:
Developer
↓
GitHub
↓
GitHub Actions
↓
Reusable Workflow
↓
Test
↓
Docker Build
↓
Git SHA Tag
↓
Amazon ECR
↓
Security Gate
├── FAIL → Stop
└── PASS
↓
Dev
↓
Staging
↓
Approval
↓
Production
Artifact flow:
frontend:a81c92f
│
├──→ frontend:dev
│
├──→ frontend:staging
│
└──→ frontend:production
Tất cả tag đều trỏ tới cùng một digest:
a81c92f
dev
staging
production
↓
sha256:abc123...
23. Kết luận
Trong Chapter 4E, chúng ta trả lời câu hỏi:
Image có đủ an toàn để tiếp tục không?
Trong Chapter 4F, chúng ta trả lời câu hỏi:
Làm thế nào để đưa đúng image đã được kiểm tra qua nhiều môi trường?
Nguyên tắc chính:
Build Once
↓
Scan Once
↓
Promote Many Times
Kết quả:
Không build lại image
Không tạo artifact khác nhau giữa các môi trường
Có thể kiểm tra bằng digest
Có approval trước Production
Có thể rollback nhanh
Đây là nền tảng quan trọng trước khi chuyển sang Chapter 5 — Kubernetes + GitOps với Amazon EKS và ArgoCD.
All rights reserved