</>Học Dev
Bài học

Tuần 1 - Ngày 5: Amazon ECR và Deploy lên ECS Fargate

Tuần 1 – Ngày 5

Tuần 1 - Ngày 5: Amazon ECR và Deploy lên ECS Fargate

Mục tiêu học tập

  • Tạo ECR repository và push Docker image từ local
  • Hiểu authentication flow ECR (aws ecr get-login-password)
  • Viết ECS Task Definition và tạo ECS Service trên Fargate
  • Hiểu sự khác biệt giữa ECS launch type EC2 vs Fargate
  • Nắm sơ lược ECS vs EKS khi nào dùng cái nào

1. Amazon ECR — Container Registry

Tại sao cần private registry?

Docker Hub public registry tiện cho open-source image nhưng không phù hợp cho production app vì:

  • Rate limiting trên free tier (100 pull/6h với IP chia sẻ)
  • Image public → ai cũng pull được
  • Không tích hợp sẵn với IAM policy

Amazon ECR giải quyết tất cả và tích hợp sâu với ECS/EKS/Fargate.

Tạo ECR repository

# Tạo repository
aws ecr create-repository \
  --repository-name myapp \
  --region ap-southeast-1 \
  --image-scanning-configuration scanOnPush=true \
  --encryption-configuration encryptionType=AES256

# Output:
# {
#   "repository": {
#     "repositoryArn": "arn:aws:ecr:ap-southeast-1:123456789012:repository/myapp",
#     "registryId": "123456789012",
#     "repositoryName": "myapp",
#     "repositoryUri": "123456789012.dkr.ecr.ap-southeast-1.amazonaws.com/myapp",
#     ...
#   }
# }

Authentication — quan trọng nhất

# ECR dùng token tạm thời (12 giờ), không phải username/password cố định
# Lấy token và pipe vào docker login

AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
AWS_REGION=ap-southeast-1

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

# Output: Login Succeeded

Điểm quan trọng: Username LUÔN là AWS (chữ hoa). Password là token tạm thời từ get-login-password. Không bao giờ hard-code credentials trong Dockerfile hay docker-compose.

Tag và Push image

# Build image (hoặc dùng image đã build)
docker build -t myapp:1.0 .

# Tag với ECR URI
REPO_URI="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/myapp"

docker tag myapp:1.0 ${REPO_URI}:1.0
docker tag myapp:1.0 ${REPO_URI}:latest

# Push lên ECR
docker push ${REPO_URI}:1.0
docker push ${REPO_URI}:latest

# Kiểm tra image đã push
aws ecr list-images --repository-name myapp --region ${AWS_REGION}

ECR Lifecycle Policy — tự động dọn image cũ

{
  "rules": [
    {
      "rulePriority": 1,
      "description": "Giữ tối đa 10 image có tag",
      "selection": {
        "tagStatus": "tagged",
        "tagPrefixList": ["v"],
        "countType": "imageCountMoreThan",
        "countNumber": 10
      },
      "action": { "type": "expire" }
    },
    {
      "rulePriority": 2,
      "description": "Xoá untagged image sau 7 ngày",
      "selection": {
        "tagStatus": "untagged",
        "countType": "sinceImagePushed",
        "countUnit": "days",
        "countNumber": 7
      },
      "action": { "type": "expire" }
    }
  ]
}

2. Amazon ECS — Kiến trúc tổng quan

AmazonECSClusterECSService(desiredcount=2)Task1Task2ContainerContainer(myapp:1.0)(myapp:1.0)Launch:FargateLaunch:FargateALBinternettraffic

Các khái niệm ECS

Khái niệmMô tả
ClusterLogical grouping của tasks/services
Task DefinitionBlueprint mô tả container(s): image, CPU, memory, env vars, ports
TaskMột instance đang chạy của Task Definition
ServiceĐảm bảo N tasks luôn chạy, tích hợp với ALB, rolling update
Launch TypeEC2 (bạn manage) hoặc Fargate (AWS manage)

3. ECS Launch Type: EC2 vs Fargate

EC2LaunchTypeFargateLaunchTypeEC2instancesAWSmanagedcompute(bnqunlý)(bnkhôngthyEC2)ECSAgentTask(vCPU/GB)TaskAContainerTaskB
Tiêu chíEC2 Launch TypeFargate
Quản lý serverBạn (patch OS, scale)AWS (fully managed)
CostEC2 instance giờ (có thể dùng Reserved/Spot)Theo vCPU/GB-giờ của task
Burst flexibilityCần scale cluster trướcScale ngay, không cần cluster capacity
GPUCó (EC2 GPU instance)Không (tính đến 2024)
Windows containers
Spot tasksCó (EC2 Spot)Có (Fargate Spot — ~70% rẻ hơn)
Best forLarge steady workload, cần GPUVariable load, microservices

4. Tạo ECS Fargate Service (CLI)

Bước 1: Tạo Task Definition

# task-definition.json
cat > task-definition.json << 'EOF'
{
  "family": "myapp",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
  "containerDefinitions": [
    {
      "name": "myapp",
      "image": "123456789012.dkr.ecr.ap-southeast-1.amazonaws.com/myapp:1.0",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        { "name": "NODE_ENV", "value": "production" }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:ap-southeast-1:123456789012:secret:myapp/db-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/myapp",
          "awslogs-region": "ap-southeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3
      }
    }
  ]
}
EOF

# Register task definition
aws ecs register-task-definition \
  --cli-input-json file://task-definition.json \
  --region ap-southeast-1

Bước 2: Tạo CloudWatch Log Group

aws logs create-log-group \
  --log-group-name /ecs/myapp \
  --region ap-southeast-1

Bước 3: Tạo ECS Cluster

aws ecs create-cluster \
  --cluster-name myapp-cluster \
  --region ap-southeast-1

Bước 4: Tạo ECS Service

aws ecs create-service \
  --cluster myapp-cluster \
  --service-name myapp-service \
  --task-definition myapp:1 \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={
    subnets=[subnet-abc123,subnet-def456],
    securityGroups=[sg-xyz789],
    assignPublicIp=ENABLED
  }" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,
    containerName=myapp,containerPort=3000" \
  --region ap-southeast-1

IAM Roles cần thiết

ecsTaskExecutionRole — cho ECS agent:
  - ecr:GetAuthorizationToken       ← pull image từ ECR
  - ecr:BatchGetImage
  - ecr:GetDownloadUrlForLayer
  - logs:CreateLogStream            ← ghi CloudWatch Logs
  - logs:PutLogEvents
  - secretsmanager:GetSecretValue   ← lấy secrets

ecsTaskRole — cho app code trong container:
  - Quyền app cần (S3, DynamoDB, v.v.) — tùy ứng dụng

5. Update service — Rolling deployment

# 1. Push image mới lên ECR
docker build -t myapp:1.1 .
docker tag myapp:1.1 ${REPO_URI}:1.1
docker push ${REPO_URI}:1.1

# 2. Đăng ký task definition mới (thay image tag)
# Sửa task-definition.json: image → myapp:1.1
aws ecs register-task-definition \
  --cli-input-json file://task-definition.json

# 3. Update service — ECS sẽ rolling deploy
aws ecs update-service \
  --cluster myapp-cluster \
  --service myapp-service \
  --task-definition myapp:2 \    # revision mới
  --region ap-southeast-1

# 4. Theo dõi deployment
aws ecs describe-services \
  --cluster myapp-cluster \
  --services myapp-service \
  --query 'services[0].deployments'

6. ECS vs EKS — khi nào chọn gì?

ECSEKSAWS-native,đơnginhơnKuberneteschuncôngnghipÍtkháinimhơnNhiuabstractionhơnTíchhpsâuAWS(ALB,IAM,Kubernetesecosystem(Helm,CloudWatch)Prometheus,ArgoCD,KEDA)KhôngcnbiếtKubernetesCnhcKubernetesMigratekhóhơnsangcloudkhácMulti-cloud/hybridfriendlyFreecontrolplaneEKS:$0.10/gipercluster(~$72/tháng)

Khi chọn ECS: team nhỏ, không có Kubernetes expertise, AWS-only, budget tight.

Khi chọn EKS: team đã có Kubernetes knowledge, cần Helm/service mesh, multi-cloud strategy, cần advanced autoscaling (KEDA, Karpenter).


Câu hỏi ôn tập

  1. Username trong lệnh docker login cho ECR luôn là gì?

    Xem đáp án

    Luôn là AWS (viết hoa). Password là token tạm thời lấy từ aws ecr get-login-password. Lệnh đầy đủ: aws ecr get-login-password --region ... | docker login --username AWS --password-stdin <registry-uri>. Token này có hiệu lực 12 giờ.

  2. Tại sao phải dùng aws ecr get-login-password thay vì username/password cố định?

    Xem đáp án

    ECR không có username/password cố định. Thay vào đó, authentication dựa trên IAM — token được sinh ra từ credentials IAM hiện tại và có hiệu lực 12 giờ. Điều này đảm bảo: (1) không cần quản lý static credentials cho registry, (2) access bị kiểm soát bởi IAM policies, (3) token tự hết hạn giảm rủi ro nếu bị lộ.

  3. Sự khác biệt giữa ecsTaskExecutionRoleecsTaskRole là gì?

    Xem đáp án

    ecsTaskExecutionRole dùng cho ECS agent (không phải app): cần để pull image từ ECR, ghi logs vào CloudWatch, và lấy secrets từ Secrets Manager khi khởi động task. ecsTaskRole dùng cho code trong container: các AWS API calls mà app cần (S3, DynamoDB, SQS...) trong lúc chạy. Hai role có trách nhiệm tách biệt rõ ràng theo least privilege.

  4. Fargate Spot tiết kiệm được bao nhiêu % so với Fargate on-demand?

    Xem đáp án

    Fargate Spot tiết kiệm khoảng ~70% so với Fargate on-demand. AWS có thể reclaim capacity khi cần, nên Fargate Spot phù hợp cho workload fault-tolerant như batch jobs, data processing, CI/CD tasks — không phù hợp cho production web server yêu cầu availability cao.

  5. Tại sao ECS Task Definition cần networkMode: awsvpc khi dùng Fargate?

    Xem đáp án

    Fargate chỉ hỗ trợ awsvpc network mode. Mỗi Fargate task được cấp một ENI (Elastic Network Interface) riêng với IP private trong VPC — giống như một EC2 instance. Điều này cho phép gắn Security Group trực tiếp vào task (không phải instance), kiểm soát traffic ở task level, và tích hợp với VPC networking (subnets, route tables, VPC endpoints).

Bài tập thực hành

# Prerequisites: AWS CLI đã configure, có quyền ECR + ECS

# 1. Tạo ECR repo
AWS_REGION=ap-southeast-1
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REPO_NAME=my-docker-demo

aws ecr create-repository \
  --repository-name ${REPO_NAME} \
  --region ${AWS_REGION}

# 2. Auth vào ECR
aws ecr get-login-password --region ${AWS_REGION} | \
  docker login --username AWS --password-stdin \
  ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com

# 3. Build và push image
docker build -t ${REPO_NAME}:latest .
docker tag ${REPO_NAME}:latest \
  ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME}:latest
docker push \
  ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com/${REPO_NAME}:latest

# 4. Kiểm tra
aws ecr describe-images \
  --repository-name ${REPO_NAME} \
  --region ${AWS_REGION}

# 5. (Optional) Xoá repo sau khi thực hành
aws ecr delete-repository \
  --repository-name ${REPO_NAME} \
  --force \
  --region ${AWS_REGION}

Tài liệu tham khảo chính thức


Tiếp theo: Ngày 6 — Security và Image Optimization