빙응의 공부 블로그

[OSTEP 영속성(persistence)]분산 시스템 본문

CS/운영체제

[OSTEP 영속성(persistence)]분산 시스템

빙응이 2025. 7. 28. 22:41

📝서론 

분산 시스템은 전세계의 구조를 바꿨습니다. 웹 브라우저가 지구 상 어딘가에 있는 웹 서버에 접속하면 

클라이언트/서버 분산 시스템이라는 구조에 한 구성원이 되는 것이다.

 

분산 시스템을 개발할 때 몇 가지 새로운 도전거리가 생겨난다. 우리는 그 중 "실패"에 관해서 알아보자.

 

핵심 질문 : 구성 요소가 실패하더라도 동작하는 시스템을 어떻게 만들까?

 

  • 분산 시스템의 핵심 사안은 여러 개가 존재한다.
    1. 실패와 고장의 극복
    2. 시스템 성능
    3. 보안

📝통신의 기본

최신 네트워킹의 핵심 가정은  통신은 신뢰할 수 없다는 것이다.

 

이것에는 여러개의 이유가 있다.

  1.  패킷 손실
    • 네트워크는 패킷 단위로 데이터를 전송하는데, 중간 유실이 가능하다.
  2. 지연, 재정렬, 중복
    • 패킷이 전송 순서와 다르게 도착하거나, 중복되거나, 지연될 수 있다.
  3. 중간 노드 장애/ 네트워크 분리
    • 라우터나 중간 서버가 고장 나거나, 네트워크가 분리될 수 있다.
    • 이로 인해 메시지 도달이 안될 수 있다.
  4. 전송 오류
  5. 보안 문제
    • 데이터가 중간에 탈취되거나 변조될 수 있다.

 

이제 이러한 네트워킹의 근본적인 문제를 어떻게 해결하는지 알아보자 

 

📝신뢰할 수 없는 통신 계층

가장 간단한 방법은 아무런 조치를 취하지 않는 것이다. 

신뢰할 수 없는 계층에 대한 예는 거의 모든 현대 시스템에 존재하는 UDP/IP 네트워크 시스템이다.

 

만약 사용한다면 패킷을 잃어버리는 경우들을 만나게 되며 메시지는 도달하지 못한다.

발신 측은 그렇기 때문에 손실에 대해서 전혀 알 수 없다. 하지만 UDP가 모든 실패에 대비를 못하는 건 아니다. 예를 들어 UDP는 체크섬을 포함하기에 일부 패킷 손상을 검출 가능하다.

 

 

📝신뢰할 수 있는 통신 계층

핵심 질문 : 발신자가 수신자가 메시지를 수신했다는 것을 어떻게 알 수 있는가?

 

쉽게 생각해보면 물어보는 것이다. 우리는 이것을 Ack(acknowledgement)라고 부른다.

간단하게 발신자가 메시지를 수신자에게 보내면 수신자는 받았다는 확인 메시지를 보내는 것이다. 

발신자가 Ack를 못 받았다면?

그래서 타임아웃이라는 추가적인 장치를 사용한다. 일정 시간이 지나면 다시 메시지를 전송하는 것이다.

 

이 과정에서 수신자는 똑같은 메시지를 두번 받는다. 그렇기에 신뢰성을 목표로하면 수신측도 각 메시지를 정확히 한번만 받는다는 보장이 필요하다.

 

 

가장 흔히 사용되는 신뢰할 수 있는 통신 계층은 TCP/IP 또는 TCP라고 부른다. 

TCP는 위에서 설명한 방법에 더해 더 정교한 기법을 사용한다. 

 

📝통신의 추상화

기본 메시징 계층을 다루었으니 이제 분산 시스템 구현에 필요한 통신 개념을 알아보자.

 

통신의 추상화란?
  • 분산시스템을 쉽게 만들기 위해 통신을 더 높은 수준으로 추상화하려는 시도이다.
  • 복잡한 메시지 전송을 마치 로컬 메모리 접근처럼 보이게 하는 방식들이 존재한다.(예. DSM)
예시 DSM(Distributed Shared Memory)
"분산된 여러 컴퓨터가 하나의 가상 메모리 공간을 공유하는 것처럼 보이게 하는 것"
  • 각각의 컴퓨터가 하나의 가상 주소 공간을 공유하는 구조
  • 마치 멀티스레드 프로그램처럼 코딩할 수 있다.
  • 실제로는 메모리 접근 시 다른 기계에 페이지 요청 -> 페이지 폴트 + 통신 발생

문제점

  • 실패 처리가 매우 어려움
  • 성능 저하
  • 실제로 많이 쓰이지 않음

 

📝 Remote Procedure Call(RPC)

앞서 소개한 DSM로는 현재 시점에서 신뢰할 수 있는 통신 계층을 만들지 않는다. 

대신 프로그래밍 언어 차원의 개념을 사용해서 만들게 되었다.

 

RPC
  • 원격 프로시스 호출기
  • 네트워크 상에 있는 다른 컴퓨터의 함수를, 마치 로컬 함수 호출하듯이 호출할 수 있게 해주는 프로그래밍 모델
  • 분산 시스템 개발에서 운영체제 레벨보다 프로그래밍 언어 차원의 추상화가 더 자연스럽고 효율적이라 인식해서 출발
RPC 구성
스텁 생성기 (Stub Generator) 인터페이스 정의를 받아 클라이언트/서버 스텁 코드를 자동 생성 (인자들을 직렬화/역직렬화하는 코드 포함) 즉, 어뎁터 역할
런타임 라이브러리 메시지 전송, 연결 관리, 동기/비동기 호출, 오류 처리 등 실행 시간 동작 담당

 

RPC 동작 방식

클라이언트 측

  • 클라이언트 코드는 마치 로컬 함수 호출하듯 func1(arg) 호출
  • 클라이언트 스텁은
    • 호출 정보를 메시지 버퍼에 직렬화(marshaling)
    • 함수 식별자와 인자 포함
    • 메시지를 서버로 전송
    • 응답을 기다림 (대부분 동기 호출)
    • 응답 메시지에서 결과 역직렬화(unmarshaling) 후 클라이언트에 반환

서버 측

  • 서버 스텁이 도착한 메시지에서 호출 정보 추출 (역직렬화)
  • 실제 프로시저(함수) 호출
  • 결과를 메시지 버퍼에 직렬화해 클라이언트에 응답 전송

 

📌 고려사항

RPC에서는 고려해야할 몇 가지 중요한 문제들이 있다.

 

스텝 컴파일러

  1. 복잡한 구조의 인자나 다수의 인자 전달 문제
    • 즉, 복잡한 자료 구조를 어떻게 패키지화해서 전송하는가?
  2. 병행성을 고려하여 서버를 구성하는 것
    • 흔한 구성 방식은 쓰레드 풀을 사용하는 것이다.

런타임 라이브러리

  1. 서비스 위치 찾기 (Naming)
    • 분산 시스템에서 원격 서비스가 어디에 있는지 알아내는 문제
    • 가장 기본적으로는 IP 주소 + 포트 번호를 사용한다.
    • 더 발전된 시스템은 DNS, 분산 네임 서비스 등 사용
  2. 전송 프로토콜 선택
    • RPC를 TCP 위에서 구현하면 데이터 전달은 확실하나
      • 확인 메시지, 타임아웃, 재전송 때문에 성능 저하 발생
    • RPC를 UDP 위에 구현하는 경우
      • RPC 계층이 타임아웃, 재시도, ACK 같은 신뢰성 메커니즘 직접 처리 

 

🔥이렇게만 보면 TCP 위에 RPC를 구현할 이유가 없지 않을까?

결론만 말하면 전혀 그렇지 않다. 여기서는 구분이 필요하다.

  • TCP는 비유하자면 네트워크에서 ‘길’(데이터 전송 경로)을 신뢰성 있게 보장하는 전송 계층 프로토콜이다..
  • RPC는 분산 시스템에서 원격 함수 호출을 가능하게 하여 분산 자원을 마치 로컬처럼 쓸 수 있게 하는 ‘가상화’ 기술 이다. 

즉 TCP가 데이터를 제대로 보내고 받는 통신이라면 RPC는 복잡한 네트워크를 추상화하는 프로그래밍 모델이다.

둘은 서로 보완하는 관계이지만 TCP에서는 중복되는 기능이 많기에 오버헤드도 증가하는 현상이 있어 UDP에서 자주 사용된다.