Auto Scaling Group 헬스 체크: EC2에서 ELB로 전환해야 할 때와 그 함정

ASG가 멀쩡히 응답하는 인스턴스를 계속 교체하고 있다면, 헬스 체크 타입 설정을 의심해야 한다. 'EC2' 타입은 인스턴스 OS가 살아있는지만 확인하고, 실제 애플리케이션이 HTTP 200을 반환하는지는 전혀 모른다 — 이 간극이 예상치 못한 인스턴스 교체 루프의 원인이 되는 경우가 많다.

TL;DR: Auto Scaling Group 헬스 체크 타입 비교

항목EC2 헬스 체크ELB 헬스 체크
확인 대상인스턴스 상태 (하이퍼바이저 레벨)애플리케이션 응답 (HTTP/TCP)
기본값예 (ASG 생성 시 기본)아니오 (명시적 활성화 필요)
Unhealthy 판정 조건stopped, terminated, stopping, shutting-downELB 타겟 그룹이 unhealthy로 표시
적합한 상황인스턴스 장애 복구만 필요한 경우앱 레벨 장애 자동 복구가 필요한 경우
오탐 위험낮음ELB 헬스 체크 설정에 따라 높을 수 있음

Auto Scaling Group 헬스 체크가 동작하는 방식

ASG는 주기적으로 각 인스턴스의 상태를 평가하고, unhealthy로 판정된 인스턴스를 종료한 뒤 새 인스턴스로 교체한다. 이 판정의 기준이 바로 헬스 체크 타입이다.

EC2 헬스 체크는 EC2 서비스 자체가 보고하는 인스턴스 상태(instance status)를 기반으로 한다. 인스턴스가 running 상태이고 시스템 상태 체크를 통과하면 healthy로 간주한다. 즉, Nginx가 죽어있어도, 앱 프로세스가 OOM으로 종료되어도 — OS가 살아있으면 ASG는 아무 조치를 취하지 않는다.

ELB 헬스 체크를 활성화하면 ASG는 연결된 로드 밸런서(ALB/NLB)의 타겟 그룹이 해당 인스턴스를 healthy로 표시하는지를 추가로 확인한다. ELB가 unhealthy로 표시하면 ASG도 그 인스턴스를 unhealthy로 처리하고 교체를 시작한다.

graph TD A["ASG 헬스 체크 평가"] --> B["EC2 인스턴스 상태 확인"] B --> C{"running 상태?"} C -- "No" --> D["Unhealthy 판정 즉시 교체"] C -- "Yes" --> E{"ELB 헬스 체크 활성화?"} E -- "No (EC2 타입)" --> F["Healthy 판정"] E -- "Yes (ELB 타입)" --> G{"Grace Period 경과?"} G -- "No" --> H["판정 보류"] G -- "Yes" --> I{"타겟 그룹 상태?"} I -- "healthy" --> F I -- "unhealthy" --> D D --> J["인스턴스 종료 및 교체"] F --> K["트래픽 유지"]
  1. EC2 상태 확인: ASG는 항상 EC2 인스턴스 상태를 먼저 확인한다. running이 아니면 즉시 unhealthy.
  2. ELB 타겟 상태 확인: ELB 헬스 체크가 활성화된 경우, 타겟 그룹의 상태를 추가로 확인한다.
  3. Grace Period: 인스턴스 시작 후 헬스 체크 유예 기간(Health Check Grace Period) 동안은 unhealthy 판정을 보류한다.
  4. 교체 결정: 두 체크 중 하나라도 unhealthy이면 ASG는 해당 인스턴스를 종료하고 새 인스턴스를 시작한다.

왜 인스턴스가 '멀쩡한데' 종료되는가 — 실제 원인 진단

ASG가 정상처럼 보이는 인스턴스를 계속 교체한다면, 먼저 어떤 헬스 체크가 unhealthy를 트리거했는지 확인해야 한다. 원인 없이 교체되는 경우는 거의 없다.

1단계: ASG 활동 기록에서 종료 이유 확인

ASG가 인스턴스를 왜 종료했는지는 활동 기록(Activity History)에 명시된다. 이것이 첫 번째 확인 지점이다 — 로그를 보기 전에 원인을 추측하는 것은 시간 낭비다.

aws autoscaling describe-scaling-activities \
  --auto-scaling-group-name my-asg \
  --region us-east-1 \
  --query 'Activities[?StatusCode==`Failed` || contains(Description, `Terminating`)].[ActivityId,Description,Cause,StatusMessage]' \
  --output table

Cause 필드에 an instance was found to be in poor health 또는 ELB가 포함되어 있는지 확인한다. ELB가 언급되면 타겟 그룹 설정으로 넘어가야 한다.

2단계: 현재 헬스 체크 타입 및 유예 기간 확인

현재 ASG에 어떤 헬스 체크가 설정되어 있는지 확인한다. 유예 기간이 너무 짧으면 앱이 완전히 기동하기 전에 unhealthy 판정이 내려진다 — 이것이 가장 흔한 '멀쩡한 인스턴스 교체' 원인이다.

aws autoscaling describe-auto-scaling-groups \
  --auto-scaling-group-names my-asg \
  --region us-east-1 \
  --query 'AutoScalingGroups[0].{HealthCheckType:HealthCheckType,GracePeriod:HealthCheckGracePeriod,LoadBalancerNames:LoadBalancerNames,TargetGroupARNs:TargetGroupARNs}' \
  --output json

HealthCheckTypeELB인데 HealthCheckGracePeriod가 애플리케이션 기동 시간보다 짧다면, 인스턴스가 완전히 준비되기 전에 교체 루프에 빠진다.

3단계: ELB 타겟 그룹에서 실제 인스턴스 상태 확인

EC2 콘솔에서 인스턴스가 running으로 보여도, ELB 타겟 그룹에서는 unhealthy일 수 있다. 이 두 상태는 완전히 독립적이다.

aws elbv2 describe-target-health \
  --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/1234567890abcdef \
  --region us-east-1 \
  --query 'TargetHealthDescriptions[*].{Id:Target.Id,Port:Target.Port,State:TargetHealth.State,Reason:TargetHealth.Reason,Description:TargetHealth.Description}' \
  --output table

Stateunhealthy이고 ReasonTarget.ResponseCodeMismatchTarget.Timeout이라면, 문제는 인스턴스가 아니라 애플리케이션 또는 헬스 체크 경로 설정에 있다.

4단계: 타겟 그룹 헬스 체크 설정 자체 검토

ELB 헬스 체크 경로, 포트, 응답 코드, 타임아웃 설정이 실제 애플리케이션 동작과 맞지 않으면 정상 인스턴스도 unhealthy로 판정된다. 헬스 체크 경로가 인증을 요구하거나 302 리다이렉트를 반환하는 경우가 대표적이다.

aws elbv2 describe-target-groups \
  --target-group-arns arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/1234567890abcdef \
  --region us-east-1 \
  --query 'TargetGroups[0].{Protocol:Protocol,Port:Port,HealthCheckPath:HealthCheckPath,HealthCheckPort:HealthCheckPort,Matcher:Matcher,HealthCheckIntervalSeconds:HealthCheckIntervalSeconds,HealthCheckTimeoutSeconds:HealthCheckTimeoutSeconds,HealthyThresholdCount:HealthyThresholdCount,UnhealthyThresholdCount:UnhealthyThresholdCount}' \
  --output json

MatcherHttpCode200인데 앱이 301을 반환하고 있다면, 이것이 정확한 원인이다. 헬스 체크 경로를 인증 없이 200을 반환하는 전용 엔드포인트(/health, /ping)로 변경해야 한다.

graph TD Start["인스턴스가 교체되는 증상"] --> Step1["1단계: ASG 활동 기록 확인 describe-scaling-activities"] Step1 --> Q1{"Cause에 ELB 언급?"} Q1 -- "No" --> Step1b["EC2 상태 체크 문제 인스턴스 시스템 로그 확인"] Q1 -- "Yes" --> Step2["2단계: 헬스 체크 타입 및 유예 기간 확인"] Step2 --> Q2{"Grace Period가 기동 시간보다 짧음?"} Q2 -- "Yes" --> Fix1["Grace Period 증가 update-auto-scaling-group"] Q2 -- "No" --> Step3["3단계: 타겟 그룹 인스턴스 상태 확인 describe-target-health"] Step3 --> Q3{"Reason 코드?"} Q3 -- "ResponseCodeMismatch" --> Fix2["헬스 체크 경로 수정 또는 Matcher 수정"] Q3 -- "Timeout" --> Fix3["보안 그룹 인바운드 규칙 또는 앱 응답 시간 확인"] Q3 -- "healthy" --> Step4["4단계: 타겟 그룹 헬스 체크 설정 검토 describe-target-groups"]

EC2에서 ELB 헬스 체크로 전환하는 방법

원인이 확인되고 ELB 헬스 체크 전환이 적절하다고 판단되면, 다음 순서로 진행한다. 전환 전에 반드시 타겟 그룹 헬스 체크 설정이 올바른지 확인해야 한다 — 잘못된 상태에서 전환하면 모든 인스턴스가 교체 루프에 빠진다.

헬스 체크 타입 변경 및 유예 기간 설정

유예 기간은 인스턴스 시작부터 애플리케이션이 완전히 준비될 때까지의 시간보다 넉넉하게 설정해야 한다. 애플리케이션 기동 시간을 측정한 뒤 그보다 여유 있는 값을 사용한다.

aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name my-asg \
  --health-check-type ELB \
  --health-check-grace-period 300 \
  --region us-east-1

타겟 그룹 헬스 체크 경로 수정 (필요한 경우)

헬스 체크 전용 엔드포인트가 없다면, 먼저 애플리케이션에 추가하고 아래 명령으로 타겟 그룹 설정을 업데이트한다.

aws elbv2 modify-target-group \
  --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/1234567890abcdef \
  --health-check-path /health \
  --health-check-interval-seconds 30 \
  --health-check-timeout-seconds 5 \
  --healthy-threshold-count 2 \
  --unhealthy-threshold-count 3 \
  --matcher HttpCode=200 \
  --region us-east-1

헬스 체크 진단에 필요한 IAM 권한

운영 환경에서 위 CLI 명령을 실행하려면 적절한 IAM 권한이 필요하다. 최소 권한 원칙에 따라 필요한 액션만 허용한다. elasticloadbalancing:DescribeTargetHealth는 특정 타겟 그룹 ARN을 리소스로 지정할 수 있다.

🔽 IAM 정책 예시 (클릭하여 펼치기)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ASGReadAccess",
      "Effect": "Allow",
      "Action": [
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DescribeScalingActivities"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ELBReadAccess",
      "Effect": "Allow",
      "Action": [
        "elasticloadbalancing:DescribeTargetGroups"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ELBTargetHealthAccess",
      "Effect": "Allow",
      "Action": [
        "elasticloadbalancing:DescribeTargetHealth"
      ],
      "Resource": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/1234567890abcdef"
    },
    {
      "Sid": "ASGWriteAccess",
      "Effect": "Allow",
      "Action": [
        "autoscaling:UpdateAutoScalingGroup"
      ],
      "Resource": "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:*:autoScalingGroupName/my-asg"
    },
    {
      "Sid": "ELBWriteAccess",
      "Effect": "Allow",
      "Action": [
        "elasticloadbalancing:ModifyTargetGroup"
      ],
      "Resource": "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-tg/1234567890abcdef"
    }
  ]
}

읽기 전용 액션(Describe*) 중 일부는 리소스 수준 제한을 지원하지 않아 "Resource": "*"가 필요하다. 쓰기 액션과 DescribeTargetHealth는 특정 리소스 ARN으로 범위를 제한할 수 있다. 각 액션의 리소스 수준 지원 여부는 AWS Service Authorization Reference에서 확인한다.

실제 장애 패턴: 오탐으로 인한 교체 루프

ELB 헬스 체크로 전환한 직후 모든 인스턴스가 교체 루프에 빠지는 상황을 겪은 적이 있다. EC2 콘솔에서는 전부 running이고, 애플리케이션도 정상 응답하는 것처럼 보였다.

처음에는 보안 그룹 문제라고 생각했다. ELB에서 인스턴스로의 인바운드 규칙을 확인했지만 문제없었다. 그 다음엔 헬스 체크 유예 기간을 늘려봤다 — 효과 없음.

실제 원인은 타겟 그룹 헬스 체크 경로였다. /로 설정된 경로가 로그인 페이지로 302 리다이렉트를 반환하고 있었고, Matcher는 200만 허용하고 있었다. 애플리케이션은 완벽하게 동작하고 있었지만 ELB 입장에서는 모든 인스턴스가 unhealthy였다.

수정은 간단했다 — 헬스 체크 경로를 인증 없이 200을 반환하는 /health로 변경하는 것. 하지만 원인을 찾는 데 30분이 걸렸다. describe-target-healthReason 필드를 먼저 확인했다면 5분이면 충분했을 것이다.

ELB 헬스 체크는 로드 밸런서가 트래픽을 보내도 되는지 판단하는 기준이다. ASG는 그 판단을 빌려 인스턴스 교체 여부를 결정한다. 헬스 체크 설정이 애플리케이션 실제 동작을 반영하지 않으면, ASG는 정상 인스턴스를 계속 교체하는 루프에 빠진다.

ELB 헬스 체크 전환 시 주의사항

ELB 헬스 체크로 전환하는 것이 항상 옳은 선택은 아니다. 다음 상황에서는 신중하게 판단해야 한다.

  • ASG가 ELB에 연결되지 않은 경우: ELB 헬스 체크 타입을 설정해도 연결된 로드 밸런서가 없으면 EC2 헬스 체크만 동작한다.
  • 배치 처리 워크로드: HTTP 엔드포인트 없이 큐에서 메시지를 처리하는 인스턴스라면 ELB 헬스 체크가 적합하지 않다.
  • 헬스 체크 유예 기간 미설정: 기동 시간이 긴 애플리케이션에서 유예 기간 없이 ELB 헬스 체크를 활성화하면 교체 루프가 발생한다.

Auto Scaling Group 헬스 체크 설정 마무리 및 다음 단계

ASG 헬스 체크 타입 선택은 단순한 설정 변경이 아니라, 어떤 기준으로 인스턴스 교체를 결정할지의 운영 정책이다. EC2 헬스 체크는 인프라 장애를 감지하고, ELB 헬스 체크는 애플리케이션 장애를 감지한다 — 두 가지를 함께 사용하면 두 레이어 모두를 커버할 수 있다.

전환 전에 반드시 describe-target-health로 현재 타겟 상태를 확인하고, 헬스 체크 경로와 응답 코드 설정이 실제 애플리케이션 동작과 일치하는지 검증한다. 그 다음 유예 기간을 충분히 설정한 뒤 전환한다.

핵심 용어 정리

용어설명
Health Check Grace Period인스턴스 시작 후 헬스 체크 판정을 유예하는 시간(초). 애플리케이션 기동 시간보다 길게 설정해야 한다.
Target GroupALB/NLB가 트래픽을 라우팅하는 대상 집합. 각 타겟의 헬스 상태를 독립적으로 추적한다.
DescribeTargetHealth ReasonELB가 타겟을 unhealthy로 판정한 구체적 이유. Target.ResponseCodeMismatch, Target.Timeout 등.
Activity HistoryASG가 수행한 스케일링 및 인스턴스 교체 작업의 기록. 종료 원인이 명시된다.
Unhealthy Threshold타겟을 unhealthy로 확정하기 전 연속 실패 횟수. 값이 낮을수록 빠르게 교체가 트리거된다.

Related Posts

댓글

이 블로그의 인기 게시물

EC2 SSH 연결 시간 초과: 확인해야 할 보안 그룹(Security Group) 규칙

EC2 SSH 연결 타임아웃 완전 해결 가이드: Security Group 인바운드 규칙부터 라우팅까지

IAM User vs IAM Role 차이점 완전 정리 — EC2에서 S3 접근 시 무엇을 써야 하는가