AWS 에서 메인터넌스 페이지 내는 3가지 방법 — ALB 규칙 · WAF · DNS 전환을 비교하고 DNS 를 버린 이유
앱이 꺼져 있어도 나오고, 평소엔 돈이 안 들고, 1초 만에 되돌릴 수 있는 쪽은 어디인가. 문서로 확인한 본문 한도와 keep-alive 함정
「지금 메인터넌스 중입니다」 화면을 AWS 안에서 끝내고 싶었다. 조건은 둘. 평소에는 돈이 안 들 것, 그리고 작업이 꼬였을 때 바로 되돌릴 수 있을 것. 이 글을 읽으면 ALB 리스너 규칙 · WAF · Route 53 DNS 전환 세 가지 중 무엇을 고를지, 각자의 본문 크기 한도가 얼마인지, DNS 전환이 왜 생각보다 느린지 알 수 있다.
ALB(Application Load Balancer)는 요청을 받아 ECS 같은 뒤쪽 서버로 나눠 주는 로드 밸런서다. WAF 는 그 앞에서 요청을 검사해 막거나 통과시키는 방화벽이다.
앱 미들웨어로 만들면 안 된다
처음 떠올리는 건 Rails 미들웨어 같은 데서 플래그를 보고 메인터넌스 화면을 내는 방법이다. 근데 메인터넌스는 앱을 내리는 순간에 쓰는 거다. DB 마이그레이션 중이거나 태스크를 다 내렸으면 그 미들웨어도 같이 죽어 있다. 앱 앞, 그러니까 ALB 나 WAF 에서 답해야 한다.
하나 더 헷갈렸던 것. 「503 규칙」 이라는 말을 들으면 「앱이 503 을 내면 메인터넌스 화면으로 바꿔 준다」 로 읽기 쉽다. 반대다. 이쪽에서 일부러 503 을 돌려주는 스위치다. 앱이 멀쩡해도 503 이 나간다.
세 가지를 나란히 놓으면
| ALB 리스너 규칙 | WAF 기본 동작 | API Gateway + Lambda + Route 53 | |
|---|---|---|---|
| 추가 리소스 | 0 | 0 (WAF 를 이미 쓰고 있다면) | 우리 구성으로 13개 |
| 켜기 · 되돌리기 | 즉시 | 즉시 | DNS 따라 — 아래 참고 |
| 본문 한도 | 1,024자 | 4 KB | 신경 안 써도 됨 |
| 사무실 IP 만 통과 | 더 높은 우선순위로 forward 규칙 추가 | IP 세트 + Allow 규칙 | 직접 구현 |
| 앱이 멈춰 있을 때 | 나온다 | 나온다 | 나온다 |
본문 한도는 문서로 확인했다. ALB 의 fixed-response 는 API 레퍼런스에 MessageBody 최대 길이 1,024 로 적혀 있다. 콘텐츠 타입은 text/html 을 포함한 다섯 가지 중 하나다. 1,024자면 CSS 를 조금 넣은 짧은 HTML 하나가 딱이다. 로고 이미지를 base64 로 박는 건 무리다.
WAF 는 좀 웃겼다. API 레퍼런스(CustomResponseBody)에는 Content 최대 길이가 10,240 으로 나온다. 그런데 쿼터 표에는 「응답 정의 하나의 본문은 최대 4 KB」 로 나온다. 처음 메모에는 10,240 을 적어 뒀는데 문서끼리 다르다. 실제로 10 KB 를 넣어 보진 않았다. 4 KB 안에 맞추는 게 안전하다.
DNS 전환이 지는 이유 — 되돌리기가 느리다
메인터넌스용 API Gateway + Lambda 를 따로 띄워 두고 Route 53 레코드를 그쪽으로 돌리는 방법이다. 제일 알기 쉬워 보여서 처음엔 이게 정답인 줄 알았다.
문제는 DNS 가 우리 뜻대로 바로 안 바뀐다는 거다. ALB 로 가는 alias 레코드는 TTL 을 우리가 못 정하고, AWS 가 ELB 의 TTL 60초를 쓴다(문서). 60초만 기다리면 될 것 같지만 그 위에 이런 게 얹힌다.
회사 · ISP 의 리졸버 중에는 최소 TTL 을 강제로 늘리는 곳이 있다
브라우저도 DNS 결과를 따로 캐시한다
이미 열려 있는 HTTP 연결은 DNS 를 다시 안 묻는다
마지막 게 제일 크다. 브라우저는 keep-alive 로 연결 하나를 계속 재사용한다. 그 연결이 끊길 때까지 요청은 옛 목적지로 간다. ALB 쪽 설정을 보면 클라이언트 keep-alive 유지 시간이 기본 3,600초다. 이 시간은 연결이 처음 열린 때부터 세고, 요청이 오가도 리셋되지 않는다(문서). 쉬지 않고 쓰는 사용자라면 최대 한 시간 가까이 옛 ALB 에 붙어 있을 수 있다는 얘기다. 물론 60초 동안 아무 요청이 없으면 idle timeout 으로 먼저 끊긴다.
감을 잡으려고 로컬에서 흉내를 내 봤다. 가짜 DNS 표를 앱 → 메인터넌스로 바꾼 뒤, 새 연결과 keep-alive 연결이 각각 어디로 가는지 찍었다.
before switch new-conn=app keep-alive=app
after switch 1 new-conn=maintenance keep-alive=app
after switch 2 new-conn=maintenance keep-alive=app
after switch 3 new-conn=maintenance keep-alive=app
after reconnect new-conn=maintenance keep-alive=maintenance
당연한 결과인데, 찍어 보니 확 와닿았다. 열린 연결은 다시 연결할 때까지 앱으로 간다.
메인터넌스 시작이 몇 분 늦는 건 참을 수 있다. 근데 작업이 실패해서 서둘러 되돌리고 싶을 때 몇 분 ~ 몇십 분을 기다리는 건 곤란하다. ALB 규칙이나 WAF 는 API 한 번이면 그 다음 요청부터 바뀐다. 연결이 이미 ALB 에 붙어 있으니 keep-alive 도 문제가 안 된다.
그래서 DNS 전환은 「ALB 자체가 없어지는」 경우에만 쓸 만하다고 정리했다. 리전 장애처럼.
ALB 규칙으로 켜고 끄기
우선순위 1 에 fixed-response 503 규칙을 넣으면 그게 기본 forward 보다 먼저 걸린다.
aws elbv2 create-rule --listener-arn <listener-arn> --priority 1 --conditions '[{"Field":"path-pattern","PathPatternConfig":{"Values":["/*"]}}]' --actions '[{"Type":"fixed-response","FixedResponseConfig":{"StatusCode":"503","ContentType":"text/html","MessageBody":"<h1>점검 중입니다</h1><p>22:00 에 돌아옵니다</p>"}}]'
끄려면 delete-rule 이다. 사무실에서만 미리 확인하고 싶으면, 그 앞(우선순위 0 은 없으니 503 규칙을 2 로 내리고 1 에)에 source-ip 조건으로 원래 타깃 그룹에 forward 하는 규칙을 둔다. source-ip 에는 「이 IP 가 아니면」 같은 부정이 없어서 이렇게 두 줄로 나눈다.
주의할 점 하나. source-ip 는 ALB 에 직접 붙은 주소를 본다. 앞에 CloudFront 가 있으면 CloudFront 의 IP 가 보인다. X-Forwarded-For 는 이 조건에 안 걸리고 http-header 조건으로 따로 봐야 한다고 문서에 적혀 있다.
WAF 로 켜고 끄기
WAF 를 이미 ALB 에 붙여 쓰고 있다면 web ACL 의 기본 동작을 Allow → Block 으로 바꾸고, 커스텀 응답으로 503 과 본문을 지정한다. 사무실 IP 는 IP 세트에 넣고 Allow 규칙을 하나 두면 끝이다. ALB 규칙과 달리 응답 헤더를 붙일 수 있어서(content-type 만 빼고) Retry-After 도 넣을 수 있다.
WAF 를 안 쓰고 있었다면 web ACL 자체에 월 요금이 붙는다. 메인터넌스 화면 하나 때문에 새로 들일 이유는 약하다. 그럴 땐 ALB 규칙이 맞다.
503 을 내는 동안에도 안쪽 작업은 다 된다
이것도 헷갈리기 쉽다. 503 은 인터넷에서 들어오는 HTTP 만 막는 문이다. 그 뒤의 길은 살아 있다.
ECS Exec — SSM Session Manager 를 탄다. ALB 도 WAF 도 안 지난다
배포 — ECR push →
ecs update-service. 전부 AWS API 다ALB 헬스체크 — ALB 가 타깃에 직접 보낸다. 리스너 규칙도 WAF 도 안 지나니까 ECS 가 「헬스체크 실패」 로 태스크를 죽이거나 롤백하지 않는다
내부 ALB 를 통한 서비스 간 호출 — WAF 를 공개 ALB 에만 붙였다면 상관없다
반대로 외형 감시(Synthetics 같은 바깥에서 두드리는 감시)는 503 을 받고 빨개진다. 켜기 전에 음소거해 두지 않으면 알림이 쏟아진다.
고르는 법
짧은 HTML 이면 된다 → ALB 리스너 규칙. 리소스 0, 설정 한 줄
디자인이 들어간 화면 · 사무실 IP 통과 ·
Retry-After가 필요하고 WAF 를 이미 쓴다 → WAF 기본 동작ALB 자체를 못 쓰는 상황 → 그때만 DNS 전환. TTL 은 지켜지지 않는다고 보고 설계한다
메인터넌스 화면은 1년에 몇 번 안 쓰니 막상 쓸 때 안 움직이는 일이 많다. 평소에 스테이징에서 한 번 켰다 꺼 보는 걸 권한다.
ECS · ALB 위에서 무중단 작업이나 메인터넌스 절차를 짜야 하는데 손이 모자라면 https://www.negabaro.com/contact 로 물어봐 주면 좋겠다.
동작 흐름
메인터넌스 화면은 앱 앞(ALB · WAF)에서 낸다 — 앱 미들웨어는 앱과 같이 죽는다
짧은 HTML 이면 ALB 리스너 규칙 fixed-response 503 (본문 1,024자까지)
WAF 를 이미 쓰면 기본 동작을 Block + 503 으로 — 본문 4 KB · IP 세트 · Retry-After
DNS 전환은 keep-alive 연결이 DNS 를 다시 안 물어서 되돌리기가 느리다 — ALB 가 없을 때만
켜기 전에 외형 감시를 음소거한다. 헬스체크 · ECS Exec · 배포는 그대로 된다