Module 09 Public Cloud 5 labs

AWS Core for DevOps

Nền tảng AWS cho DevOps Engineer: IAM, VPC 3-tier, EC2 + ALB, S3 bucket policy, ECR/EKS container registry và CloudWatch observability — toàn bộ thực hành bằng AWS CLI thật.

Công cụ thực hành AWS CLI v2, Docker, kubectl, AWS Console
Nền tảng Public Cloud — Amazon Web Services (AWS)
Thời điểm phát hành 23/05/2026
Ngày biên soạn 23/05/2026
Người biên soạn Trần Văn Hòa — Microsoft Certified Trainer (MCT)

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

1. Lý thuyết cốt lõi

1.1. Mô hình trách nhiệm chia sẻ (Shared Responsibility Model)

AWS chịu trách nhiệm bảo mật của cloud (phần cứng, hypervisor, network vật lý, cơ sở hạ tầng managed services); khách hàng chịu trách nhiệm bảo mật trong cloud (OS patching trên EC2, cấu hình Security Group, quản lý IAM key, mã hóa data). DevOps Engineer phải nắm ranh giới này để thiết kế đúng control plane.

1.2. IAM — Identity and Access Management

IAM là trung tâm kiểm soát ai được làm gì với tài nguyên nào trong AWS. Các khái niệm cốt lõi:

1.3. Networking — VPC, Subnet, Security Group

Kiến trúc VPC 3-tier điển hình

  • Public subnet: ALB, Bastion Host — có route 0.0.0.0/0 → Internet Gateway.
  • Private subnet (App tier): EC2/ECS/EKS worker — route 0.0.0.0/0 → NAT Gateway (outbound only).
  • Private subnet (Data tier): RDS, ElastiCache — không có outbound internet route.
  • Security Group: stateful firewall ở cấp instance; chỉ allow inbound cần thiết, cho phép SG khác tham chiếu thay vì dùng CIDR cố định.
  • NACL: stateless firewall ở cấp subnet; dùng làm lớp bảo vệ bổ sung, deny explicit.

1.4. Compute — EC2, Auto Scaling, ALB

Thành phầnChức năngGhi chú DevOps
EC2Virtual machine IaaS, chọn instance type theo CPU/RAM/GPUDùng Launch Template + User Data script để bake AMI
Auto Scaling GroupScale in/out tự động theo CPU metric hoặc scheduleMin/Max/Desired; Rolling update policy
ALBLayer-7 load balancer; path/host-based routingGắn với Target Group, health check /healthz

1.5. Storage — S3, EBS, EFS

S3 (Simple Storage Service) là object storage vô hạn, 11 nines durability. Với DevOps: lưu artifacts CI/CD, Terraform state, static website, log. Bảo mật qua Bucket Policy (resource-based) + IAM Policy (identity-based). Bật Block Public Access theo mặc định, dùng pre-signed URL cho truy cập tạm thời. EBS là block storage gắn với EC2 (dữ liệu persistent). EFS là NFS managed, chia sẻ giữa nhiều EC2/pod.

1.6. Container — ECR, ECS, EKS

1.7. Observability — CloudWatch, X-Ray, CloudTrail

CloudWatch Metrics: thu thập mặc định CPU, Network, Disk từ EC2; custom metric qua PutMetricData API. CloudWatch Logs: tập trung log từ EC2 (qua CloudWatch Agent), Lambda, EKS. CloudWatch Alarms: trigger SNS notification hoặc Auto Scaling action. CloudTrail: audit log mọi API call vào AWS account — bắt buộc bật cho compliance. X-Ray: distributed tracing cho microservices.

2. Thực hành (Labs)

LAB-041

Tạo VPC 3-tier bằng AWS CLI

AWS CLI · AWS Console

🎯 Mục tiêu: Tạo VPC với 3 tầng subnet (public/private-app/private-data), Internet Gateway, NAT Gateway, Route Table hoàn chỉnh bằng AWS CLI.

🧰 Công cụ / nền tảng: AWS CLI v2 (đã configure profile), jq, bash hoặc PowerShell.

📦 Chuẩn bị: Cài AWS CLI v2 (aws --version ≥ 2.x), chạy aws configure với Access Key + Secret Key + region ap-southeast-1. Cần quyền IAM: ec2:*.

▶️ Các bước (AWS CLI):

# 1. Tạo VPC
VPC_ID=$(aws ec2 create-vpc \
  --cidr-block 10.0.0.0/16 \
  --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=devops-vpc}]' \
  --query 'Vpc.VpcId' --output text)
echo "VPC: $VPC_ID"

# 2. Bật DNS hostname
aws ec2 modify-vpc-attribute --vpc-id $VPC_ID --enable-dns-hostnames

# 3. Tạo Internet Gateway và attach
IGW_ID=$(aws ec2 create-internet-gateway \
  --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=devops-igw}]' \
  --query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID

# 4. Tạo 3 subnet (public, private-app, private-data)
PUB_SN=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 \
  --availability-zone ap-southeast-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1a}]' \
  --query 'Subnet.SubnetId' --output text)

APP_SN=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID --cidr-block 10.0.11.0/24 \
  --availability-zone ap-southeast-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-app-1a}]' \
  --query 'Subnet.SubnetId' --output text)

DATA_SN=$(aws ec2 create-subnet \
  --vpc-id $VPC_ID --cidr-block 10.0.21.0/24 \
  --availability-zone ap-southeast-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-data-1a}]' \
  --query 'Subnet.SubnetId' --output text)

# 5. Public Route Table → IGW
PUB_RT=$(aws ec2 create-route-table --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=rt-public}]' \
  --query 'RouteTable.RouteTableId' --output text)
aws ec2 create-route --route-table-id $PUB_RT \
  --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID
aws ec2 associate-route-table --route-table-id $PUB_RT --subnet-id $PUB_SN

# 6. Tạo Elastic IP + NAT Gateway trong public subnet
EIP=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
NAT_ID=$(aws ec2 create-nat-gateway \
  --subnet-id $PUB_SN --allocation-id $EIP \
  --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=devops-nat}]' \
  --query 'NatGateway.NatGatewayId' --output text)
echo "Waiting for NAT Gateway to become available..."
aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT_ID

# 7. Private Route Table → NAT Gateway
PRIV_RT=$(aws ec2 create-route-table --vpc-id $VPC_ID \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=rt-private}]' \
  --query 'RouteTable.RouteTableId' --output text)
aws ec2 create-route --route-table-id $PRIV_RT \
  --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT_ID
aws ec2 associate-route-table --route-table-id $PRIV_RT --subnet-id $APP_SN
aws ec2 associate-route-table --route-table-id $PRIV_RT --subnet-id $DATA_SN

echo "VPC=$VPC_ID  PUB=$PUB_SN  APP=$APP_SN  DATA=$DATA_SN"

🖥️ Đối chiếu AWS Console:

VPC → Your VPCs: thấy devops-vpc CIDR 10.0.0.0/16. Subnets: 3 subnet đúng AZ và CIDR. Route Tables: rt-public có route 0.0.0.0/0 → igw-xxx; rt-private có route 0.0.0.0/0 → nat-xxx.

✅ Kết quả mong đợi: aws ec2 describe-vpcs --vpc-ids $VPC_ID --query 'Vpcs[0].State' trả về "available". Ba subnet tồn tại, 2 route table với route mặc định đúng target.

🧹 Cleanup:

# Xóa theo thứ tự ngược (NAT trước, rồi IGW, rồi VPC)
aws ec2 delete-nat-gateway --nat-gateway-id $NAT_ID
aws ec2 wait nat-gateway-deleted --nat-gateway-ids $NAT_ID
aws ec2 release-address --allocation-id $EIP
aws ec2 detach-internet-gateway --internet-gateway-id $IGW_ID --vpc-id $VPC_ID
aws ec2 delete-internet-gateway --internet-gateway-id $IGW_ID
aws ec2 delete-subnet --subnet-id $PUB_SN
aws ec2 delete-subnet --subnet-id $APP_SN
aws ec2 delete-subnet --subnet-id $DATA_SN
aws ec2 delete-route-table --route-table-id $PUB_RT
aws ec2 delete-route-table --route-table-id $PRIV_RT
aws ec2 delete-vpc --vpc-id $VPC_ID
LAB-042

Deploy EC2 sau ALB

AWS CLI · AWS Console

🎯 Mục tiêu: Tạo 2 EC2 instance chạy Nginx trong private subnet, đặt sau Application Load Balancer ở public subnet; kiểm tra health check và load balancing.

🧰 Công cụ / nền tảng: AWS CLI v2, AWS Console để xem ALB DNS.

📦 Chuẩn bị: Đã hoàn thành LAB-041 (có VPC_ID, PUB_SN, APP_SN). Cần Key Pair và AMI ID Amazon Linux 2023 cho region.

# Lấy AMI Amazon Linux 2023 mới nhất
AMI_ID=$(aws ec2 describe-images \
  --owners amazon \
  --filters 'Name=name,Values=al2023-ami-*-x86_64' 'Name=state,Values=available' \
  --query 'sort_by(Images, &CreationDate)[-1].ImageId' --output text)
echo "AMI: $AMI_ID"

▶️ Các bước:

# 1. Security Group cho EC2 (chỉ nhận từ ALB SG)
EC2_SG=$(aws ec2 create-security-group \
  --group-name sg-ec2-app --description "App EC2 SG" \
  --vpc-id $VPC_ID --query 'GroupId' --output text)

# 2. Security Group cho ALB (nhận 80 từ 0.0.0.0/0)
ALB_SG=$(aws ec2 create-security-group \
  --group-name sg-alb --description "ALB SG" \
  --vpc-id $VPC_ID --query 'GroupId' --output text)
aws ec2 authorize-security-group-ingress \
  --group-id $ALB_SG --protocol tcp --port 80 --cidr 0.0.0.0/0

# EC2 SG cho phép port 80 từ ALB SG
aws ec2 authorize-security-group-ingress \
  --group-id $EC2_SG --protocol tcp --port 80 \
  --source-group $ALB_SG

# 3. User Data script cài Nginx
USER_DATA=$(base64 -w0 << 'EOF'
#!/bin/bash
dnf install -y nginx
systemctl enable --now nginx
echo "

Hello from $(hostname)

" > /usr/share/nginx/html/index.html EOF ) # 4. Tạo 2 EC2 instance trong private subnet for i in 1 2; do aws ec2 run-instances \ --image-id $AMI_ID \ --instance-type t3.micro \ --subnet-id $APP_SN \ --security-group-ids $EC2_SG \ --user-data "$USER_DATA" \ --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=app-server-$i}]" \ --count 1 \ --no-associate-public-ip-address \ --query 'Instances[0].InstanceId' --output text done # Lưu Instance IDs INST=$(aws ec2 describe-instances \ --filters "Name=tag:Name,Values=app-server-*" "Name=instance-state-name,Values=running,pending" \ --query 'Reservations[*].Instances[0].InstanceId' --output text) echo "Instances: $INST" aws ec2 wait instance-running --instance-ids $INST # 5. Tạo Target Group TG_ARN=$(aws elbv2 create-target-group \ --name tg-app \ --protocol HTTP --port 80 \ --vpc-id $VPC_ID \ --health-check-path / \ --health-check-interval-seconds 15 \ --healthy-threshold-count 2 \ --query 'TargetGroups[0].TargetGroupArn' --output text) # 6. Đăng ký EC2 vào Target Group for id in $INST; do aws elbv2 register-targets \ --target-group-arn $TG_ARN \ --targets Id=$id done # 7. Tạo thêm public subnet thứ 2 cho ALB (cần ≥2 AZ) PUB_SN2=$(aws ec2 create-subnet \ --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 \ --availability-zone ap-southeast-1b \ --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1b}]' \ --query 'Subnet.SubnetId' --output text) aws ec2 associate-route-table --route-table-id $PUB_RT --subnet-id $PUB_SN2 # 8. Tạo ALB ALB_ARN=$(aws elbv2 create-load-balancer \ --name alb-devops \ --type application \ --subnets $PUB_SN $PUB_SN2 \ --security-groups $ALB_SG \ --query 'LoadBalancers[0].LoadBalancerArn' --output text) ALB_DNS=$(aws elbv2 describe-load-balancers \ --load-balancer-arns $ALB_ARN \ --query 'LoadBalancers[0].DNSName' --output text) # 9. Tạo Listener aws elbv2 create-listener \ --load-balancer-arn $ALB_ARN \ --protocol HTTP --port 80 \ --default-actions Type=forward,TargetGroupArn=$TG_ARN echo "ALB DNS: $ALB_DNS" echo "Test: curl http://$ALB_DNS"

🖥️ Đối chiếu AWS Console:

EC2 → Load Balancers: chọn alb-devops → tab Target groups → status healthy. Dán DNS vào trình duyệt thấy trang Nginx.

✅ Kết quả mong đợi: curl http://$ALB_DNS trả về Hello from ip-10-0-11-xxx. Reload nhiều lần thấy hostname khác nhau (round-robin). Target Group health = healthy.

🧹 Cleanup:

aws elbv2 delete-load-balancer --load-balancer-arn $ALB_ARN
aws elbv2 delete-target-group --target-group-arn $TG_ARN
aws ec2 terminate-instances --instance-ids $INST
aws ec2 wait instance-terminated --instance-ids $INST
aws ec2 delete-security-group --group-id $EC2_SG
aws ec2 delete-security-group --group-id $ALB_SG
LAB-043

Tạo S3 bucket với versioning, encryption và bucket policy

AWS CLI · AWS Console

🎯 Mục tiêu: Tạo S3 bucket cho artifacts CI/CD; bật versioning, server-side encryption (SSE-S3); viết bucket policy chỉ cho phép role CI/CD upload; kiểm chứng Block Public Access.

🧰 Công cụ / nền tảng: AWS CLI v2.

📦 Chuẩn bị: Tên bucket phải globally unique. Thay ACCOUNT_ID bằng ID account thật (aws sts get-caller-identity --query Account --output text).

▶️ Các bước:

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
BUCKET="devops-artifacts-${ACCOUNT_ID}-lab"
REGION="ap-southeast-1"

# 1. Tạo bucket
aws s3api create-bucket \
  --bucket $BUCKET \
  --region $REGION \
  --create-bucket-configuration LocationConstraint=$REGION

# 2. Chặn toàn bộ public access
aws s3api put-public-access-block \
  --bucket $BUCKET \
  --public-access-block-configuration \
    BlockPublicAcls=true,IgnorePublicAcls=true,\
BlockPublicPolicy=true,RestrictPublicBuckets=true

# 3. Bật versioning
aws s3api put-bucket-versioning \
  --bucket $BUCKET \
  --versioning-configuration Status=Enabled

# 4. Bật SSE-S3 encryption mặc định
aws s3api put-bucket-encryption \
  --bucket $BUCKET \
  --server-side-encryption-configuration '{
    "Rules": [{
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "AES256"
      },
      "BucketKeyEnabled": true
    }]
  }'

# 5. Tạo IAM Role cho CI/CD
aws iam create-role \
  --role-name cicd-s3-uploader \
  --assume-role-policy-document '{
    "Version":"2012-10-17",
    "Statement":[{
      "Effect":"Allow",
      "Principal":{"Service":"ec2.amazonaws.com"},
      "Action":"sts:AssumeRole"
    }]
  }'

# 6. Bucket policy: chỉ cho role cicd-s3-uploader put object
cat > /tmp/bucket-policy.json << EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCICDUpload",
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::${ACCOUNT_ID}:role/cicd-s3-uploader"
      },
      "Action": ["s3:PutObject","s3:GetObject","s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::${BUCKET}",
        "arn:aws:s3:::${BUCKET}/*"
      ]
    },
    {
      "Sid": "DenyNonEncrypted",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::${BUCKET}/*",
      "Condition": {
        "StringNotEquals": {
          "s3:x-amz-server-side-encryption": "AES256"
        }
      }
    }
  ]
}
EOF
aws s3api put-bucket-policy --bucket $BUCKET \
  --policy file:///tmp/bucket-policy.json

# 7. Upload test artifact và xem version
echo "build-v1.0" | aws s3 cp - s3://$BUCKET/artifacts/build.txt
aws s3api list-object-versions \
  --bucket $BUCKET --prefix artifacts/build.txt \
  --query 'Versions[*].{VersionId:VersionId,IsLatest:IsLatest}'

🖥️ Đối chiếu AWS Console:

S3 → Bucket devops-artifacts-... → tab Properties: Versioning = Enabled, Default encryption = SSE-S3. Tab Permissions: Block public access = ON, Bucket policy hiện JSON.

✅ Kết quả mong đợi: list-object-versions hiển thị 1 version với "IsLatest": true. Thử upload không có header encryption bị Deny (nếu test bằng presigned URL không có SSE header).

🧹 Cleanup:

aws s3 rm s3://$BUCKET --recursive
aws s3api delete-bucket --bucket $BUCKET
aws iam delete-role --role-name cicd-s3-uploader
LAB-044

Push Docker image lên ECR và deploy lên EKS

AWS CLI · Docker · kubectl

🎯 Mục tiêu: Build image Nginx tùy chỉnh, push lên ECR private registry, tạo EKS cluster (eksctl), deploy Deployment + Service dùng image từ ECR.

🧰 Công cụ / nền tảng: Docker Desktop, AWS CLI v2, eksctl, kubectl, helm.

📦 Chuẩn bị: Cài eksctl (choco install eksctl trên Windows hoặc brew trên macOS). IAM user cần quyền ecr:*, eks:*, ec2:*, iam:PassRole.

▶️ Các bước:

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION="ap-southeast-1"
REPO_NAME="devops-app"

# 1. Tạo ECR repository
aws ecr create-repository \
  --repository-name $REPO_NAME \
  --image-scanning-configuration scanOnPush=true \
  --image-tag-mutability IMMUTABLE \
  --region $REGION

# 2. Build image
mkdir -p /tmp/devops-app
cat > /tmp/devops-app/Dockerfile << 'EOF'
FROM nginx:1.27-alpine
COPY index.html /usr/share/nginx/html/index.html
EOF
echo "

DevOps App v1.0 on EKS

" > /tmp/devops-app/index.html docker build -t $REPO_NAME:1.0 /tmp/devops-app # 3. Login ECR và push aws ecr get-login-password --region $REGION | \ docker login --username AWS \ --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com docker tag $REPO_NAME:1.0 \ ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO_NAME}:1.0 docker push ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO_NAME}:1.0 # Kiểm tra image đã được scan aws ecr describe-image-scan-findings \ --repository-name $REPO_NAME --image-id imageTag=1.0 \ --query 'imageScanFindings.findingSeverityCounts' # 4. Tạo EKS cluster (mất ~15 phút) eksctl create cluster \ --name devops-cluster \ --region $REGION \ --nodegroup-name workers \ --node-type t3.medium \ --nodes 2 \ --nodes-min 1 \ --nodes-max 3 \ --managed # 5. Update kubeconfig aws eks update-kubeconfig \ --name devops-cluster --region $REGION kubectl get nodes # 6. Deploy workload từ ECR IMAGE_URI="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO_NAME}:1.0" kubectl create deployment devops-app \ --image=$IMAGE_URI \ --replicas=2 kubectl expose deployment devops-app \ --type=LoadBalancer --port=80 # 7. Chờ External IP và kiểm tra kubectl rollout status deployment/devops-app kubectl get svc devops-app -w # Khi có EXTERNAL-IP, test: # curl http://

🖥️ Đối chiếu AWS Console:

ECR → Repositories → devops-app: thấy tag 1.0, scan status. EKS → Clusters → devops-cluster → Workloads: Deployment devops-app Running. EC2 → Load Balancers: thấy Classic ELB tạo bởi kubectl.

✅ Kết quả mong đợi: kubectl get pods hiển thị 2 pod Running. curl http://<EXTERNAL-IP> trả về DevOps App v1.0 on EKS. ECR scan không có CRITICAL findings.

🧹 Cleanup:

kubectl delete svc devops-app
kubectl delete deployment devops-app
eksctl delete cluster --name devops-cluster --region $REGION
aws ecr delete-repository --repository-name $REPO_NAME \
  --force --region $REGION
LAB-045

Cấu hình CloudWatch Alarm và Dashboard giám sát EC2

AWS CLI · AWS Console

🎯 Mục tiêu: Tạo CloudWatch alarm cảnh báo khi CPU EC2 vượt 70%; tạo Log Group thu thập system log; tạo Dashboard tổng hợp metrics; cài CloudWatch Agent trên EC2.

🧰 Công cụ / nền tảng: AWS CLI v2, AWS Console.

📦 Chuẩn bị: Có ít nhất 1 EC2 instance đang chạy. Cần SNS Topic để nhận email alert.

▶️ Các bước:

REGION="ap-southeast-1"
# Thay bằng Instance ID thật từ LAB-042 hoặc instance bất kỳ
INSTANCE_ID="i-0123456789abcdef0"
EMAIL="[email protected]"

# 1. Tạo SNS Topic để gửi alert
TOPIC_ARN=$(aws sns create-topic \
  --name devops-alerts \
  --query 'TopicArn' --output text)
aws sns subscribe \
  --topic-arn $TOPIC_ARN \
  --protocol email \
  --notification-endpoint $EMAIL
echo "Kiểm tra email để confirm subscription!"

# 2. Tạo CloudWatch Alarm: CPU > 70% trong 2 periods liên tiếp
aws cloudwatch put-metric-alarm \
  --alarm-name "cpu-high-${INSTANCE_ID}" \
  --alarm-description "CPU utilization > 70% for 2 consecutive 5-min periods" \
  --metric-name CPUUtilization \
  --namespace AWS/EC2 \
  --statistic Average \
  --period 300 \
  --threshold 70 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 2 \
  --dimensions Name=InstanceId,Value=$INSTANCE_ID \
  --alarm-actions $TOPIC_ARN \
  --ok-actions $TOPIC_ARN \
  --treat-missing-data notBreaching

# 3. Kiểm tra trạng thái alarm
aws cloudwatch describe-alarms \
  --alarm-names "cpu-high-${INSTANCE_ID}" \
  --query 'MetricAlarms[0].{State:StateValue,Reason:StateReason}'

# 4. Tạo Log Group (30 ngày retention)
aws logs create-log-group \
  --log-group-name /devops/ec2/system \
  --region $REGION
aws logs put-retention-policy \
  --log-group-name /devops/ec2/system \
  --retention-in-days 30

# 5. Tạo CloudWatch Dashboard
DASHBOARD_BODY=$(cat << EOF
{
  "widgets": [
    {
      "type": "metric",
      "properties": {
        "title": "EC2 CPU Utilization",
        "metrics": [["AWS/EC2","CPUUtilization","InstanceId","${INSTANCE_ID}"]],
        "period": 300,
        "stat": "Average",
        "view": "timeSeries"
      }
    },
    {
      "type": "metric",
      "properties": {
        "title": "EC2 Network In/Out",
        "metrics": [
          ["AWS/EC2","NetworkIn","InstanceId","${INSTANCE_ID}"],
          ["AWS/EC2","NetworkOut","InstanceId","${INSTANCE_ID}"]
        ],
        "period": 300,
        "stat": "Sum",
        "view": "timeSeries"
      }
    }
  ]
}
EOF
)

aws cloudwatch put-dashboard \
  --dashboard-name "DevOps-EC2-Monitor" \
  --dashboard-body "$DASHBOARD_BODY"

# 6. Simulate high CPU để kích alarm (chạy trên EC2 qua SSM)
# (Cần IAM role có AmazonSSMManagedInstanceCore attached)
aws ssm send-command \
  --instance-ids $INSTANCE_ID \
  --document-name "AWS-RunShellScript" \
  --parameters 'commands=["stress --cpu 4 --timeout 120 &"]' \
  --comment "Stress test for CloudWatch lab" \
  --query 'Command.CommandId' --output text

# 7. Xem metrics ngay lập tức
aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUUtilization \
  --dimensions Name=InstanceId,Value=$INSTANCE_ID \
  --start-time $(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 60 \
  --statistics Average \
  --query 'sort_by(Datapoints, &Timestamp)[*].{Time:Timestamp,CPU:Average}'

🖥️ Đối chiếu AWS Console:

CloudWatch → Alarms: thấy alarm cpu-high-xxx trạng thái OK/ALARM. CloudWatch → Dashboards: mở DevOps-EC2-Monitor thấy 2 widget đồ thị. Nếu stress test chạy, alarm chuyển ALARM và email được gửi.

✅ Kết quả mong đợi: Alarm tồn tại ở trạng thái OK. Log Group /devops/ec2/system hiện trong Logs. Dashboard có 2 widget CPU + Network. Sau stress test: alarm → ALARM, nhận email SNS.

🧹 Cleanup:

aws cloudwatch delete-alarms --alarm-names "cpu-high-${INSTANCE_ID}"
aws cloudwatch delete-dashboards --dashboard-names DevOps-EC2-Monitor
aws logs delete-log-group --log-group-name /devops/ec2/system
aws sns delete-topic --topic-arn $TOPIC_ARN

3. Tình huống doanh nghiệp thực tế

Bối cảnh

Một startup fintech cần migrate từ single-server VPS sang AWS để đáp ứng traffic tăng đột biến mùa sale. Yêu cầu: high availability, auto-scale, chi phí tối ưu, audit log đầy đủ cho compliance PCI-DSS.

Kiến trúc đề xuất

  • IAM: Tạo role riêng cho mỗi service (EKS IRSA cho pod); bật MFA cho root + admin; IAM Access Analyzer phát hiện over-privileged policy.
  • Networking: VPC 3-tier multi-AZ (2 AZ tối thiểu); ALB + Auto Scaling Group cho web tier; RDS Multi-AZ cho database tier.
  • Container: ECR với immutable tag + scan on push; EKS với HPA tự scale khi CPU > 60%; Fargate cho batch job tránh quản lý node.
  • Storage: S3 lưu artifacts + static assets; SSE-KMS (không SSE-S3) cho dữ liệu PCI scope; S3 Lifecycle policy archive logs sau 90 ngày → Glacier.
  • Observability: CloudWatch Container Insights cho EKS; CloudTrail multi-region trail → S3 không thể xóa (MFA delete); CloudWatch Alarm + PagerDuty cho on-call.
  • Cost: Reserved Instance cho baseline EC2; Spot Instance cho worker không stateful; AWS Cost Explorer + Budget Alert khi vượt ngưỡng.

📚 Nguồn tham khảo

Module 08: On-prem Kubernetes Module 10: Microsoft Azure Core
Zalo