소개
애플리케이션에는 Application Load Balancer 뒤에 서버가 두 대 있습니다. EC2 인스턴스가 실행 중이어도 애플리케이션은 장애가 날 수 있습니다. 이 실습에서는 상태 검사를 설정하고 실제 장애를 만든 뒤 정상 서버가 계속 요청을 처리하는지 확인합니다. 이후 장애 애플리케이션을 복구하고 로드 밸런싱 리소스를 삭제합니다.
ALB 리스너, 대상 그룹, EC2 SSH 연결을 알고 있어야 합니다. 이 새 환경은 네트워크, 실행 중인 두 서버, 대상 등록, ALB 리스너를 제공합니다. 이전 실습에 의존하지 않으며 AWS CLI와 연결 파일도 준비되어 있습니다.
자격증 시험 연계
이 실습은 다음 시험 주제의 입문 실습을 제공합니다.
- Solutions Architect – Associate (SAA-C03) · 과제 2.2: 애플리케이션 상태와 로드 밸런싱이 서버 장애 시 가용성을 지원하는 방식 이해.
- CloudOps Engineer – Associate (SOA-C03) · 과제 2.2: ELB 상태 검사 설정 및 비정상 대상 진단.
대상 상태 검사 설정하기
제공된 서비스를 살펴보고 ALB가 애플리케이션 준비 상태를 확인하는 방식을 설정합니다.
준비된 디렉터리로 이동하고 네트워크 변수를 불러옵니다.
cd /home/labex/project
source launch.env
제공된 로드 밸런서와 그룹을 이름으로 찾습니다. 이후 명령을 위해 ARN을, HTTP 요청을 위해 DNS 이름을 저장합니다.
LB_ARN=$(aws elbv2 \
describe-load-balancers \
--names application-alb \
--query 'LoadBalancers[0].LoadBalancerArn' \
--output text)
TG_ARN=$(aws elbv2 \
describe-target-groups \
--names application-targets \
--query 'TargetGroups[0].TargetGroupArn' \
--output text)
LB_DNS=$(aws elbv2 \
describe-load-balancers \
--load-balancer-arns "$LB_ARN" \
--query 'LoadBalancers[0].DNSName' \
--output text)
상태 검사는 클라이언트 요청과 독립적으로 각 대상을 확인합니다. 경로는 애플리케이션이 트래픽을 처리할 수 있는지 알려 줘야 합니다. 현재 설정을 확인합니다.
aws elbv2 \
describe-target-groups \
--target-group-arns "$TG_ARN" \
--query 'TargetGroups[].{Path:HealthCheckPath,Interval:HealthCheckIntervalSeconds,Timeout:HealthCheckTimeoutSeconds,Healthy:HealthyThresholdCount,Unhealthy:UnhealthyThresholdCount,Matcher:Matcher}'
제공 그룹은 30초마다 /health를 확인합니다. 작은 실습을 위해 간격을 5초, 제한 시간을 2초로 설정합니다. 연속 실패 2회에 대상을 제외하고 연속 성공 2회에 다시 포함합니다. matcher는 HTTP 200을 성공으로 판단합니다.
aws elbv2 \
modify-target-group \
--target-group-arn "$TG_ARN" \
--health-check-protocol HTTP \
--health-check-path /health \
--health-check-interval-seconds 5 \
--health-check-timeout-seconds 2 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 2 \
--matcher HttpCode=200 \
--query 'TargetGroups[].{Path:HealthCheckPath,Interval:HealthCheckIntervalSeconds,Healthy:HealthyThresholdCount,Unhealthy:UnhealthyThresholdCount}'
간격은 검사 빈도를, 제한 시간은 각 검사 대기 시간을 결정합니다. 임계값은 일시적인 실패에 즉시 반응하지 않도록 합니다. 운영 값은 시작과 장애 특성을 반영해야 하며 여기서는 관찰하기 쉬운 짧은 값을 사용합니다.
두 대상이 정상이 될 때까지 기다리고 실제 상태를 확인합니다.
aws elbv2 \
wait target-in-service \
--target-group-arn "$TG_ARN"
aws elbv2 \
describe-target-health \
--target-group-arn "$TG_ARN" \
--query 'TargetHealthDescriptions[].{Instance:Target.Id,Port:Target.Port,Health:TargetHealth.State}' \
--output table
AWS View를 열고 두 healthy를 확인한 뒤 Send request를 클릭합니다. 실제 HTTP 200과 대상 상태가 초기 조건을 확인해 줍니다.
실제 애플리케이션 장애 관찰하기
EC2 인스턴스는 실행한 채 app-a의 상태 엔드포인트를 실패하게 만듭니다.
제공된 이름 태그로 ID를 가져오고 SSH 연결 주소도 가져옵니다.
APP_A=$(aws ec2 \
describe-instances \
--filters Name=tag:Name,Values=app-a \
--query 'Reservations[0].Instances[0].InstanceId' \
--output text)
APP_B=$(aws ec2 \
describe-instances \
--filters Name=tag:Name,Values=app-b \
--query 'Reservations[0].Instances[0].InstanceId' \
--output text)
APP_A_IP=$(aws ec2 \
describe-instances \
--instance-ids "$APP_A" \
--query 'Reservations[0].Instances[0].PublicIpAddress' \
--output text)
애플리케이션은 요청마다 /etc/report-app/config.json을 읽습니다. healthy는 /health가 200 또는 503을 반환할지 결정합니다. 제공된 키와 설정으로 SSH를 사용합니다. jq는 이 필드만 변경하고, 임시 파일은 읽는 중 덮어쓰기를 막으며, install은 읽기 가능한 권한으로 파일을 교체합니다.
ssh -F ssh_config "ubuntu@$APP_A_IP" \
'sudo jq ".healthy = false" /etc/report-app/config.json > /tmp/report-config.json && sudo install -m 644 /tmp/report-config.json /etc/report-app/config.json'
서버 내부에서 직접 확인합니다. -o /dev/null은 본문을 버리고 -w는 HTTP 상태를 표시합니다.
ssh -F ssh_config "ubuntu@$APP_A_IP" \
'curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/health'
503을 예상합니다. 이는 애플리케이션 장애이며 EC2 중지가 아닙니다. 두 검사 시간을 기다리고 원인을 확인합니다.
sleep 12
aws elbv2 \
describe-target-health \
--target-group-arn "$TG_ARN" \
--query 'TargetHealthDescriptions[].{Instance:Target.Id,Health:TargetHealth.State,Reason:TargetHealth.Reason}' \
--output table
503이 200과 일치하지 않으므로 APP_A는 unhealthy, 이유는 Target.ResponseCodeMismatch여야 합니다. APP_B는 healthy를 유지합니다. 검사는 비동기이므로 전환 중이면 잠시 기다린 뒤 다시 조회합니다.
EC2 인스턴스가 계속 실행되는지 확인합니다.
aws ec2 \
describe-instances \
--instance-ids "$APP_A" "$APP_B" \
--query 'Reservations[].Instances[].{Instance:InstanceId,State:State.Name}' \
--output table
ALB를 통해 별도의 요청을 여섯 번 보냅니다.
for request in 1 2 3 4 5 6; do
curl --config client.conf -sS "http://$LB_DNS/health"
echo
done
모든 응답이 APP_B를 식별해야 합니다. AWS View에서 비정상 대상 하나와 정상 대상 하나를 확인합니다. Send request를 여러 번 눌러 정상 서버의 응답을 확인합니다. 정상 대상이 있으면 ALB는 장애 대상을 제외합니다.

예시는 한 대상이 Target.ResponseCodeMismatch를 보고하고 정상 대상이 실제 HTTP 200을 반환하는 모습입니다. 자신의 ID는 다르므로 응답을 자신의 정상 대상과 비교합니다.
모든 대상이 비정상이면 ALB는 fail open으로 비정상 대상에도 전달할 수 있습니다. app-b를 정상으로 유지하는 것이 중요합니다. 모두 비정상이라는 상태가 ALB의 모든 전달 중단을 증명하지는 않습니다.
대상을 복구하고 다시 포함하기
app-a를 복구하고 상태 검사 성공 후 요청 처리에 다시 참여하는지 관찰합니다.
같은 안전한 파일 교체 방식으로 healthy만 복구합니다.
ssh -F ssh_config "ubuntu@$APP_A_IP" \
'sudo jq ".healthy = true" /etc/report-app/config.json > /tmp/report-config.json && sudo install -m 644 /tmp/report-config.json /etc/report-app/config.json'
직접 엔드포인트가 이제 200을 반환하는지 확인합니다.
ssh -F ssh_config "ubuntu@$APP_A_IP" \
'curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8081/health'
애플리케이션은 복구되었지만 ALB는 설정한 연속 성공을 확인해야 합니다. 정상 상태를 기다리고 두 대상을 확인합니다.
aws elbv2 \
wait target-in-service \
--target-group-arn "$TG_ARN"
aws elbv2 \
describe-target-health \
--target-group-arn "$TG_ARN" \
--query 'TargetHealthDescriptions[].{Instance:Target.Id,Health:TargetHealth.State}' \
--output table
둘 다 healthy여야 합니다. 다시 요청을 여섯 번 보냅니다.
for request in 1 2 3 4 5 6; do
curl --config client.conf -sS "http://$LB_DNS/health"
echo
done
두 ID를 찾습니다. AWS View의 두 정상 카드와 Send request 응답을 확인합니다. 애플리케이션을 고치고 검사를 성공시킨 것이며 인스턴스를 교체하거나 등록 해제하지 않았습니다.
로드 밸런싱 리소스 삭제하기
복구된 서버와 네트워크는 보존하고 제공된 로드 밸런싱 설정을 삭제합니다.
리스너 ARN을 가져온 뒤 리스너, ALB, 대상 그룹 순서로 삭제합니다.
LISTENER_ARN=$(aws elbv2 \
describe-listeners \
--load-balancer-arn "$LB_ARN" \
--query 'Listeners[0].ListenerArn' \
--output text)
aws elbv2 \
delete-listener \
--listener-arn "$LISTENER_ARN"
aws elbv2 \
delete-load-balancer \
--load-balancer-arn "$LB_ARN"
aws elbv2 \
delete-target-group \
--target-group-arn "$TG_ARN"
두 리소스 목록이 비었는지 확인합니다.
aws elbv2 \
describe-load-balancers \
--query 'LoadBalancers[].LoadBalancerName'
aws elbv2 \
describe-target-groups \
--query 'TargetGroups[].TargetGroupName'
두 쿼리의 []와 AWS View의 빈 영역을 확인합니다. 제공된 EC2 서버와 네트워크는 남겨 둡니다.
요약
ALB 상태 검사를 설정하고 실제 장애를 만들어 EC2 중지와 구분했습니다. 정상 서버의 트래픽을 관찰하고 애플리케이션 복구 후 두 서버의 응답을 확인했습니다. 마지막으로 로드 밸런싱 리소스를 삭제하고 서버와 네트워크를 보존했습니다.
다음 실습에서는 기존 서버를 수동으로 고치는 대신 Auto Scaling 그룹으로 장애 인스턴스를 교체합니다.



