Route 53 DNS 페일오버 설정: EC2 장애 시 S3 정적 사이트로 자동 전환하기
새벽 2시에 온콜 알림이 울렸다. EC2 인스턴스가 응답을 멈췄는데, DNS가 여전히 죽은 서버를 가리키고 있어서 사용자들은 그냥 타임아웃 화면만 보고 있었다. Route 53 DNS 페일오버를 미리 구성해뒀다면, 헬스 체크가 실패하는 순간 트래픽이 S3 정적 백업 사이트로 자동 전환됐을 것이다. 이 글은 그 설정을 처음부터 끝까지 다룬다.
TL;DR — Route 53 DNS 페일오버 핵심 요약
| 단계 | 구성 요소 | 역할 |
|---|---|---|
| 1 | Route 53 헬스 체크 | EC2 엔드포인트 상태를 주기적으로 확인 |
| 2 | Primary 레코드 (Failover) | EC2를 가리키며 헬스 체크와 연결 |
| 3 | Secondary 레코드 (Failover) | S3 정적 웹사이트 엔드포인트를 가리킴 |
| 4 | S3 버킷 정적 웹사이트 호스팅 | 백업 페이지 서빙 |
| 5 | 자동 전환 | 헬스 체크 실패 시 Secondary로 DNS 응답 변경 |
Route 53 DNS 페일오버 동작 원리
Route 53의 페일오버 라우팅 정책은 Active-Passive 구조다. Primary 레코드에 헬스 체크를 연결해두면, Route 53 헬스 체커가 전 세계 여러 위치에서 해당 엔드포인트를 주기적으로 폴링한다. 헬스 체크가 임계값 이상 실패하면 Route 53은 해당 레코드를 'Unhealthy'로 표시하고, 동일한 이름과 타입을 가진 Secondary 레코드로 DNS 응답을 전환한다.
중요한 점은 이것이 DNS 레벨의 전환이라는 것이다. 기존 TCP 연결은 끊기지 않으며, TTL이 만료된 이후 새로운 DNS 조회부터 Secondary 주소를 받게 된다. 따라서 TTL 값 설정이 실제 전환 속도에 직접적인 영향을 준다.
- 정상 상태: Route 53 헬스 체커가 EC2 엔드포인트를 폴링하고 Healthy 상태를 유지한다.
- DNS 조회: 클라이언트가 도메인을 조회하면 Route 53은 Primary 레코드(EC2 IP)를 반환한다.
- 장애 감지: EC2가 응답하지 않으면 헬스 체커가 연속 실패를 기록하고 레코드를 Unhealthy로 전환한다.
- 자동 페일오버: 이후 DNS 조회에서 Route 53은 Secondary 레코드(S3 웹사이트 엔드포인트)를 반환한다.
- 복구: EC2가 복구되어 헬스 체크가 다시 통과하면 Primary로 자동 복귀한다.
사전 준비 사항
설정을 시작하기 전에 다음이 준비되어 있어야 한다.
- Route 53에서 관리 중인 호스팅 존 (예:
example.com) - 퍼블릭 IP가 할당된 EC2 인스턴스 (또는 Elastic IP)
- 백업용 S3 버킷 — 버킷 이름이 도메인과 일치해야 한다 (예:
www.example.com) - AWS CLI 설정 완료 (
aws configure)
S3 정적 웹사이트 호스팅에서 버킷 이름을 도메인과 동일하게 맞춰야 하는 이유는, Route 53이 S3 웹사이트 엔드포인트를 Alias 레코드로 연결할 때 버킷 이름으로 라우팅을 결정하기 때문이다. 이름이 다르면 S3가 요청을 거부한다.
1단계: S3 버킷 정적 웹사이트 호스팅 설정
백업 사이트가 될 S3 버킷을 먼저 구성한다. 버킷 이름은 반드시 실제 도메인 이름과 동일해야 한다. 여기서는 www.example.com을 예시로 사용한다.
버킷 생성 및 퍼블릭 액세스 설정, 정적 웹사이트 호스팅 활성화를 순서대로 진행한다. 퍼블릭 액세스 차단을 해제하지 않으면 버킷 정책이 적용되지 않으므로 주의한다.
🔽 S3 버킷 생성 및 웹사이트 호스팅 설정 CLI (클릭하여 펼치기)
# 버킷 생성 (버킷 이름 = 도메인 이름)
aws s3api create-bucket \
--bucket www.example.com \
--region us-east-1
# 퍼블릭 액세스 차단 해제 (정적 웹사이트 호스팅에 필요)
aws s3api put-public-access-block \
--bucket www.example.com \
--public-access-block-configuration \
'BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false'
# 정적 웹사이트 호스팅 활성화
aws s3api put-bucket-website \
--bucket www.example.com \
--website-configuration '{
"IndexDocument": {"Suffix": "index.html"},
"ErrorDocument": {"Key": "error.html"}
}'
# 퍼블릭 읽기 버킷 정책 적용
aws s3api put-bucket-policy \
--bucket www.example.com \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::www.example.com/*"
}
]
}'
# 백업 index.html 업로드
aws s3 cp index.html s3://www.example.com/index.html
웹사이트 엔드포인트 형식은 리전마다 다르다. us-east-1의 경우 www.example.com.s3-website-us-east-1.amazonaws.com 형식이다. 다른 리전은 AWS 문서에서 확인해야 한다.
2단계: Route 53 헬스 체크 생성
헬스 체크는 페일오버의 핵심 트리거다. EC2의 퍼블릭 IP 또는 도메인을 대상으로 생성한다. 헬스 체크 없이 Primary 레코드를 만들면 페일오버가 동작하지 않는다 — 이 부분을 빠뜨리는 경우가 많다.
# EC2 IP를 대상으로 HTTP 헬스 체크 생성
aws route53 create-health-check \
--caller-reference "ec2-primary-$(date +%s)" \
--health-check-config '{
"IPAddress": "203.0.113.10",
"Port": 80,
"Type": "HTTP",
"ResourcePath": "/health",
"FullyQualifiedDomainName": "www.example.com",
"RequestInterval": 30,
"FailureThreshold": 3
}'
위 명령 실행 후 반환되는 HealthCheckId를 메모해둔다. 다음 단계에서 레코드와 연결할 때 사용한다.
# 생성된 헬스 체크 ID 확인
aws route53 list-health-checks \
--query 'HealthChecks[*].{ID:Id,Config:HealthCheckConfig.IPAddress}' \
--output table
RequestInterval은 30초(표준) 또는 10초(빠른 헬스 체크, 추가 비용 발생) 중 선택할 수 있다. FailureThreshold는 Unhealthy로 전환하기 위해 연속으로 실패해야 하는 횟수다. 기본값 3과 30초 간격을 조합하면 최소 90초 후 페일오버가 시작된다. 여기에 TTL이 더해진다.
Route 53 DNS 페일오버 레코드 설정 (핵심)
3단계: Primary 레코드 생성 (EC2)
Primary 레코드는 EC2 인스턴스를 가리키며, 앞서 생성한 헬스 체크와 연결된다. 헬스 체크 ID를 반드시 포함해야 페일오버가 동작한다.
🔽 Primary 레코드 생성 CLI (클릭하여 펼치기)
# 호스팅 존 ID 확인
aws route53 list-hosted-zones \
--query 'HostedZones[*].{Name:Name,ID:Id}' \
--output table
# Primary 레코드 생성 (HOSTED_ZONE_ID, HEALTH_CHECK_ID를 실제 값으로 교체)
aws route53 change-resource-record-sets \
--hosted-zone-id HOSTED_ZONE_ID \
--change-batch '{
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"SetIdentifier": "primary-ec2",
"Failover": "PRIMARY",
"TTL": 60,
"ResourceRecords": [
{"Value": "203.0.113.10"}
],
"HealthCheckId": "HEALTH_CHECK_ID"
}
}
]
}'
4단계: Secondary 레코드 생성 (S3)
Secondary 레코드는 S3 웹사이트 엔드포인트를 Alias로 가리킨다. Alias 레코드를 사용하면 S3 웹사이트 엔드포인트에 대한 DNS 조회 비용이 발생하지 않으며, S3 엔드포인트의 IP 변경에도 자동으로 대응한다. Secondary 레코드에는 헬스 체크를 연결하지 않는 것이 일반적이다 — 백업이 다운됐을 때 Primary가 복구되지 않은 상황에서 Secondary마저 제외되면 레코드가 없어진다.
🔽 Secondary 레코드 생성 CLI (클릭하여 펼치기)
# Secondary 레코드 생성 (S3 Alias)
# us-east-1 S3 웹사이트 호스팅 존 ID는 Z3AQBSTGFYJSTF (AWS 문서에서 리전별 확인 필요)
aws route53 change-resource-record-sets \
--hosted-zone-id HOSTED_ZONE_ID \
--change-batch '{
"Changes": [
{
"Action": "CREATE",
"ResourceRecordSet": {
"Name": "www.example.com",
"Type": "A",
"SetIdentifier": "secondary-s3",
"Failover": "SECONDARY",
"AliasTarget": {
"HostedZoneId": "Z3AQBSTGFYJSTF",
"DNSName": "www.example.com.s3-website-us-east-1.amazonaws.com",
"EvaluateTargetHealth": false
}
}
}
]
}'
S3 웹사이트 호스팅 Alias 레코드에 사용하는 HostedZoneId는 S3 버킷의 호스팅 존 ID이며, 리전마다 다르다. us-east-1의 경우 Z3AQBSTGFYJSTF이지만, 다른 리전 값은 반드시 AWS 공식 문서의 S3 웹사이트 엔드포인트 목록에서 확인해야 한다.
설정 검증 및 페일오버 테스트
설정이 완료됐다고 끝이 아니다. 실제로 장애가 발생하기 전에 페일오버가 동작하는지 확인해야 한다. 헬스 체크 상태와 레코드 설정을 먼저 검증하고, 실제 전환 동작을 테스트한다.
# 헬스 체크 현재 상태 확인
aws route53 get-health-check-status \
--health-check-id HEALTH_CHECK_ID \
--query 'HealthCheckObservations[*].{Region:Region,Status:StatusReport.Status}' \
--output table
# 레코드 설정 확인
aws route53 list-resource-record-sets \
--hosted-zone-id HOSTED_ZONE_ID \
--query 'ResourceRecordSets[?Name==`www.example.com.`]' \
--output json
헬스 체크 상태가 Success로 표시되고, Primary/Secondary 레코드가 모두 존재하면 기본 설정은 완료된 것이다.
실제 페일오버 테스트는 EC2 인스턴스의 보안 그룹에서 인바운드 80번 포트를 임시로 차단하거나, 인스턴스를 중지하는 방식으로 진행할 수 있다. 헬스 체크가 Unhealthy로 전환되는 데 RequestInterval × FailureThreshold 시간이 걸리고, 이후 TTL이 만료되면 클라이언트는 S3 엔드포인트를 받게 된다.
# 페일오버 테스트: dig으로 DNS 응답 변화 모니터링
watch -n 10 'dig www.example.com +short'
실제 장애 상황에서 발견한 함정 — 잘못된 진단과 실제 원인
페일오버 설정을 완료했는데 EC2가 다운됐음에도 트래픽이 S3로 넘어가지 않는 상황이 있었다. CloudWatch에서 헬스 체크가 Unhealthy로 표시되는 걸 확인했고, 처음엔 Route 53 전파 지연 문제라고 생각했다.
실제 원인은 달랐다. Primary 레코드를 생성할 때 HealthCheckId 필드를 누락했던 것이다. 헬스 체크는 존재하고 Unhealthy 상태였지만, 레코드와 연결되지 않았기 때문에 Route 53 입장에서는 Primary 레코드가 항상 Healthy인 것으로 간주했다. 결과적으로 DNS는 계속 죽은 EC2를 가리켰다.
# 레코드에 헬스 체크가 연결됐는지 확인
aws route53 list-resource-record-sets \
--hosted-zone-id HOSTED_ZONE_ID \
--query 'ResourceRecordSets[?Name==`www.example.com.` && Failover==`PRIMARY`].HealthCheckId' \
--output text
이 명령이 아무것도 반환하지 않으면, Primary 레코드에 헬스 체크가 연결되지 않은 것이다. UPSERT 액션으로 레코드를 업데이트해서 HealthCheckId를 추가해야 한다.
페일오버 미동작"] --> B{"Primary 레코드에
HealthCheckId 연결됨?"} B -- 아니오 --> C["레코드 UPSERT로
HealthCheckId 추가"] B -- 예 --> D{"헬스 체크 상태가
실제로 Unhealthy?"} D -- 아니오 --> E["EC2 엔드포인트
실제 응답 확인"] D -- 예 --> F{"TTL 만료 대기
완료됐는가?"} F -- 아니오 --> G["TTL 만료 후
재확인"] F -- 예 --> H["S3 Secondary로
정상 전환됨"]
- 잘못된 진단: 헬스 체크가 Unhealthy임에도 전환이 안 되면 DNS 전파 지연으로 오해하기 쉽다.
- 실제 확인 포인트: Primary 레코드에
HealthCheckId가 연결됐는지 먼저 확인한다. - 연결 누락 시: UPSERT로 레코드를 업데이트해 헬스 체크 ID를 추가한다.
- 연결 확인 후: 헬스 체크 상태가 Unhealthy가 되면 Secondary로 자동 전환된다.
TTL과 헬스 체크 간격의 관계 — 실제 전환 시간 계산
페일오버 설정을 완료한 후 가장 많이 받는 질문이 '얼마나 빨리 전환되나요?'다. 이론적인 최대 전환 시간은 다음과 같이 계산된다.
최대 전환 시간 = (RequestInterval × FailureThreshold) + TTL
기본값(30초 간격, 임계값 3, TTL 60초)을 적용하면 최대 150초다. 빠른 헬스 체크(10초 간격)와 낮은 TTL(30초)을 조합하면 최대 60초까지 줄일 수 있다. 단, 빠른 헬스 체크는 추가 비용이 발생하며, TTL을 너무 낮추면 DNS 조회 빈도가 증가한다. 가격 및 제한은 항상 AWS 공식 가격 페이지에서 확인해야 한다.
또한 클라이언트 측 DNS 캐시(브라우저, OS)가 TTL을 무시하고 더 오래 캐시할 수 있다는 점도 고려해야 한다. 이는 Route 53이 아닌 클라이언트 동작의 문제다.
필요한 IAM 권한
Route 53 페일오버 설정을 자동화하거나 CI/CD 파이프라인에서 관리할 경우, 최소 권한 원칙에 따라 다음 권한이 필요하다.
🔽 Route 53 페일오버 설정용 IAM 정책 (클릭하여 펼치기)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Route53FailoverManagement",
"Effect": "Allow",
"Action": [
"route53:CreateHealthCheck",
"route53:GetHealthCheck",
"route53:GetHealthCheckStatus",
"route53:ListHealthChecks",
"route53:UpdateHealthCheck",
"route53:ChangeResourceRecordSets",
"route53:ListResourceRecordSets",
"route53:ListHostedZones",
"route53:GetHostedZone"
],
"Resource": "*"
},
{
"Sid": "S3WebsiteHosting",
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:PutBucketWebsite",
"s3:PutBucketPolicy",
"s3:PutPublicAccessBlock",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::www.example.com",
"arn:aws:s3:::www.example.com/*"
]
}
]
}
Route 53의 대부분의 List/Get 액션은 리소스 수준 제한을 지원하지 않아 "Resource": "*"가 필요하다. AWS Service Authorization Reference에서 각 액션의 리소스 수준 지원 여부를 확인할 수 있다.
Route 53 DNS 페일오버 마무리 및 다음 단계
Route 53 DNS 페일오버는 설정 자체는 단순하지만, 헬스 체크와 레코드의 연결 관계를 정확히 이해하지 않으면 실제 장애 시 동작하지 않는다. 핵심은 Primary 레코드에 헬스 체크 ID가 반드시 연결되어 있어야 한다는 것이다.
이 구성을 기반으로 다음 단계를 고려할 수 있다.
- CloudWatch 알람 연동: 헬스 체크 상태 변화 시 SNS를 통해 알림을 받도록 설정
- CloudFront + S3 조합: S3 정적 사이트 앞에 CloudFront를 두면 HTTPS와 캐싱을 추가할 수 있다
- 다중 리전 페일오버: Latency 라우팅과 페일오버를 조합해 리전 간 장애 대응 구성
관련 주제로 Route 53 가중치 기반 라우팅으로 블루/그린 배포 구성하기도 참고할 수 있다.
핵심 용어 정리
| 용어 | 설명 |
|---|---|
| 페일오버 라우팅 정책 | Route 53에서 Primary/Secondary 레코드를 지정하고 헬스 체크 기반으로 자동 전환하는 라우팅 방식 |
| 헬스 체크 (Health Check) | Route 53이 엔드포인트의 가용성을 주기적으로 확인하는 기능. 결과에 따라 레코드 Healthy/Unhealthy 상태가 결정됨 |
| Alias 레코드 | AWS 리소스(S3, CloudFront, ELB 등)를 직접 가리키는 Route 53 전용 레코드 타입. CNAME과 달리 루트 도메인에도 사용 가능 |
| TTL (Time To Live) | DNS 응답이 클라이언트 캐시에 유지되는 시간(초). 낮을수록 페일오버 전환이 빠르지만 DNS 조회가 증가 |
| 정적 웹사이트 호스팅 | S3 버킷을 HTTP 웹 서버처럼 동작하게 하는 기능. HTML, CSS, JS 파일을 직접 서빙 가능 |
댓글
댓글 쓰기