헬스 체크
고친 사람 github-actions[bot]
헬스 체크는 서버가 지금 요청을 받아도 되는지 밖에서 주기적으로 물어 확인합니다. 제때 정상이라고 답한 서버에만 일을 넘기고, 답이 없거나 정상이 아닌 서버는 후보에서 뺍니다. 사람이 지켜보지 않아도 멈춘 서버가 저절로 빠집니다.
쉽고 빠른 이해
헬스 체크는 「지금 일을 맡겨도 되나」를 밖에서 대신 물어봐 주는 검사입니다. 로드 밸런서처럼 요청을 나눠 주는 쪽이 몇 초마다 서버마다 작은 요청을 하나씩 보냅니다. 제대로 답한 서버에만 손님 요청을 넘깁니다. 이렇게 검사를 보내는 쪽을 이 편에서는 「감시하는 쪽」이라고 부릅니다.
이게 없으면 멈춘 서버로 요청이 계속 흘러갑니다. 손님이 오류를 본 뒤에야 사람이 알아챕니다. 그 서버는 손으로 빼야 합니다.
어떻게 도나:
- 감시하는 쪽이 정해진 간격마다 서버에 작은 요청을 보냅니다
- 정해 둔 시간 안에 정상이라는 답이 오면 그 서버를 후보에 둡니다
- 연달아 몇 번 실패하면 후보에서 빼고, 다시 연달아 몇 번 성공하면 되돌립니다
대가가 있습니다. 검사가 얕으면 겉만 멀쩡한 서버를 못 걸러 내고, 깊으면 여러 서버가 같이 쓰는 데이터베이스 한 곳이 흔들릴 때 멀쩡한 서버까지 한꺼번에 빠집니다.
상세
헬스 체크는 서버 한 대만 놓고는 쓸 데가 없는 검사입니다. 같은 일을 하는 서버가 여럿 있고, 그 앞에서 손님 요청을 누구에게 넘길지 고르는 쪽이 있을 때 씁니다.
이 편에서는 검사를 보내는 쪽을 「감시하는 쪽」, 검사를 받는 서버 한 대를 「대상」이라고 부르겠습니다. 감시하는 쪽이 손님 요청을 넘겨도 되겠다고 보고 있는 대상의 목록은 「후보」라고 부르겠습니다.
비유로 보는 헬스 체크
배달 앱은 가게 목록에 지금 주문을 받는 가게만 올립니다. 그래서 몇 분마다 가게마다 전화를 걸어 지금 주문을 받을 수 있는지 물어봅니다. 전화를 안 받거나 오늘은 못 받는다고 답한 가게는 목록에서 내립니다.
이 확인을 서버 사이에서 하는 것이 헬스 체크입니다. 전화를 거는 앱이 감시하는 쪽입니다. 전화를 받는 가게가 대상입니다.
감시하는 쪽이 놓이는 배치
flowchart TD
C["손님 요청"] --> W["감시하는 쪽"]
subgraph T["대상"]
S1["서버 1 · 후보에 있음"]
S2["서버 2 · 후보에 있음"]
S3["서버 3 · 후보에서 빠짐"]
end
W -- 요청을 넘긴다 --> S1
W -- 요청을 넘긴다 --> S2
W -. 검사 .-> S1
W -. 검사 .-> S2
W -. 검사 .-> S3
감시하는 쪽은 두 가지를 같이 합니다. 점선은 검사이고 실선은 손님 요청입니다. 검사에 제대로 답한 대상만 후보에 남습니다.
후보에 있는 대상만 손님 요청을 받습니다. 그림의 서버 3은 검사에 실패해 후보에서 빠졌으므로 검사만 계속 받습니다.
물어보는 쪽과 알리는 쪽
살아 있는지 알아내는 방법은 크게 둘로 갈립니다. 살아 있는 쪽이 먼저 짧은 신호를 계속 보내 알리는 방법을 하트비트라고 부릅니다. 헬스 체크는 반대쪽입니다. 감시하는 쪽이 먼저 묻고 답을 받습니다.
sequenceDiagram
participant W as 감시하는 쪽
participant T as 대상
Note over W,T: 하트비트 · 대상이 먼저 알린다
T->>W: 살아 있다는 짧은 신호
T->>W: 살아 있다는 짧은 신호
Note over W,T: 헬스 체크 · 감시하는 쪽이 먼저 묻는다
W->>T: 지금 일을 맡겨도 되나
T-->>W: 됩니다
화살표가 시작하는 쪽이 다릅니다. 하트비트는 대상에서 시작하고 헬스 체크는 감시하는 쪽에서 시작합니다.
묻는 쪽이 먼저 나서면 무엇을 물을지 고를 수 있습니다. 「살아 있나」 대신 「이 요청을 처리할 수 있나」를 물을 수 있다는 뜻입니다. 대신 대상이 늘면 물어볼 곳도 그만큼 늘어 감시하는 쪽의 일이 커집니다.
떠 있는 것과 일할 수 있는 것의 차이
프로세스가 떠 있다는 것과 요청을 처리할 수 있다는 것은 같은 말이 아닙니다. 서버는 멀쩡히 돌고 있는데 데이터베이스로 가는 연결이 다 끊겨 있으면, 들어오는 요청은 전부 오류로 끝납니다.
그래서 헬스 체크는 「살아 있나」보다 「지금 일을 맡겨도 되나」를 묻습니다. 앞엣것만 확인하면 겉은 멀쩡하고 속은 망가진 서버로 요청이 계속 흘러갑니다.
이 물음에 답할 주소를 애플리케이션이 따로 하나 열어 둡니다. /healthz 나 /health 같은 이름을 흔히 씁니다. 사람이 열어 보는 화면이 아니라 감시하는 쪽만 부르는 주소입니다.
검사의 세 가지 깊이
검사는 깊이가 다릅니다. 얕은 검사는 값이 싸고 빨리 끝납니다. 걸러내는 고장은 적습니다.
| 검사 | 확인하는 것 | 이것으로는 못 아는 것 |
|---|---|---|
| 포트에 연결만 해 본다 | 프로세스가 떠서 연결을 받나 | 요청을 처리할 수 있는지 |
| 정해 둔 주소를 불러 본다 | 애플리케이션이 정상 상태 코드로 답하나 | 딸린 데이터베이스가 살아 있는지 |
| 딸린 곳까지 눌러 본다 | 일에 필요한 것에 다 닿나 | 못 아는 것은 없다. 대신 대가가 있다 |
아래로 갈수록 걸러내는 고장이 늡니다.
flowchart TD
subgraph P["서버 · 프로세스"]
A["애플리케이션"]
end
D["딸린 데이터베이스"]
A --> D
C1["포트에 연결만 해 본다"] --> P
C2["정해 둔 주소를 불러 본다"] --> A
C3["딸린 곳까지 눌러 본다"] --> D
깊이는 검사가 어디까지 닿느냐입니다. 첫 검사는 프로세스 바깥에서 멈춥니다. 둘째 검사는 애플리케이션까지 들어갑니다. 셋째 검사는 애플리케이션이 쓰는 데이터베이스까지 갑니다.
빼는 임계와 되돌리는 임계
한 번 실패했다고 바로 빼지 않습니다. 길이 잠깐 막히거나 그때만 응답이 늦어도 검사는 실패하기 때문입니다. 답을 얼마나 기다렸다가 실패로 칠지 정하는 값이 타임아웃입니다.
연달아 몇 번 실패하면 뺄지를 미리 정해 둡니다. 이렇게 정해 둔 횟수를 임계라고 부릅니다. 빼는 임계와 되돌리는 임계는 따로 정합니다.
둘을 같은 수로 두면 경계에 걸린 대상이 빠졌다 돌아왔다를 되풀이합니다. 이렇게 오가는 것을 플래핑이라고 부릅니다.
stateDiagram-v2
state "후보에 있음" as IN
state "후보에서 빠짐" as OUT
[*] --> IN
IN --> IN: 검사 성공
IN --> OUT: 연달아 임계만큼 실패
OUT --> OUT: 검사는 계속 간다
OUT --> IN: 연달아 임계만큼 성공
한 번의 결과로는 상태가 안 바뀝니다. 후보에서 빠진 대상에도 검사는 계속 가고, 성공이 임계만큼 쌓여야 후보로 돌아옵니다.
깊은 검사가 부르는 무더기 배제
딸린 곳까지 눌러 보는 검사에는 대가가 있습니다. 여러 대가 같은 데이터베이스를 쓰면 그 한 곳이 흔들릴 때 모든 대상의 검사가 동시에 실패합니다. 멀쩡한 대상까지 한꺼번에 후보에서 빠지는 이것이 무더기 배제입니다.
flowchart TD
DB["데이터베이스 · 느려진다"]
subgraph SG["같은 데이터베이스를 쓰는 서버"]
S1["서버 1 · 검사 실패"]
S2["서버 2 · 검사 실패"]
S3["서버 3 · 검사 실패"]
end
DB --> S1
DB --> S2
DB --> S3
S1 --> R["후보가 하나도 안 남는다"]
S2 --> R
S3 --> R
느려진 곳은 하나뿐입니다. 검사는 세 대 모두에서 실패합니다. 감시하는 쪽은 서버가 전부 죽었다고 읽어 전부 뺍니다. 남는 후보가 없어 서비스가 통째로 멈춥니다.
그래서 깊게 물을수록 무엇을 실패로 볼지 가려야 합니다. 딸린 곳이 느려졌다고 자기까지 실패로 답하면 한 곳의 고장이 전체의 고장이 됩니다.
여기서 나온 방식은 물음을 가르는 것입니다. 딸린 곳의 상태는 따로 확인합니다. 검사에는 자기가 요청을 받을 수 있는지만 답하게 둡니다.
물음을 둘로 나누는 방식
검사를 하나만 두면 답이 하나뿐이라 무엇을 할지 못 정합니다. 「지금은 요청을 주지 마라」와 「이 서버를 다시 띄워라」는 다른 처분입니다.
물음을 둘로 나눠 두기도 합니다. 하나는 지금 요청을 받을 준비가 됐는지 묻고, 실패하면 후보에서만 뺍니다. 다른 하나는 되살려야 할 만큼 망가졌는지 묻고, 실패하면 그 서버를 다시 띄웁니다.
두 물음의 느슨함은 다릅니다. 앞엣것은 서버를 막 띄운 동안이나 잠깐 바쁠 때 실패해도 후보에서 잠시 빠질 뿐입니다. 뒤엣것이 잘못 실패하면 멀쩡한 서버가 재시작을 되풀이하므로 더 느슨하게 잡습니다.
검사가 만드는 부하
검사 요청도 서버가 받아 처리하는 요청입니다. 감시하는 쪽이 여럿이고 대상도 여럿이면, 오가는 검사 수는 두 수를 곱한 만큼이 됩니다.
그래서 검사가 하는 일은 작게 잡습니다. 부를 때마다 계산이 많거나 데이터베이스를 뒤지는 주소를 검사에 쓰면, 평소에도 그 부하가 끊임없이 돕니다.
간격을 줄이면 고장을 더 이르게 알아챕니다. 대신 이 부하가 늘고, 잠깐 늦은 응답을 죽음으로 읽는 오판도 늡니다.
고장을 늦게 아는 손해와, 멀쩡한 서버를 잘못 빼는 손해 가운데 무엇이 더 비싼지가 간격을 정합니다.
서버를 내릴 때의 쓰임
서버를 일부러 내릴 때도 이 검사를 씁니다. 끄기 전에 검사를 일부러 실패하게 만들면 감시하는 쪽이 그 서버를 후보에서 먼저 뺍니다.
새 요청이 안 오게 된 뒤에 이미 받아 둔 요청을 다 끝내고 프로세스를 끕니다. 이렇게 하면 내리는 순간에 들어오던 요청이 끊기지 않습니다. 이 순서를 그레이스풀 셧다운이라고 부릅니다.
관련 항목
헬스 체크 결과를 받아 대상을 넣고 빼는 장치
로드 밸런서 · 리버스 프록시 · 부하 분산 · 오케스트레이션 · Kubernetes · nginx · 서비스 디스커버리 · 서비스 메시
헬스 체크가 던지는 물음을 나눠 맡는 검사
프로브 · 준비성 프로브 · 활성 프로브 · 시작 프로브 · 헬스 엔드포인트 · kubelet
헬스 체크와 같은 목적을 다른 방법으로 이루는 살핌 수단
하트비트 · 폴링 · 워치독 · 모니터링 · 관측성 · 골든 시그널
헬스 체크 판정을 좌우하는 시간 설정
타임아웃 · 검사 간격 · 실패 임계 · 지수 백오프 · 재시도
대상을 뺀 뒤에 이어지는 뒤처리
페일오버 · 자가 치유 · 롤링 업데이트 · 블루-그린 배포 · 카나리 배포 · 커넥션 드레이닝 · 그레이스풀 셧다운
헬스 체크 판정이 틀려서 나는 사고
플래핑 · 연쇄 장애 · 재시도 폭풍 · 부분 실패 · 단일 장애점 · 스플릿 브레인
헬스 체크가 지키려는 성질
고가용성 · 가용성 · 신뢰성 · 무중단 배포 · 서비스 수준 목표 · 평균 복구 시간
검사 요청이 타고 흐르는 규약과 값
다른 이름: health check · healthcheck · 헬스체크 · 상태 검사 · 상태 점검