0

🚀 Chapter 4 — Production: Phần A — Đưa Online Boutique lên Amazon ECR

Ở Chapter 3D, chúng ta đã xây dựng được CI/CD có khả năng scale cho nhiều microservice bằng:

GitHub
   ↓
Matrix
   ↓
Reusable Workflow
   ↓
Docker Build
   ↓
SSH
   ↓
EC2

Nhưng vẫn còn một vấn đề.

Ở Chapter 3D, EC2 vẫn phải build Docker image:

GitHub Actions
      ↓
SSH
      ↓
EC2
      ↓
docker compose build
      ↓
docker compose up

Khi hệ thống lớn hơn, chúng ta muốn tách quá trình:

BUILD
  ↓
ARTIFACT
  ↓
DEPLOY

Vì vậy Chapter 4A sẽ đưa Amazon ECR vào giữa.

Kiến trúc mới:

GitHub
   ↓
GitHub Actions
   ↓
Matrix
   ↓
Reusable Workflow
   ↓
Docker Build
   ↓
Amazon ECR
   ↓
SSH
   ↓
EC2
   ↓
docker pull
   ↓
docker compose up

Online Boutique là ứng dụng microservices gồm nhiều service viết bằng nhiều ngôn ngữ khác nhau, rất phù hợp để tiếp tục sử dụng làm lab cho phần này.


1. Mục tiêu

Sau Chapter 4A, pipeline phải đạt được:

Developer
    ↓
git push
    ↓
GitHub
    ↓
GitHub Actions
    ↓
Matrix
    ↓
Reusable Workflow
    ↓
Docker Build
    ↓
Push Image → ECR
    ↓
SSH → EC2
    ↓
Pull Image ← ECR
    ↓
Docker Compose
    ↓
Running

Có 4 service:

frontend
cartservice
productcatalogservice
checkoutservice

Mỗi service sẽ có một Docker image riêng trong ECR.

Ví dụ:

ECR
├── online-boutique-frontend
├── online-boutique-cartservice
├── online-boutique-productcatalogservice
└── online-boutique-checkoutservice

2. So sánh Chapter 3D và Chapter 4A

Chapter 3D

GitHub
   ↓
Matrix
   ↓
Reusable Workflow
   ↓
Docker Build
   ↓
SSH
   ↓
EC2
   ↓
Docker Compose Build
   ↓
Docker Compose Up

EC2 tự build image.


Chapter 4A

GitHub
   ↓
Matrix
   ↓
Reusable Workflow
   ↓
Docker Build
   ↓
ECR
   ↓
SSH
   ↓
EC2
   ↓
Docker Pull
   ↓
Docker Compose Up

EC2 không build image nữa.

Đây là thay đổi quan trọng nhất của Chapter 4A.


3. Kiểm tra trạng thái từ Chapter 3D

Trên local:

cd online-boutique-cicd

Kiểm tra:

git status

Kiểm tra workflow:

find .github/workflows -type f

Kết quả:

.github/workflows/reusable-ci-cd.yml
.github/workflows/scale-ci-cd.yml

Đây chính là trạng thái cuối của Chapter 3D.


4. Kiểm tra Matrix Workflow

Chạy:

cat .github/workflows/scale-ci-cd.yml

Bạn sẽ có cấu trúc tương tự:

name: Scale CI/CD

on:
  push:
    branches:
      - main
    paths:
      - "src/**"
      - ".github/workflows/**"

jobs:
  deploy:
    strategy:
      matrix:
        service:
          - frontend
          - cartservice
          - productcatalogservice
          - checkoutservice

    uses: ./.github/workflows/reusable-ci-cd.yml

    with:
      service: ${{ matrix.service }}

    secrets: inherit

Sau này có thể đổi tên job:

deploy

thành:

build-and-deploy

nhưng chưa cần thay đổi ngay.


5. Tạo ECR Repository

Vào:

AWS Console
→ ECR
→ Repositories
→ Create repository

Chọn:

Visibility:
Private

Tạo repository đầu tiên:

online-boutique-frontend

Sau đó tạo:

online-boutique-cartservice
online-boutique-productcatalogservice
online-boutique-checkoutservice

Kết quả:

ECR
│
├── online-boutique-frontend
├── online-boutique-cartservice
├── online-boutique-productcatalogservice
└── online-boutique-checkoutservice

ECR private repository phải tồn tại trước khi push image nếu bạn không dùng repository creation template.


6. Lấy AWS Account ID

Trên local:

aws sts get-caller-identity

Kết quả:

{
    "UserId": "...",
    "Account": "123456789012",
    "Arn": "..."
}

Lưu:

AWS_ACCOUNT_ID=123456789012

Region sử dụng:

ap-northeast-1

ECR registry sẽ có dạng:

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

7. Tạo GitHub OIDC Provider trong AWS

Chúng ta không muốn lưu AWS Access Key trực tiếp vào GitHub Actions.

Flow sẽ là:

GitHub Actions
      ↓
GitHub OIDC
      ↓
AWS IAM Role
      ↓
Amazon ECR

AWS cũng hỗ trợ pattern GitHub Actions sử dụng OIDC để build Docker image và push vào ECR.

Vào:

AWS Console
→ IAM
→ Identity providers
→ Add provider

Chọn:

Provider type:
OpenID Connect

URL:

https://token.actions.githubusercontent.com

Audience:

sts.amazonaws.com

Sau đó:

Add provider

8. Tạo IAM Policy cho GitHub Actions

Vào:

IAM
→ Policies
→ Create policy

Chọn:

JSON

Dùng:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:CompleteLayerUpload",
        "ecr:InitiateLayerUpload",
        "PutImage",
        "UploadLayerPart"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-1:YOUR_ACCOUNT_ID:repository/online-boutique-*"
    }
  ]
}

Thay:

YOUR_ACCOUNT_ID

bằng AWS Account ID.

Ví dụ:

arn:aws:ecr:ap-northeast-1:123456789012:repository/online-boutique-*

Đặt tên:

GitHubActionsOnlineBoutiqueECRPush

9. Tạo IAM Role cho GitHub Actions

Vào:

IAM
→ Roles
→ Create role

Chọn:

Custom trust policy

Dùng:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::YOUR_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:YOUR_GITHUB_USERNAME/online-boutique-cicd:ref:refs/heads/main"
        }
      }
    }
  ]
}

Thay:

YOUR_ACCOUNT_ID

và:

YOUR_GITHUB_USERNAME

Ví dụ:

repo:Juu-dev/online-boutique-cicd:ref:refs/heads/main

Đặt tên:

GitHubActionsOnlineBoutiqueECRRole

10. Attach ECR Policy vào Role

Mở:

IAM
→ Roles
→ GitHubActionsOnlineBoutiqueECRRole

Chọn:

Add permissions

Attach:

GitHubActionsOnlineBoutiqueECRPush

Flow:

GitHub
   ↓
OIDC
   ↓
GitHubActionsOnlineBoutiqueECRRole
   ↓
ECR

11. Lấy Role ARN

Mở:

IAM
→ Roles
→ GitHubActionsOnlineBoutiqueECRRole

Copy ARN.

Ví dụ:

arn:aws:iam::123456789012:role/GitHubActionsOnlineBoutiqueECRRole

12. Thêm GitHub Repository Variables

Vào repository:

GitHub
→ online-boutique-cicd
→ Settings
→ Secrets and variables
→ Actions
→ Variables

Tạo:

AWS_ROLE_ARN

Value:

arn:aws:iam::123456789012:role/GitHubActionsOnlineBoutiqueECRRole

Tạo thêm:

AWS_REGION

Value:

ap-northeast-1

13. Chuẩn bị EC2 để Pull ECR

Bây giờ GitHub Actions đã có quyền:

Push → ECR

Nhưng EC2 cũng cần quyền:

Pull ← ECR

Không nên lưu AWS Access Key trên EC2.

Thay vào đó sử dụng:

EC2
 ↓
IAM Role
 ↓
ECR Read

14. Tạo IAM Policy cho EC2

Vào:

IAM
→ Policies
→ Create policy

Dùng:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecr:BatchCheckLayerAvailability",
        "ecr:GetDownloadUrlForLayer",
        "ecr:BatchGetImage"
      ],
      "Resource": "arn:aws:ecr:ap-northeast-1:YOUR_ACCOUNT_ID:repository/online-boutique-*"
    }
  ]
}

Đặt tên:

EC2OnlineBoutiqueECRPull

15. Attach Role cho EC2

Tạo IAM Role:

EC2OnlineBoutiqueRole

Attach policy:

EC2OnlineBoutiqueECRPull

Sau đó:

EC2
→ Actions
→ Security
→ Modify IAM role

Chọn:

EC2OnlineBoutiqueRole

16. Kiểm tra AWS CLI trên EC2

SSH vào EC2:

ssh -i ~/Downloads/chapter1-key.pem ubuntu@YOUR_EC2_IP

Kiểm tra:

aws --version

Nếu chưa có:

sudo apt update
sudo apt install -y awscli

Kiểm tra IAM Role:

aws sts get-caller-identity

Bạn phải thấy Account ID của AWS.


17. Kiểm tra EC2 Login ECR

Trên EC2:

aws ecr get-login-password \
  --region ap-northeast-1 \
  | docker login \
  --username AWS \
  --password-stdin \
  YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com

Nếu thành công:

Login Succeeded

Đây là cách AWS hướng dẫn authenticate Docker với private ECR. Authorization token có thời hạn 12 giờ.


18. Sửa Docker Compose trên EC2

Đây là thay đổi quan trọng.

Chapter 3D:

docker compose
      ↓
build source code

Chapter 4A:

docker compose
      ↓
pull image từ ECR

Mở:

cd ~/online-boutique

Mở file Compose:

nano compose.yaml

Với frontend, thay phần build bằng image:

services:
  frontend:
    image: YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/online-boutique-frontend:${FRONTEND_TAG}

cartservice:

  cartservice:
    image: YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/online-boutique-cartservice:${CARTSERVICE_TAG}

productcatalogservice:

  productcatalogservice:
    image: YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/online-boutique-productcatalogservice:${PRODUCTCATALOGSERVICE_TAG}

checkoutservice:

  checkoutservice:
    image: YOUR_ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/online-boutique-checkoutservice:${CHECKOUTSERVICE_TAG}

Nếu file Compose hiện tại của bạn dùng cấu trúc khác, chỉ thay build: của 4 service này bằng image: tương ứng. Không cần thay đổi các service dependency khác.


19. Không dùng latest

Không dùng:

frontend:latest

Mà dùng:

frontend:<git-sha>

Ví dụ:

frontend:a81c92f

Flow:

Git Commit
    ↓
a81c92f
    ↓
Docker Image
    ↓
ECR
    ↓
frontend:a81c92f

Điều này giúp EC2 biết chính xác version nào đang chạy.


20. Tạo file .env trên EC2

Trong:

cd ~/online-boutique

Tạo:

nano .env

Ví dụ:

FRONTEND_TAG=a81c92f
CARTSERVICE_TAG=a81c92f
PRODUCTCATALOGSERVICE_TAG=a81c92f
CHECKOUTSERVICE_TAG=a81c92f

Nhưng chúng ta sẽ không sửa file này thủ công mỗi lần deploy.

GitHub Actions sẽ cập nhật nó.


21. Test Pull Image thủ công

Trước khi đưa vào GitHub Actions, test một lần.

Trên EC2:

export AWS_REGION=ap-northeast-1
export AWS_ACCOUNT_ID=YOUR_ACCOUNT_ID

Login:

aws ecr get-login-password \
  --region $AWS_REGION \
  | docker login \
  --username AWS \
  --password-stdin \
  $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

Pull:

docker pull \
  $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/online-boutique-frontend:IMAGE_TAG

Kiểm tra:

docker images

Nếu thấy:

online-boutique-frontend

thì ECR → EC2 đã hoạt động.


22. Sửa Reusable Workflow

Bây giờ mở local:

nano .github/workflows/reusable-ci-cd.yml

Thay workflow cũ bằng:

name: Reusable Build and Deploy

on:
  workflow_call:
    inputs:
      service:
        required: true
        type: string

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      id-token: write

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

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

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build Docker image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: online-boutique-${{ inputs.service }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build \
            -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
            src/${{ inputs.service }}

      - name: Push Docker image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: online-boutique-${{ inputs.service }}
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker push \
            $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Deploy to EC2
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            cd ~/online-boutique

            export AWS_REGION=${{ vars.AWS_REGION }}
            export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

            aws ecr get-login-password \
              --region $AWS_REGION \
              | docker login \
              --username AWS \
              --password-stdin \
              $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

            docker pull \
              $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/online-boutique-${{ inputs.service }}:${{ github.sha }}

            echo "${{ inputs.service }}_TAG=${{ github.sha }}" > .deploy-${{ inputs.service }}

            case "${{ inputs.service }}" in
              frontend)
                export FRONTEND_TAG=${{ github.sha }}
                ;;
              cartservice)
                export CARTSERVICE_TAG=${{ github.sha }}
                ;;
              productcatalogservice)
                export PRODUCTCATALOGSERVICE_TAG=${{ github.sha }}
                ;;
              checkoutservice)
                export CHECKOUTSERVICE_TAG=${{ github.sha }}
                ;;
            esac

            docker compose up -d ${{ inputs.service }}

23. Một vấn đề quan trọng với .env

Workflow trên mới chỉ export biến trong SSH session.

Nếu Compose sử dụng:

${FRONTEND_TAG}

thì cần đảm bảo biến đó được truyền vào Compose.

Cách đơn giản hơn cho lab là cập nhật .env trên EC2.

Thay phần:

echo "${{ inputs.service }}_TAG=${{ github.sha }}" > .deploy-${{ inputs.service }}

bằng:

sed -i "/^${{ inputs.service }}_TAG=/d" .env
echo "${{ inputs.service }}_TAG=${{ github.sha }}" >> .env

Sau đó:

docker compose up -d ${{ inputs.service }}

Vì vậy phần deploy cuối cùng nên là:

      - name: Deploy to EC2
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            cd ~/online-boutique

            export AWS_REGION=${{ vars.AWS_REGION }}
            export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

            aws ecr get-login-password \
              --region $AWS_REGION \
              | docker login \
              --username AWS \
              --password-stdin \
              $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com

            docker pull \
              $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/online-boutique-${{ inputs.service }}:${{ github.sha }}

            sed -i "/^${{ inputs.service }}_TAG=/d" .env
            echo "${{ inputs.service }}_TAG=${{ github.sha }}" >> .env

            docker compose up -d ${{ inputs.service }}

24. Kiểm tra GitHub Secrets

Vào:

GitHub
→ Settings
→ Secrets and variables
→ Actions
→ Secrets

Bạn phải có:

EC2_HOST
EC2_SSH_KEY

Trong đó:

EC2_HOST

là Public IP hoặc DNS của EC2.

EC2_SSH_KEY

là private key:

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

Không commit private key vào repository.


25. Kiểm tra workflow

Local:

cat .github/workflows/reusable-ci-cd.yml

Flow phải là:

Checkout
   ↓
Configure AWS credentials
   ↓
Login ECR
   ↓
Docker Build
   ↓
Docker Push
   ↓
SSH EC2
   ↓
ECR Login
   ↓
Docker Pull
   ↓
Update .env
   ↓
Docker Compose Up

26. Commit thay đổi

Kiểm tra:

git status

Xem diff:

git diff

Add:

git add .github/workflows

Commit:

git commit -m "deploy images from ecr"

Push:

git push origin main

27. GitHub Actions chạy Matrix

Vào:

GitHub
→ Actions
→ Scale CI/CD

Bạn sẽ thấy:

Scale CI/CD
│
├── build-and-deploy (frontend)
│
├── build-and-deploy (cartservice)
│
├── build-and-deploy (productcatalogservice)
│
└── build-and-deploy (checkoutservice)

Mỗi job:

Docker Build
    ↓
Push ECR
    ↓
SSH EC2
    ↓
Pull ECR
    ↓
Docker Compose

28. Kiểm tra ECR

Vào:

AWS Console
→ ECR
→ Repositories

Mở:

online-boutique-frontend

Bạn sẽ thấy:

Image tag
a81c92f

Tương tự:

online-boutique-cartservice
online-boutique-productcatalogservice
online-boutique-checkoutservice

ECR sẽ lưu các image được push từ GitHub Actions.


29. Kiểm tra EC2

SSH vào EC2:

ssh -i ~/Downloads/chapter1-key.pem ubuntu@YOUR_EC2_IP

Kiểm tra:

docker images

Bạn sẽ thấy:

online-boutique-frontend
online-boutique-cartservice
online-boutique-productcatalogservice
online-boutique-checkoutservice

Kiểm tra container:

docker compose ps

Ví dụ:

NAME              STATUS
frontend           Up
cartservice        Up
productcatalog     Up
checkoutservice    Up

30. Kiểm tra Image đang chạy

Frontend:

docker inspect frontend \
  --format '{{.Config.Image}}'

Kết quả:

123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/online-boutique-frontend:a81c92f

Đây chính là điều chúng ta muốn.

EC2 đang chạy:

ECR Image

chứ không phải:

EC2 tự build source code

31. Test Deployment

Thay đổi một phần frontend.

Ví dụ:

git checkout -b test-ecr-deployment

Thực hiện một thay đổi nhỏ trong:

src/frontend/

Commit:

git add .
git commit -m "test ecr deployment"
git push origin test-ecr-deployment

Sau đó merge vào main.

GitHub Actions chạy:

GitHub
 ↓
Matrix
 ↓
frontend
 ↓
Docker Build
 ↓
ECR
 ↓
SSH
 ↓
EC2
 ↓
docker pull
 ↓
docker compose up

32. Kiểm tra Git SHA

Local:

git rev-parse HEAD

Ví dụ:

a81c92f

ECR:

frontend:a81c92f

EC2:

docker inspect frontend \
  --format '{{.Config.Image}}'

Kết quả:

.../online-boutique-frontend:a81c92f

Ba nơi đều cùng một version:

Git
 ↓
a81c92f
 ↓
ECR
 ↓
a81c92f
 ↓
EC2
 ↓
a81c92f

33. Architecture cuối Chapter 4A

                         Developer
                             │
                             ▼
                           GitHub
                             │
                             ▼
                      GitHub Actions
                             │
                             ▼
                          Matrix
                             │
          ┌──────────────────┼──────────────────┐
          ▼                  ▼                  ▼
      frontend          cartservice       checkoutservice
          │                  │                  │
          └──────────────────┼──────────────────┘
                             ▼
                    Reusable Workflow
                             │
                             ▼
                       Docker Build
                             │
                             ▼
                     Amazon ECR
                             │
              ┌──────────────┼──────────────┐
              ▼              ▼              ▼
          frontend         cart          checkout
           image           image           image
              │              │              │
              └──────────────┼──────────────┘
                             ▼
                           SSH
                             │
                             ▼
                            EC2
                             │
                     ECR Authentication
                             │
                             ▼
                        Docker Pull
                             │
                             ▼
                      Docker Compose
                             │
                             ▼
                          Running

34. Sự thay đổi quan trọng

Chapter 3D

GitHub Actions
      ↓
Docker Build
      ↓
SSH
      ↓
EC2
      ↓
Docker Compose Build
      ↓
Container

Chapter 4A

GitHub Actions
      ↓
Docker Build
      ↓
ECR
      ↓
SSH
      ↓
EC2
      ↓
Docker Pull
      ↓
Docker Compose
      ↓
Container

Build không còn xảy ra trên EC2.

Đây chính là bước đầu tiên để chuyển từ:

Source Code → Server

sang:

Source Code
     ↓
Docker Image
     ↓
Container Registry
     ↓
Server

35. Kết quả cần đạt

Cuối Chapter 4A:

✅ GitHub Actions
✅ Matrix
✅ Reusable Workflow
✅ Docker Build
✅ Amazon ECR
✅ GitHub OIDC → AWS
✅ EC2 IAM Role → ECR
✅ SSH deployment
✅ Docker Pull
✅ Docker Compose
✅ Git SHA image tag

Architecture:

GitHub
   ↓
Matrix
   ↓
Reusable Workflow
   ↓
Docker Build
   ↓
ECR
   ↓
SSH
   ↓
EC2 Pull
   ↓
Docker Compose

Chưa làm:

❌ ECR Lifecycle Policy
❌ Vulnerability Scanning
❌ Immutable Tag
❌ Security Gate
❌ Approval
❌ Dev → Staging → Production Promotion

Các phần này sẽ được xây dựng tiếp trong Chapter 4B trở đi.


36. Tư duy của Chapter 4A

Chapter 3D giải quyết:

Làm sao chạy CI/CD cho nhiều microservice mà không copy-paste workflow?

Chapter 4A giải quyết:

Build image xong thì quản lý và deploy artifact đó như thế nào?

Và architecture bắt đầu trở thành:

             BUILD
               ↓
         Docker Image
               ↓
              ECR
               ↓
            DEPLOY
               ↓
              EC2

Đây là nền tảng để các phần Production tiếp theo xây dựng:

4A
ECR + Deploy
      ↓
4B
Lifecycle
      ↓
4C
Security Scan
      ↓
4D
Immutable Artifact
      ↓
4E
Security Gate
      ↓
4F
Artifact Promotion

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.