하트비트
고친 사람 github-actions[bot]
하트비트는 아직 살아 있다고 알리려고 짧은 신호를 일정한 간격으로 계속 보냅니다. 받는 쪽은 그 신호가 제때 오는 동안만 상대를 살아 있는 것으로 셉니다. 신호가 한동안 안 오면 죽었다고 보고 뒤처리를 시작합니다.
쉽고 빠른 이해
하트비트는 「나 아직 살아 있다」를 짧은 간격으로 되풀이해 말해 주는 신호입니다. 서버 한 대가 몇 초마다 감시하는 쪽에 한 줄을 보내면, 감시하는 쪽은 그 줄이 오는 동안 그 서버를 정상으로 봅니다.
이게 없으면 상대가 죽었는지 알 길이 없습니다. 이미 죽은 서버로 요청을 계속 보내게 되고, 대표 노릇을 하던 서버가 죽었는데도 아무도 새 대표를 뽑지 않습니다.
어떻게 도나:
- 보내는 쪽이 정해진 간격마다 짧은 신호를 보냅니다
- 받는 쪽은 신호가 올 때마다 기다림 시계를 처음으로 되돌립니다
- 정해 둔 시간 동안 신호가 안 오면 죽은 것으로 보고 뒤처리를 시작합니다
대가가 있습니다. 죽지 않았는데 죽었다고 판정하는 일을 없앨 수 없습니다. 네트워크가 잠깐 막히거나 상대가 잠깐 멈추기만 해도 신호가 늦기 때문입니다. 이르게 알아채려고 간격을 줄이면 이 오판이 늘고, 오판을 줄이려고 오래 기다리면 죽은 것을 늦게 압니다.
상세
이 절은 하트비트가 어떤 모양의 신호인지부터 봅니다. 그다음 살아 있는지 알아내는 다른 방법과 무엇이 다른지 가르고, 보내는 간격과 기다리는 시간을 어떻게 정하는지 봅니다. 끝으로 이 판정이 왜 틀릴 수 있는지와 신호에 무엇을 실어 보내는지를 짚습니다.
이 편에서는 신호를 보내는 쪽을 「보내는 쪽」, 받아서 판정하는 쪽을 「감시하는 쪽」이라고 부르겠습니다. 서버 한 대나 프로그램 하나처럼 신호를 주고받는 단위는 노드라고 부릅니다.
비유로 보는 하트비트
이름은 병원의 심박 모니터에서 왔습니다. 환자의 심장이 뛸 때마다 화면에 신호가 하나씩 찍히고, 신호가 멈추면 경보가 울립니다. 간호사는 환자를 계속 들여다보지 않아도 화면만 보고 상태를 압니다.
살아 있는지 모르면 곤란한 것들
한 대로 도는 프로그램은 자기가 살아 있는지 물을 일이 없습니다. 여러 대가 함께 일하면 이야기가 달라집니다. 옆 노드가 아직 일하고 있는지 모르면 다음 판단을 내릴 수 없습니다.
죽은 노드를 살아 있다고 알면 그쪽으로 요청을 계속 보냅니다. 요청은 응답 없이 쌓이고, 그 몫을 대신 맡을 노드는 아무도 나서지 않습니다. 대표를 두는 시스템이라면 대표가 죽은 채로 전체가 멈춰 있게 됩니다.
반대로 살아 있는 노드를 죽었다고 알면 멀쩡한 일손을 떼어 냅니다. 그래서 「지금 살아 있나」를 값싸게 계속 확인할 방법이 필요합니다. 하트비트가 그 방법입니다.
신호는 짧고 규칙적이다
하트비트 메시지에는 담을 내용이 별로 없습니다. 무엇이 적혀 있느냐보다 제때 도착했느냐가 뜻을 지기 때문입니다. 그래서 보내는 쪽 이름 정도만 담은 아주 작은 메시지를 씁니다.
이 신호를 다루려면 시간 값 두 개를 정해야 합니다. 하나는 얼마나 자주 보낼지 정한 보내는 간격이고, 다른 하나는 얼마나 안 오면 죽었다고 볼지 정한 기다리는 시간입니다. 뒤엣것을 타임아웃이라고 부릅니다.
기다리는 시간은 보내는 간격보다 길게 잡습니다. 메시지 하나는 길이 잠깐 막히기만 해도 사라지거나 늦을 수 있습니다. 한 번 놓친 것으로 바로 죽었다고 판정하면 오판이 잦아지니, 두세 번 놓칠 만큼 기다렸다가 판정합니다.
sequenceDiagram
participant 보내는쪽
participant 감시하는쪽
보내는쪽->>감시하는쪽: 하트비트
Note over 감시하는쪽: 기다림 시계를 되돌린다
보내는쪽->>감시하는쪽: 하트비트
Note over 감시하는쪽: 다시 되돌린다
Note over 보내는쪽: 여기서 멈춘다
Note over 감시하는쪽: 기다리는 시간이 다 지난다
Note over 감시하는쪽: 죽은 것으로 판정한다
그림의 흐름을 말로 옮기면 이렇습니다. 신호가 올 때마다 감시하는 쪽은 기다림 시계를 처음으로 되돌립니다. 신호가 끊기면 시계가 안 돌아가고, 정해 둔 시간이 다 지나면 그때 죽었다고 판정합니다.
알리는 쪽과 물어보는 쪽
살아 있는지 알아내는 방법은 크게 둘로 갈립니다. 하트비트는 살아 있는 쪽이 스스로 알리는 방법입니다. 감시하는 쪽이 먼저 물어보고 답을 받는 방법도 있고, 그것을 헬스 체크라고 부릅니다.
| 하트비트 | 헬스 체크 | |
|---|---|---|
| 누가 먼저 말하나 | 살아 있는 쪽이 알린다 | 감시하는 쪽이 묻는다 |
| 대상이 늘면 | 받기만 하면 된다 | 물어볼 곳이 그만큼 늘어난다 |
| 무엇을 알 수 있나 | 살아는 있다 | 물어본 일을 해낼 수 있다 |
셋째 줄이 둘을 가르는 대목입니다. 하트비트는 신호를 보내는 부분만 살아 있어도 계속 옵니다. 정작 요청을 처리하는 부분이 막혀 있어도 감시하는 쪽은 그 노드를 정상으로 봅니다. 그래서 둘을 같이 쓰는 경우가 많습니다.
간격을 줄일수록 오판이 는다
보내는 간격을 줄이면 죽은 것을 더 이르게 알아챕니다. 대신 오가는 신호 수가 그만큼 늘어 네트워크와 감시하는 쪽에 부담이 됩니다. 노드가 많아질수록 이 부담이 눈에 띄게 커집니다.
오판도 같이 늘어납니다. 기다리는 시간이 짧아지면 잠깐 늦은 신호가 죽음으로 읽히기 때문입니다. 멀쩡한 노드를 죽었다고 판정하면 필요 없는 뒤처리가 돌고, 그 뒤처리가 다시 부하를 만듭니다.
그래서 두 값은 함께 정합니다. 몇 초 늦게 알아채도 괜찮은 시스템은 간격을 넉넉히 잡고, 곧바로 알아채야 하는 시스템은 오판을 감수하고 짧게 잡습니다. 어느 쪽으로 갈지는 죽음을 늦게 아는 값과 오판하는 값 가운데 무엇이 더 비싼지가 정합니다.
신호가 안 온다고 죽은 것은 아니다
신호를 받아 살았는지 죽었는지 판정하는 일을 장애 감지라고 부릅니다. 하트비트로 하는 장애 감지는 틀릴 수 있고, 그 틀림을 없앨 방법이 없습니다.
원인은 셋입니다. 첫째, 네트워크가 붐비면 신호가 늦거나 사라집니다.
둘째, 보내는 쪽이 잠깐 서는 일이 있습니다. GC 멈춤이 그런 경우로, 쓰레기 수집(Garbage Collection)이 도는 동안 프로그램 전체가 멈춰 신호를 못 보냅니다.
셋째, 한쪽 방향만 끊길 수 있습니다. 감시하는 쪽으로 가는 길만 막히면 노드는 멀쩡히 일하는데 죽은 것으로 읽힙니다. 네트워크가 갈라져 서로 못 보게 된 상태를 분단이라고 부릅니다.
오판이 무서운 까닭은 뒤처리에 있습니다. 죽었다고 판정하면 대개 그 몫을 대신할 노드를 세웁니다. 원래 노드가 살아 있는데 대신할 노드까지 서면 같은 일을 둘이 하게 되고, 이 사고를 스플릿 브레인이라고 부릅니다.
그래서 하트비트가 주는 것과 못 주는 것을 갈라 두어야 합니다. 신호가 왔다는 것은 그 노드가 살아 있었다는 증거입니다. 신호가 안 온다는 것은 죽었다는 증명이 못 됩니다. 판정은 어디까지나 추측이고, 시스템은 그 추측이 틀릴 수 있다는 전제 위에서 뒤처리를 짭니다.
의심 단계를 두는 방식
그래서 살았음과 죽음 두 값으로만 두지 않고 그 사이에 의심 단계를 두기도 합니다. 신호가 한 번 늦으면 의심으로 옮기고, 그 뒤로도 안 오면 죽음으로 옮깁니다. 의심하는 동안 다시 신호가 오면 정상으로 되돌립니다.
stateDiagram-v2
[*] --> 정상
정상 --> 의심: 기다리는 시간이 지났다
의심 --> 정상: 신호가 다시 왔다
의심 --> 죽음: 더 기다려도 안 온다
죽음 --> 정상: 돌아와 다시 신호를 보낸다
의심 단계를 두면 잠깐의 지연 때문에 뒤처리가 도는 일이 줄어듭니다. 대신 죽음으로 옮기기까지 시간이 더 걸립니다. 앞 절의 맞바꿈이 단계를 하나 더 두는 방식으로 나타난 것입니다.
신호에 무엇을 실어 보내나
하트비트에 몇 가지를 더 싣기도 합니다. 순번을 실으면 감시하는 쪽이 신호를 몇 개나 놓쳤는지 셀 수 있습니다. 뒤늦게 도착한 옛 신호를 가려내기도 합니다.
대표를 두는 시스템에서는 이 신호가 「내가 아직 대표다」를 겸합니다. 여러 노드 가운데 한 대를 대표로 정하는 일을 리더 선출이라고 하는데, 대표가 보내던 하트비트가 끊기면 남은 노드들이 새 대표를 뽑기 시작합니다. 그래서 신호에 대표가 몇 번째인지 가리키는 번호를 같이 싣습니다.
기한을 실어 보내는 방식도 있습니다. 「이 시각까지는 내가 맡는다」는 약속을 리스라고 부릅니다. 하트비트가 그 기한을 계속 미뤄 두고, 신호가 끊기면 기한이 지나면서 맡고 있던 몫이 저절로 풀립니다.
연결을 살려 두는 쓰임
하트비트라는 말은 연결 하나를 살려 두는 신호에도 씁니다. 오래 아무것도 안 오가는 연결은 중간 장비가 끊어 버리기도 하고, 상대가 이미 사라졌는데 이쪽만 모르고 있기도 합니다. 빈 신호를 이따금 흘려보내면 그 둘을 막습니다.
이 쓰임은 KeepAlive(keep-alive, 연결 유지)라는 이름으로도 불립니다. 하는 일은 같습니다. 살아 있음을 알리는 짧은 신호를 일정한 간격으로 보내는 것이고, 살피는 대상이 노드가 아니라 연결 하나일 뿐입니다.
모두가 모두에게 보내지는 않는다
노드가 몇 대뿐이면 서로에게 하트비트를 보내도 됩니다. 노드 수가 늘면 이 방식이 버티지 못합니다. 주고받는 신호 수가 노드 수의 제곱으로 늘기 때문입니다.
그래서 큰 무리에서는 보내는 상대를 줄입니다. 저마다 몇 대만 골라 보내고, 알아낸 것을 옆 노드에 조금씩 옮겨 결국 모두가 같은 것을 알게 만듭니다. 소문이 번지듯 퍼뜨리는 이 방식을 가십이라고 부릅니다.
노드를 고리 모양으로 세워 자기 옆 노드만 살피게 하는 방식도 있습니다. 어느 쪽이든 노드 하나가 보내는 신호 수를 몇 개로 묶어 두는 것이 목적입니다.
관련 항목
하트비트를 주고받는 참여자
노드 · 프로세스 · 리더 · 팔로워 · 레플리카 · 클러스터
하트비트와 짝을 이루는 시간 설정
타임아웃 · 선거 시한 · 무작위 타임아웃 · 백오프 · 클럭 드리프트
같은 목적을 다른 방법으로 이루는 살핌 수단
헬스 체크 · KeepAlive · 폴링 · 워치독 · 데드맨 스위치 · 파이 누적 장애 감지기
하트비트가 끊긴 뒤 도는 절차
장애 감지 · 리더 선출 · 페일오버 · 재조정 · 멤버십
하트비트 판정이 틀려서 나는 사고
스플릿 브레인 · 분단 · 좀비 리더 · GC 멈춤 · 정족수 상실 · 플래핑
하트비트를 규칙에 넣은 프로토콜과 알고리즘
Raft · 가십 · SWIM · 합의 · 장애 감지기
하트비트가 기대는 장치와 지키려는 성질
다른 이름: heartbeat · 하트비트 신호 · 생존 신호