Tối ưu chi phí: pricing model, Savings Plans vs RI vs Spot, Cost Explorer, Budgets, tagging

6 câu hỏi~45 phútTừ khóa: On-Demand, Savings Plans, Reserved Instance, Spot, Dedicated Host, data transfer, NAT Gateway cost, cost allocation tag, Cost Explorer, Budgets, Compute Optimizer, Trusted Advisor, SAA: Domain 4 Cost-Optimized

Tiếp đi: tối ưu chi phí. Hóa đơn tài khoản lab tháng này của mình cao hơn dự kiến dù EC2 vẫn nằm trong Free Tier. Mình không biết tiền đi đâu, cũng không biết shop-app lên production thì nên trả tiền kiểu gì cho rẻ.

Đây là bài gần như ai làm AWS cũng "trả học phí" một lần. Tin tốt: chi phí AWS rất đo được, chỉ cần biết nhìn đúng chỗ. Bài này đi theo đúng Domain 4 của SAA-C03 (Design Cost-Optimized Architectures, 20% đề): hiểu các pricing model, biết chi phí ẩn (data transfer, NAT Gateway), rồi dùng bộ công cụ Cost Explorer, Budgets, Compute Optimizer, Trusted Advisor để nhìn và cắt.

1. Bài toán: hóa đơn shop-app lab "tự nhiên" tăng

Qua 18 bài, tài khoản lab của shop-app đã có khá nhiều thứ. Giả sử một tháng bạn làm lab đều đặn và bỏ quên vài tài nguyên:

Tài nguyên còn sống         Free Tier?   Ghi chú
───────────────────────────  ──────────  ─────────────────────────────────
EC2 t3.micro x2 (shop-asg)   có (750h)   nhưng 2 instance = 1.440h/tháng > 750h
NAT Gateway (shop-vpc)       KHÔNG       tính theo giờ + theo GB đi qua
ALB shop-alb                 KHÔNG*      tính theo giờ + LCU
RDS shop-db Multi-AZ         KHÔNG       Free Tier chỉ Single-AZ db.t3.micro
ElastiCache shop-cache       KHÔNG       tính theo giờ node
EBS snapshot cũ              có 1 GB     còn lại tính theo GB-tháng
S3 shop-app-assets           có 5 GB     data transfer ra Internet tính riêng
CloudWatch custom metric     10 metric   mỗi metric thêm tính tiền
(*) ALB có 750h Free Tier 12 tháng đầu nhưng LCU vẫn tính nếu vượt

Bạn mở trang Billing thấy một con số tổng. Không biết phần nào của shop-app, phần nào của lab khác. Không có cảnh báo trước. Đó là bài toán.

2. Vấn đề: không có "nhãn", không có ngưỡng, trả tiền kiểu mặc định

3. Giải pháp: 5 pricing model + bộ công cụ nhìn chi phí

3.1 Năm cách trả tiền cho compute (EC2, cũng áp dụng tương tự Fargate/Lambda với Savings Plans)

ModelCam kếtGiảm giá (khoảng)Dùng khiTừ khóa đề thi
On-DemandKhông0% (giá chuẩn)Workload mới, ngắn, không dự đoán được; dev/test bật tắt"unpredictable", "short-term", "cannot be interrupted" + "no commitment"
Savings Plans (Compute / EC2 Instance / SageMaker)$/giờ trong 1 hoặc 3 nămtới ~66–72%Workload ổn định, muốn linh hoạt đổi instance family, region, hoặc chuyển sang Fargate/Lambda"steady-state", "flexibility to change instance types / move to Fargate"
Reserved Instance (Standard / Convertible)Instance cụ thể 1 hoặc 3 nămtới ~72% (Standard), ~66% (Convertible)Workload ổn định, biết chắc instance type; cần capacity reservation theo AZ; RDS/ElastiCache/Redshift/OpenSearch chỉ có RI"reserved capacity in a specific AZ", "RDS running 24/7 for 3 years"
SpotKhông; AWS có thể thu hồi với báo trước 2 phúttới ~90%Workload chịu được gián đoạn: batch, CI, big data, image processing, stateless worker"fault-tolerant", "can be interrupted", "batch", "lowest cost"
Dedicated Host / Dedicated InstanceThuê cả server vật lý (Host) hoặc chỉ cách ly tenancy (Instance)đắt hơnLicense theo socket/core (BYOL Windows, Oracle), compliance yêu cầu không share hardware"BYOL per-socket license", "regulatory requirement for dedicated hardware"

Đề thi hay bẫy Dedicated Host vs Dedicated Instance: Host cho bạn thấy socket/core vật lý (cần cho license), Instance chỉ đảm bảo không chạy chung với account khác.

3.2 Savings Plans: 3 loại

Compute Savings Plans        EC2 Instance Savings Plans     SageMaker SP
───────────────────────────  ─────────────────────────────  ────────────
linh hoạt nhất:              gắn với 1 instance family      cho ML
  bất kỳ family, size,       trong 1 region (vd m5 ở
  OS, tenancy, region;         ap-southeast-1); đổi size,
  cả Fargate và Lambda         OS, AZ tự do
giảm tới ~66%                giảm tới ~72%

Cam kết tính bằng $/giờ (vd $2/giờ). Phần dùng trong cam kết hưởng giá SP; phần vượt tính On-Demand. Vì vậy chiến thuật chuẩn: cam kết cho phần baseline, phần đỉnh để On-Demand hoặc Spot.

3.3 Reserved Instance: thuộc tính cần nhớ

3.4 Spot: cách dùng an toàn

3.5 Chi phí ngoài compute: S3 và data transfer

KhoảnTính thế nàoCách giảm
S3 storageGB-tháng theo storage classLifecycle sang IA / Glacier; Intelligent-Tiering khi không biết access pattern (xem 22.6)
S3 requestTheo số GET/PUT/LISTCloudFront cache phía trước; gộp object nhỏ
S3 retrievalGlacier tính phí lấy ra + thời gian chờChọn đúng class; Glacier Instant khi cần truy cập ngay thỉnh thoảng
Data transfer IN Internet → AWSMiễn phí—
Data transfer OUT AWS → InternetTheo GB, bậc thangCloudFront (giá OUT rẻ hơn và S3 → CloudFront miễn phí); nén; cache
Transfer giữa AZTính cả 2 chiều theo GBĐặt app và DB cùng AZ khi hợp lý; dùng Gateway endpoint cho S3/DynamoDB
Transfer giữa regionTheo GBChỉ replicate thứ cần; nén
Transfer cùng AZ qua private IPMiễn phíDùng private IP, không đi vòng qua public IP

3.6 NAT Gateway: cái bẫy kinh điển

Private subnet ──► NAT Gateway ──► IGW ──► S3 (public endpoint)
                   │ tính giờ        │
                   │ + tính mỗi GB   └─ transfer OUT Internet (nếu khác region)
                   ▼
Tải 500 GB ảnh từ S3 về EC2 private = 500 GB x giá NAT processing
                                     (ngoài giá NAT theo giờ)

Cách đúng:
Private subnet ──► S3 Gateway Endpoint ──► S3    (miễn phí, không qua NAT)
Private subnet ──► Interface Endpoint (PrivateLink) ──► ECR, Secrets Manager, SQS…
                   (tính theo giờ + GB nhưng thường rẻ hơn NAT với traffic lớn)

Đề thi: "EC2 in private subnet transfers large amounts of data to S3, costs are high, reduce cost" → S3 Gateway Endpoint. Tương tự DynamoDB. Chi tiết endpoint ở 22.3.

3.7 Bộ công cụ nhìn chi phí

Công cụLàm gìTừ khóa đề
Cost ExplorerBiểu đồ chi phí theo thời gian, lọc/group theo service, tag, account; dự báo; RI/SP recommendations"visualize, analyze and forecast costs", "recommend Savings Plans"
AWS BudgetsĐặt ngưỡng cost / usage / RI utilization / SP coverage; alert qua SNS/email khi actual hoặc forecasted vượt; Budget Actions tự áp IAM policy/SCP hoặc stop EC2/RDS"notify when spend exceeds threshold", "take action automatically"
Cost and Usage Report (CUR) / Data ExportsChi tiết nhất, xuất CSV/Parquet về S3, query bằng Athena, vẽ QuickSight"most granular billing data", "query with Athena"
Cost Anomaly DetectionML phát hiện chi phí bất thường, alert"unexpected spike", "anomaly"
Compute OptimizerĐề xuất right-size EC2, ASG, EBS, Lambda, ECS Fargate dựa trên metric CloudWatch thực tế (over/under-provisioned)"right-size", "recommend instance type based on utilization"
Trusted AdvisorKiểm tra 5–6 nhóm: Cost, Performance, Security, Fault Tolerance, Service Limits, Operational Excellence; cost checks đầy đủ cần Business/Enterprise Support"idle load balancer", "underutilized EBS", "best practice checks"
Pricing CalculatorƯớc tính trước khi xây"estimate cost before deployment"
Billing Conductor / Cost CategoriesGom chi phí theo nhóm nghiệp vụ, show-back/charge-back"allocate costs to business units"
Consolidated Billing (Organizations)Một bill cho nhiều account, gộp volume để hưởng bậc giá rẻ, chia sẻ RI/SP giữa account"share Reserved Instances across accounts"

4. Cấu hình từng bước: nhìn thấy chi phí shop-app

Bước 1: Tag mọi tài nguyên. Chương này đã quy ước Project=shop-app, Env=lab. Với tài nguyên cũ chưa tag, dùng Resource Groups Tagging API gắn hàng loạt:

# Tìm tài nguyên trong region chưa có tag Project
aws resourcegroupstaggingapi get-resources \
  --region $AWS_REGION --profile shop-lab \
  --query "ResourceTagMappingList[?!contains(keys(Tags[0] || \`{}\`), 'Project')].ResourceARN" \
  --output text

# Gắn tag theo ARN (ví dụ ALB và target group)
aws resourcegroupstaggingapi tag-resources \
  --resource-arn-list arn:aws:elasticloadbalancing:$AWS_REGION:$ACCOUNT_ID:loadbalancer/app/shop-alb/abc123 \
  --tags Project=shop-app,Env=lab \
  --region $AWS_REGION --profile shop-lab

Với ASG, nhớ tag ở launch template có PropagateAtLaunch=true để instance sinh ra đã có tag.

Bước 2: Kích hoạt tag làm cost allocation tag. Tag chỉ xuất hiện trong Cost Explorer sau khi activate trong Billing (và chờ tới 24 giờ):

aws ce update-cost-allocation-tags-status \
  --cost-allocation-tags-status TagKey=Project,Status=Active TagKey=Env,Status=Active \
  --profile shop-lab   # Cost Explorer API luôn ở us-east-1, không cần --region

Bước 3: Hỏi Cost Explorer theo tag.

aws ce get-cost-and-usage \
  --time-period Start=$(date -d "-30 days" +%F),End=$(date +%F) \
  --granularity DAILY --metrics UnblendedCost \
  --filter '{"Tags":{"Key":"Project","Values":["shop-app"]}}' \
  --group-by Type=DIMENSION,Key=SERVICE \
  --profile shop-lab

Bước 4: Budget có cảnh báo. Chi tiết nằm ở lab bên dưới. Ý chính: budget theo tháng, alert ở 80% actual và 100% forecasted, gửi về email và SNS topic shop-order-topic hoặc topic riêng.

Bước 5 (Java): tag từ code. Khi app tự tạo tài nguyên (ví dụ Lambda shop-image-resize tạo object S3, hoặc job tạo snapshot), gắn tag luôn bằng SDK v2:

// Tag object S3 khi upload: dùng cho lifecycle rule theo tag và cost allocation
PutObjectRequest req = PutObjectRequest.builder()
        .bucket(bucket)
        .key("products/" + productId + "/" + fileName)
        .tagging(Tagging.builder().tagSet(
                Tag.builder().key("Project").value("shop-app").build(),
                Tag.builder().key("Env").value(System.getenv("APP_ENV")).build()
        ).build())
        .build();
s3.putObject(req, RequestBody.fromBytes(bytes));

// Tag snapshot EBS do job tạo
ec2.createSnapshot(CreateSnapshotRequest.builder()
        .volumeId(volumeId)
        .tagSpecifications(TagSpecification.builder()
                .resourceType(ResourceType.SNAPSHOT)
                .tags(software.amazon.awssdk.services.ec2.model.Tag.builder()
                        .key("Project").value("shop-app").build())
                .build())
        .build());

Credential vẫn lấy từ default provider chain (role EC2/ECS), không có access key trong code.

5. Cách hoạt động: chi phí chảy từ đâu ra bill

Tài nguyên (EC2, RDS, NAT, S3…)  ─ usage record mỗi giờ ─►  Billing
      │ tag Project=shop-app                                   │
      ▼                                                        ▼
  Cost allocation tag (đã Activate)  ─────────────►  Cost Explorer / CUR
                                                        │ group by tag
                                                        ▼
                                              "shop-app: $X / tháng"
                                                        │
                   ┌────────────────────────────────────┼───────────────────┐
                   ▼                                    ▼                   ▼
            AWS Budgets                        Compute Optimizer     Trusted Advisor
      actual 80% → SNS → email              "t3.micro → t4g.micro"   "idle ALB", "unattached EBS"
      forecast 100% → Budget Action
                     (IAM deny / stop EC2)

Thứ tự quyết định khi tối ưu (đúng tinh thần Well-Architected Cost pillar):

1. Nhìn thấy (tag + Cost Explorer + Budget)
2. Tắt thứ không dùng (idle, orphan: EBS, EIP, snapshot, ALB không target)
3. Right-size (Compute Optimizer; đổi sang Graviton t4g/m7g nếu Java chạy được: hầu hết đều được)
4. Chọn pricing model (SP/RI cho baseline, Spot cho batch)
5. Đổi kiến trúc (serverless, S3 lifecycle, CloudFront, Gateway endpoint)

6. So sánh kiểu đề thi

Tiêu chíSavings Plans (Compute)Reserved Instance (Standard)Spot
Cam kết$/giờ, 1–3 nămInstance type cụ thể, 1–3 nămKhông
Linh hoạt family/regionCó, cả Fargate/LambdaKhông (Convertible: đổi được, giảm ít hơn)Tự chọn mỗi lần
Capacity reservationKhôngCó với Zonal RIKhông, có thể bị thu hồi
Áp cho RDS/ElastiCacheKhôngCó (Reserved DB Instance / Reserved Node)Không
Giảm giátới ~66%tới ~72%tới ~90%
Bán lạiKhôngStandard: Marketplace—
Chọn khi đề nói"flexibility", "may move to Fargate/Lambda""specific instance type", "reserve capacity", "RDS 24/7""fault-tolerant", "batch", "interruptions acceptable"
Cost ExplorerBudgetsCURTrusted AdvisorCompute Optimizer
Nhìn và phân tích quá khứ + forecastĐặt ngưỡng và cảnh báo/hành độngDữ liệu thô chi tiết nhấtChecklist best practice đa lĩnh vựcĐề xuất size compute theo metric

7. Khi nào dùng gì cho shop-app

Savings Plans và Reserved Instance đều giảm khoảng 60–70%, mình thấy gần như nhau. Cho shop-asg và shop-db thì mình nên mua loại nào? Và mua xong lỡ shop-app chuyển lên ECS Fargate thì tiền có mất không?

Bạn hiểu đúng ở điểm mức giảm gần nhau, chỉ cần chỉnh một điểm: khác biệt không nằm ở phần trăm mà ở cái bạn cam kết. RI cam kết một instance cụ thể; Savings Plans cam kết một số tiền mỗi giờ.

1. Quy tắc chọn

Dịch vụ là EC2 / Fargate / Lambda?
   ├─ Có ──► Có thể đổi family hoặc chuyển sang container/serverless trong 1–3 năm tới?
   │           ├─ Có  ──► Compute Savings Plans
   │           └─ Không, chỉ ổn định 1 family trong 1 region ──► EC2 Instance SP
   │                     (giảm nhiều hơn Compute SP, vẫn đổi size/OS/AZ)
   │        Cần GIỮ CAPACITY trong 1 AZ (DR, event lớn)? ──► Zonal RI hoặc
   │                                                        On-Demand Capacity Reservation
   └─ Không (RDS, ElastiCache, Redshift, OpenSearch, DynamoDB reserved capacity)
             ──► chỉ có Reserved (Reserved DB Instance / Reserved Node)

Cho shop-asg: Compute SP, vì bạn đang cân nhắc ECS Fargate (22.13). Cam kết cho phần baseline: 2 × t3.micro chạy 24/7 → nhìn Cost Explorer mục Savings Plans recommendations, nó tính sẵn số $/giờ nên cam kết dựa trên 7/30/60 ngày gần nhất.

Cho shop-db: Reserved DB Instance cho db.t3.micro (hoặc size đã ổn định). RDS RI có size flexibility trong cùng family và engine (ví dụ 1 RI db.m5.large ≈ nửa RI db.m5.xlarge).

2. Nếu chuyển lên Fargate thì sao

3. Hai chỉ số phải theo dõi sau khi mua

Chỉ sốNghĩaTheo dõi ở
Utilization% cam kết đã được dùng. Thấp = mua thừa, đang trả tiền cho thứ không dùngCost Explorer → Savings Plans/RI utilization; Budgets loại RI/SP utilization
Coverage% usage đủ điều kiện được SP/RI phủ. Thấp = còn nhiều On-Demand, có thể mua thêmCost Explorer → coverage report; Budgets loại coverage

Đề thi hay hỏi: "Company has Reserved Instances but still paying On-Demand, how to identify?" → Cost Explorer RI utilization/coverage report, hoặc Trusted Advisor check Reserved Instance Optimization.

4. Ở Organizations

RI và SP được chia sẻ mặc định giữa các account trong cùng Organization (consolidated billing). Account A mua RI m5.large không dùng hết, account B có m5.large chạy sẽ hưởng. Có thể tắt sharing per account ở Billing preferences. Đề: "share RIs across accounts" → Organizations consolidated billing.

Spot rẻ tới 90%, mà shop-app là stateless, có ASG rồi. Sao mình không cho cả shop-asg chạy Spot luôn? Instance bị thu hồi thì ASG tạo cái mới là xong chứ?

Ý tưởng đúng một nửa. Stateless + ASG là điều kiện cần để dùng Spot, nhưng "cả ASG chạy Spot" thì có hai rủi ro thực tế.

1. Rủi ro Spot

2. Cách dùng Spot đúng cho shop-asg

shop-asg (min 2, max 4) với MixedInstancesPolicy
  ├─ OnDemandBaseCapacity = 2        ← luôn có 2 On-Demand (phủ bằng Savings Plans)
  ├─ OnDemandPercentageAboveBaseCapacity = 0   ← phần scale-out 100% Spot
  ├─ SpotAllocationStrategy = price-capacity-optimized
  └─ Overrides: t3.micro, t3a.micro, t4g.micro*, t3.small
                (*t4g là ARM Graviton, cần build jar chạy JDK ARM: Java chạy tốt)
aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name shop-asg \
  --mixed-instances-policy '{
    "LaunchTemplate": {
      "LaunchTemplateSpecification": {"LaunchTemplateName":"shop-lt","Version":"$Latest"},
      "Overrides": [{"InstanceType":"t3.micro"},{"InstanceType":"t3a.micro"},{"InstanceType":"t3.small"}]
    },
    "InstancesDistribution": {
      "OnDemandBaseCapacity": 2,
      "OnDemandPercentageAboveBaseCapacity": 0,
      "SpotAllocationStrategy": "price-capacity-optimized"
    }
  }' \
  --capacity-rebalance \
  --region $AWS_REGION --profile shop-lab

--capacity-rebalance: ASG nhận tín hiệu "Spot có nguy cơ bị thu hồi" (EC2 Rebalance Recommendation) và chủ động thay instance trước khi có interruption notice.

3. Java nhận interruption notice

// Poll IMDSv2 mỗi 5 giây trong một @Scheduled, hoặc để EventBridge rule
// "EC2 Spot Instance Interruption Warning" → SNS → app. Cách đơn giản:
String token = http.put("http://169.254.169.254/latest/api/token",
        Map.of("X-aws-ec2-metadata-token-ttl-seconds", "60"));
HttpResponse<String> r = http.get("http://169.254.169.254/latest/meta-data/spot/instance-action",
        Map.of("X-aws-ec2-metadata-token", token));
if (r.statusCode() == 200) {          // 404 nghĩa là chưa có thông báo
    // {"action":"terminate","time":"2026-..."}: bắt đầu drain
    healthIndicator.markOutOfService();   // /actuator/health trả DOWN → ALB ngừng gửi request
}

Kết hợp server.shutdown=graceful và spring.lifecycle.timeout-per-shutdown-phase=30s.

4. Khi nào nên 100% Spot

Worker đọc shop-order-events từ SQS, job resize ảnh batch, CI runner, EMR task node: hỏng giữa chừng thì message quay lại queue (visibility timeout, xem 22.11). Đó là workload "fault-tolerant" mà đề thi gán cho Spot. Web tier nhận request người dùng thì trộn như trên.

Trong bill lab của mình, dòng NAT Gateway còn cao hơn cả EC2. NAT chỉ để app trong private subnet ra Internet pull image và gọi S3, sao lại tốn thế? Mình bỏ NAT được không?

Đúng là chuyện rất hay gặp, và không chỉ ở lab. NAT Gateway có hai khoản: phí theo giờ (chạy 24/7 kể cả không ai dùng, nhân 2 nếu bạn đặt một NAT mỗi AZ) và phí xử lý mỗi GB đi qua. Khoản thứ hai mới là thứ làm bill nhảy: pull image ECR vài trăm MB mỗi lần deploy, tải ảnh sản phẩm từ S3, gọi API ngoài… tất cả đều tính GB.

1. Traffic nào đang đi qua NAT của shop-app

EC2 private ──► NAT ──► S3 (ảnh sản phẩm, log)           ← thủ phạm số 1
EC2 private ──► NAT ──► ECR (pull image shop-app)         ← mỗi deploy
EC2 private ──► NAT ──► Secrets Manager, CloudWatch Logs   ← nhỏ nhưng liên tục
EC2 private ──► NAT ──► payment provider ngoài AWS          ← bắt buộc qua NAT

Kiểm tra bằng VPC Flow Logs (22.3) hoặc CloudWatch metric BytesOutToDestination của NAT Gateway.

2. Cắt từng dòng

TrafficThay bằngChi phí mới
S3, DynamoDBGateway VPC Endpoint (route table entry prefix list)Miễn phí
ECR (api + dkr), Secrets Manager, CloudWatch Logs, SQS, SSM, KMSInterface Endpoint (PrivateLink)Theo giờ mỗi endpoint mỗi AZ + theo GB; rẻ hơn NAT khi GB lớn, đắt hơn nếu traffic rất nhỏ mà nhiều endpoint
Payment provider ngoàiGiữ NAT; cân nhắc 1 NAT cho lab/dev thay vì mỗi AZ một cái (production vẫn nên mỗi AZ một NAT để chịu lỗi AZ)Giảm phí giờ
Lab không cần Internet đi raXóa NAT, dùng SSM Session Manager qua interface endpoint để vào máy0
# Gateway endpoint cho S3: miễn phí, chỉ cần gắn vào route table của private subnet
aws ec2 create-vpc-endpoint \
  --vpc-id $VPC_ID --vpc-endpoint-type Gateway \
  --service-name com.amazonaws.$AWS_REGION.s3 \
  --route-table-ids $PRIVATE_RT_ID \
  --tag-specifications 'ResourceType=vpc-endpoint,Tags=[{Key=Project,Value=shop-app},{Key=Env,Value=lab}]' \
  --region $AWS_REGION --profile shop-lab

3. NAT Instance thay NAT Gateway?

Đề thi cũ hay có lựa chọn NAT Instance (EC2 tự cài). Rẻ hơn ở traffic nhỏ, nhưng bạn tự vận hành, giới hạn băng thông theo instance size, không tự HA. Đề mới hầu như luôn chọn NAT Gateway trừ khi câu hỏi nhấn "lowest cost for very small dev environment" và chấp nhận tự quản lý. Ở lab, bạn có thể tắt NAT, thêm S3 Gateway Endpoint và vào máy bằng Session Manager: vừa rẻ vừa an toàn hơn.

Nguyên tắc chung: traffic tới dịch vụ AWS nên đi qua endpoint, NAT chỉ dành cho Internet thật.

Cho mình bài lab để tự tay làm phần này nhé: gắn tag, nhìn chi phí theo tag, đặt cảnh báo, rồi xem AWS gợi ý cắt gì.

Lab 22.19: Nhìn thấy và cảnh báo chi phí shop-app

Mục tiêu

Chi phí & thời gian

Chuẩn bị

export AWS_REGION=ap-southeast-1
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text --profile shop-lab)
export MY_EMAIL=you@example.com

Các bước

Bước 1. Quét tài nguyên thiếu tag và gắn hàng loạt.

# Liệt kê tất cả ARN có tag Project=shop-app
aws resourcegroupstaggingapi get-resources \
  --tag-filters Key=Project,Values=shop-app \
  --query 'ResourceTagMappingList[].ResourceARN' --output table \
  --region $AWS_REGION --profile shop-lab

# Liệt kê ARN KHÔNG có key Project (dò tài nguyên mồ côi)
aws resourcegroupstaggingapi get-resources \
  --query "ResourceTagMappingList[?length(Tags[?Key=='Project'])==\`0\`].ResourceARN" \
  --output text --region $AWS_REGION --profile shop-lab | tr '\t' '\n' > untagged.txt
cat untagged.txt

# Gắn tag cho các ARN đó (tối đa 20 ARN mỗi lần gọi)
xargs -n 20 -a untagged.txt -I{} echo {} | head -20 | xargs \
  aws resourcegroupstaggingapi tag-resources \
    --tags Project=shop-app,Env=lab \
    --region $AWS_REGION --profile shop-lab --resource-arn-list

Console: Resource Groups & Tag Editor → Tag Editor → Regions: ap-southeast-1, Resource types: All → Search → chọn tất cả → Manage tags of selected resources.

Chú ý: một số tài nguyên (IAM role, Route 53 hosted zone) là global, phải quét thêm --region us-east-1.

Bước 2. Kích hoạt cost allocation tag.

aws ce update-cost-allocation-tags-status \
  --cost-allocation-tags-status TagKey=Project,Status=Active TagKey=Env,Status=Active \
  --profile shop-lab

aws ce list-cost-allocation-tags --status Active --profile shop-lab

Console: Billing and Cost Management → Cost allocation tags → User-defined cost allocation tags → chọn Project, Env → Activate. Tag chỉ hiện trong Cost Explorer sau khoảng 24 giờ; làm tiếp các bước sau rồi quay lại.

Bước 3. Cost Explorer: chi phí 30 ngày theo service, rồi theo tag.

START=$(date -u -d "-30 days" +%F 2>/dev/null || date -u -v-30d +%F)
END=$(date -u +%F)

# Theo service
aws ce get-cost-and-usage --time-period Start=$START,End=$END \
  --granularity MONTHLY --metrics UnblendedCost \
  --group-by Type=DIMENSION,Key=SERVICE \
  --query 'ResultsByTime[0].Groups[?Metrics.UnblendedCost.Amount>`0.01`].[Keys[0],Metrics.UnblendedCost.Amount]' \
  --output table --profile shop-lab

# Theo tag Project (có dữ liệu sau khi tag Active ~24h)
aws ce get-cost-and-usage --time-period Start=$START,End=$END \
  --granularity MONTHLY --metrics UnblendedCost \
  --group-by Type=TAG,Key=Project \
  --output table --profile shop-lab

# Dự báo tháng này
aws ce get-cost-forecast \
  --time-period Start=$END,End=$(date -u -d "+30 days" +%F 2>/dev/null || date -u -v+30d +%F) \
  --metric UNBLENDED_COST --granularity MONTHLY --profile shop-lab

Console: Billing → Cost Explorer → Date range: Last 30 days → Group by: Service; rồi Group by: Tag → Project. Bật filter Charge type để loại Credit/Refund nếu bị lệch.

Bước 4. SNS topic cho cảnh báo chi phí.

TOPIC_ARN=$(aws sns create-topic --name shop-cost-alerts \
  --tags Key=Project,Value=shop-app Key=Env,Value=lab \
  --query TopicArn --output text --region $AWS_REGION --profile shop-lab)

aws sns subscribe --topic-arn $TOPIC_ARN --protocol email \
  --notification-endpoint $MY_EMAIL --region $AWS_REGION --profile shop-lab
# Mở email → Confirm subscription

# Cho phép dịch vụ Budgets publish vào topic
aws sns set-topic-attributes --topic-arn $TOPIC_ARN \
  --attribute-name Policy --attribute-value "{
  \"Version\":\"2012-10-17\",
  \"Statement\":[{
    \"Effect\":\"Allow\",
    \"Principal\":{\"Service\":\"budgets.amazonaws.com\"},
    \"Action\":\"SNS:Publish\",
    \"Resource\":\"$TOPIC_ARN\",
    \"Condition\":{\"StringEquals\":{\"aws:SourceAccount\":\"$ACCOUNT_ID\"}}
  }]}" --region $AWS_REGION --profile shop-lab

Bước 5. Tạo Budget có 2 alert.

cat > budget.json <<EOF
{
  "BudgetName": "shop-app-monthly",
  "BudgetLimit": {"Amount": "10", "Unit": "USD"},
  "TimeUnit": "MONTHLY",
  "BudgetType": "COST",
  "CostFilters": {"TagKeyValue": ["user:Project\$shop-app"]},
  "CostTypes": {"IncludeCredit": false, "IncludeRefund": false,
                "IncludeTax": true, "IncludeSubscription": true,
                "UseBlended": false, "IncludeSupport": true,
                "IncludeOtherSubscription": true, "IncludeUpfront": true,
                "IncludeDiscount": true, "IncludeRecurring": true, "UseAmortized": false}
}
EOF

cat > notifications.json <<EOF
[
  {
    "Notification": {"NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN",
                     "Threshold": 80, "ThresholdType": "PERCENTAGE"},
    "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "$MY_EMAIL"},
                    {"SubscriptionType": "SNS", "Address": "$TOPIC_ARN"}]
  },
  {
    "Notification": {"NotificationType": "FORECASTED", "ComparisonOperator": "GREATER_THAN",
                     "Threshold": 100, "ThresholdType": "PERCENTAGE"},
    "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "$MY_EMAIL"}]
  }
]
EOF

aws budgets create-budget --account-id $ACCOUNT_ID \
  --budget file://budget.json \
  --notifications-with-subscribers file://notifications.json \
  --profile shop-lab

aws budgets describe-budgets --account-id $ACCOUNT_ID --profile shop-lab

Console: Billing → Budgets → Create budget → Customize → Cost budget → Monthly, Fixed $10 → Filter: Tag Project = shop-app → Alert thresholds: 80% Actual, 100% Forecasted → Email + SNS topic.

Nếu tag chưa Active, filter theo tag chưa dùng được: tạm bỏ CostFilters để budget theo toàn account, sau 24 giờ cập nhật lại bằng update-budget.

Bước 6. Bật Compute Optimizer.

aws compute-optimizer update-enrollment-status --status Active \
  --region $AWS_REGION --profile shop-lab

aws compute-optimizer get-enrollment-status --region $AWS_REGION --profile shop-lab
# Sau ~12–24 giờ có dữ liệu (cần instance chạy ít nhất 30 giờ liên tục):
aws compute-optimizer get-ec2-instance-recommendations \
  --query 'instanceRecommendations[].[instanceArn,currentInstanceType,finding,recommendationOptions[0].instanceType]' \
  --output table --region $AWS_REGION --profile shop-lab

aws compute-optimizer get-auto-scaling-group-recommendations \
  --auto-scaling-group-arns arn:aws:autoscaling:$AWS_REGION:$ACCOUNT_ID:autoScalingGroup:*:autoScalingGroupName/shop-asg \
  --region $AWS_REGION --profile shop-lab 2>/dev/null || echo "chưa có dữ liệu"

Console: AWS Compute Optimizer → Get started → Opt in → Dashboard → EC2 instances / Auto Scaling groups / EBS volumes / Lambda functions.

Bước 7. Trusted Advisor cost checks.

# Trusted Advisor API ở us-east-1. Bản Basic/Developer chỉ có core checks (security + service limits);
# cost checks đầy đủ cần Business Support, lệnh vẫn chạy để liệt kê check có sẵn.
aws trustedadvisor list-checks --language en --pillar cost_optimizing \
  --query 'checkSummaries[].[name,id]' --output table \
  --region us-east-1 --profile shop-lab

aws trustedadvisor list-recommendations --pillar cost_optimizing \
  --region us-east-1 --profile shop-lab 2>&1 | head -30

Console: Trusted Advisor → Recommendations → Cost optimization. Ở bản Basic sẽ thấy khóa với dòng "Upgrade your support plan"; ghi nhận đây là điều đề thi hỏi ("full Trusted Advisor checks require Business or Enterprise Support").

Bước 8. Tìm tài nguyên mồ côi bằng tay (thay cho Trusted Advisor cost checks).

# EBS volume không gắn vào instance nào
aws ec2 describe-volumes --filters Name=status,Values=available \
  --query 'Volumes[].[VolumeId,Size,CreateTime]' --output table \
  --region $AWS_REGION --profile shop-lab

# Elastic IP không gắn
aws ec2 describe-addresses --query 'Addresses[?AssociationId==null].[PublicIp,AllocationId]' \
  --output table --region $AWS_REGION --profile shop-lab

# Snapshot của account, cũ hơn 30 ngày
aws ec2 describe-snapshots --owner-ids $ACCOUNT_ID \
  --query "Snapshots[?StartTime<'$(date -u -d '-30 days' +%F 2>/dev/null || date -u -v-30d +%F)'].[SnapshotId,VolumeSize,StartTime]" \
  --output table --region $AWS_REGION --profile shop-lab

# NAT Gateway đang chạy (từng cái tính tiền theo giờ)
aws ec2 describe-nat-gateways --filter Name=state,Values=available \
  --query 'NatGateways[].[NatGatewayId,SubnetId]' --output table \
  --region $AWS_REGION --profile shop-lab

# ALB không có target healthy
for TG in $(aws elbv2 describe-target-groups --query 'TargetGroups[].TargetGroupArn' --output text --region $AWS_REGION --profile shop-lab); do
  echo "$TG"; aws elbv2 describe-target-health --target-group-arn $TG \
    --query 'TargetHealthDescriptions[].TargetHealth.State' --output text --region $AWS_REGION --profile shop-lab
done

Kiểm tra kết quả

# 1. Không còn tài nguyên thiếu tag Project (mong đợi: rỗng)
aws resourcegroupstaggingapi get-resources \
  --query "ResourceTagMappingList[?length(Tags[?Key=='Project'])==\`0\`].ResourceARN" \
  --output text --region $AWS_REGION --profile shop-lab

# 2. Tag Active
aws ce list-cost-allocation-tags --status Active --profile shop-lab
# "TagKey": "Project", "Status": "Active"

# 3. Budget có 2 notification
aws budgets describe-notifications-for-budget --account-id $ACCOUNT_ID \
  --budget-name shop-app-monthly --profile shop-lab
# NotificationType ACTUAL 80 và FORECASTED 100

# 4. Compute Optimizer Active
aws compute-optimizer get-enrollment-status --region $AWS_REGION --profile shop-lab
# "status": "Active"

Sau 24 giờ: Cost Explorer group by Tag Project phải có dòng Project$shop-app và một dòng không tag (tài nguyên tạo trước khi activate không được tính lại hồi tố).

Thử thêm

Dọn tài nguyên

# Budget (tự nó không tốn nhiều nhưng từ budget thứ 3 tính phí)
aws budgets delete-budget --account-id $ACCOUNT_ID --budget-name shop-app-monthly --profile shop-lab

# SNS topic + subscription
aws sns delete-topic --topic-arn $TOPIC_ARN --region $AWS_REGION --profile shop-lab

# Compute Optimizer: có thể để Active (miễn phí); nếu muốn tắt:
aws compute-optimizer update-enrollment-status --status Inactive --region $AWS_REGION --profile shop-lab

# Tài nguyên mồ côi tìm được ở bước 8: xóa sau khi chắc chắn không dùng
aws ec2 delete-volume --volume-id vol-xxxx --region $AWS_REGION --profile shop-lab
aws ec2 release-address --allocation-id eipalloc-xxxx --region $AWS_REGION --profile shop-lab
aws ec2 delete-snapshot --snapshot-id snap-xxxx --region $AWS_REGION --profile shop-lab

# Cost allocation tag: giữ Active (không tốn tiền, cần cho bài 22.21)

Không nằm trong Free Tier nếu quên: NAT Gateway (theo giờ + GB), EBS volume mồ côi (GB-tháng), Elastic IP không gắn (theo giờ), snapshot (GB-tháng), ALB (giờ + LCU). Nhắc lại lệnh dọn đầy đủ của cả stack ở 22.21.

Phỏng vấn và đề thi SAA hỏi về tối ưu chi phí thì sao? Mình thấy đề nào cũng có chữ "MOST cost-effective".

Q1. Giải thích các pricing model của EC2 và cách bạn chọn cho một web app chạy 24/7 có giờ cao điểm.

🇻🇳 Tiếng Việt:

EC2 có On-Demand trả theo giờ không cam kết, Savings Plans cam kết số tiền mỗi giờ 1–3 năm, Reserved Instance cam kết instance cụ thể, Spot dùng capacity dư với giá rẻ nhưng có thể bị thu hồi báo trước 2 phút, và Dedicated Host cho license hoặc compliance. Với web app 24/7 có đỉnh, em tách baseline và đỉnh: baseline phủ bằng Compute Savings Plans để còn linh hoạt chuyển sang Fargate, phần đỉnh trộn On-Demand và Spot trong ASG với nhiều instance type và capacity rebalance. Database thì dùng Reserved DB Instance vì RDS không có Savings Plans.

🇬🇧 English:

EC2 offers On-Demand with no commitment, Savings Plans that commit to an hourly spend for one or three years, Reserved Instances that commit to a specific instance type, Spot that uses spare capacity at a steep discount but can be reclaimed with a two-minute warning, and Dedicated Hosts for licensing or compliance. For a 24/7 web app with peaks I split baseline from peak: the baseline is covered by Compute Savings Plans so we keep the flexibility to move to Fargate, and the peak runs as a mix of On-Demand and Spot in an ASG with several instance types and capacity rebalancing. The database uses Reserved DB Instances because RDS has no Savings Plans.

Q2. Hóa đơn tăng bất thường, bạn điều tra thế nào?

🇻🇳 Tiếng Việt:

Em mở Cost Explorer lọc theo ngày, group theo service để thấy dịch vụ nào nhảy, rồi group theo tag Project và Env để biết team hoặc môi trường nào. Nếu cần chi tiết hơn em dùng Cost and Usage Report query bằng Athena. Thủ phạm hay gặp là NAT Gateway data processing, data transfer ra Internet, EBS hoặc snapshot mồ côi, và instance quên tắt. Sau đó em đặt Budget với alert actual và forecasted, bật Cost Anomaly Detection để lần sau được báo sớm.

🇬🇧 English:

I open Cost Explorer with daily granularity, group by service to see which service spiked, then group by the Project and Env tags to find the team or environment. For more detail I query the Cost and Usage Report with Athena. The usual culprits are NAT Gateway data processing, Internet egress, orphaned EBS volumes or snapshots, and instances nobody turned off. Then I set a Budget with actual and forecasted alerts and enable Cost Anomaly Detection so next time we are warned early.

Q3. Compute Optimizer và Trusted Advisor khác nhau chỗ nào?

🇻🇳 Tiếng Việt:

Compute Optimizer chuyên về right-sizing: dựa trên metric CloudWatch thực tế để đề xuất instance type, ASG, EBS, Lambda memory phù hợp. Trusted Advisor là bộ check best practice rộng hơn: cost, performance, security, fault tolerance, service limits; phần cost nó chỉ ra idle load balancer, EBS ít dùng, RI hết hạn, nhưng đầy đủ check cần Business hoặc Enterprise Support.

🇬🇧 English:

Compute Optimizer is focused on right-sizing: it uses real CloudWatch metrics to recommend instance types, ASG configurations, EBS volumes and Lambda memory. Trusted Advisor is a broader set of best-practice checks across cost, performance, security, fault tolerance and service limits; its cost checks flag idle load balancers, underutilized EBS and expiring RIs, but the full set requires Business or Enterprise Support.

Q4. Vì sao NAT Gateway hay làm tăng chi phí và bạn giảm thế nào?

🇻🇳 Tiếng Việt:

NAT Gateway tính theo giờ và theo mỗi GB đi qua. App trong private subnet gọi S3, ECR, CloudWatch qua NAT thì mọi GB đó đều tính tiền. Em đổi traffic tới S3 và DynamoDB sang Gateway Endpoint miễn phí, các dịch vụ khác sang Interface Endpoint khi lượng dữ liệu đủ lớn, và chỉ để NAT cho traffic Internet thật.

🇬🇧 English:

NAT Gateway charges per hour and per GB processed. When private-subnet apps reach S3, ECR or CloudWatch through NAT, every GB is billed. I route S3 and DynamoDB through free Gateway Endpoints, move other services to Interface Endpoints when the volume justifies it, and keep NAT only for real Internet traffic.

Góc thi SAA-C03

Câu 1: Công ty chạy một ứng dụng web ổn định trên 10 EC2 m5.large 24/7 trong 3 năm tới. Họ dự định trong năm sau sẽ chuyển một phần sang ECS Fargate. Giải pháp nào MOST cost-effective mà vẫn giữ được tính linh hoạt?
A. Standard Reserved Instances 3 năm All Upfront
B. Compute Savings Plans 3 năm
C. EC2 Instance Savings Plans 3 năm
D. Spot Instances với capacity rebalance

Đáp án: B. Compute Savings Plans áp cho EC2 bất kỳ family và cho cả Fargate/Lambda, nên khi chuyển sang Fargate cam kết vẫn được dùng. A giảm nhiều nhất nhưng gắn chặt m5.large, chuyển Fargate thì RI nằm không. C chỉ áp cho EC2 family m5 trong region, không bao Fargate. D không phù hợp web app ổn định cần luôn sẵn sàng.

Câu 2: Ứng dụng xử lý ảnh chạy mỗi đêm trên fleet EC2, mất khoảng 4 giờ, có thể chạy lại từ checkpoint nếu bị gián đoạn. Phương án nào có chi phí THẤP NHẤT?
A. On-Demand Instances trong ASG
B. Reserved Instances 1 năm
C. Spot Instances với nhiều instance type và checkpoint ra S3
D. Dedicated Hosts

Đáp án: C. Workload chịu được gián đoạn, có checkpoint, chạy batch: đúng hồ sơ Spot, giảm tới ~90%. A trả giá đầy đủ. B cam kết 24/7 cho job chỉ chạy 4 giờ/ngày là lãng phí. D đắt nhất, chỉ dành cho license/compliance.

Câu 3: EC2 trong private subnet tải nhiều TB dữ liệu từ S3 mỗi ngày qua NAT Gateway. Chi phí NAT tăng mạnh. Solutions architect cần giảm chi phí với LEAST operational overhead. Chọn gì?
A. Thay NAT Gateway bằng NAT Instance kích cỡ lớn
B. Tạo S3 Gateway VPC Endpoint và cập nhật route table của private subnet
C. Chuyển EC2 sang public subnet với Elastic IP
D. Bật S3 Transfer Acceleration

Đáp án: B. Gateway Endpoint cho S3 miễn phí, traffic không qua NAT, chỉ cần thêm route. A vẫn tốn và tự vận hành. C phá vỡ thiết kế bảo mật và vẫn tính data transfer. D tăng tốc upload từ xa qua edge, không giảm chi phí mà còn tăng.

Cách nhớ ngắn gọn

Từ khóa trong đềNghĩ ngay tới
"MOST cost-effective" + steady-state + flexibilityCompute Savings Plans
"reserve capacity" / "specific AZ"Zonal RI hoặc On-Demand Capacity Reservation
"fault-tolerant", "batch", "can be interrupted"Spot
"BYOL per-socket", "dedicated hardware"Dedicated Host
"visualize costs", "forecast", "recommend RI/SP"Cost Explorer
"alert when exceeds", "forecasted spend"AWS Budgets
"most granular", "Athena"Cost and Usage Report
"right-size based on utilization"Compute Optimizer
"idle resources", "best practices", needs Business SupportTrusted Advisor
"per-team cost", "chargeback"Cost allocation tags + Cost Categories
"share RI across accounts"Organizations consolidated billing
"NAT cost to S3/DynamoDB"Gateway VPC Endpoint
"reduce data transfer out"CloudFront
"unknown access pattern" S3Intelligent-Tiering

Tự kiểm tra

Compute Savings Plans khác EC2 Instance Savings Plans chỗ nào?

Compute SP áp cho mọi EC2 family/region, Fargate và Lambda (giảm tới ~66%). EC2 Instance SP gắn với một family trong một region, đổi được size/OS/AZ, giảm nhiều hơn (~72%) nhưng không bao Fargate/Lambda.

Vì sao tag phải "Activate" trong Billing, và dữ liệu cũ có được tính lại không?

Tag tài nguyên chỉ thành cost allocation tag sau khi activate; mất tới 24 giờ để xuất hiện trong Cost Explorer. Không hồi tố: chi phí trước khi activate nằm ở nhóm "No tag".

Dịch vụ nào không mua được Savings Plans mà phải dùng Reserved?

RDS (Reserved DB Instance), ElastiCache, Redshift, OpenSearch (Reserved Node), DynamoDB reserved capacity. Savings Plans chỉ cho EC2, Fargate, Lambda, SageMaker.

Hai chỉ số theo dõi sau khi mua RI/SP là gì?

Utilization (% cam kết được dùng; thấp = mua thừa) và Coverage (% usage được RI/SP phủ; thấp = còn nhiều On-Demand). Xem trong Cost Explorer, có thể đặt Budget theo hai chỉ số này.

Khoản data transfer nào miễn phí, khoản nào tính tiền?

Miễn phí: vào AWS từ Internet, cùng AZ qua private IP, S3 → CloudFront. Tính tiền: ra Internet, giữa AZ (cả hai chiều), giữa region, và mọi GB đi qua NAT Gateway.

🧪 Thực hành bài này: Challenge: Kiến trúc tham chiếu + DR + cost (chưa triển khai) · lab labs/aws