노드, 엣지, 방향
그래프 위에서의 요청으로서의 워크
요청이 만나는 모든 구성 요소는 노드입니다: 클라이언트, DNS 리졸버, CDN 엣지, 리버스 프록시, 백엔드 리플리카, 데이터베이스, 캐시.
두 노드 사이의 모든 연결은 방향성 엣지입니다: 요청은 앞으로 흐르고, 응답은 뒤로 흐릅니다. 순방향 엣지는 열린 TCP 연결과 그 위의 프로토콜을 나타냅니다.
단일 요청은 이 그래프를 통한 경로입니다. 시스템이 요청에 응답하기 위해 수행하는 총 작업량은 각 노드에서의 작업량의 합과 각 엣지의 지연 시간의 합과 같습니다.
왜 중요할까? 그래프를 그리면 코드에서는 볼 수 없었던 속성들이 눈에 띄기 시작합니다:
- 홉 수(Hop count): 경로에 포함된 엣지(연결)의 수입니다. 각 홉은 지연 시간(네트워크 왕복 + 노드 처리)을 증가시킵니다. 홉 수가 적을수록 지연 시간의 하한선이 낮아집니다.
- 입도(In-degree): 노드로 들어오는 엣지의 수입니다. 입도가 높다는 것은 노드가 많은 소스에서 요청을 수신한다는 뜻이며, 이를 위해 확장(scale)하거나 자신을 보호(shield)해야 합니다.
- 출도(Out-degree): 노드에서 나가는 엣지의 수입니다. 출도가 높다는 것은 노드가 많은 다운스트림에 의존한다는 뜻이며, 실패할 수 있는 경로가 많다는 것을 의미합니다.
- 절단 꼭짓점(Cut vertex): 제거하면 그래프가 분리되는 단일 노드입니다. 피어(peer)가 없는 리버스 프록시는 절단 꼭짓점이며, 이를 제거하면 해당 오리진에 대한 모든 접근이 차단됩니다.
트래픽이 집중되는 곳
팬인 = 집중
노드의 진입 차수(in-degree) = 해당 노드로 들어오는 간선의 수입니다. 요청 그래프에서 진입 차수 = 요청을 보내는 상위 소스의 수입니다.
Fan-in 패턴: 많은 클라이언트 -> 하나의 CDN; 많은 CDN 엣지 -> 소수의 오리진 프록시; 많은 프록시 -> 더 적은 백엔드 리플리카; 많은 백엔드 -> 단일 데이터베이스.
집중도가 중요한 이유는 가장 높은 인-도(in-degree) 노드가 가장 많은 총합 부하를 보기 때문이다. 체인의 끝에 있는 DB는 시스템 내 모든 활성 요청에서 온 쿼리를 볼 수 있으며, 이는 개별 사용자가 많은 부하를 생성하지 않는 경우에도 마찬가지이다.
Fan-Out = 의존성
노드의 아웃-도(out-degree) = 그 노드에서 나가는 엣지의 수. 높은 아웃-도는 많은 하류(downstream) 의존성을 의미한다.
데이터베이스, 두 개의 캐시, 세 개의 외부 API, 그리고 하나의 큐를 호출하는 백엔드의 아웃-도는 7이다. (성공적인 응답을 위해 모든 하류가 필수적이라면) 그 성공 확률은 각 하류의 성공 확률의 곱과 거의 같다.
0.999 ^ 7 ≈ 0.993: 99.9%의 가용성을 가진 7개의 하류를 가진 백엔드는 자체 버그가 없더라도 자체 가용성을 약 99.3%까지만 달성할 수 있다.
아웃-도를 줄이는 방법: 하류 결과를 캐싱하기, 비핵심 하류를 선택 사항으로 만들기(우아한 열화, graceful degradation), 병렬화가 가능한 것을 병렬화하기.
비대칭성
Fan-in은 부하를 집중시키고, fan-out은 리스크를 증폭시킵니다. 잘 설계된 그래프는 가장 큰 영향을 미치는 노드에서 이 두 가지를 모두 최소화합니다.
데이터베이스 (가장 높은 fan-in): 부하를 줄이기 위해 공격적으로 캐싱을 수행하세요. 읽기 전용 리플리카를 사용하여 fan-in을 여러 노드에 분산하세요.
오케스트레이터 서비스 (가장 높은 fan-out): 각 종속성에 대해 서킷 브레이커, 우아한 열화(graceful degradation), 벌크헤드(bulkhead) 패턴을 적용하세요.
삽입된 노드가 유연성을 확보한다
간접 참조 = 중간 노드 추가
프록시 없이 그래프는 다음과 같습니다: client -> backend. 클라이언트는 백엔드의 주소를 알아야 합니다. 백엔드를 이동하려면 클라이언트를 업데이트해야 합니다(DNS 또는 구성을 통해). 이는 긴밀한 결합입니다.
프록시가 있으면 그래프는 다음과 같습니다: client -> proxy -> backend. 클라이언트는 프록시만 알면 됩니다. 백엔드를 이동하려면 클라이언트가 아닌 프록시의 업스트림 구성을 업데이트하면 됩니다.
그래프 연산: 기존 엣지 중간에 노드를 삽입합니다. 새로운 엣지 client -> proxy는 안정적이며, 새로운 엣지 proxy -> backend는 이제 팀이 관리하게 됩니다.
기하학적 해석: 간접 참조는 업스트림 변경과 다운스트림 변경을 분리하는 계층을 추가합니다. 각 계층의 엣지는 독립적으로 재배선될 수 있습니다.
간접 참조의 비용
각 레이어는 다음을 추가합니다:
- 지연 시간 1회분(클라이언트에서 프록시로 가는 엣지)
- 경로상 절단 꼭짓점 1개(프록시 자체)
- 설정 오류가 발생할 수 있는 곳 1곳
장점(재배선, 확장, 보호, TLS 종료, 부하 분산)은 일반적으로 비자명한(non-trivial) 시스템의 경우 비용보다 이점이 큽니다. 하지만 한계가 있습니다: 모든 간접 참조 레이어는 또 다른 홉과 또 다른 SPOF(단일 장애 지점) 후보를 추가합니다.
민속 규칙(folklore rule): 모든 문제는 간접 참조 레이어를 추가함으로써 해결할 수 있습니다(간접 참조 레이어가 너무 많은 문제는 제외).
아키텍처를 그래프로 읽기
종합
이제 시스템 아키텍처를 그래프로 읽을 수 있습니다: 홉(hop) 수를 세고, 절단 꼭짓점(cut vertex)을 식별하며, 팬인(fan-in) 집중도를 측정하고, 팬아웃(fan-out)에서 가용성 상한을 계산하며, & 간접화(indirection)의 트레이드오프를 평가합니다.
네 가지 모두 적용해 보세요.
새로운 서비스가 다음과 같은 아키텍처를 가집니다: 클라이언트 -> CDN -> 리버스 프록시 (2개 리플리카) -> 백엔드 티어 (8개 리플리카) -> { DB 프라이머리, 캐시 클러스터 (3개 노드), 외부 API }.
동반 노트
동반 노트
이 기하학(geometric) 레슨은 프록시 & 오리지널(Proxies & Origins) 메인 레슨을 방향 그래프 분석으로 재구성합니다.
이 코스에서 다음 동반 레슨인 geometry_of_stateless_horizontal_scaling은 메인 스케일링 레슨의 레플리카 수학을 바탕으로 대기열 곡선, 리틀의 법칙(Little's Law), 그리고 80% 가용성 무릎 지점(knee)을 기하학적으로 유도합니다.
수고하셨습니다.