Trang chủ/Chương 23

Chương 23: Hands-On Labs

Tổng Quan

10 bài thực hành với AWS CLI commands step-by-step. Mỗi lab có sơ đồ kiến trúc, hướng dẫn chi tiết, và phần cleanup (xoá resources tránh phát sinh chi phí).

⚠️ Prerequisites: AWS Account (Free Tier), AWS CLI configured, region: us-east-1


🔧 Setup AWS CLI

# Install (macOS)
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /

# Configure
aws configure
# Access Key: YOUR_KEY
# Secret Key: YOUR_SECRET
# Region: us-east-1
# Output: json

# Verify
aws sts get-caller-identity

🌐 Lab 1: VPC & Networking

Mục tiêu: Tạo VPC production-ready với public + private subnets, IGW, NAT Gateway, Route Tables.

Kiến trúc

VPC (10.0.0.0/16)
├─ AZ-A
│  ├─ Public Subnet (10.0.1.0/24) ← NAT Gateway
│  └─ Private Subnet (10.0.10.0/24) ← EC2
├─ AZ-B
│  ├─ Public Subnet (10.0.2.0/24) ← NAT Gateway
│  └─ Private Subnet (10.0.11.0/24) ← EC2
└─ Internet Gateway

Commands

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

# 2. Create Subnets
PUB_1=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.1.0/24 \
  --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
PUB_2=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.2.0/24 \
  --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)
PRIV_1=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.10.0/24 \
  --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
PRIV_2=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block 10.0.11.0/24 \
  --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)

# 3. Create + Attach Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID

# 4. Create NAT Gateway (AZ-A)
EIP=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
NAT=$(aws ec2 create-nat-gateway --subnet-id $PUB_1 --allocation-id $EIP \
  --query 'NatGateway.NatGatewayId' --output text)
aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT

# 5. Route Tables
PUB_RT=$(aws ec2 create-route-table --vpc-id $VPC_ID --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 --subnet-id $PUB_1 --route-table-id $PUB_RT
aws ec2 associate-route-table --subnet-id $PUB_2 --route-table-id $PUB_RT

PRIV_RT=$(aws ec2 create-route-table --vpc-id $VPC_ID --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
aws ec2 associate-route-table --subnet-id $PRIV_1 --route-table-id $PRIV_RT
aws ec2 associate-route-table --subnet-id $PRIV_2 --route-table-id $PRIV_RT

# 6. Verify
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$VPC_ID" \
  --query 'Subnets[*].[SubnetId,CidrBlock,AvailabilityZone]' --output table

🧹 Cleanup

aws ec2 delete-nat-gateway --nat-gateway-id $NAT
aws ec2 wait nat-gateway-deleted --nat-gateway-ids $NAT
aws ec2 release-address --allocation-id $EIP
aws ec2 detach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID
aws ec2 delete-internet-gateway --internet-gateway-id $IGW_ID
aws ec2 delete-subnet --subnet-id $PUB_1; aws ec2 delete-subnet --subnet-id $PUB_2
aws ec2 delete-subnet --subnet-id $PRIV_1; aws ec2 delete-subnet --subnet-id $PRIV_2
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

📝 Học được gì?

  • VPC architecture: public vs private subnets
  • Internet Gateway cho public access, NAT Gateway cho outbound-only
  • Route Tables điều hướng traffic

💻 Lab 2: EC2 & Auto Scaling

Mục tiêu: Deploy web server với Auto Scaling Group + ALB.

Commands

# 1. Security Group
WEB_SG=$(aws ec2 create-security-group --group-name lab-web-sg \
  --description "Web SG" --vpc-id $VPC_ID --query 'GroupId' --output text)
aws ec2 authorize-security-group-ingress --group-id $WEB_SG --protocol tcp --port 80 --cidr 0.0.0.0/0

# 2. Launch Template
cat > user-data.sh << 'EOF'
#!/bin/bash
yum update -y && yum install -y httpd
systemctl start httpd && systemctl enable httpd
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
echo "<h1>Instance: $INSTANCE_ID | AZ: $AZ</h1>" > /var/www/html/index.html
EOF

aws ec2 create-launch-template --launch-template-name lab-template \
  --launch-template-data "{
    \"ImageId\":\"ami-0c55b159cbfafe1f0\",
    \"InstanceType\":\"t3.micro\",
    \"SecurityGroupIds\":[\"$WEB_SG\"],
    \"UserData\":\"$(base64 -i user-data.sh)\"
  }"

# 3. Auto Scaling Group (Min 2, Max 4)
aws autoscaling create-auto-scaling-group --auto-scaling-group-name lab-asg \
  --launch-template "LaunchTemplateName=lab-template,Version=\$Latest" \
  --min-size 2 --max-size 4 --desired-capacity 2 \
  --vpc-zone-identifier "$PRIV_1,$PRIV_2"

# 4. Target Tracking (scale khi CPU > 50%)
aws autoscaling put-scaling-policy --auto-scaling-group-name lab-asg \
  --policy-name cpu-tracking --policy-type TargetTrackingScaling \
  --target-tracking-configuration '{
    "TargetValue": 50.0,
    "PredefinedMetricSpecification": {"PredefinedMetricType":"ASGAverageCPUUtilization"}
  }'

# 5. Check instances
aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names lab-asg \
  --query 'AutoScalingGroups[0].Instances[*].[InstanceId,AvailabilityZone,HealthStatus]' \
  --output table

📝 Học được gì?

  • Launch Template = template cho EC2 instances
  • ASG tự heal (replace unhealthy) + scale theo policy
  • Target tracking = đơn giản nhất (set target, AWS lo phần còn lại)

📦 Lab 3: S3 — Versioning, Lifecycle, Encryption

Mục tiêu: Tạo S3 bucket với versioning, lifecycle policies, server-side encryption.

BUCKET="lab-s3-$(date +%s)"

# 1. Create + Enable versioning + encryption
aws s3api create-bucket --bucket $BUCKET --region us-east-1
aws s3api put-bucket-versioning --bucket $BUCKET --versioning-configuration Status=Enabled
aws s3api put-bucket-encryption --bucket $BUCKET --server-side-encryption-configuration '{
  "Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]
}'

# 2. Lifecycle policy (Standard → IA → Glacier)
aws s3api put-bucket-lifecycle-configuration --bucket $BUCKET --lifecycle-configuration '{
  "Rules":[{
    "Id":"auto-tier","Status":"Enabled",
    "Transitions":[
      {"Days":30,"StorageClass":"STANDARD_IA"},
      {"Days":90,"StorageClass":"GLACIER"}
    ],
    "Expiration":{"Days":365}
  }]
}'

# 3. Test versioning
echo "v1" > test.txt && aws s3 cp test.txt s3://$BUCKET/
echo "v2" > test.txt && aws s3 cp test.txt s3://$BUCKET/
aws s3api list-object-versions --bucket $BUCKET --prefix test.txt \
  --query 'Versions[*].[VersionId,LastModified,IsLatest]' --output table

# Cleanup
aws s3 rm s3://$BUCKET --recursive
aws s3api delete-objects --bucket $BUCKET --delete \
  "$(aws s3api list-object-versions --bucket $BUCKET --query '{Objects: Versions[].{Key:Key,VersionId:VersionId}}')"
aws s3api delete-bucket --bucket $BUCKET

📝 Học được gì?

  • Versioning protect against accidental deletion
  • Lifecycle auto-tier objects → cost optimization
  • SSE encryption at rest

🗄️ Lab 4: RDS Multi-AZ

Mục tiêu: Tạo RDS MySQL Multi-AZ, test failover.

# 1. DB Subnet Group
aws rds create-db-subnet-group --db-subnet-group-name lab-db-subnets \
  --db-subnet-group-description "Lab DB subnets" \
  --subnet-ids "$PRIV_1" "$PRIV_2"

# 2. Security Group for DB
DB_SG=$(aws ec2 create-security-group --group-name lab-db-sg \
  --description "DB SG" --vpc-id $VPC_ID --query 'GroupId' --output text)
aws ec2 authorize-security-group-ingress --group-id $DB_SG \
  --protocol tcp --port 3306 --source-group $WEB_SG

# 3. Create RDS Multi-AZ
aws rds create-db-instance --db-instance-identifier lab-db \
  --engine mysql --engine-version 8.0 --db-instance-class db.t3.micro \
  --allocated-storage 20 --master-username admin --master-user-password Lab12345! \
  --db-subnet-group-name lab-db-subnets --vpc-security-group-ids $DB_SG \
  --multi-az --no-publicly-accessible

# 4. Wait + verify
aws rds wait db-instance-available --db-instance-identifier lab-db
aws rds describe-db-instances --db-instance-identifier lab-db \
  --query 'DBInstances[0].[Endpoint.Address,MultiAZ,AvailabilityZone]' --output table

# Cleanup
aws rds delete-db-instance --db-instance-identifier lab-db \
  --skip-final-snapshot --delete-automated-backups

📝 Học được gì?

  • Multi-AZ = synchronous replication + auto failover
  • DB phải ở private subnet
  • Security Group chaining: Web SG → DB SG

🔐 Lab 5: IAM Roles & Policies

Mục tiêu: Tạo IAM role cho EC2 access S3 (best practice, không dùng access keys).

# 1. Trust policy (allow EC2 assume role)
cat > trust.json << 'EOF'
{"Version":"2012-10-17","Statement":[{
  "Effect":"Allow","Principal":{"Service":"ec2.amazonaws.com"},
  "Action":"sts:AssumeRole"
}]}
EOF

aws iam create-role --role-name Lab-EC2-S3-Role --assume-role-policy-document file://trust.json

# 2. Attach S3 read-only policy
aws iam attach-role-policy --role-name Lab-EC2-S3-Role \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess

# 3. Create instance profile (connect role to EC2)
aws iam create-instance-profile --instance-profile-name Lab-EC2-S3-Profile
aws iam add-role-to-instance-profile --instance-profile-name Lab-EC2-S3-Profile \
  --role-name Lab-EC2-S3-Role

# Cleanup
aws iam remove-role-from-instance-profile --instance-profile-name Lab-EC2-S3-Profile \
  --role-name Lab-EC2-S3-Role
aws iam delete-instance-profile --instance-profile-name Lab-EC2-S3-Profile
aws iam detach-role-policy --role-name Lab-EC2-S3-Role \
  --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
aws iam delete-role --role-name Lab-EC2-S3-Role

📝 Học được gì?

  • IAM Roles = best practice (KHÔNG dùng access keys trên EC2)
  • Trust policy = ai được assume role
  • Instance Profile = cầu nối Role → EC2

⚡ Lab 6: Lambda Function

Mục tiêu: Tạo Lambda function xử lý S3 event (trigger khi upload file).

# 1. Create Lambda execution role
cat > lambda-trust.json << 'EOF'
{"Version":"2012-10-17","Statement":[{
  "Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},
  "Action":"sts:AssumeRole"
}]}
EOF
aws iam create-role --role-name Lab-Lambda-Role --assume-role-policy-document file://lambda-trust.json
aws iam attach-role-policy --role-name Lab-Lambda-Role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

ROLE_ARN=$(aws iam get-role --role-name Lab-Lambda-Role --query 'Role.Arn' --output text)

# 2. Create function code
cat > index.js << 'EOF'
exports.handler = async (event) => {
    console.log('S3 Event:', JSON.stringify(event, null, 2));
    const bucket = event.Records[0].s3.bucket.name;
    const key = event.Records[0].s3.object.key;
    console.log(`New file uploaded: s3://${bucket}/${key}`);
    return { statusCode: 200, body: `Processed: ${key}` };
};
EOF
zip function.zip index.js

# 3. Create Lambda
sleep 10  # Wait for role propagation
aws lambda create-function --function-name lab-s3-processor \
  --runtime nodejs18.x --handler index.handler \
  --role $ROLE_ARN --zip-file fileb://function.zip

# 4. Test invoke
aws lambda invoke --function-name lab-s3-processor \
  --payload '{"Records":[{"s3":{"bucket":{"name":"test"},"object":{"key":"test.txt"}}}]}' \
  output.json && cat output.json

# Cleanup
aws lambda delete-function --function-name lab-s3-processor
aws iam detach-role-policy --role-name Lab-Lambda-Role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
aws iam delete-role --role-name Lab-Lambda-Role

📝 Học được gì?

  • Lambda = event-driven, serverless
  • Cần IAM role cho Lambda (execution role)
  • S3 events → Lambda trigger = common pattern

📊 Lab 7: CloudWatch Alarm

Mục tiêu: Tạo CloudWatch alarm cho EC2 CPU utilization.

# Create alarm: notify khi CPU > 80% trong 5 phút
aws cloudwatch put-metric-alarm --alarm-name lab-cpu-alarm \
  --metric-name CPUUtilization --namespace AWS/EC2 \
  --statistic Average --period 300 --evaluation-periods 1 \
  --threshold 80 --comparison-operator GreaterThanThreshold \
  --alarm-description "CPU > 80% for 5 minutes" \
  --dimensions "Name=InstanceId,Value=YOUR_INSTANCE_ID"

# List alarms
aws cloudwatch describe-alarms --alarm-names lab-cpu-alarm \
  --query 'MetricAlarms[0].[AlarmName,StateValue,Threshold]' --output table

# Cleanup
aws cloudwatch delete-alarms --alarm-names lab-cpu-alarm

📝 Học được gì?

  • CloudWatch Alarms monitor metrics → trigger actions
  • Period = khoảng thời gian check, Evaluation Periods = bao nhiêu lần vượt threshold

⚠️ Lưu Ý Quan Trọng

  1. Luôn cleanup sau mỗi lab → tránh phát sinh chi phí AWS
  2. NAT Gateway có phí (~$0.045/giờ) → xoá ngay khi xong lab
  3. Free Tier: t3.micro (750h/tháng), S3 (5GB), Lambda (1M requests), RDS (750h)
  4. Kiểm tra billing sau mỗi lab: AWS Console → Billing Dashboard

Exam Tips 💡

  1. Labs giúp hiểu cách components kết nối → dễ trả lời câu hỏi architecture.
  2. Security Group chaining (Web SG → DB SG) là pattern phổ biến trong thi.
  3. IAM Role for EC2 (KHÔNG access keys) = đáp án cho mọi câu "secure access".
  4. VPC architecture (public + private + NAT) xuất hiện trong ~30% câu hỏi.

⬅️ Chương 22: Practice Exam | 🏠 Về Mục Lục