클라우드

Application Load Balancer, 헬스 체크와 등록 해제는 어떻게 동작할까

ALB는 헬스 체크를 통과한 대상에만 요청을 보내지만, 대상이 모두 비정상이면 전부에게 다시 보낸다.
대상을 뺄 때 진행 중인 요청을 기다리는 시간은 배포와 롤백 속도에도 그대로 걸린다.

이 글의 목차리스너에서 대상까지5
  1. 리스너에서 대상까지
  2. 헬스 체크는 무엇을 확인하나
  3. 대상이 모두 비정상이면
  4. 대상을 뺄 때는 기다린다
  5. 실무에서 맞춰 두는 것들

서버 한 대로 운영하던 서비스를 두 대로 늘리면 곧바로 풀어야 할 문제가 둘 생긴다.
사용자가 접속할 주소를 하나로 정해야 하고, 한 대가 고장 나면 그쪽으로 가던 요청을 다른 서버로 돌려야 한다.

이 일을 맡는 것이 로드밸런서다.
사용자는 로드밸런서 주소 하나로 접속하고, 로드밸런서가 뒤에 있는 여러 서버에 요청을 나눠 보낸다.
AWS에서 HTTP 요청을 나눠 주는 로드밸런서가 ALB(Application Load Balancer)⁠다.

그런데 콘솔에서 ALB를 만들면 리스너, 대상 그룹, 헬스 체크처럼 낯선 칸이 줄줄이 나온다.
요청이 서버에 닿기까지 거치는 길부터 따라간다.

리스너에서 대상까지

ALB 안에서 요청이 처음 닿는 곳은 리스너다.
리스너는 HTTPS 443번처럼 정해 둔 프로토콜과 포트로 들어오는 연결을 받는다.

리스너에는 규칙이 붙는다.
규칙은 요청 경로나 호스트 이름 같은 조건을 보고 어느 대상 그룹으로 보낼지 정한다.
/api⁠로 시작하는 요청은 API 서버 묶음으로, 나머지는 웹 서버 묶음으로 보내는 식이다.

규칙은 우선순위 순서대로 하나씩 대조하고, 처음 맞는 규칙에서 멈춘다.
어느 규칙에도 맞지 않은 요청은 맨 마지막의 기본 규칙이 받는다.

리스너 규칙에 따른 대상 그룹 분기 규칙은 우선순위 순서로 대조하고, 처음 맞는 규칙의 대상 그룹에서 건강한 대상 하나가 요청을 받는다. 개념 도해 리스너 규칙에 따른 대상 그룹 분기 요청 라우팅 경로 ① 리스너 프로토콜 · 포트 ② 우선순위 규칙 호스트 조건 ③ 그룹 가 ④ 건강한 대상 경로 조건 ③ 그룹 나 ④ 건강한 대상 기본 규칙 ③ 기본 그룹 ④ 건강한 대상 규칙 평가 순서 ① 앞 규칙 조건 불일치 ② 첫 일치 경로 조건 평가 중단 ③ 뒤 규칙 미평가 대상 그룹 나 평가 순서 규칙은 우선순위 순서로 대조하고, 처음 맞는 규칙의 대상 그룹에서 건강한 대상 하나가 요청을 받는다. SOSHIN · DEV NOTES
요청은 처음 맞는 규칙이 가리키는 대상 그룹으로 가고, 그 그룹 안의 건강한 대상이 처리한다.

대상 그룹에는 요청을 실제로 처리하는 대상(target)⁠이 등록된다.
AWS의 가상 서버인 EC2 인스턴스가 대상이 되기도 하고, 컨테이너나 IP 주소가 대상이 되기도 한다.

대상 그룹 안에서는 기본적으로 대상을 돌아가며 하나씩 고르는 라운드 로빈 방식으로 요청을 나눈다.

경로를 보고 보낼 곳을 고른다는 것은 ALB가 HTTP 요청의 내용을 읽는다는 뜻이다.
로드밸런서는 이 점에서 4계층에서 동작하는 것과 7계층에서 동작하는 것으로 나뉜다.

여기서 계층은 네트워크 통신을 역할별로 나눈 OSI 모델의 층을 말한다.
4계층 로드밸런서는 TCP 연결만 다루고 패킷 안의 HTTP 내용은 들여다보지 않는다.

7계층 로드밸런서는 HTTP 헤더까지 읽기 때문에, 같은 주소로 온 요청도 경로에 따라 다른 서버 묶음으로 보낼 수 있다.
ALB는 7계층 쪽이다.

헬스 체크는 무엇을 확인하나

고장 난 서버를 피하려면 로드밸런서는 어느 서버가 멀쩡한지 알아야 한다.
ALB는 등록된 대상마다 주기적으로 요청을 보내 상태를 확인하는데, 이것을 헬스 체크(health check)⁠라고 한다.

기본 헬스 체크 설정에서는 30초마다 대상의 / 경로로 HTTP 요청을 보내고, 200 응답이 오면 성공으로 본다.
두 번 연속 실패하면 비정상으로 판정해 요청을 보내지 않고, 다섯 번 연속 성공해야 다시 정상으로 돌아온다.

헬스 체크 연속 결과에 따른 대상 상태 실패가 두 번 이어지면 그 대상은 요청에서 빠지고, 다시 요청을 받으려면 성공이 다섯 번 이어져야 한다. 개념 도해 헬스 체크 연속 결과에 따른 대상 상태 헬스 체크 결과 기본값 · 30초마다 검사 · 200 응답이면 성공 검사 연속 횟수 성공 실패 실패 성공 성공 성공 성공 성공 실패 1 ① 실패 2 비정상 판정 성공 1 성공 2 성공 3 성공 4 ② 성공 5 정상 복귀 대상 상태와 요청 전달 ① ② 대상 상태 정상 비정상 정상 요청 전달 보냄 보내지 않음 보냄 30초 시간 실패가 두 번 이어지면 그 대상은 요청에서 빠지고, 다시 요청을 받으려면 성공이 다섯 번 이어져야 한다. SOSHIN · DEV NOTES
실패한 검사가 한 번뿐일 때는 요청이 계속 가고, 상태는 연속 횟수가 기준에 닿는 검사에서 바뀐다.

새로 등록한 대상은 조금 다르다.
첫 헬스 체크를 한 번만 통과하면 정상이 되고, 그때부터 요청을 받는다.
앱이 준비를 덜 마쳤는데 /⁠가 먼저 200을 돌려주면 준비 안 된 서버로 요청이 들어간다.

처음 설정할 때 자주 막히는 곳도 있다.
헬스 체크가 계속 실패하면 가장 먼저 확인할 곳은 서버에 붙는 방화벽 규칙인 보안 그룹이다.
대상의 보안 그룹이 로드밸런서에서 헬스 체크 포트로 오는 트래픽을 허용해야 한다.

응답 코드에서 걸리기도 한다.
/ 경로가 로그인 페이지로 넘기는 302 응답을 돌려주면, 브라우저로 볼 때는 멀쩡해도 기본 성공 코드인 200과 맞지 않아 검사는 실패한다.

대상이 모두 비정상이면

헬스 체크에 실패한 대상은 요청을 받지 않는다고만 외워 두면 한 가지 예외를 놓친다.
대상 그룹의 대상이 전부 비정상이면, ALB는 상태와 상관없이 그 대상 모두에게 요청을 보낸다.
이 동작을 페일 오픈(fail-open)⁠이라고 한다.

정상 대상이 하나도 없는데 아무 데도 보내지 않으면 요청은 전부 실패한다.
페일 오픈은 그 상황에서도 요청이 처리될 가능성을 남겨 둔다.

모두 비정상이면 요청은 어디로 갈까

한 대상 그룹에 등록된 서버 두 대의 상태에 따라 요청이 가는 곳이 달라진다.

서버 A

정상

요청 대상

서버 B

정상

요청 대상

하나가 비정상이면 정상 대상만 고르지만, 등록 대상이 전부 비정상이면 ALB는 그 대상들에도 요청을 보낸다.

모든 가용 영역의 등록 대상이 비정상인 fail-open 예시다. 빈 대상 그룹이나 다른 라우팅 설정은 다루지 않는다.

이 예외가 문제가 되는 것은 모든 대상이 함께 쓰는 자원을 헬스 체크에 넣었을 때다.
헬스 체크에 답하면서 데이터베이스까지 조회하도록 만든 서비스가 그런 경우다.

이런 서비스에서 데이터베이스가 느려지면 모든 대상의 헬스 체크가 한꺼번에 실패한다.
서버는 멀쩡한데 전부 비정상이 되고, ALB는 페일 오픈으로 다시 모두에게 요청을 보낸다.
헬스 체크가 걸러 낸 대상은 하나도 없다.

헬스 체크 결과로 대상을 교체하는 서비스와 함께 쓰면 더 나빠질 수 있다.
컨테이너를 실행해 주는 AWS 서비스인 ECS는 로드밸런서 헬스 체크에 실패한 태스크를 비정상으로 보고 새 태스크로 바꾼다.
데이터베이스가 잠깐 느려졌을 뿐인데 멀쩡한 컨테이너가 줄줄이 교체되기도 한다.

그래서 헬스 체크에는 이 대상 하나를 빼면 나아지는 고장만 담는다.
프로세스가 요청을 받을 수 있는지, 시작할 때 읽어야 할 설정을 다 읽었는지 같은 것들이다.

대상을 뺄 때는 기다린다

배포하거나 서버 수를 줄일 때는 대상을 대상 그룹에서 뺀다.
이것을 등록 해제(deregistration)⁠라고 한다.
그런데 빼기로 한 순간에도 그 대상은 요청 몇 개를 처리하고 있을 수 있다.

등록 해제 지연에 따른 요청 처리 결과 지연 한도 안에 끝난 요청은 완료되고, 남은 요청은 대상 종료와 함께 실패할 수 있다. 개념 도해 등록 해제 지연에 따른 요청 처리 결과 등록 해제 경로 ② 신규 요청 차단 ③ 진행 요청 마무리 처리 중 A 완료 처리 중 B 미완료 ① 등록 해제 시작 ④ 지연 한도 시간 남은 처리 시간과 지연 한도 등록 해제 지연 남은 처리 짧음 완료 남은 처리 김 연결 종료 · 오류 가능 남은 처리 시간 지연 한도 안에 끝난 요청은 완료되고, 남은 요청은 대상 종료와 함께 실패할 수 있다. SOSHIN · DEV NOTES
빼기로 한 순간과 실제로 끊는 순간 사이에 기다리는 구간이 있고, 그 길이는 대상 그룹 설정으로 정한다.

ALB는 등록 해제 중인 대상에 새 요청을 보내지 않고, 기본값으로 300초를 기다린 뒤 해제를 마친다.
이 시간을 등록 해제 지연(deregistration delay)⁠이라고 한다.

그동안 대상 상태는 draining으로 표시된다.
진행 중인 요청과 연결이 없으면 해제는 기다리지 않고 바로 끝난다.

ECS에서는 컨테이너를 띄운 실행 단위를 태스크라고 부른다.
ECS가 태스크를 멈출 때는 먼저 대상 그룹에서 등록을 해제하고, 그다음 컨테이너에 SIGTERM을 보낸다.
설정해 둔 시간이 지나도 프로세스가 살아 있으면 SIGKILL로 강제 종료한다.

SIGTERM은 프로세스에 하던 일을 정리하고 끝내라고 알리는 리눅스 신호다.
반면 SIGKILL은 프로세스가 막을 수 없는 강제 종료 신호다.

이 순서라면 로드밸런서를 거친 요청이 끊기는 것은 등록 해제 지연이 모자랄 때다.
지연보다 오래 걸리는 요청은 해제가 끝나는 순간 끊긴다.
기다리는 동안 대상이 먼저 연결을 끊어도 사용자는 500번대 오류를 받는다.

반대로 지연을 길게 잡으면 교체가 그만큼 느려질 수 있다.
이 시간은 문제가 생겨 이전 버전으로 되돌릴 때도 똑같이 걸린다.

AWS는 응답 시간이 1초 안쪽인 서비스라면 이 값을 5초로 줄여 배포를 빠르게 하라고 권한다.
느린 파일 업로드나 스트리밍처럼 오래 붙어 있는 요청이 있다면 그렇게 줄이지 말라는 단서도 달려 있다.

실무에서 맞춰 두는 것들

헬스 체크 경로는 /health⁠처럼 따로 만들어 두는 경우가 많다.
로그인이나 리다이렉트를 거치지 않고, 이 프로세스가 요청을 받을 수 있는지만 빠르게 200으로 답하는 경로다.

데이터베이스 같은 공용 의존성의 상태는 헬스 체크에 넣지 않고 CloudWatch 알람으로 따로 본다.
그래야 데이터베이스가 흔들릴 때 멀쩡한 대상까지 한꺼번에 비정상으로 몰리지 않는다.

대상의 보안 그룹에는 소스로 로드밸런서의 보안 그룹을 적어서, 로드밸런서에서 오는 요청과 헬스 체크만 받게 한다.
포트와 방화벽 글에서 본 것처럼 인터넷 쪽 포트는 로드밸런서에만 열어 두면 된다.

등록 해제 지연은 정상 요청 가운데 가장 오래 걸리는 것보다 조금 길게 잡는다.
앱은 SIGTERM을 받으면 새 작업을 받지 않고, 큐에서 꺼내 처리하던 일처럼 요청 밖에서 하던 작업을 정해진 시간 안에 마무리하도록 만들어 둔다.

소신을 상징하는 까마귀 목판화 일러스트
작성자소신 · Soshin

웹과 서버를 만들고, 개발하며 배운 것들을 글로 남깁니다.

좌우로 움직여 보세요