EC2: instance type, AMI, user data, EBS, instance store, placement group, pricing model

6 câu hỏi~55 phútTừ khóa: instance family, Graviton, burstable (T3) credit, AMI, user data, cloud-init, IMDSv2, instance profile, EBS gp3/io2, snapshot, instance store, placement group, On-Demand/Spot/Reserved/Savings Plans, Session Manager · SAA: Domain 4 Cost / Domain 2 Resilient

Tiếp đi: EC2. Mình muốn chạy shop-app.jar trên một máy EC2 trong shop-vpc. Mở trang chọn instance thấy t3.micro, m6i.large, c7g.xlarge, r6g… không biết chọn cái nào cho một app Spring Boot. Và lần nào tạo instance mình cũng SSH vào cài Java bằng tay, chắc có cách tự động?

Hai câu hỏi đúng là hai nửa của EC2: chọn máy và làm máy tự dựng. Ở bài 18.5 ta đã so EC2 với ECS/EKS/Lambda để chọn nơi chạy; bài này giả định đã chọn EC2 (hoặc cần hiểu EC2 vì ECS trên EC2, EKS node, ASG đều đứng trên nó) và đi vào bên trong.

1. Bài toán: một máy chạy shop-app.jar, không ai phải SSH

Mục tiêu bài này:
  shop-vpc / shop-public-a
  ┌──────────────────────────────────────────┐
  │ EC2 t3.micro, Amazon Linux 2023          │
  │  user data: cài Java 21, tải jar từ S3,  │
  │             tạo systemd service          │
  │  role: shop-app-ec2-role (đọc S3, SSM)   │
  │  :8080  /actuator/health → {"status":"UP"}│
  │  EBS gp3 8 GB (root) + snapshot          │
  └──────────────────────────────────────────┘
Bài 22.5 nhân máy này lên bằng Launch Template + ASG sau ALB.

2. Vấn đề với cách "tạo rồi SSH vào cài"

3. Giải pháp: các khối của EC2 và thuật ngữ đề thi

Instance type: đọc tên là biết máy.

  m6i.large
  │ ││  └── size: nano, micro, small, medium, large, xlarge, 2xlarge … (mỗi bậc gấp đôi vCPU/RAM)
  │ │└───── thuộc tính: i = Intel, a = AMD, g = Graviton (ARM), d = có NVMe local, n = network cao
  │ └────── generation: số càng lớn càng mới, thường rẻ hơn trên mỗi đơn vị hiệu năng
  └──────── family: mục đích
FamilyTỷ lệ vCPU:RAMDùng choVí dụ với shop-app
T (t3, t3a, t4g) burstable1:4 nhưng CPU theo creditDev/test, traffic thấp, labt3.micro cho lab (Free Tier); t4g.small cho staging
M general purpose1:4Web/app server cân bằngm7g.large cho shop-app production
C compute optimized1:2CPU nặng: encode, batch, gateway nhiều TLSJob resize ảnh nếu không dùng Lambda
R memory optimized1:8Cache, DB trong RAM, JVM heap lớnRedis tự quản, Elasticsearch
X / High memory1:16+SAP HANA, DB khổng lồKhông
I / D storage optimizedNVMe/HDD local lớnNoSQL tự quản, data warehouse, Kafka brokerKafka tự quản (bài 8)
P / G / Inf / Trn acceleratedGPU / chip AIML training/inference, renderKhông
HpcNetwork cực thấp latencyMô phỏng khoa họcKhông

Cho một Spring Boot bình thường: bắt đầu từ M hoặc T, ưu tiên Graviton (g) nếu image/jar không phụ thuộc native x86 (JVM chạy ARM rất tốt, rẻ hơn khoảng 20% cho cùng hiệu năng). JVM cần RAM thật: heap + metaspace + thread stack + off-heap ≈ 1.5–2× -Xmx. shop-app với -Xmx512m chạy được trên 1 GB (t3.micro) cho lab, production nên 2 GB trở lên.

AMI (Amazon Machine Image): ảnh đĩa gốc + metadata (kiến trúc, loại virtualization, block device). AMI là regional: muốn dùng ở region khác phải copy. Ba nguồn: AWS cung cấp (Amazon Linux 2023, Ubuntu, Windows), Marketplace, và AMI tự tạo ("golden AMI": đã cài Java, agent, cấu hình, để máy mới khởi động nhanh). Tìm AMI mới nhất đúng cách là qua SSM public parameter, không hardcode ID (ID đổi theo region và theo tháng):

aws ssm get-parameters \
  --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query 'Parameters[0].Value' --output text --profile shop-lab
# ami-0c1234567890abcde   (Graviton: ...-arm64)

User data: script chạy một lần khi instance khởi động lần đầu (bởi cloud-init), với quyền root. Đây là nơi "cài Java, tải jar, tạo service". Giới hạn 16 KB; log ở /var/log/cloud-init-output.log. Thứ dài hơn thì user data chỉ tải script từ S3 rồi chạy. Muốn chạy mỗi lần boot thì phải dùng cloud-init #cloud-boothook hoặc systemd, user data mặc định không chạy lại.

Instance metadata (IMDS): endpoint http://169.254.169.254/latest/meta-data/ mà chính instance gọi để biết về mình: instance ID, AZ, private IP, và credential tạm của role. SDK lấy credential ở đây. IMDSv2 đòi token qua PUT trước (chống SSRF kiểu Capital One 2019); luôn đặt HttpTokens=required.

TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 300")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/shop-app-ec2-role
# { "AccessKeyId": "ASIA...", "SecretAccessKey": "...", "Token": "...", "Expiration": "..." }

Instance profile: "vỏ" để gắn một IAM role vào EC2. Console tạo ngầm, CLI phải tạo tường minh (create-instance-profile + add-role-to-instance-profile). Đặt tên profile trùng tên role cho đỡ nhầm.

Lưu trữ:

EBSInstance storeEFS (bài 22.16)
Bản chấtĐĩa mạng, tách khỏi instance, trong một AZNVMe/SSD gắn vật lý vào hostFile system NFS chia sẻ, nhiều AZ
Dữ liệu khi stop/terminateGiữ (root volume mặc định xóa khi terminate, tùy DeleteOnTermination)Mất khi stop, terminate, hoặc host hỏngGiữ, độc lập instance
Hiệu nănggp3: 3 000 IOPS/125 MB/s nền, mua thêm tới 16 000 IOPS; io2 Block Express tới 256 000 IOPSHàng triệu IOPS, latency thấp nhấtScale theo kích thước, latency cao hơn EBS
Gắn vàoMột instance (Multi-Attach chỉ io1/io2, cluster FS)Instance đóHàng nghìn instance cùng lúc
BackupSnapshot incremental lên S3; copy sang region khác; làm AMITự loAWS Backup
Dùng choRoot, database, dữ liệu bềnCache, scratch, buffer, Kafka với replicationUpload chia sẻ, CMS, home dir

Loại EBS: gp3 (mặc định, tách IOPS khỏi dung lượng, nên dùng thay gp2), io2/io2 Block Express (database cần IOPS cao, 99.999% durability), st1 (HDD throughput: log, big data), sc1 (HDD lạnh, rẻ nhất). Thi: "16 000 IOPS trở xuống → gp3; cao hơn hoặc cần sub-ms → io2; throughput tuần tự rẻ → st1; boot volume → chỉ SSD (gp/io)".

Placement group: gợi ý cho AWS xếp instance ở đâu.

LoạiXếpDùng choTừ khóa đề
ClusterCùng rack, cùng AZ, network 10–100 Gbps, latency thấp nhấtHPC, ML training, in-memory grid"low latency", "high network throughput", "tightly coupled"
SpreadMỗi instance một rack riêng, tối đa 7 instance/AZVài máy quan trọng không được chết cùng lúc (3 node ZooKeeper, master DB)"minimize correlated failures", "small number of critical instances"
PartitionChia thành partition (tối đa 7/AZ), mỗi partition rack riêng, nhiều instance/partitionKafka, HDFS, Cassandra: biết partition để đặt replica"large distributed workload", "Hadoop/Kafka/Cassandra"

Pricing model (chi tiết chiến lược ở bài 22.19; đây là định nghĩa cần thuộc):

ModelCam kếtGiảmKhi nào
On-DemandKhông, tính theo giây (Linux, tối thiểu 60 giây)0%Lab, workload mới chưa đoán được, ngắn hạn
SpotKhông; AWS thu hồi với báo trước 2 phút khi cần capacityTới 90%Batch, CI, xử lý ảnh, stateless có thể gián đoạn; không cho database
Reserved Instance1 hoặc 3 năm, một instance family/region (Standard) hoặc đổi được (Convertible)Tới 72%Workload ổn định, biết rõ loại máy; RDS/ElastiCache/OpenSearch chỉ có RI
Savings Plans1 hoặc 3 năm, cam kết USD/giờ; Compute SP linh hoạt cả EC2/Fargate/Lambda, EC2 Instance SP theo family+regionTới 72% (EC2 Instance), 66% (Compute)Thay RI cho EC2 trong hầu hết trường hợp hiện đại
Dedicated Host / InstanceThuê cả máy vật lý / instance trên host riêngĐắt hơnGiấy phép theo socket/core (Windows, Oracle BYOL), tuân thủ "không chia host"
Capacity ReservationGiữ chỗ capacity trong AZ, trả On-Demand, không cần cam kết dài0% (có thể kết hợp SP)Chắc chắn có máy lúc sale/DR; đề: "guarantee capacity in a specific AZ"

4. Cấu hình từng bước

Bước 1: Role và instance profile shop-app-ec2-role. Quyền: đọc jar và ảnh từ S3, ghi CloudWatch Logs, và AmazonSSMManagedInstanceCore để vào máy bằng Session Manager thay SSH.

{
  "Version": "2012-10-17",
  "Statement": [
    { "Sid": "ReadArtifactsAndAssets", "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::shop-app-assets-ACCOUNT_ID", "arn:aws:s3:::shop-app-assets-ACCOUNT_ID/*"] },
    { "Sid": "WriteLogs", "Effect": "Allow",
      "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams"],
      "Resource": "arn:aws:logs:ap-southeast-1:ACCOUNT_ID:log-group:/shop/app:*" }
  ]
}

Bước 2: User data cài Java 21 và chạy jar như systemd service.

#!/bin/bash
set -euxo pipefail
dnf install -y java-21-amazon-corretto-headless
useradd -r -s /sbin/nologin shop || true
mkdir -p /opt/shop && cd /opt/shop

# tải jar từ S3 bằng credential của role (không có key nào trong script)
BUCKET=shop-app-assets-$(curl -s -H "X-aws-ec2-metadata-token: $(curl -sX PUT http://169.254.169.254/latest/api/token -H 'X-aws-ec2-metadata-token-ttl-seconds: 60')" \
         http://169.254.169.254/latest/dynamic/instance-identity/document | grep -oP '"accountId"\s*:\s*"\K[0-9]+')
aws s3 cp s3://$BUCKET/releases/shop-app.jar /opt/shop/shop-app.jar
chown -R shop:shop /opt/shop

cat > /etc/systemd/system/shop-app.service <<'UNIT'
[Unit]
Description=shop-app Spring Boot
After=network-online.target
Wants=network-online.target
[Service]
User=shop
WorkingDirectory=/opt/shop
Environment=SPRING_PROFILES_ACTIVE=aws
Environment=JAVA_TOOL_OPTIONS=-Xmx512m -XX:+UseSerialGC -XX:MaxRAMPercentage=60
ExecStart=/usr/bin/java -jar /opt/shop/shop-app.jar
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now shop-app

Điểm hay bị hỏi: Restart=always để app crash tự dậy (ASG chỉ thay máy khi máy hỏng hoặc health check ALB fail, không biết process chết); JAVA_TOOL_OPTIONS để JVM tự đọc; -XX:MaxRAMPercentage thay cho Xmx cố định khi đổi instance size. Trên t3.micro 1 GB, Serial GC hoặc G1 với heap nhỏ đều ổn; production nhiều core thì để G1 mặc định.

Bước 3: Java đọc cấu hình từ môi trường AWS. Trên EC2 không cần cấu hình gì cho credential. Khi app cần biết "mình là instance nào" (để log, metric), dùng SDK thay vì gọi IMDS tay:

// software.amazon.awssdk:imds
import software.amazon.awssdk.imds.Ec2MetadataClient;

@Configuration
public class InstanceInfoConfig {
    @Bean
    public String instanceId() {
        try (Ec2MetadataClient imds = Ec2MetadataClient.create()) {   // IMDSv2 mặc định
            return imds.get("/latest/meta-data/instance-id").asString();
        } catch (Exception e) {
            return "local";                                           // chạy local không có IMDS
        }
    }
}
// Dùng: logging.pattern thêm %X{instanceId}, hoặc CloudWatch metric dimension InstanceId

Và đọc jar/ảnh từ S3 trong code: S3Client.create(), không truyền credential; chain tìm IMDS. Timeout mặc định của chain với IMDS là 1 giây, nên ở local app khởi động chậm vài giây khi SDK thử IMDS: đặt AWS_EC2_METADATA_DISABLED=true ở local để bỏ qua.

5. Cách hoạt động: vòng đời một instance

run-instances
   │  chọn host vật lý trong AZ, gắn ENI (private IP từ subnet; public IP nếu subnet cấp),
   │  tạo EBS root từ snapshot của AMI, gắn instance profile
   ▼
pending ──► running
              │ cloud-init chạy user data (một lần) ── log /var/log/cloud-init-output.log
              │ IMDS cung cấp credential role (xoay tự động ~mỗi 6 giờ)
              │ CloudWatch: CPUUtilization, NetworkIn/Out, StatusCheckFailed (mặc định 5 phút; detailed 1 phút tính tiền)
              │ 2 status check: System (host/hardware AWS) + Instance (OS/network của bạn)
              ▼
stop ──► stopping ──► stopped      EBS giữ; RAM, instance store, public IP (không EIP) mất
              │                    không tính tiền compute, vẫn tính EBS; có thể đổi instance type ở đây
              ▼
start ──► running (host MỚI, public IP mới nếu không có EIP, user data KHÔNG chạy lại)

hibernate ──► stopped (RAM ghi vào EBS root, start lại là tiếp tục, giới hạn RAM ≤ 150 GB)

terminate ──► shutting-down ──► terminated   root EBS xóa (DeleteOnTermination=true), EBS khác giữ
                                              có "termination protection" để chặn xóa nhầm

Hai hệ quả thực tế: (1) muốn sửa user data phải stop → edit → start, và sửa xong nó cũng không chạy lại, nên user data phải idempotent và máy nên thay chứ không sửa; (2) reboot (restart OS) không đổi host, không mất instance store, không đổi IP; stop/start thì có. Đề thi: "instance needs to move to a new underlying host (hardware degraded)" → stop/start, không phải reboot.

EBS snapshot là incremental: lần đầu copy toàn bộ block đã dùng, lần sau chỉ block thay đổi; xóa snapshot giữa chuỗi không hỏng các snapshot sau (AWS giữ block cần thiết). Snapshot nằm ở S3 (bạn không thấy bucket), có thể copy sang region/account khác, mã hóa khi copy, và tạo AMI từ snapshot. Data Lifecycle Manager (DLM) hoặc AWS Backup tự tạo và xóa snapshot theo lịch; đề thi: "automate EBS snapshots with retention" → DLM/AWS Backup.

6. So sánh: những cặp hay ra đề

CặpKhác nhauChọn khi
Stop/Start vs RebootStop/Start: host mới, IP public mới, instance store mất, user data không chạy. Reboot: cùng host, giữ mọi thứHardware degraded / đổi instance type: stop/start. Chỉ restart OS: reboot
EBS vs Instance storeBền, mạng, snapshot vs tạm, local, nhanh nhấtDữ liệu phải sống qua stop: EBS. Scratch/cache cực nhanh: instance store
gp3 vs io2gp3 tới 16k IOPS, rẻ; io2 tới 64k (Block Express 256k), durability 99.999%, Multi-AttachHầu hết: gp3. DB IOPS rất cao / Multi-Attach: io2
Spot vs On-DemandRẻ tới 90% nhưng thu hồi báo 2 phút vs ổn địnhSpot: batch, CI, một phần ASG stateless. On-Demand: phần "base", DB, stateful
RI vs Savings PlansRI gắn instance type/region (Convertible đổi được); SP cam kết tiền/giờ, Compute SP áp cả Fargate/LambdaEC2 mới: SP. RDS/ElastiCache/Redshift: chỉ có RI
Dedicated Host vs Dedicated InstanceHost: thấy socket/core, kiểm soát đặt instance, BYOL theo socket. Instance: host riêng nhưng không thấy chi tiếtLicense theo socket/core: Host. Chỉ cần "không chia host với khách khác": Instance
Cluster vs Spread vs PartitionGần nhau nhất / xa nhau nhất (≤7 per AZ) / theo nhómHPC / vài node quan trọng / Kafka-Hadoop
SSH + key pair vs SSM Session ManagerMở 22, quản .pem vs không port, IAM + CloudTrail audit, ghi lại phiênHiện đại: Session Manager. Đề: "no inbound ports, audit all sessions" → SSM
Public IP vs Elastic IPĐổi khi stop/start vs cố định, thuộc tài khoảnServer cần IP cố định không qua ALB: EIP. Có ALB/Route 53: không cần EIP
EC2 Instance Connect vs Session ManagerĐẩy SSH key tạm 60 giây qua API, vẫn cần port 22 (hoặc Instance Connect Endpoint) vs không cần SSHCần SSH thật (scp, tunnel kiểu cũ): Instance Connect. Còn lại: SSM

7. Khi nào dùng / không dùng EC2 cho shop-app

Mình chưa yên tâm chuyện đĩa. Nếu mình stop instance rồi start lại thì file log và ảnh upload tạm ở /opt/shop/uploads còn không? Instance store là gì mà "mất dữ liệu"? Và snapshot EBS khác AMI chỗ nào?

Câu trả lời phụ thuộc vào đĩa đó là loại gì. Với t3.micro thì chắc chắn là EBS (dòng T không có instance store), nên /opt/shop/uploads còn sau stop/start. Nhưng hiểu rõ từng loại thì sau này không bất ngờ.

1. Ba tình huống với EBS root

stop → start        : EBS giữ nguyên. Mất: RAM, public IP (không EIP), instance store nếu có.
reboot              : giữ hết, kể cả instance store.
terminate           : root volume XÓA nếu DeleteOnTermination=true (mặc định cho root);
                      volume gắn thêm (data volume) mặc định GIỮ → thành volume "available" vẫn tính tiền!

Bẫy chi phí số 1: terminate instance mà data volume còn lại ở trạng thái available, tính tiền hàng tháng không ai thấy. Dọn bằng aws ec2 describe-volumes --filters Name=status,Values=available.

Và lưu ý thiết kế: dù EBS giữ dữ liệu, ảnh upload không nên nằm trên EBS của một máy. Bài 22.5 chạy 2 máy: ảnh ở máy A, request đến máy B là 404. Ảnh thuộc về S3 (bài 22.6); EBS chỉ cho OS, app, và dữ liệu "của máy đó".

2. Instance store (ephemeral)

Là SSD/NVMe gắn vật lý vào host chạy instance của bạn (các type có chữ d: m6id, c6gd, i3, i4i…). Vì gắn vào host, khi instance stop hay terminate, hoặc host hỏng, AWS đưa instance lên host khác và đĩa cũ không theo. Dữ liệu mất là đúng thiết kế. Đổi lại: hàng triệu IOPS, latency micro-giây, không tốn thêm tiền ngoài giá instance. Dùng cho: cache cục bộ, buffer, temp của Spark/Kafka khi đã có replication. Không snapshot được. Trên AL2023, instance store không tự mount; phải format và mount trong user data mỗi lần boot.

3. Snapshot, volume, AMI: quan hệ

EBS volume (đang gắn, trong AZ-a)
   │ create-snapshot (incremental, lưu trên S3, regional, không bị trói AZ)
   ▼
Snapshot snap-0abc  ──► create-volume --availability-zone ap-southeast-1b  (đưa dữ liệu sang AZ khác)
   │                ──► copy-snapshot --source-region ... --destination-region us-east-1 (DR)
   │                ──► copy-snapshot --encrypted (mã hóa volume chưa mã hóa)
   ▼
register-image (hoặc create-image từ instance): snapshot + metadata boot = AMI
   ──► run-instances từ AMI: máy mới có đúng đĩa đó (golden AMI, nhanh hơn user data cài từ đầu)

4. Snapshot tốn tiền thế nào

Theo GB lưu trên S3 của phần dữ liệu thay đổi, không theo kích thước volume. Lab snapshot 8 GB root dùng ~2 GB thực chỉ vài cent/tháng, nhưng chuỗi snapshot hằng ngày cho DB 500 GB thì đáng kể; EBS Snapshots Archive rẻ hơn ~75% cho snapshot giữ trên 90 ngày (khôi phục mất 24–72 giờ). Free Tier: 1 GB snapshot/tháng trong 12 tháng đầu.

Mình để ý t3.micro có "CPU credits". Spring Boot khởi động 30–40 giây ăn 100% CPU, rồi chạy bình thường. Hết credit thì sao, app bị dừng à? Có nên bật chế độ unlimited không, và khi nào thì nên bỏ dòng T sang dòng M?

Không, app không bị dừng. Hết credit thì CPU bị kẹp xuống mức baseline (với t3.micro là 10% của 2 vCPU, tức tương đương 0.2 vCPU). App vẫn chạy, chỉ chậm. Đây là lý do "t3 lúc nhanh lúc chậm" mà nhiều người không giải thích được.

1. Cơ chế credit

t3.micro: 2 vCPU, baseline 10% mỗi vCPU → kiếm 12 credit/giờ, tối đa tích 288 credit (24 giờ)
1 credit = 1 vCPU chạy 100% trong 1 phút

Khởi động Spring Boot: ~2 vCPU × 100% × 0.75 phút ≈ 1.5 credit   ← rẻ, không đáng lo
Chạy idle 5% CPU                                                  ← dưới baseline, tích credit
Traffic tăng, 60% CPU liên tục                                     ← tiêu ~1 credit/phút, 288 credit cạn sau ~5 giờ
Hết credit → kẹp 10% → latency tăng vọt, health check có thể timeout → ALB đánh unhealthy

Instance mới được cấp launch credit (30 credit/vCPU cho t2; t3 thì dùng cơ chế unlimited mặc định) nên khởi động không phải vấn đề. Vấn đề là tải đều cao hơn baseline trong nhiều giờ.

Theo dõi: metric CPUCreditBalance và CPUCreditUsage trong CloudWatch (namespace AWS/EC2). Đặt alarm khi CPUCreditBalance < 50. Một instance "CPU 10% mà app chậm" với credit balance = 0 là chẩn đoán xong.

2. Unlimited mode

t3 mặc định unlimited: hết credit vẫn burst, và bạn trả thêm cho phần vượt (tính theo vCPU-giờ, khoảng 0.05 USD/vCPU-giờ cho Linux, kiểm tra pricing). Tốt khi burst hiếm; nếu liên tục vượt baseline thì tiền phụ thu có thể cao hơn giá một instance M cùng cỡ. Chuyển về standard:

aws ec2 modify-instance-credit-specification --profile shop-lab \
  --instance-credit-specifications "InstanceId=$INSTANCE_ID,CpuCredits=standard"
# hoặc lúc run-instances: --credit-specification CpuCredits=standard

Lab nên để standard để không có hóa đơn bất ngờ; nếu bị kẹp, bạn thấy ngay và hiểu vì sao.

3. Khi nào rời dòng T

Dấu hiệuHành động
Trung bình CPU dưới baseline, có đỉnh ngắnT là đúng, rẻ nhất
CPU trung bình > baseline nhiều giờ/ngày, credit về 0 hằng ngàySang M cùng cỡ (m7g.medium/large) hoặc tăng size T
Phụ thu unlimited > chênh giá T→MSang M
Cần hiệu năng dự đoán được cho SLO (production)M/C/R; T chỉ cho dev/test và dịch vụ phụ

Compute Optimizer (bài 22.19) nhìn 14 ngày metric và gợi ý thẳng "t3.micro → t4g.small" hay "→ m7g.medium". Thi: "application performance degrades after several hours of sustained load on T instances" → CPU credit cạn; đáp án là M/C family hoặc unlimited, tùy đề nhấn cost hay performance.

4. JVM trên máy nhỏ

Trên 1 GB RAM: -Xmx512m hoặc -XX:MaxRAMPercentage=60, để OS và metaspace còn chỗ, nếu không OOM killer giết java và systemd restart mãi. Bật -XX:+UseSerialGC hoặc để JVM tự chọn (dưới 2 CPU/2 GB JVM tự dùng Serial). Spring Boot 3 với AOT/CDS (-XX:SharedArchiveFile) giảm thời gian khởi động và CPU lúc boot; container hóa sau này còn có CRaC. Không cần cho lab, cần khi scale-out phải nhanh.

Spot rẻ tới 90% mà shop-app là stateless. Mình chạy một instance Spot cho production được không? Nếu không thì Spot hợp với việc gì trong shop-app, và khi bị thu hồi thì mình xử lý thế nào?

Một instance Spot duy nhất cho production thì không: bạn đổi 70–90% tiền lấy khả năng website biến mất bất kỳ lúc nào với 2 phút báo trước. Nhưng Spot hợp với một phần capacity sau ALB và với các job nền của shop-app. Bài 22.19 bàn chiến lược mua; ở đây là cơ chế.

1. Spot hoạt động thế nào

Mỗi (instance type, AZ) có một Spot pool với giá thay đổi chậm theo cung cầu (không còn kiểu đấu giá).
Bạn đặt max price (mặc định = On-Demand). AWS thu hồi khi: cần capacity cho On-Demand, hoặc giá > max price.
Thu hồi: gửi "interruption notice" 2 phút trước, qua IMDS và EventBridge; EC2 Rebalance Recommendation báo sớm hơn khi pool có rủi ro.

Nguyên tắc: đa dạng hóa pool. ASG hoặc EC2 Fleet với 5–10 instance type tương đương (m6i.large, m6a.large, m5.large, m7g.large…) ở nhiều AZ; chiến lược price-capacity-optimized chọn pool ít bị thu hồi và rẻ. Một pool bị thu hồi, ASG lấy máy từ pool khác.

2. Xử lý khi bị thu hồi

# Trong máy: poll IMDS (mỗi 5 giây), 404 là chưa có thông báo
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/spot/instance-action
# {"action": "terminate", "time": "2025-01-10T09:12:00Z"}

# Hoặc EventBridge rule: source aws.ec2, detail-type "EC2 Spot Instance Interruption Warning"
#   → Lambda/SSM Run Command: deregister khỏi target group, drain, graceful shutdown

Với shop-app sau ALB: bật Capacity Rebalancing trong ASG để ASG tự tạo máy thay trước khi máy cũ bị thu hồi; ALB deregistration delay (mặc định 300 giây, giảm xuống 30) cho request đang xử lý xong; Spring Boot bật server.shutdown=graceful và spring.lifecycle.timeout-per-shutdown-phase=20s để không cắt request giữa chừng. Kết quả: người dùng không nhận lỗi, chỉ có máy thay nhau.

3. Spot hợp và không hợp với gì trong shop-app

WorkloadSpot?Vì sao
shop-app web tier sau ALB, ASG min 2Một phần: base On-Demand 2 máy + phần scale-out Spot (mixed instances policy)Luôn còn máy On-Demand khi Spot bị thu hồi hàng loạt
Job resize ảnh, sinh báo cáo đêm, re-index searchCó, 100%Chạy lại được, checkpoint theo batch
CI runner, môi trường testCóGián đoạn chỉ tốn thời gian
Kafka consumer xử lý orderCẩn thận: được nếu idempotent, commit offset sau khi xử lýThu hồi giữa batch → rebalance, message xử lý lại
PostgreSQL, Redis tự quản, ZooKeeperKhôngStateful, mất quorum, recovery dài

Cách khác để dùng Spot mà không tự xử lý: Spot Fleet / EC2 Fleet (quản lý nhiều pool), ECS capacity provider và Karpenter trên EKS (tự chọn Spot cho pod chịu được gián đoạn), AWS Batch (job queue trên Spot, retry tự động). Và Spot Blocks (chạy 1–6 giờ không bị thu hồi) đã ngừng cho khách mới; đề thi mới không còn coi là đáp án.

4. Câu trả lời cho shop-app

Production: ASG với OnDemandBaseCapacity=2, OnDemandPercentageAboveBaseCapacity=0 (phần vượt 100% Spot), 6 instance type, Capacity Rebalancing bật. Lab bài này: một t3.micro On-Demand trong Free Tier là đủ; bài 22.5 cấu hình mixed instances.

Cho mình bài lab để tự tay làm phần này nhé: chạy shop-app.jar trên một EC2 trong shop-public-a bằng user data, có instance profile, vào máy không cần SSH, và thử snapshot EBS.

Lab 22.4: shop-app.jar trên EC2 t3.micro với user data, instance profile và EBS snapshot

Kết quả: một máy trong shop-public-a tự cài Java 21, tự tải jar từ S3 (hoặc tự dựng placeholder nếu bạn chưa có jar), chạy như systemd service, trả /actuator/health. Role và bucket tạo ở đây được bài 22.5 dùng tiếp cho Launch Template.

Mục tiêu

Chi phí & thời gian

Chuẩn bị

Các bước

Bước 1: Bucket và jar.

BUCKET=shop-app-assets-$ACCOUNT_ID
aws s3 mb s3://$BUCKET --region $AWS_REGION --profile shop-lab
aws s3api put-public-access-block --bucket $BUCKET --profile shop-lab \
  --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
aws s3api put-bucket-tagging --bucket $BUCKET --profile shop-lab \
  --tagging 'TagSet=[{Key=Project,Value=shop-app},{Key=Env,Value=lab}]'

# nếu có jar:
aws s3 cp target/shop-app.jar s3://$BUCKET/releases/shop-app.jar --profile shop-lab
# không có: bỏ qua, user data sẽ tự tạo placeholder

Console: S3 → Create bucket (tên duy nhất toàn cầu, region Singapore, Block all public access bật), upload vào thư mục releases/. Chi tiết S3 ở bài 22.6.

Bước 2: Role và instance profile.

cat > trust-ec2.json <<'EOF'
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow",
  "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" }] }
EOF
cat > shop-app-ec2-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    { "Sid": "ReadAssets", "Effect": "Allow", "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::${BUCKET}", "arn:aws:s3:::${BUCKET}/*"] },
    { "Sid": "WriteLogs", "Effect": "Allow",
      "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents", "logs:DescribeLogStreams"],
      "Resource": "arn:aws:logs:${AWS_REGION}:${ACCOUNT_ID}:log-group:/shop/app:*" }
  ]
}
EOF

aws iam create-role --role-name shop-app-ec2-role --assume-role-policy-document file://trust-ec2.json \
  --tags Key=Project,Value=shop-app Key=Env,Value=lab --profile shop-lab
aws iam put-role-policy --role-name shop-app-ec2-role --policy-name ShopAppEc2Inline \
  --policy-document file://shop-app-ec2-policy.json --profile shop-lab
aws iam attach-role-policy --role-name shop-app-ec2-role \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore --profile shop-lab

aws iam create-instance-profile --instance-profile-name shop-app-ec2-role --profile shop-lab
aws iam add-role-to-instance-profile --instance-profile-name shop-app-ec2-role --role-name shop-app-ec2-role --profile shop-lab
sleep 10

Console: IAM → Roles → Create role → AWS service → EC2; Console tự tạo instance profile cùng tên. Thi: "EC2 cannot access S3 although role has permission" → quên gắn instance profile, hoặc SCP/bucket policy chặn.

Bước 3: User data. Script tự phát hiện có jar trên S3 không; không có thì dựng placeholder bằng jwebserver (có sẵn trong JDK 21) trả /actuator/health.

cat > user-data.sh <<EOF
#!/bin/bash
set -euxo pipefail
exec > >(tee -a /var/log/shop-app-bootstrap.log) 2>&1
dnf install -y java-21-amazon-corretto-headless
useradd -r -s /sbin/nologin shop || true
mkdir -p /opt/shop
BUCKET=${BUCKET}

if aws s3 ls s3://\$BUCKET/releases/shop-app.jar >/dev/null 2>&1; then
  aws s3 cp s3://\$BUCKET/releases/shop-app.jar /opt/shop/shop-app.jar
  EXEC="/usr/bin/java -jar /opt/shop/shop-app.jar"
else
  # placeholder: web server tĩnh trả /actuator/health giống Spring Boot
  mkdir -p /opt/shop/www/actuator/health
  echo '{"status":"UP","placeholder":true}' > /opt/shop/www/actuator/health/index.html
  echo "shop-app placeholder on \$(hostname)" > /opt/shop/www/index.html
  EXEC="/usr/bin/jwebserver -b 0.0.0.0 -p 8080 -d /opt/shop/www"
fi
chown -R shop:shop /opt/shop

cat > /etc/systemd/system/shop-app.service <<UNIT
[Unit]
Description=shop-app
After=network-online.target
Wants=network-online.target
[Service]
User=shop
WorkingDirectory=/opt/shop
Environment=SPRING_PROFILES_ACTIVE=aws
Environment=JAVA_TOOL_OPTIONS=-XX:MaxRAMPercentage=60
ExecStart=\$EXEC
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now shop-app
sleep 3 && curl -sf http://localhost:8080/actuator/health && echo "shop-app UP"
EOF
cat user-data.sh | head -20     # kiểm tra BUCKET đã được thay bằng tên thật

Heredoc ngoài không có nháy để ${BUCKET} được điền; các \$ giữ lại cho script chạy trên máy. AL2023 có sẵn AWS CLI v2 và SSM agent nên không cần cài.

Bước 4: Rule tạm cho 8080 từ IP của bạn. Theo thiết kế, shop-app-sg chỉ nhận từ ALB; bài này chưa có ALB nên mở tạm từ IP nhà bạn, bước dọn sẽ gỡ.

MY_IP=$(curl -s https://checkip.amazonaws.com)
aws ec2 authorize-security-group-ingress --group-id $APP_SG_ID --profile shop-lab \
  --ip-permissions "IpProtocol=tcp,FromPort=8080,ToPort=8080,IpRanges=[{CidrIp=${MY_IP}/32,Description=TEMP lab 22.4 my laptop}]"

Console: EC2 → Security Groups → shop-app-sg → Edit inbound rules → Add rule, Source "My IP".

Bước 5: Chạy instance.

AMI_ID=$(aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query 'Parameters[0].Value' --output text --profile shop-lab)

INSTANCE_ID=$(aws ec2 run-instances --profile shop-lab \
  --image-id $AMI_ID --instance-type t3.micro \
  --subnet-id $PUBLIC_A_ID --security-group-ids $APP_SG_ID \
  --iam-instance-profile Name=shop-app-ec2-role \
  --user-data file://user-data.sh \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --credit-specification CpuCredits=standard \
  --block-device-mappings 'DeviceName=/dev/xvda,Ebs={VolumeSize=8,VolumeType=gp3,Encrypted=true,DeleteOnTermination=true}' \
  --tag-specifications \
    'ResourceType=instance,Tags=[{Key=Name,Value=shop-app-1},{Key=Project,Value=shop-app},{Key=Env,Value=lab}]' \
    'ResourceType=volume,Tags=[{Key=Name,Value=shop-app-1-root},{Key=Project,Value=shop-app},{Key=Env,Value=lab}]' \
  --query 'Instances[0].InstanceId' --output text)
echo $INSTANCE_ID
echo "export INSTANCE_ID=$INSTANCE_ID" >> ~/shop-lab/shop-vpc.env

aws ec2 wait instance-running --instance-ids $INSTANCE_ID --profile shop-lab
PUBLIC_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --profile shop-lab \
  --query 'Reservations[0].Instances[0].PublicIpAddress' --output text)
echo $PUBLIC_IP

Không có --key-name: máy này không có SSH key, cố ý. Console: EC2 → Launch instance: Name, AMI Amazon Linux 2023, t3.micro, Key pair "Proceed without a key pair", Network: shop-vpc / shop-public-a / Auto-assign public IP Enable / Select existing SG shop-app-sg; Storage 8 GB gp3 Encrypted; Advanced details: IAM instance profile shop-app-ec2-role, Metadata version V2 only (token required), Credit specification Standard, User data dán script.

Bước 6: Chờ user data chạy (1–3 phút) và kiểm tra từ ngoài.

for i in $(seq 1 30); do
  curl -s -m 3 http://$PUBLIC_IP:8080/actuator/health && break || { echo "chờ... ($i)"; sleep 10; }
done
# {"status":"UP"}            ← jar thật
# {"status":"UP","placeholder":true}   ← placeholder

Bước 7: Vào máy bằng Session Manager, không SSH.

aws ssm describe-instance-information --profile shop-lab \
  --query 'InstanceInformationList[?InstanceId==`'$INSTANCE_ID'`].PingStatus' --output text   # Online
aws ssm start-session --target $INSTANCE_ID --profile shop-lab

Trong phiên:

sudo systemctl status shop-app --no-pager | head -5
sudo tail -20 /var/log/shop-app-bootstrap.log         # output user data
sudo tail -5 /var/log/cloud-init-output.log
ss -lntp | grep 8080                                   # 0.0.0.0:8080 java
TOKEN=$(curl -sX PUT http://169.254.169.254/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/info     # InstanceProfileArn shop-app-ec2-role
curl -s http://169.254.169.254/latest/meta-data/instance-id                                        # 401: IMDSv1 bị chặn, đúng ý
aws sts get-caller-identity                             # assumed-role/shop-app-ec2-role/i-...  (credential từ IMDS, không có key nào trên máy)
aws s3 ls s3://shop-app-assets-$(aws sts get-caller-identity --query Account --output text)/   # được
aws s3 ls                                               # AccessDenied: role không có ListAllMyBuckets, đúng least privilege
exit

Console: EC2 → Instances → shop-app-1 → Connect → Session Manager → Connect. CloudTrail ghi StartSession với danh tính của bạn; có thể bật ghi lại nội dung phiên vào S3/CloudWatch trong Systems Manager → Session Manager → Preferences.

Bước 8: EBS snapshot và phục hồi.

ROOT_VOL_ID=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --profile shop-lab \
  --query 'Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId' --output text)

SNAP_ID=$(aws ec2 create-snapshot --volume-id $ROOT_VOL_ID --description "shop-app-1 root after bootstrap" \
  --tag-specifications 'ResourceType=snapshot,Tags=[{Key=Name,Value=shop-app-1-root-snap},{Key=Project,Value=shop-app},{Key=Env,Value=lab}]' \
  --query SnapshotId --output text --profile shop-lab)
aws ec2 wait snapshot-completed --snapshot-ids $SNAP_ID --profile shop-lab      # 1-3 phút
aws ec2 describe-snapshots --snapshot-ids $SNAP_ID --profile shop-lab \
  --query 'Snapshots[0].{state:State,size:VolumeSize,encrypted:Encrypted,start:StartTime}'

# Phục hồi thành volume mới ở CÙNG AZ với instance, gắn vào máy làm đĩa thứ hai
AZ=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --profile shop-lab \
  --query 'Reservations[0].Instances[0].Placement.AvailabilityZone' --output text)
RESTORE_VOL_ID=$(aws ec2 create-volume --snapshot-id $SNAP_ID --availability-zone $AZ --volume-type gp3 \
  --tag-specifications 'ResourceType=volume,Tags=[{Key=Name,Value=shop-app-1-restore},{Key=Project,Value=shop-app},{Key=Env,Value=lab}]' \
  --query VolumeId --output text --profile shop-lab)
aws ec2 wait volume-available --volume-ids $RESTORE_VOL_ID --profile shop-lab
aws ec2 attach-volume --volume-id $RESTORE_VOL_ID --instance-id $INSTANCE_ID --device /dev/sdf --profile shop-lab

Trong phiên SSM: lsblk thấy đĩa mới (nvme1n1); vì nó là bản sao root có cùng UUID, mount bằng sudo mkdir /mnt/restore && sudo mount -o nouuid /dev/nvme1n1p4 /mnt/restore (số partition xem trong lsblk), ls /mnt/restore/opt/shop thấy jar/www. Đây là cách cứu file từ máy hỏng: snapshot → volume → gắn vào máy khác. Console: EC2 → Volumes → chọn → Actions → Create snapshot; Snapshots → Actions → Create volume from snapshot; Volumes → Attach.

Kiểm tra kết quả

curl -s http://$PUBLIC_IP:8080/actuator/health                       # {"status":"UP"...}
aws ec2 describe-instances --instance-ids $INSTANCE_ID --profile shop-lab --query 'Reservations[0].Instances[0].{
  type:InstanceType, az:Placement.AvailabilityZone, subnet:SubnetId, profile:IamInstanceProfile.Arn,
  imdsv2:MetadataOptions.HttpTokens, credits:CpuOptions, key:KeyName}'
# key: null (không SSH key), imdsv2: required, profile: .../shop-app-ec2-role
aws ec2 describe-instance-credit-specifications --instance-ids $INSTANCE_ID --profile shop-lab --query 'InstanceCreditSpecifications[0].CpuCredits'   # standard
aws ec2 describe-snapshots --owner-ids self --filters Name=tag:Project,Values=shop-app --profile shop-lab --query 'Snapshots[].{id:SnapshotId,state:State}'
aws cloudwatch get-metric-statistics --namespace AWS/EC2 --metric-name CPUCreditBalance \
  --dimensions Name=InstanceId,Value=$INSTANCE_ID --statistics Average --period 300 \
  --start-time $(date -u -d '-30 min' +%FT%TZ) --end-time $(date -u +%FT%TZ) --profile shop-lab --query 'Datapoints[-1].Average'

Thử thêm

Dọn tài nguyên

Giữ lại cho bài 22.5: role/instance profile shop-app-ec2-role, bucket với jar, shop-vpc. Xóa: instance (tốn giờ Free Tier và public IP), volume phục hồi, snapshot, rule SG tạm, AMI thử nghiệm nếu có.

source ~/shop-lab/shop-vpc.env
# 1. Gỡ volume phục hồi rồi xóa (volume "available" tính tiền âm thầm)
aws ec2 detach-volume --volume-id $RESTORE_VOL_ID --profile shop-lab
aws ec2 wait volume-available --volume-ids $RESTORE_VOL_ID --profile shop-lab
aws ec2 delete-volume --volume-id $RESTORE_VOL_ID --profile shop-lab
# 2. Terminate instance (root volume tự xóa vì DeleteOnTermination=true)
aws ec2 terminate-instances --instance-ids $INSTANCE_ID --profile shop-lab
aws ec2 wait instance-terminated --instance-ids $INSTANCE_ID --profile shop-lab
# 3. Snapshot
aws ec2 delete-snapshot --snapshot-id $SNAP_ID --profile shop-lab
# 4. AMI thử nghiệm (nếu đã tạo): deregister rồi xóa snapshot của nó
for ami in $(aws ec2 describe-images --owners self --filters Name=name,Values='shop-app-golden-*' --query 'Images[].ImageId' --output text --profile shop-lab); do
  SNAPS=$(aws ec2 describe-images --image-ids $ami --query 'Images[0].BlockDeviceMappings[].Ebs.SnapshotId' --output text --profile shop-lab)
  aws ec2 deregister-image --image-id $ami --profile shop-lab
  for s in $SNAPS; do aws ec2 delete-snapshot --snapshot-id $s --profile shop-lab; done
done
# 5. EIP nếu đã thử: aws ec2 release-address --allocation-id eipalloc-xxx --profile shop-lab
# 6. Rule SG tạm từ laptop
aws ec2 revoke-security-group-ingress --group-id $APP_SG_ID --profile shop-lab \
  --ip-permissions "IpProtocol=tcp,FromPort=8080,ToPort=8080,IpRanges=[{CidrIp=${MY_IP}/32}]"
# 7. Kiểm kê: không còn volume mồ côi, không còn instance
aws ec2 describe-volumes --filters Name=status,Values=available --profile shop-lab --query 'Volumes[].VolumeId'
aws ec2 describe-instances --filters Name=tag:Project,Values=shop-app Name=instance-state-name,Values=running,stopped --profile shop-lab --query 'Reservations[].Instances[].InstanceId'
sed -i '/INSTANCE_ID/d' ~/shop-lab/shop-vpc.env

Nếu bỏ hẳn cả chương: thêm aws s3 rb s3://$BUCKET --force, gỡ role (remove-role-from-instance-profile → delete-instance-profile → detach-role-policy, delete-role-policy → delete-role) và chạy cleanup-vpc.sh của lab 22.3.

Phỏng vấn và đề thi SAA hỏi về EC2 thì sao?

Q1. Bạn chọn instance type cho một ứng dụng Spring Boot thế nào?

🇻🇳 Tiếng Việt:

Em đọc tên instance: family cho mục đích, generation, hậu tố cho CPU. Spring Boot web thường cân bằng CPU và RAM nên bắt đầu ở dòng M, ưu tiên Graviton vì JVM chạy ARM tốt và rẻ hơn. RAM tính bằng heap cộng khoảng 50–100% cho metaspace, thread và off-heap. Dòng T chỉ dùng cho dev/test vì CPU theo credit, tải đều cao sẽ bị kẹp xuống baseline. Sau đó em dùng Compute Optimizer và metric thật để chỉnh cỡ.

🇬🇧 English:

I read the instance name: the family for purpose, the generation, and the suffix for the CPU. A Spring Boot web service is usually balanced between CPU and memory, so I start with the M family and prefer Graviton because the JVM runs well on ARM at a lower price. I size memory as heap plus roughly 50–100% for metaspace, threads and off-heap. T instances are for dev and test only, since their CPU is credit-based and sustained load gets throttled to the baseline. Then I right-size with Compute Optimizer and real metrics.

Q2. Làm sao để instance mới tự cài và chạy app mà không SSH? Credential cho app lấy ở đâu?

🇻🇳 Tiếng Việt:

Em dùng user data: script cloud-init chạy một lần lúc boot đầu, cài Java, tải jar từ S3 và tạo systemd service với Restart=always. Hoặc nhanh hơn là golden AMI đã cài sẵn. Credential không có trong script: em gắn IAM role qua instance profile, SDK và CLI tự lấy credential tạm từ IMDSv2 và tự xoay. Vào máy khi cần thì dùng Session Manager, không mở port 22, mọi phiên đều có trong CloudTrail.

🇬🇧 English:

I use user data: a cloud-init script that runs once at first boot, installs Java, downloads the jar from S3 and creates a systemd service with Restart=always. Or, for faster startup, a golden AMI with everything preinstalled. There are no credentials in the script: I attach an IAM role through an instance profile, and the SDK and CLI fetch temporary credentials from IMDSv2 and rotate them automatically. When I need a shell I use Session Manager, with no port 22 open and every session logged in CloudTrail.

Q3. EBS và instance store khác nhau thế nào? Snapshot dùng để làm gì?

🇻🇳 Tiếng Việt:

EBS là đĩa mạng bền, trói trong một AZ, giữ dữ liệu qua stop/start và có thể snapshot. Instance store là đĩa gắn vật lý vào host, nhanh nhất nhưng mất khi stop, terminate hay host hỏng, nên chỉ cho cache và dữ liệu tạm. Snapshot là bản sao incremental của volume lưu trên S3, mang tính regional: dùng để backup, tạo volume ở AZ khác, copy sang region khác cho DR, mã hóa volume cũ, và làm AMI. Lịch snapshot em giao cho Data Lifecycle Manager hoặc AWS Backup.

🇬🇧 English:

EBS is durable network storage bound to one AZ; it survives stop/start and can be snapshotted. Instance store is physically attached to the host, the fastest option, but it is lost on stop, terminate or host failure, so it is only for caches and scratch data. A snapshot is an incremental, regional copy of a volume stored in S3: used for backups, creating volumes in another AZ, copying to another region for DR, encrypting an unencrypted volume, and building AMIs. I schedule snapshots with Data Lifecycle Manager or AWS Backup.

Q4. Khi nào dùng Spot, Reserved, Savings Plans, On-Demand?

🇻🇳 Tiếng Việt:

On-Demand cho workload mới hoặc ngắn hạn chưa đoán được. Spot cho việc chịu được gián đoạn như batch, CI, phần scale-out stateless; em đa dạng hóa instance type và xử lý thông báo thu hồi 2 phút bằng graceful shutdown. Phần tải ổn định em cam kết bằng Savings Plans, linh hoạt hơn Reserved Instance vì cam kết theo tiền mỗi giờ và áp được cả Fargate, Lambda; Reserved Instance còn dùng cho RDS, ElastiCache vì không có Savings Plans. Database và stateful không bao giờ chạy Spot.

🇬🇧 English:

On-Demand for new or short-lived workloads I cannot predict yet. Spot for interruption-tolerant work such as batch jobs, CI and the stateless scale-out portion; I diversify instance types and handle the two-minute interruption notice with graceful shutdown. For the steady baseline I commit with Savings Plans, which are more flexible than Reserved Instances because the commitment is in dollars per hour and also covers Fargate and Lambda; Reserved Instances remain for RDS and ElastiCache, which have no Savings Plans. Databases and stateful services never run on Spot.

Góc thi SAA-C03

Câu 1: Một ứng dụng mô phỏng tài chính chạy trên nhiều EC2 trao đổi dữ liệu liên tục, yêu cầu độ trễ mạng giữa các node THẤP NHẤT và băng thông cao nhất. Cấu hình nào phù hợp?

A. Spread placement group trải trên 3 AZ.
B. Cluster placement group trong một AZ với instance hỗ trợ enhanced networking.
C. Partition placement group với 7 partition.
D. Instance ở nhiều region với Global Accelerator.

Đáp án: B. Cluster đặt instance gần nhau trên cùng rack trong một AZ, cho latency thấp nhất và throughput cao nhất; đúng từ khóa "tightly coupled HPC". A: spread tách xa nhau để giảm lỗi tương quan, ngược mục tiêu. C: partition cho hệ phân tán lớn như Hadoop, không tối ưu latency. D: đa region tăng latency.

Câu 2: Một công ty chạy job xử lý ảnh hằng đêm trên EC2, mất khoảng 4 giờ, có thể chạy lại từ checkpoint nếu bị gián đoạn. Giải pháp nào TIẾT KIỆM CHI PHÍ NHẤT?

A. On-Demand instance chạy 4 giờ mỗi đêm.
B. Reserved Instance 1 năm.
C. Spot instance với nhiều instance type trong một Auto Scaling group, xử lý interruption notice để checkpoint.
D. Dedicated Host để đảm bảo capacity.

Đáp án: C. Job chịu gián đoạn, có checkpoint, chạy ngắn → Spot giảm tới 90%. A: đắt hơn nhiều. B: RI trả cho 24 giờ/ngày trong khi chỉ dùng 4 giờ. D: đắt nhất, không liên quan.

Câu 3: Một database tự quản trên EC2 cần volume 2 TB với 40 000 IOPS ổn định và độ bền cao nhất. Loại EBS nào đáp ứng?

A. General Purpose SSD gp3 với IOPS cấu hình 40 000.
B. Provisioned IOPS SSD io2 Block Express với 40 000 IOPS.
C. Throughput Optimized HDD st1.
D. Instance store NVMe trên instance dòng i4i.

Đáp án: B. gp3 tối đa 16 000 IOPS nên A không đạt. io2 Block Express cấp tới 256 000 IOPS và độ bền 99.999%. C: HDD cho throughput tuần tự, IOPS thấp. D: nhanh nhưng mất dữ liệu khi stop/host hỏng, không phù hợp "độ bền cao nhất" cho database.

Cách nhớ ngắn gọn

Từ khóa trong đềNghĩ đến
lowest latency between nodes / HPC / tightly coupledCluster placement group
small number of critical instances, avoid correlated failureSpread placement group
Hadoop / Kafka / Cassandra on EC2Partition placement group
fault-tolerant, batch, can be interrupted, lowest costSpot
steady-state, commit 1–3 years, flexible across Fargate/LambdaCompute Savings Plans
software license per socket/core, BYOLDedicated Host
guarantee capacity in an AZ for an eventOn-Demand Capacity Reservation
> 16 000 IOPS, sub-millisecond, Multi-Attachio2 / io2 Block Express
temporary data, highest IOPS, can be lostInstance store
run script at launch / install software automaticallyUser data (cloud-init) hoặc golden AMI
no SSH, no inbound ports, audited shell accessSSM Session Manager
instance performance drops after hours of sustained loadT-family CPU credits cạn → M/C hoặc unlimited
move instance to new hardware / change instance typeStop → modify → Start
automate snapshots with retentionData Lifecycle Manager / AWS Backup

Tự kiểm tra

User data chạy khi nào? Sửa user data rồi start lại thì có chạy không?

Chỉ một lần ở lần boot đầu (cloud-init). Sửa xong start lại cũng không chạy, trừ khi dùng cloud-boothook hoặc tự cấu hình. Nên coi máy là thứ để thay, không để sửa.

IMDSv2 khác IMDSv1 chỗ nào và vì sao bắt buộc?

IMDSv2 đòi lấy token bằng PUT (có TTL, hop limit) trước khi đọc metadata; chặn tấn công SSRF đọc credential role qua một request GET đơn giản như vụ Capital One 2019. Đặt HttpTokens=required.

Stop/start khác reboot ở điểm nào?

Stop/start đưa instance lên host mới: public IP đổi (nếu không EIP), instance store mất, có thể đổi instance type. Reboot cùng host, giữ mọi thứ.

Volume gắn thêm (không phải root) còn lại gì sau khi terminate?

Mặc định DeleteOnTermination=false cho volume gắn thêm, nên nó thành volume available, vẫn tính tiền. Phải xóa tay hoặc đặt cờ lúc gắn.

t3.micro bị chậm sau vài giờ tải cao dù CPU chỉ 10%: nguyên nhân?

CPU credit cạn, instance bị kẹp xuống baseline 10%/vCPU. Kiểm tra CPUCreditBalance. Giải pháp: unlimited (trả thêm), tăng size, hoặc sang dòng M.

🧪 Thực hành bài này: Basic: Nền tảng: IAM + VPC + EC2 (chưa triển khai) · lab labs/aws