부하를 더 많이 실어 나르는 두 가지 방법
환영
부하가 증가하면 서비스가 부상을 당할 때 운영자에게 선택지가 있습니다. 기존의 기계를 더 크게 만듭니다(더 많은 CPU, 더 많은 RAM, 더 빠른 디스크) 또는 각 작업을 동일하게 수행하는 더 많은 기계를 추가합니다.
첫 번째 경로는 수직 확장(업스케일)로 가고, 두 번째 경로는 수평 확장(아웃스케일)으로 가입니다.
이 강의는 대부분의 현대 웹 아키텍처가 수평을 선택하는 이유를 가르치고, 그 선택이 가능한 작업 부하의 속성이 무엇인지 설명합니다. 그 선택이 가능한 이유는 숨겨진 한 단어에 있습니다: 상태.
이제 이해할 수 있는 내용은 다음과 같습니다:
- 수직 및 수평 확장의 비용 곡선 및 각 경우에서 적합한 곳
- '상태ful'과 'stateless'의 실제 의미 및 하나는 저렴하게 복제되는 이유
- 예상 및 갑작스러운 부하 하에서 복제舰의 규모를 결정하는 수학
- 계층이 큐잉 기침을 넘어 무너지지 않도록 하는 헤드룸 규칙
- 상태가 어디에 있어야 하는지(그것은 사라지지 않음) 및 확장에 필요한 레이어에서 그것을 밀어내는 방법
수평이 임계점을 넘어 이길 수 있는 이유
수직 확장: 더 큰 상자 하나
장점: 간단함. 코드 변경이 없음. 조정 없음. 동일한 프로세스가 더 많은 CPU를 가집니다.
단점: 천장. 상업적으로 이용 가능한 가장 큰 VM은 유한한 RAM 및 코어를 가지고 있습니다. 그 위에, 돈으로 더 많은 헤드룸을 얻을 수 없습니다. 비용은 공급자의 옵션의 스위트 스포트에선 비례하지 않습니다. 단일 기계가 실패하면 전체 서비스가 내려갑니다.
수평 확장: 더 작은 상자 여러 개
장점: 천장 없음(업계가 기계를 구매하고 조정할 의사만 있으면 됩니다. 용량은 복제 수에 선형적으로 추가됩니다. 단일 복제본 실패는 용량의 1/N을 제거하지만 100%가 아닙니다.
단점: 작업 부하가 그것을 지원해야 합니다. 일부 작업 부하(단일 큰 데이터베이스, 상태ful 게임 서버에서 실시간 세션을 유지하는 것)는 수평 확장에 저항합니다. 복제 및 부하 분배는 운영 문제가 됩니다.
크로스오버: 어떤 프로덕션 서비스가 단일 기계 실패를 견디려면 최소 두 대의 기계에서 실행되어야 한다. 두 대 이상을 선택하면 이미 수평 확장을 선택한 것이다. 그 이후의 질문은 '다음 복제본을 추가하는 데 얼마나 저렴하게 할 수 있을까?'가 아니라 '해야 할까?'이다.
주요 촉진자: 기계 자체에 상태를 유지하지 않는 작업负载. 그러면 어떤 복제본도 요청을 처리할 수 있으며, 복제본을 추가하면 조정 없이 용량이 증가한다.
상태ful vs 상태less 실제 사례
상태는 사라지지 않습니다. 그냥 위치가 바뀌어요
상태ful 구성 요소: 정보 손실이 행동을 변경할 경우 유지됩니다. 사용자 계정 저장되는 데이터베이스. 세션 토큰을 저장하는 캐시. 특정 사용자에 대한 길게 실행되는 스트리밍 연결을 고정시키는 작업자.
상태less 구성 요소: 손실이 중요하지 않은 정보를 유지합니다. 요청을 읽고 데이터베이스를 쿼리한 후 응답을 작성하는 웹 계층. 각 요청은 요청 간에 기억하지 않으며, 계층이 기억하지 않습니다.
중요한 인사이트: 시스템에서 상태는 사라지지 않습니다. 상태를 유지하는 데 설계된 계층(데이터베이스, Redis 클러스터, 객체 저장소)로 이동합니다. 트래픽을 직면하는 계층은 상태less가 될 수 있으며, 상태less 계층은 상태를 처리할 수 있으므로 수평으로 확장할 수 있습니다.
실제 테스트: 이 계층에서 무작위로 프로세스를 종료하고 다시 시작한 경우 사용자가 잘못된 답변을 받거나 세션을 잃을 수 있습니까? 그렇다면 상태를 유지합니다. 그렇지 않다면 상태를 유지하지 않습니다.
예시
- Python 웹 프로세스: 요청을 읽고 Postgres를 쿼리한 후 JSON을 반환: 상태less. 상태는 Postgres에서 유지됩니다.
- Python 웹 프로세스: 로컬 메모리에서 사용자 쇼핑 카트를 유지: 상태ful. 프로세스를 종료하면 카트가 사라집니다.
- WebSocket 서버: 채팅 사용자와 열린 연결을 유지: 연결에 대한 상태ful. 프로세스를 종료하면 연결이 끊겨야 합니다.; 클라이언트는 다시 연결해야 합니다. 이러한 경우에도 주의를 기울여 수평으로 확장할 수 있습니다(고정된 세션, 일관된 해싱).
- 리디스 캐시가 포그레스를 앞에 가리는 경우: 캐시 내용이 상태ful하지만, 캐시 미스가 용인 가능한 경우입니다. 레플리카 실패는 캐시 미스이지만, 데이터 손실이 아닙니다.
수평 확장 설계 = 확장해야 하는 계층의 상태를 밀어내는 것입니다.
사용자 계층 감사
팀이 역프록시 뒤에 6개의 백엔드 VM에서 추천 API를 실행하고 있습니다. 애플리케이션: 요청에서 사용자 ID를 읽고, 사용자의 최근 활동을 포그레스에서 가져옵니다, 스코링 알고리즘을 실행하고, 추천 항목 목록을 반환합니다. 두 가지 비표준 행동:
- 애플리케이션: 사용자의 최근 활동 캐시를 프로세스 메모리에 저장하고, 첫 번째 요청에서 사용자의 활동을 가져오고, 이후 요청에서 재사용합니다.
- 애플리케이션: 사용자가 VM #3에 도달하면, 그들의 모든 후속 요청이 VM #3으로 이동합니다(프록시가 쿠키에 따라 고정된 라우팅이 구성되어 있습니다).
레플리카 공식을 사용합니다
가장 간단한 용량 공식
계층이 상태가 없게 되면 그 크기를 산술 연산으로 결정할 수 있습니다. 일정한 로드가 정상 작동에서 지속적으로 도착하고 빠져나오도록 충분한 복제본이 필요합니다. 급증에도 여유가 있습니다.
공식:
복제본 = ⌈ (peak_load × surge_factor) / per_replica_capacity ⌉ + headroom
여기서:
- peak_load: 정상 작동에서 예상되는 최대 지속적인 요청/초
- surge_factor: 예측 가능한 트래픽에 대해 1.5x에서 2x로 일반적으로 사용되는 짧은 급증 위에 곱셈자, 바이럴/예측할 수 없는 경우 3x 이상
- per_replica_capacity: 적절한 레이턴시 및 사용률에서 하나의 복제본이 처리할 수 있는 요청/초(일반적으로 70% CPU에서 측정됨,飽和점에서 측정하지 않음)
- headroom: 몇 개의 복제본 실패가 계층을 붕괴시키지 않도록 추가 복제본(작은 함정에 대해 1-2개, 더 큰 경우 10-20%)
작업 예시: 백엔드가 각 레플리카당 100 req/s를 처리하고 70% CPU를 사용합니다. 최대 처리량은 600 req/s입니다. 가끔 2배의 급증이 예상됩니다. 2개의 레플리카 실패에도 서비스를 유지하려면 합니다.
레플리카 수 = ⌈ (600 × 2) / 100 ⌉ + 2 = 12 + 2 = 14 레플리카
80% 규칙
레플리카당 처리량은 상화가 아닌 70-80% CPU 사용률에서 측정하십시오. 100% 사용률에서 측정하지 마십시오.
80% 활용률을 넘어가면 대기열 곡선이 급격히 상승합니다: 60% 활용률에서 대기열이 10ms로 처리되면 90% 활용률에서 80ms로 처리됩니다. 대기 시간이 먼저 터지게 됩니다. (상태없는 수평 확장에 관한 동반 강의에서는 이 곡선을 수학적으로 도출합니다.)
자동 확장 vs 정적 배포
정적: 최대 처리량 × 급증 여유를 고려하여 낮은 활용률로 실행되는 비용을 수용합니다.
자동 확장: 관찰된 활용률, 대상 대기 시간 또는 대기열 깊이 기반으로 컨트롤러가 레플리카를 추가하고 제거합니다.
자동 확장 주의사항: 새로운 레플리카가 2분이 걸릴 경우, 자동 확장은 30초의 급증에 응답할 수 없습니다. 성숙한 자동 확장은 확장 상승 임계값 바로 아래에 예비 배포된 레플리카 풀을 유지합니다.
새 서비스를 위한 클러스터 크기 설정
당신의 팀은 비디오 메타데이터 API를 출시할 계획입니다. 벤치마크 결과에 따르면 단일 레플리카당 250 req/s를 70% CPU와 50ms p99 대기 시간으로 처리할 수 있습니다. 마케팅은 최대 처리량이 프라임 타임 시간대에 4,000 req/s로 예상됩니다. 계획된 홍보 이벤트는 짧은 시간 동안 3배의 급증을 예상합니다. 3개 동시 레플리카 실패에도 서비스를 유지하고 생존자들에게는 80% 활용률을 초과하지 않도록 합니다.
냉각 시작, 느린 배출 및 기타 실용적인 가장자리
실용적인 클러스터는 실용적인 가장자리를 가지고 있습니다
계산식은 레플리카가 즉시 나타나고, 트래픽을 즉시 받아들여, 트래픽을 즉시 방출한다고 가정합니다. 이러한 조건은 실제 프로덕션에서 적용되지 않습니다.
콜드 스타트: 새로운 복제본이 OS를 부팅하고 프로세스를 시작하고 구성 파일을 로드하고 캐시를 따뜻하게 하고 건강 검사를 통과해야 합니다. 컨테이너 재시작에서는 5초, 완전한 VM 부팅 및 이미지 풀에서는 5분까지 걸릴 수 있습니다. 오토스케일링은 이 지연 시간보다 짧은 버스트에 응답할 수 없습니다.
느린 배출: 복제본이 풀에서 제거되는 경우 인플라이 요청을 완료하는 데 시간이 필요합니다. 그렇지 않으면 사용자가 잘린 응답을 볼 수 있습니다. 역 프록시에서는 배출을 지원합니다(새로운 요청을 수락 중지, 활성한 것들을 마무리)하지만 몇 초에서 몇 분까지 걸릴 수 있습니다.
워밍 풀: 생산 부대는 신호에 따라 트래픽을 받을 준비가 된 미리 프로비전된但비어있는 복제본을 유지합니다. 일정한 지속 비용을 교환하여 빠른 대응을 가능하게 합니다.
배출 중단 vs 즉각적인 킬: 부드러운 종료가 중요합니다. SIGTERM이 배출을 트리거하면 SIGKILL보다 더 오래 걸리지만 사용자의 요청을 끊기지 않습니다.
건강 검사 윈도우: 복제본이 시작된 직후에는 데이터베이스 연결 풀이 따뜻해질 때까지 첫 번째 건강 검사를 통과할 수 있습니다; 프록시는 실제 경로를 테스트하기 위해 건강 검사를 조정합니다. 프로세스 생존만 확인하는 것이 아니라.
끈기 스크립: nominally 상태가 없는 계층은 시간이 지남에 따라 끈기를 얻습니다(캐디, DNS 해결기 캐시). '동일한 복제본'이지만 다르게 행동하는 것으로 의심하십시오.
워밍 풀 또는 반응형 오토스케일링?
당신의 비디오 메타데이터 API(이전 질문에서 사용한 것과 동일, 정상 최대치 및 급증 시 51개의 복제수로 크기 조정)는 새로운 바이럴 비디오가 업로드될 때마다 5x 정상 부하로 30초 간 급증합니다. 오토스케일링은 현재 90초가 걸리고 새로운 복제본을 차갑게(이미지 풀 + 워밍업) 시작합니다. 90초 간격 동안 대기 시간이 급격히 높아지고 일부 요청이 타임아웃됩니다.
제한 조건 하에 상태가 없는 계층을 설계하십시오
합성
당신은 왜 수평 확장이 작은 임계점을 넘어선다고 배웠고, 상태가 실제로 어떤 의미인지 이해하고, 예상 및 충격 부하 하에서 플릿을 크기 조절하는 방법, 그리고 수평 확장이 가장자리에서 부서지는 곳을 알게 되었습니다.
모든 네 가지를 적용하십시오.
feed.example.com의 소셜 피드 API 백엔드 계층을 설계하십시오. 제약 조건: 각 레플리카의 처리 능력 200 req/s, CPU 사용률 70%; 예상 최대 부하 1500 req/s; 충격 인수 2.5x(일시적인 트렌딩 스토리); 2개 동시 레플리카 실패에 대비; 냉각 시작 시간 60초; 급증은 45초 동안 지속; 예산은 일부 비활성 용량을 허용하지만 영구 예약량은 2.5배로 확장하지 않습니다.
이 과정의 다음 단계
이 과정의 다음 단계
이제 무상태 계층의 작동 모델을 갖추게 되었습니다. 왜 확장되는지, 어떻게 크기를 조절하는지, 어떤 경계에서 실패하는지, 그리고 상태를 레플리카가 증가하는 계층에서 이동시켜야 하는 곳을 알게 되었습니다.
이 과정의 다음 수업(cs_distsys_ingress_egress_separation)은 상태가 있는 무상태 계층에서 발생할 수 있는 놀라운 실패를 해결하는 더 섬세한 문제를 다룹니다. 입력 및 출력 트래픽이 동일한 네트워크 경로를 공유하는 경우에도 완벽하게 크기 조절된 무상태 계층이 실패할 수 있습니다. 예를 들어, 프록시가 자신自身에 연결하려고 할 때 이 문제를 해결하려면 한 계층을 두 가지 다른 책임으로 나누어야 합니다.
보조 수업: geometry_of_stateless_horizontal_scaling은 큐잉 곡선, 레플리카 집합에 적용된 리틀의 법칙, 그리고 80% 활용률 무릎의 기하학적 의미를 도출합니다.
잘 했어요. 계속 진행하세요.