Files
kodekloud-engineer/aws-44.md

190 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Task 44
The DevOps team is tasked with setting up a highly available web application using AWS. To achieve this, they plan to use an Auto Scaling Group (ASG) to ensure that the required number of EC2 instances are always running, and an Application Load Balancer (ALB) to distribute traffic across these instances. The goal of this task is to set up an ASG that automatically scales EC2 instances based on CPU utilization, and an ALB that directs incoming traffic to the instances. The EC2 instances should have Nginx installed and running to serve web traffic.
Create an EC2 launch template named xfusion-launch-template that specifies the configuration for the EC2 instances, including the Amazon Linux 2 AMI, t2.micro instance type, and a security group that allows HTTP traffic on port 80.
Add a User Data script to the launch template to install Nginx on the EC2 instances when they are launched. The script should install Nginx, start the Nginx service, and enable it to start on boot.
Create an Auto Scaling Group named xfusion-asg that uses the launch template and ensures a minimum of 1 instance, desired capacity is 1 instance and a maximum of 2 instances are running based on CPU utilization. Set the target CPU utilization to 50%.
Create a target group named xfusion-tg, an Application Load Balancer named xfusion-alb and configure it to listen on port 80. Ensure the ALB is associated with the Auto Scaling Group and distributes traffic across the instances.
Configure health checks on the ALB to ensure it routes traffic only to healthy instances.
Verify that the ALB's DNS name is accessible and that it displays the default Nginx page served by the EC2 instances.
### Solution
# ASG + ALB Auto-Scaling Task (xfusion)
The full auto-scaling stack — launch template → ASG with target-tracking → ALB/TG wired to the ASG. The concept that ties it together: you **don't register instances into the target group manually**; you attach the TG to the ASG (`--target-group-arns`), and the ASG auto-registers every instance it launches (and deregisters on scale-in). And scaling "based on CPU at 50%" = a **target-tracking scaling policy** on the `ASGAverageCPUUtilization` metric — not a manual CloudWatch alarm.
## Version note — Amazon Linux 2 is EOL
The task says "Amazon Linux 2 AMI," but **AL2 hit end-of-life June 30, 2026** (already past). The AMIs are still *launchable* (they don't vanish at EOL, they just stop getting patches), so if the grader specifically checks for AL2, use the AL2 alias below. But for anything real you'd use AL2023. Both aliases given — pick per what the grader wants. On AL2 the Nginx install uses `amazon-linux-extras`; on AL2023 it's `dnf`. The user-data below is written for **AL2** to match the task literally.
Run on `aws-client`.
## Phase 1 — Security group (HTTP 80)
```bash
REGION=us-east-1
VPC_ID=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true \
--region $REGION --query 'Vpcs[0].VpcId' --output text)
SG_ID=$(aws ec2 create-security-group \
--group-name xfusion-web-sg \
--description "HTTP 80 for xfusion ASG" \
--vpc-id $VPC_ID --region $REGION \
--query 'GroupId' --output text)
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID --protocol tcp --port 80 --cidr 0.0.0.0/0 --region $REGION
```
## Phase 2 — Launch template (with base64 user-data)
```bash
# AL2 AMI via SSM alias (EOL but still launchable). AL2023 alternative commented.
AMI_ID=$(aws ssm get-parameters \
--names /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 \
--region $REGION --query 'Parameters[0].Value' --output text)
# AL2023: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64
# User-data for AL2 (amazon-linux-extras) — must be base64 for launch templates
USERDATA=$(base64 -w0 << 'EOF'
#!/bin/bash
amazon-linux-extras install nginx1 -y
systemctl start nginx
systemctl enable nginx
EOF
)
# (AL2023 user-data would be: dnf install -y nginx; systemctl enable --now nginx)
aws ec2 create-launch-template \
--launch-template-name xfusion-launch-template \
--region $REGION \
--launch-template-data "{
\"ImageId\": \"$AMI_ID\",
\"InstanceType\": \"t2.micro\",
\"SecurityGroupIds\": [\"$SG_ID\"],
\"UserData\": \"$USERDATA\"
}"
```
**Key point:** in a launch template, `UserData` must be **base64-encoded** (unlike `run-instances --user-data file://` which encodes for you). `base64 -w0` produces a single line with no wrapping — critical, since embedded newlines break the JSON.
## Phase 3 — Target group + ALB + listener
```bash
read -r SUBNET1 SUBNET2 <<< $(aws ec2 describe-subnets \
--filters "Name=vpc-id,Values=$VPC_ID" "Name=map-public-ip-on-launch,Values=true" \
--region $REGION --query 'Subnets[].SubnetId | [0:2]' --output text)
# Target group with explicit health check on "/"
TG_ARN=$(aws elbv2 create-target-group \
--name xfusion-tg \
--protocol HTTP --port 80 \
--vpc-id $VPC_ID --target-type instance \
--health-check-protocol HTTP \
--health-check-path / \
--health-check-interval-seconds 30 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 2 \
--region $REGION \
--query 'TargetGroups[0].TargetGroupArn' --output text)
# ALB across two AZs, wearing the web SG
ALB_ARN=$(aws elbv2 create-load-balancer \
--name xfusion-alb \
--subnets $SUBNET1 $SUBNET2 \
--security-groups $SG_ID \
--scheme internet-facing --type application \
--region $REGION \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
# Listener on 80 -> forward to TG
aws elbv2 create-listener \
--load-balancer-arn $ALB_ARN \
--protocol HTTP --port 80 \
--default-actions Type=forward,TargetGroupArn=$TG_ARN \
--region $REGION
```
The **health check** (task req #5) is configured on the target group: `HTTP GET /` every 30s, healthy after 2 passes, unhealthy after 2 fails. The ALB only routes to targets the TG reports healthy — that's the "route traffic only to healthy instances" requirement.
## Phase 4 — Auto Scaling Group (attached to the TG)
```bash
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name xfusion-asg \
--launch-template "LaunchTemplateName=xfusion-launch-template,Version=\$Latest" \
--min-size 1 --max-size 2 --desired-capacity 1 \
--vpc-zone-identifier "$SUBNET1,$SUBNET2" \
--target-group-arns $TG_ARN \
--health-check-type ELB \
--health-check-grace-period 90 \
--region $REGION
```
The two crucial bits here:
- **`--target-group-arns $TG_ARN`** is what "associates the ALB with the ASG." The ASG now auto-registers each instance it launches into the TG (and deregisters on scale-in). You never call `register-targets` manually — that's the whole point of ASG+ALB integration.
- **`--health-check-type ELB`** makes the ASG use the *load balancer's* health check to decide instance health, not just the EC2 status check. So an instance whose Nginx died gets replaced even though the VM is "running." `--health-check-grace-period 90` gives user-data time to install Nginx before health checks start counting (otherwise the ASG kills the instance mid-bootstrap).
## Phase 5 — Target-tracking scaling policy (CPU 50%)
```bash
cat > /tmp/tt-config.json << 'EOF'
{
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"TargetValue": 50.0
}
EOF
aws autoscaling put-scaling-policy \
--auto-scaling-group-name xfusion-asg \
--policy-name xfusion-cpu-target-tracking \
--policy-type TargetTrackingScaling \
--target-tracking-configuration file:///tmp/tt-config.json \
--region $REGION
```
**Target tracking** is the modern way to do "scale based on CPU at 50%." It auto-creates the CloudWatch alarms behind the scenes and adjusts capacity to keep average CPU near 50% — scale out above, scale in below, within the min/max (12) bounds. Far cleaner than manually wiring step-scaling policies to CloudWatch alarms (the old way).
## Verify
```bash
# ASG has the instance + correct sizing
aws autoscaling describe-auto-scaling-groups \
--auto-scaling-group-names xfusion-asg --region $REGION \
--query 'AutoScalingGroups[0].{Min:MinSize,Max:MaxSize,Desired:DesiredCapacity,
Instances:Instances[].{Id:InstanceId,Health:HealthStatus,State:LifecycleState},
TGs:TargetGroupARNs}'
# Poll target health until healthy
for n in $(seq 1 12); do
H=$(aws elbv2 describe-target-health --target-group-arn $TG_ARN --region $REGION \
--query 'TargetHealthDescriptions[0].TargetHealth.State' --output text)
echo "target: $H"; [ "$H" = "healthy" ] && break; sleep 20
done
# Hit the ALB DNS — should return the Nginx page
ALB_DNS=$(aws elbv2 describe-load-balancers --load-balancer-arns $ALB_ARN \
--region $REGION --query 'LoadBalancers[0].DNSName' --output text)
echo "http://$ALB_DNS"
curl -I "http://$ALB_DNS"
```
Want: ASG showing min1/max2/desired1 with one instance `Healthy`/`InService`, the TG target reaching `healthy`, and `curl -I` on the ALB DNS returning `HTTP/1.1 200 OK` with an `nginx` Server header. That 200 through the ALB DNS is the task's success proof — the whole chain (ASG launches instance → user-data installs Nginx → TG health check passes → ALB routes to it) works.
## Timing & debug
Two stacked waits: user-data installing Nginx (~60-90s) plus the TG health-check threshold (2 × 30s). Even after the ALB is `active`, give it ~3 min for the target to go healthy. If it stays `unhealthy`:
1. **`Target.Timeout`** → SG not allowing 80 from the ALB, or Nginx not up. Since ALB and instances share `xfusion-web-sg` here and it allows 80 from `0.0.0.0/0`, the ALB can reach the instance; if still failing, Nginx didn't install — check `/var/log/cloud-init-output.log`.
2. **`Target.ResponseCodeMismatch`** → Nginx serving something other than 200 on `/`, or user-data failed (on AL2023, `amazon-linux-extras` doesn't exist — wrong user-data for the AMI).
3. **ASG launches then immediately terminates instances** → health-check grace period too short; the ELB health check failed a bootstrapping instance. Raise `--health-check-grace-period`.
## Note on launch template vs launch configuration
This uses a **launch template**, not the deprecated launch configuration. AWS ended new-account access to launch configurations; templates are the current standard and support versioning (`Version=$Latest`), mixed instances, and newer features. Always reach for `create-launch-template`.