주요 컨텐츠로 이동
데이터 엔지니어링

Lakebase Postgres 오토스케일링

실시간으로 Postgres를 확장하는 방법에 대한 심층 분석

작성자: Carlota Soto

  • 오토스케일링 아키텍처 요구사항
  • 용량을 확장 및 축소해야 하는 시점
  • PostgreSQL을 중단하지 않고 용량을 조정하는 방법

워크로드를 알기도 전에 데이터베이스 인스턴스 크기를 선택하는 것은 오래된 구축 패턴입니다. 이 프로세스는 일반적으로 불안정하며, 특히 컴퓨팅이 사치품이 되어가고 있는 지금 컴퓨팅 자원을 매우 낭비하는 것처럼 느껴집니다.

Lakebase Postgres는 오토스케일링 덕분에 크기 조정 과정을 완전히 생략합니다. 오토스케일링의 빠른 반응성은 인플레이스(in-place) VM 크기 조정과 CPU, 메모리, 데이터베이스의 워킹 세트(working set)를 추적하는 알고리즘에서 비롯됩니다.

image9.png

임의의 Lakebase Postgres 데이터베이스 샘플에서 오토스케일링이 작동하는 모습입니다. 단 1시간 동안의 기록이라는 점에 유의하세요.

아키텍처 요구사항

기존 Postgres는 머신 및 해당 디스크에 종속된 상태 저장(stateful) 프로세스로 실행됩니다. 머신이 실행과 영구 상태를 모두 소유하기 때문에 해당 머신을 교체하거나 크기를 조정하는 것은 데이터베이스 작업입니다. 하지만 Lakebase Postgres 아키텍처는 이러한 책임을 분리합니다:

  • 컴퓨팅 레이어는 Postgres를 실행하고 쿼리를 수행합니다. 지연 시간이 짧은 액세스를 위해 RAM과 로컬 NVMe를 사용하며, 영구 상태를 소유하지 않습니다.
  • 스토리지 레이어는 영구성 및 이력을 관리합니다. WAL은 SSD에서 실행되는 safekeeper에 의해 복제되고, 페이지 서버(역시 SSD)는 페이지 버전을 재구성하며, 오브젝트 스토리지는 장기 불변 레코드를 보관합니다. (이 블로그 게시물은 컴퓨팅에 초점을 맞추고 있지만, 관심이 있으시다면 스토리지 부분에 대해 자세히 다룬 글도 확인해 보세요.)

따라서 컴퓨팅 노드는 그 아래의 데이터베이스를 이동하지 않고도 시작, 중지, 이동 또는 크기 변경을 수행할 수 있습니다. 이는 필수적인 기반입니다.

image8.png

이제 오토스케일링을 구현할 때 고려해야 할 두 가지 부분이 있습니다. 첫째는 용량을 언제 늘리고 줄일지 결정하는 것이고, 둘째는 Postgres를 중단하지 않고 어떻게 처리할지입니다.

두 가지를 순서대로 살펴보겠습니다.

파트 I: 알고리즘

세 가지 오토스케일링 신호

크기 조정 시기를 추론하기 위해 Lakebase Postgres 오토스케일링 알고리즘은 세 가지 신호를 추적하며, 각 신호는 고유한 대상 컴퓨팅 크기를 생성합니다.

  1. CPU 부하: cpuGoalCU
  2. 메모리 사용량: memGoalCU
  3. 컴퓨팅 캐시 워킹 세트 크기: lfcGoalCU

최종 스케일링 대상은 이 세 가지 중 가장 큰 값이며, 사용자가 해당 데이터베이스에 대해 구성한 최소 및 최대 컴퓨팅 크기(오토스케일링 제한)로 제한됩니다.

CPU (cpuGoalCU)

CPU는 세 가지 신호 중 가장 직관적입니다. 알고리즘은 프로세서가 얼마나 열심히 작동하고 있는지 면밀히 감시합니다.

  • 5초마다 autoscaler-agent는 VM의 1분 평균 CPU 부하를 읽습니다.
  • CPU 목표는 해당 부하를 사용 가능한 CPU 용량의 90% 이하로 유지하는 것입니다.
  • 부하가 해당 목표 이상으로 상승하면 cpuGoalCU이(가) 증가합니다. 지속 부하가 감소하면 목표도 함께 감소합니다.

1분 평균을 사용하면 매우 짧은 변동은 필터링하면서도 수요의 유의미한 변화에 대응할 수 있습니다. 5초의 폴링 주기를 통해 시스템은 해당 평균이 이동함에 따라 목표를 업데이트할 수 있습니다.

그러나 CPU만으로는 Postgres를 적절하게 오토스케일링하기에 충분하지 않습니다. 네트워크를 통해 데이터가 도착하기를 기다리는 쿼리는 성능이 저하되는 동안에도 낮은 CPU 사용량을 보일 수 있습니다. 알고리즘은 메모리 및 캐시 압박도 고려해야 합니다.

메모리 (memGoalCU)

메모리는 CPU와 다른 실패 모드를 가집니다. 수요가 일시적으로 사용 가능한 CPU를 초과하면 쿼리가 느려지지만, Postgres가 VM이 가진 것보다 더 많은 메모리를 할당하면 커널이 프로세스를 종료할 수 있습니다. 따라서 오토스케일러는 메모리 고갈에 대해 CPU보다 훨씬 빠른 신호가 필요합니다.

그래서 시스템은 두 가지 주기로 메모리를 감시합니다.

  • 5초마다 autoscaler-agent는 VM의 전반적인 메모리 메트릭을 읽습니다.
  • 100밀리초마다 vm-monitor는 Postgres가 사용하는 메모리를 확인합니다.

메모리 목표는 사용량을 할당된 RAM의 75% 미만으로 유지하는 것입니다. 이러한 여유 공간은 시스템이 새로운 할당에 대응할 수 있는 공간을 제공하고 게스트 운영 체제 및 기타 프로세스를 위한 메모리를 남겨둡니다.

vm-monitor는 제안된 모든 다운스케일링도 확인합니다. 실행 중인 프로세스에 충분한 공간이 남지 않게 된다면 메모리를 제거할 수 없습니다.

약간의 역사: 이 폴링 방식은 cgroup memory.high 이벤트를 기반으로 한 이전 설계를 대체했습니다. memory.high을(를) 초과하면 Linux가 메모리를 회수하고 cgroup 내부의 프로세스를 제한(throttle)하게 되었습니다. 폴링은 시스템에 Postgres 메모리에 대한 100밀리초 단위의 뷰를 제공하면서도 더 예측 가능하고 안정적인 것으로 입증되었습니다.

컴퓨팅 캐시 (lfcGoalCU)

세 번째 신호는 워크로드의 활성 데이터가 Postgres와 가까운 곳에 적합하게 위치하는지 측정합니다. 대략적인 이야기는 다음과 같습니다.

Lakebase Postgres는 스토리지와 컴퓨팅을 분리합니다. 페이지를 로컬에서 사용할 수 없는 경우 컴퓨팅은 페이지 서버에 이를 요청하고, 반환된 페이지는 후속 읽기를 위해 캐시됩니다. 원래 로컬 파일 캐시(Local File Cache) 또는 (LFC)라고 불렀던 컴퓨팅 캐시는 커널 페이지 캐시에 맞게 크기가 조정되는 디스크 기반 캐시입니다. 이는 Postgres 공유 버퍼의 크기 조정 가능한 확장 역할을 합니다. 컴퓨팅이 확장되면 vm-monitor는 캐시를 확장하여 추가된 메모리의 일부를 사용합니다.

많은 OLTP 워크로드의 경우, 워킹 세트가 로컬 메모리에 들어맞으면 성능이 급격히 변화합니다. 이는 CPU 전용 오토스케일링의 맹점을 드러냅니다. 캐시 미스로 인해 쿼리가 네트워크 요청을 대기하게 되고, 이로 인해 CPU 사용량이 감소합니다. 따라서 시스템은 더 큰 캐시가 성능을 향상시킬 수 있는 바로 그 순간에 낮은 CPU 압박을 감지할 수 있습니다. 그래서 Lakebase Postgres에는 Postgres 워킹 세트를 직접 추정하는 세 번째 오토스케일링 신호가 있습니다.

이 부분이 알고리즘에서 가장 흥미로운 부분이므로, 이 추정이 어떻게 작동하는지 살펴보겠습니다.

자세히 보기: Postgres 워킹 세트를 추정하는 방법

워크로드의 워킹 세트는 특정 기간 동안 반복적으로 액세스하는 데이터베이스 및 인덱스 페이지의 집합입니다. 오토스케일링을 목적으로 모든 페이지를 정확하게 계산하려면 너무 많은 메모리가 필요하므로, 이를 해결하는 전통적인 방법은 고정된 소량의 상태를 사용하여 집합 내 고유 항목 수를 추정할 수 있는 확률적 카디널리티 추정기인 HyperLogLog에 의존하는 것입니다.

각 Postgres 페이지 액세스에 대해 표준 HyperLogLog 구현은 다음과 같이 작동합니다.

  1. 페이지 식별자를 해시합니다.
  2. 해시의 첫 번째 비트를 사용하여 레지스터를 선택합니다.
  3. 남은 비트에서 선행 제로(leading zero)의 개수를 셉니다.
  4. 이 관측값이 이전 값을 초과하는 경우 선택된 레지스터를 업데이트합니다.

이러한 레지스터 값의 분포는 얼마나 많은 고유 페이지가 관측되었는지에 대한 추정치를 제공합니다.

image10.png

그러나 오토스케일링에 단순히 HyperLogLog를 사용하는 데는 문제가 있습니다. 표준 HyperLogLog는 증가하기만 합니다. 레지스터가 값을 관측하고 나면 어떤 항목이 그 값을 생성했는지 또는 그 항목이 마지막으로 언제 관측되었는지 알 수 없습니다.

따라서 이는 "Postgres가 시작된 이후 이 컴퓨팅이 얼마나 많은 고유 페이지에 액세스했는가?"라는 질문에 답하는 데는 유용합니다. 하지만 오토스케일링에는 "지금 실행 중인 워크로드에 속한 고유 페이지는 몇 개인가?"에 더 가까운 다른 답이 필요합니다.

시간 경계가 없으면 오래된 가져오기(import) 또는 분석 쿼리가 추정치에 계속 남아 작업이 끝난 후에도 오랫동안 컴퓨팅 크기를 과도하게 유지하게 됩니다. 그래서 우리는 HyperLogLog 레지스터가 저장하는 내용을 변경했습니다.

HyperLogLog에 시간 추가하기

이것이 Lakebase Postgres에서 실제로 작동하는 방식입니다.

해시가 관측될 때 비트를 설정하는 대신, 추정기는 해당 위치에 현재 타임스탬프를 저장합니다. T 시간 이후의 카디널리티를 추정하기 위해, T 이후에 업데이트된 위치는 설정된 것으로 취급하고 더 오래된 위치는 설정되지 않은 것으로 취급합니다.

image6.png

Lakebase Postgres 오토스케일링의 수정된 HyperLogLog.

이것은 현재 시점에 끝나는 모든 윈도우에 대한 추정치를 생성하며, 다음을 포함합니다.

  • 지난 1분 동안 액세스된 고유 페이지 수
  • 지난 5분 동안 액세스된 고유 페이지 수
  • 지난 1시간 동안 액세스된 고유 페이지 수

다시 알고리즘으로 돌아가서, 세분성이 실제로 작동하는 방식은 다음과 같습니다. 20초마다 autoscaler-agent가 1분에서 60분 사이의 윈도우에 대한 워킹 세트 추정치를 수집합니다.

하지만 이야기는 여기서 끝나지 않습니다. 눈치채셨겠지만, 이는 매우 넓은 시간 윈도우입니다. 실제로 이 윈도우를 어떻게 선택할까요?

워킹 세트 시간 윈도우 선택하기

문제는 데이터베이스의 현재 워킹 세트를 설명하는 보편적인 윈도우가 없다는 점입니다. 짧은 윈도우를 선택하면 워크로드가 끝날 때 오토스케일링 엔진이 빠르게 반응하지만, 버스트 사이에 캐시를 너무 공격적으로 폐기하게 됩니다. 긴 윈도우를 선택하면 알고리즘이 캐시를 보호하지만, 더 이상 실행되지 않는 작업을 위해 메모리를 계속 할당된 상태로 유지하게 됩니다.

알고리즘은 시간이 지남에 따라 워킹 세트가 어떻게 변하는지 살펴봄으로써 이 문제를 해결합니다. 예를 들어, 안정적인 워크로드의 경우 추정된 페이지 수는 처음에 증가하다가 점차 평탄해집니다. 윈도우를 확장하면 시간이 추가되지만, 동일한 워킹 세트에 반복해서 액세스하기 때문에 새로운 페이지는 거의 추가되지 않습니다.

image5.png

이제 최근에 종료된 과중한 워크로드를 생각해 보겠습니다. 짧은 윈도우에는 현재의 더 가벼운 워크로드만 포함되지만, 윈도우가 이전 워크로드를 포함할 만큼 충분히 과거로 거슬러 올라가면 추정치가 급증합니다. 알고리즘은 현재의 안정 상태의 끝을 나타내는 이 급증 지점을 검색합니다.

image4.png

요약하자면 다음과 같습니다.

구현은 5분 후에 검색을 시작합니다. 이렇게 하면 짧은 일시 중지 동안 컴퓨팅이 즉시 축소되었다가 다음 버스트 때 다시 확장되는 것을 방지할 수 있습니다. 하지만 알고리즘이 급격한 증가를 발견하지 못하면 60분 추정치를 사용합니다. 이는 1시간 내내 워킹 세트가 활성 상태로 유지되는 안정적인 워크로드에서 예상되는 결과입니다.

image1.png

캐시 성장 예측하기

마지막 한 가지 단계가 더 있습니다. 현재 워킹 세트를 측정하는 것은 약간 늦을 수 있습니다. 예를 들어 워크로드가 새로운 페이지 세트를 스캔하기 시작한다고 가정해 보겠습니다. 해당 페이지를 읽은 후에만 컴퓨팅 캐시가 확장된다면, 이전 페이지들은 이후 페이지들을 위한 공간을 만들기 위해 이미 제거되었을 수 있습니다. 그러면 캐시는 동일한 데이터 중 일부를 다시 가져와야 합니다.

따라서 알고리즘은 워킹 세트의 성장을 미리 예측하기도 합니다. 한 기간에서 다음 기간으로 추정치가 어떻게 증가하는지 검토하고, 다음 제어 주기에서 예상되는 워킹 세트에 충분한 캐시를 할당합니다.

캐시 메트릭은 20초마다 가져오기 때문에 예측 범위는 1분의 일부분에 불과합니다. 더 길게 예측하면 더 일찍 반응하겠지만, 짧은 스파이크를 증폭시켜 컴퓨팅을 흔들리게 만들 수도 있습니다.

image2.png

예측된 크기는 (마침내!) lfcGoalCU가 됩니다. 그리고 알고리즘의 목표는 컴퓨팅 RAM의 최대 75%까지 컴퓨팅 캐시가 사용할 수 있는 메모리 부분 내에 워킹 세트를 맞추는 것입니다.

파트 II: 실행 중인 컴퓨팅 크기 조정

요약하자면, 스케일링 목표는 다음과 같았습니다.

이 세 가지 신호는 시스템에 목표로 할 크기를 알려줍니다. 해당 크기를 적용한다는 것은 Postgres를 중단하지 않고 실행 중인 VM에서 CPU와 메모리를 변경하는 것을 의미합니다.

Lakebase Postgres의 각 Postgres 인스턴스는 Kubernetes 클러스터 내의 자체 가상 머신(VM)에서 실행됩니다. VM을 사용하는 이유는 강력한 격리 경계를 제공하고, 기존의 컨테이너 할당과 달리 실행 중인 게스트에서 CPU와 메모리를 추가하거나 제거할 수 있기 때문입니다.

네 개의 구성 요소가 각 컴퓨팅 크기 조정을 조정합니다.

  1. autoscaler-agent는 모든 Kubernetes 노드에서 실행됩니다. 이 에이전트는 해당 노드의 Postgres VM에서 메트릭을 수집하고, 목표 크기를 계산하며, 스케일링을 시작합니다.
  2. vm-monitor는 각 VM 내부에서 실행됩니다. Postgres 메모리를 면밀히 모니터링하고, 다운스케일링 요청을 검증하며, 컴퓨팅 캐시 크기를 조정합니다.
  3. 수정된 Kubernetes 스케줄러는 사용 가능한 리소스의 전역 뷰를 유지합니다. 메모리가 할당되기 전에 모든 업스케일은 스케줄러의 승인을 받아야 합니다.
  4. NeonVM이 변경 사항을 적용합니다. 이는 QEMU 및 KVM으로 빌드된 맞춤형 Kubernetes 리소스 및 컨트롤러로, 실행 중인 VM에서 CPU와 메모리를 추가하거나 제거할 수 있습니다. (면책 조항: Lakebase Postgres 아키텍처는 Neon에서 시작되었으므로 리소스/컨트롤러 이름은 동일하게 유지됩니다.)
image3.png

스케일 업

방금 살펴보았듯이, 세 가지 목표 중 하나가 VM이 현재 보유한 것보다 더 많은 컴퓨팅을 요구할 때 스케일 업이 발생합니다. 업스케일은 다음 순서로 진행됩니다.

  1. autoscaler-agent가 CPU, 메모리 및 워킹 세트 목표로부터 새로운 목표를 계산합니다.
  2. Kubernetes 스케줄러가 메모리를 과도하게 할당하지 않고 노드가 요청을 충족할 수 있는지 확인합니다.
  3. 승인되면 autoscaler-agent가 NeonVM 리소스를 업데이트합니다.
  4. NeonVM 컨트롤러가 실행 중인 VM에 CPU와 메모리를 추가합니다.
  5. vm-monitor가 새로운 용량을 사용할 수 있도록 컴퓨팅 캐시를 확장합니다.

스케줄러는 할당을 위한 단일 진실 공급원입니다. 일반 Kubernetes 스케줄링과 오토스케일링 요청을 모두 확인합니다. 이러한 조정이 없다면, 오토스케일러가 남은 메모리를 Postgres VM에 할당하는 것과 동시에 스케줄러가 노드에 새로운 워크로드를 배치할 수 있습니다.

노드가 너무 가득 차서 제자리에서 확장할 수 없는 경우, NeonVM은 VM을 다른 노드로 실시간 마이그레이션할 수 있습니다. VM은 IP 주소를 유지하므로 기존 연결이 계속 열려 있습니다. Lakebase Postgres 컴퓨팅은 이동할 영구 로컬 상태가 거의 없기 때문에 마이그레이션은 주로 VM 메모리 및 런타임 상태로 이루어집니다.

스케일 다운

스케일 다운은 정확히 동일한 구성 요소를 사용하며, VM 내부에서 한 가지 추가 확인을 거칩니다. vm-monitor는 메모리를 제거해도 Postgres 및 나머지 게스트에 충분한 메모리가 남는지 확인합니다. 그렇지 않은 경우 스케일 다운이 진행되지 않습니다.

주의: 스케일 다운은 스케일 업만큼이나 중요합니다. 일부 오토스케일링 시스템은 용량을 빠르게 추가하지만 반환하는 데는 느려서, 스파이크가 지난 후에도 오랫동안 데이터베이스가 과도하게 큰 상태로 유지됩니다. Lakebase Postgres는 두 방향을 모두 동일하게 처리합니다. 목표는 매 순간 워크로드를 최대한 밀접하게 추적하여 용량이 필요 없어지는 즉시 비용 지불을 중단하는 것입니다.

요약

Lakebase Postgres는 실행 중인 워크로드를 모니터링하고 이에 맞춰 실시간으로 컴퓨팅 크기를 조정합니다. Lakebase 아키텍처 덕분에 이것이 가능합니다. 스토리지가 분리되어 있고 자체적으로 내구성이 있기 때문에, 컴퓨팅은 데이터에 대해 걱정할 필요 없이 자유롭게 이동할 수 있습니다.

그 결과 시스템은 연결을 끊지 않고 라이브 데이터베이스에서 양방향으로 스케일링을 수행합니다. 가장 중요한 점은 명백한 신호 그 이상을 바라본다는 것입니다. CPU만 추적하면 캐시 미스로 인해 지연된 워크로드를 놓칠 수 있으므로, 알고리즘은 메모리 압박과 시간에 따른 워킹 세트 추정치도 함께 추적합니다.

최종 루프는 세 가지 시간 단위로 실행됩니다.

  • 100밀리초: vm-monitor가 Postgres 메모리를 확인하여 빠른 할당을 감지합니다.
  • 5초: autoscaler-agent가 CPU 및 전반적인 메모리를 읽습니다.
  • 20초: autoscaler-agent가 1분에서 60분 사이의 윈도우에서 워킹 세트 추정치를 평가합니다.

이것이 바로 프로덕션 데이터베이스가 한 달에 32,000회 이상 크기를 변경할 수 있는 방법입니다.

image7.png

컴퓨팅 비용이 더 비싸지고 경쟁이 치열해짐에 따라, 거의 도달하지 않는 피크 용량에 대해 비용을 지불하는 빌드 패턴은 조만간 불가능해질 수 있습니다. 오토스케일링은 낭비되는 컴퓨팅을 감당할 수 없는 워크로드에 대비하여 Postgres를 준비시킵니다.

실행하기

에이전트에게 Lakebase Postgres 배포를 요청하고 오토스케일링 성능을 직접 테스트해 보세요. 여기서 시작하세요.

Lakebase Postgres는 독립형 데이터베이스로 사용할 수 있으며, Unity Catalog 거버넌스, 레이크하우스 분석, 노트북, AI 워크플로우 등 Databricks Data + AI Platform의 다른 기능과도 통합할 수 있습니다.

(이 글은 AI의 도움을 받아 번역되었습니다. 원문이 궁금하시다면 여기를 클릭해 주세요)

최신 게시물을 이메일로 받아보세요

블로그를 구독하고 최신 게시물을 이메일로 받아보세요.