LabHub
배우기 러닝패스 코스

クラウドネットワーク設計

L4とL7、そしてヘルスチェックがやっていること

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

로드밸런서는 트래픽을 나누는 장치가 아니라, 고장 난 대상을 빼는 장치 입니다. 나누는 건 부수 효과이고, 값은 헬스체크에서 나옵니다.

概念マップ: 고장 난 대상을 빼는 장치・대상의 상태를 계속 확인하는 장치・그 대상을 목록에서 뺍니다.・경로

왜 이게 필요했나

서버를 두 대로 늘리면 곧바로 두 가지 질문이 생깁니다. 사용자를 어느 쪽으로 보낼 것인가, 그리고 한 대가 죽었다는 것을 무엇이 알아채는가.

DNS 에 주소를 두 개 적어 두는 방법이 가장 싸 보이지만 두 번째 질문에서 무너집니다. DNS 는 대상이 살아 있는지 모르기 때문에 죽은 서버의 주소도 계속 나눠 주고, 레코드를 고쳐도 TTL 이 남아 있는 동안은 반영되지 않습니다.

그래서 트래픽 앞에 대상의 상태를 계속 확인하는 장치를 둡니다. 부하를 나누는 것은 그 장치가 겸사겸사 하는 일이고, 값을 하는 부분은 고장 난 대상을 목록에서 빼는 쪽입니다.

L4 와 L7

L4 (네트워크) L7 (애플리케이션)
보는 것 IP, 포트 HTTP 헤더, 경로, 호스트
할 수 있는 것 전달 경로 기반 라우팅, 호스트 분기, 리다이렉트
TLS 통과시키거나 종료 대개 종료
지연 아주 낮음 약간 더
쓰임 TCP·gRPC·게임·DB 웹 API, 마이크로서비스

/api/* 는 A 서비스로, /img/* 는 B 서비스로 보내는 것은 L7 만 할 수 있습니다. 반대로 극단적인 처리량이 필요하거나 HTTP 가 아니면 L4 입니다.

헬스체크가 진짜 일이다

로드밸런서는 주기적으로 대상에 요청을 보내고, 실패가 임계를 넘으면 그 대상을 목록에서 뺍니다. 이 동작 덕분에 인스턴스 하나가 죽어도 사용자는 모릅니다.

설정에서 중요한 것들.

앞 코스에서 다룬 liveness/readiness 구분이 여기서도 똑같이 적용됩니다 — 로드밸런서의 헬스체크는 readiness 에 가깝습니다. "지금 트래픽을 받아도 되는가" 를 묻는 것이지 "재시작해야 하는가" 를 묻는 게 아닙니다.

연결 드레이닝

인스턴스를 뺄 때 진행 중인 요청을 끊으면 사용자에게 오류가 갑니다. 드레이닝(deregistration delay)은 새 요청은 안 보내되 진행 중인 것은 끝나게 기다리는 시간입니다.

배포 시 순서는 이렇습니다.

1. 대상을 목록에서 빼기 시작 (새 요청 중단)
2. 드레이닝 대기 — 진행 중 요청 완료
3. 애플리케이션에 SIGTERM
4. 정상 종료

3번이 컨테이너 코스에서 다룬 그 SIGTERM 입니다. 드레이닝 시간이 애플리케이션의 정상 종료 시간보다 짧으면 요청이 잘립니다.

DNS 와 TTL

로드밸런서 앞단은 대개 DNS 입니다. 여기서 TTL 이 장애 대응 속도를 정합니다.

그래서 DNS 전환을 장애 대응의 주 수단으로 삼지 않는 것이 원칙입니다. 로드밸런서 안에서 대상을 빼는 편이 훨씬 빠르고 확실합니다.

고정 세션(sticky session)

같은 사용자를 같은 서버로 보내는 기능입니다. 편해 보이지만 대가가 있습니다.

세션을 외부 저장소(Redis 등)에 두면 고정이 필요 없어집니다. 가능하면 그쪽이 낫습니다.

로드밸런서 뒤에서 무너지는 순간들

로드밸런서는 평소에 조용하다가 부하가 걸릴 때와 배포할 때 문제를 드러낸다. 그 두 순간에 무엇이 일어나는지 알면 대부분을 피할 수 있다.

헬스체크가 애플리케이션과 같은 자원을 쓰면 함께 무너진다. /health 가 데이터베이스를 조회하도록 만들어 두면, DB 가 느려지는 순간 모든 인스턴스가 동시에 unhealthy 로 떨어진다. 하나가 아니라 전부 빠지므로 서비스가 통째로 멈춘다. 살아 있는지 보는 검사(liveness)와 트래픽을 받을 준비가 되었는지 보는 검사(readiness)를 나누고, 앞의 것은 의존성을 건드리지 않게 한다.

빠지는 판정은 느슨하게, 돌아오는 판정은 엄격하게. 일시적인 지연으로 인스턴스가 빠지면 남은 인스턴스에 부하가 몰려 그것들도 빠진다. 연쇄가 시작되는 자리다. 실패 임계는 넉넉히(3~5회), 복구 임계는 짧게 잡는 편이 안전하다.

배포 때 502 가 나는 이유는 대개 순서다. 로드밸런서가 그 인스턴스를 목록에서 빼기 전에 애플리케이션이 먼저 죽으면, 그사이에 들어온 요청이 갈 곳을 잃는다. 순서를 뒤집는다: SIGTERM 을 받으면 먼저 헬스체크를 실패로 바꾸고, 로드밸런서가 알아챌 시간(체크 간격 × 임계)만큼 기다린 다음, 그때 처리 중인 요청을 마무리하고 종료한다. 쿠버네티스에서는 preStop 훅과 terminationGracePeriodSeconds 가 이 시간을 만든다.

연결 드레이닝 시간은 가장 긴 요청보다 길어야 한다. 30초로 두었는데 60초짜리 보고서 생성 요청이 있다면, 그 요청은 매 배포마다 끊긴다.

타임아웃은 층마다 다르고, 안쪽이 더 짧아야 한다. 로드밸런서 60초, 애플리케이션 55초, 데이터베이스 50초처럼 안으로 갈수록 짧게 잡는다. 반대로 되어 있으면 로드밸런서가 먼저 끊고, 애플리케이션은 아무도 기다리지 않는 응답을 계속 만들면서 연결을 붙들고 있다.

유휴 타임아웃이 커넥션 풀을 망가뜨린다. 로드밸런서가 조용한 연결을 60초에 끊는데 애플리케이션의 풀은 그 연결을 살아 있다고 믿으면, 다음 요청에서 connection reset 이 난다. 풀의 유휴 시간을 로드밸런서보다 짧게 잡는 것이 답이다.

현장에서 만나는 모습

다음 코스

비용. 지금까지의 모든 결정에 값이 붙어 있고, 그 값을 읽는 법을 다룹니다.