Một EC2 instance chạy được, nhưng khi nó chết giữa đêm và traffic gấp 3 ngày Black Friday, bạn cần ASG + ALB. Incident $120K DDoS: ASG không set max capacity, attacker spin up 360 m5.24xlarge trong 72 giờ.

ASG + ALB là cặp bài trùng: ALB phân phối traffic, ASG đảm bảo đúng số lượng instance healthy.


  flowchart LR
    User["User"] --> ALB["ALB<br/>HTTPS listener"]
    ALB --> TG["Target Group<br/>health check /health"]
    TG --> ASG["ASG<br/>min=2 · desired=3 · max=10"]
    ASG --> I1["i-001 (healthy)"]
    ASG --> I2["i-002 (healthy)"]
    ASG --> I3["i-003 (healthy)"]
    CW["CloudWatch: CPU > 60%"] -->|"scale-out +1"| ASG

ASG: 3 con số quyết định chi phí

aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name myapp-asg \
  --launch-template LaunchTemplateName=myapp-template,Version='$Latest' \
  --vpc-zone-identifier "subnet-a,subnet-b,subnet-c" \
  --min-size 2 --max-size 10 --desired-capacity 3 \
  --health-check-type ELB --health-check-grace-period 300 \
  --target-group-arns $TG_ARN
ParameterÝ nghĩaSai lầm
min-sizeSố instance tối thiểuSet = 0 → app offline khi scale-in
max-sizeGiới hạn chi phíSet quá cao/không set → $120K bill
desired-capacitySố instance mục tiêuSet thấp → không handle được spike
aws autoscaling put-scaling-policy --auto-scaling-group-name myapp-asg \
  --policy-name cpu60 --policy-type TargetTrackingScaling \
  --target-tracking-configuration '{
    "PredefinedMetricSpecification": {"PredefinedMetricType": "ASGAverageCPUUtilization"},
    "TargetValue": 60,
    "ScaleInCooldown": 300, "ScaleOutCooldown": 60
  }'

Target CPU 50-60%, không phải 80%. 80% không đủ buffer cho spike — instance mới cần 2-5 phút launch.


ALB: routing layer 7

aws elbv2 create-load-balancer --name myapp-alb --scheme internet-facing --type application \
  --subnets subnet-public-a subnet-public-b --security-groups sg-alb

aws elbv2 create-listener --load-balancer-arn $ALB_ARN --protocol HTTPS --port 443 \
  --certificates CertificateArn=$ACM_ARN \
  --default-actions Type=forward,TargetGroupArn=$TG_ARN

# Path-based routing
aws elbv2 create-rule --listener-arn $LISTENER_ARN --priority 10 \
  --conditions Field=path-pattern,Values='/api/*' \
  --actions Type=forward,TargetGroupArn=$BACKEND_TG

Health check đúng: kiểm tra dependency

app.get("/health", async (req, res) => {
  const checks = {
    db: await db
      .raw("SELECT 1")
      .then(() => "ok")
      .catch(() => "fail"),
    redis: await redis
      .ping()
      .then(() => "ok")
      .catch(() => "fail"),
    uptime: process.uptime(),
  };
  const healthy = Object.values(checks).every((v) => v === "ok");
  res.status(healthy ? 200 : 503).json(checks);
});

Mình từng thấy team set max-size quá cao và nhận bill $120K. ASG max capacity là firewall chi phí, set thấp hơn khả năng tài chính.

  • Target tracking CPU 50-60% — buffer cho spike, instance mới cần 2-5 phút
  • Health check kiểm tra dependency (DB, Redis) — không chỉ return 200
  • ALB rules path-based + host-based — routing linh hoạt

Bài sau: Phần 10: Lambda cơ bản — serverless từ dòng code đầu tiên