Application Load Balancer로 요청 라우팅하기

AWSBeginner
지금 연습하기

소개

팀에는 애플리케이션 서버가 두 대 있지만 클라이언트에는 서비스의 공통 주소가 필요합니다. 이 실습에서는 Application Load Balancer를 만들고 리스너를 대상 그룹에 연결한 뒤 두 서버를 등록합니다. 실제 HTTP 응답으로 각 요청을 처리한 서버를 확인합니다.

앞선 강좌의 EC2 인스턴스, VPC 서브넷, 보안 그룹을 이해하고 있어야 합니다. 환경에는 네트워크와 실행 중인 서버가 준비되어 있고 AWS CLI도 설정되어 있습니다. 로드 밸런싱 리소스를 직접 만들고 마지막에 삭제합니다.

자격증 시험 연계

이 실습은 다음 시험 주제의 입문 실습을 제공합니다.

Application Load Balancer 만들기

이 단계에서는 준비된 서버 두 대의 공통 진입점을 만듭니다.

**Application Load Balancer(ALB)**는 HTTP 또는 HTTPS 요청을 애플리케이션 서버로 분산합니다. **Network Load Balancer(NLB)**는 TCP와 UDP 같은 전송 계층 연결에 중점을 둡니다. 이 HTTP 애플리케이션에는 ALB가 적합하므로 실습 전체에서 ALB를 사용합니다.

준비된 작업 디렉터리로 이동합니다.

cd /home/labex/project

launch.env에는 제공된 네트워크의 식별자가 들어 있습니다. 내용을 읽고 source로 변수 할당을 현재 셸에 불러옵니다.

cat launch.env
source launch.env

SUBNET_ID와 SECOND_SUBNET_ID는 서로 다른 가용 영역의 서브넷입니다. ALB에는 이러한 서브넷이 최소 두 개 필요합니다. ALB_SECURITY_GROUP_ID는 포트 80의 HTTP 리스너 트래픽을 허용하는 준비된 보안 그룹입니다. 서버에는 애플리케이션 포트를 위한 별도 그룹이 있습니다.

application-alb라는 인터넷 연결 애플리케이션 로드 밸런서를 만듭니다. 인터넷 연결은 주소 지정 방식을 뜻합니다. --query는 ARN만 선택하고 --output text는 재사용하기 쉬운 형식으로 출력합니다. 셸의 $(...) 구문은 출력을 LB_ARN에 저장합니다.

LB_ARN=$(aws elbv2 \
  create-load-balancer \
  --name application-alb \
  --type application \
  --scheme internet-facing \
  --subnets "$SUBNET_ID" "$SECOND_SUBNET_ID" \
  --security-groups "$ALB_SECURITY_GROUP_ID" \
  --query 'LoadBalancers[0].LoadBalancerArn' \
  --output text)

**Amazon 리소스 이름(ARN)**은 AWS 리소스를 식별합니다. 저장한 ARN으로 로드 밸런서를 확인합니다.

aws elbv2 \
  describe-load-balancers \
  --load-balancer-arns "$LB_ARN" \
  --query 'LoadBalancers[].{Name:LoadBalancerName,Type:Type,Scheme:Scheme,DNS:DNSName}'

이름 application-alb, 유형 application, 방식 internet-facing을 찾습니다. DNS 이름은 클라이언트가 사용할 주소이며 환경마다 다릅니다. 로드 밸런서만 만들면 서버는 아직 연결되지 않습니다.

Terminal 옆의 AWS View를 클릭합니다. 페이지는 CLI와 같은 리소스 상태를 읽으며 application-alb를 표시합니다. 아직 연결하지 않았으므로 대상 그룹과 대상 영역은 비어 있습니다.

대상 그룹 연결 전 생성된 ALB

이미지는 생성 확인 지점을 보여 줍니다. 리소스 이름은 실습과 같고 DNS 이름은 예시입니다.

리스너를 대상 그룹에 연결하기

이 단계에서는 들어오는 HTTP 요청을 어디로 보낼지 정의합니다.

대상 그룹은 애플리케이션을 제공할 서버를 포함합니다. 그룹의 포트는 서버에 접속할 때 사용합니다. 리스너는 로드 밸런서에서 클라이언트 연결을 받고 동작으로 전달 위치를 결정합니다. 여기서 클라이언트는 포트 80을 사용하고 서버 애플리케이션은 8081에서 실행됩니다.

제공된 VPC에 인스턴스 유형 대상 그룹을 만듭니다. --target-type instance는 EC2 인스턴스 ID를 등록한다는 뜻입니다. /health는 애플리케이션의 준비 상태 엔드포인트입니다.

TG_ARN=$(aws elbv2 \
  create-target-group \
  --name application-targets \
  --protocol HTTP \
  --port 8081 \
  --vpc-id "$VPC_ID" \
  --target-type instance \
  --health-check-path /health \
  --query 'TargetGroups[0].TargetGroupArn' \
  --output text)

HTTP 리스너를 만듭니다. 기본 동작은 요청을 TG_ARN으로 전달합니다. CLI 축약 구문 Type=forward,TargetGroupArn=...으로 동작을 지정합니다.

LISTENER_ARN=$(aws elbv2 \
  create-listener \
  --load-balancer-arn "$LB_ARN" \
  --protocol HTTP \
  --port 80 \
  --default-actions "Type=forward,TargetGroupArn=$TG_ARN" \
  --query 'Listeners[0].ListenerArn' \
  --output text)

생성된 리스너를 확인합니다.

aws elbv2 \
  describe-listeners \
  --load-balancer-arn "$LB_ARN" \
  --query 'Listeners[].{Protocol:Protocol,Port:Port,Actions:DefaultActions}'

출력에는 HTTP 포트 80과 대상 그룹을 가리키는 전달 동작이 있어야 합니다. AWS View에서는 ALB와 서버 영역 사이에 그룹이 나타납니다. 등록된 대상이 없으므로 서버를 연결해야 합니다.

두 서버 등록 및 테스트하기

이 단계에서는 실행 중인 두 서버를 등록하고 실제 요청이 양쪽에 도달하는 것을 관찰합니다.

준비된 서버를 나열합니다. 이름 필터는 태그를 선택하고 쿼리는 연결에 유용한 필드를 보여 줍니다.

aws ec2 \
  describe-instances \
  --filters 'Name=tag:Name,Values=app-a,app-b' \
  --query 'Reservations[].Instances[].{Instance:InstanceId,Name:Tags[?Key==`Name`].Value|[0],State:State.Name,PrivateIP:PrivateIpAddress}' \
  --output table

두 인스턴스 모두 running이어야 합니다. 목록 순서에 의존하지 않도록 이름별로 ID를 따로 가져옵니다.

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)

대상을 등록하면 이 ID들이 대상 그룹에 연결됩니다. Port를 명시적으로 재정의하지 않으면 각 대상은 그룹의 애플리케이션 포트 8081을 사용합니다.

aws elbv2 \
  register-targets \
  --target-group-arn "$TG_ARN" \
  --targets "Id=$APP_A" "Id=$APP_B"

ALB는 /health를 검사해 가용성을 판단합니다. 대상이 정상으로 표시될 때까지 기다립니다. CLI waiter는 조건이 충족되거나 시간이 초과될 때까지 읽기 전용 상태 쿼리를 반복합니다.

aws elbv2 \
  wait target-in-service \
  --target-group-arn "$TG_ARN"

등록된 대상의 ID, 포트, 상태를 확인합니다.

aws elbv2 \
  describe-target-health \
  --target-group-arn "$TG_ARN" \
  --query 'TargetHealthDescriptions[].{Instance:Target.Id,Port:Target.Port,Health:TargetHealth.State}' \
  --output table

두 대상에 포트 8081과 상태 healthy가 표시되어야 합니다. 로드 밸런서의 DNS 이름을 저장합니다.

LB_DNS=$(aws elbv2 \
  describe-load-balancers \
  --load-balancer-arns "$LB_ARN" \
  --query 'LoadBalancers[0].DNSName' \
  --output text)

HTTP 클라이언트 curl로 상태 엔드포인트를 요청합니다. --config client.conf는 제공된 연결 설정을 읽고, -sS는 진행 표시를 숨기면서 오류는 표시합니다.

curl --config client.conf -sS "http://$LB_DNS/health"

성공한 응답은 다음 예시와 비슷합니다. 인스턴스 ID는 예시 값입니다.

{"service":"Report server","message":"Application ready","instance_id":"i-..."}

별도의 요청을 여섯 번 보냅니다. for 루프는 요청을 반복하고 echo는 응답마다 줄을 바꿉니다.

for request in 1 2 3 4 5 6; do
  curl --config client.conf -sS "http://$LB_DNS/health"
  echo
done

응답에서 APP_A와 APP_B의 값을 찾습니다. 이는 두 서버에 분산됨을 증명하며 요청 횟수로 성능이나 용량을 추론해서는 안 됩니다. 기본 알고리즘은 라운드 로빈입니다. 운영 환경에서는 연결과 다른 설정도 분산에 영향을 줍니다.

AWS View를 열어 두 대상이 정상인지 확인합니다. Send request를 여러 번 클릭하고 응답의 instance_id를 읽습니다. 요청은 리스너를 통해 애플리케이션으로 갑니다. 카드는 리소스 상태를 보여 주고 응답은 실제 처리한 서버를 식별합니다.

정상 대상 두 개와 실제 애플리케이션 응답

예시는 정상 대상 두 개와 성공한 요청을 보여 줍니다. 자신의 ID는 다르므로 응답을 자신의 대상 ID와 비교합니다.

로드 밸런싱 리소스 삭제하기

이 단계에서는 자신이 만든 리소스를 삭제하고 제공된 서버와 네트워크는 보존합니다.

리스너를 먼저 삭제해 대상 그룹에 대한 전달 의존성을 제거합니다.

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 서버는 계속 실행됩니다. 실습 준비 리소스이므로 직접 정리할 대상이 아닙니다.

요약

Application Load Balancer를 만들고 HTTP 리스너를 대상 그룹에 연결한 뒤 EC2 서버 두 대를 등록했습니다. 대상 상태를 확인하고 실제 응답으로 두 서버를 식별했습니다. 마지막으로 로드 밸런싱 리소스를 삭제하고 준비된 서버와 네트워크를 보존했습니다.

다음 실습에서는 상태 검사가 애플리케이션 장애를 발견하고 정상 대상으로 서비스를 유지하며 복구된 대상을 다시 참여시키는 과정을 살펴봅니다.