원문은 2019년 12월 12일 ObjectRocket.com/blog에 처음 게시되었습니다.
연말 시즌이 다가오고 있습니다. 그리고 이번 시즌은 Rackspace ObjectRocket의 역사에서 중요한 이정표가 된 지 1주년이 되는 때이기도 합니다. 2019년 연휴 시즌을 맞아 저희는 ObjectRocket PostgreSQL® 서비스에 고가용성(High Availability, HA) 기능을 선보였습니다. ObjectRocket에서 제공하는 모든 데이터스토어는 프로덕션 워크로드를 견디도록 설계되었으며, 따라서 모든 고객에게 HA는 필수적인 요소입니다.
HA(고가용성)는 왜 중요할까요?
HA라는 용어가 낯선 분들을 위해, HA가 왜 중요한지 간략히 짚어보겠습니다. HA가 제공하는 세 가지 핵심 이점은 다음과 같습니다.
- 다운타임 제로 또는 획기적인 감소
- 데이터 손실 방지
- 데이터베이스 성능 향상
데이터스토어 전반에 HA를 구현하는 방법은 여러 가지가 있고, 활용 가능한 기술도 다양합니다. 하지만 거의 모든 HA 솔루션의 핵심 구성요소는 바로 데이터 복제본(replica)입니다. 사용자에게는 하나의 데이터셋 또는 데이터베이스만 보이지만, 그 이면에는 동일한 데이터의 복제본(사본)이 하나 이상 존재하는 방식입니다. 대부분의 복제 방식에서 주 데이터베이스인 프라이머리(primary)에 하드웨어 장애, 소프트웨어 오류, 데이터 손상 같은 문제가 발생하면, 복제본이 프라이머리를 대신할 수 있습니다.
여기서 HA 시스템의 두 번째 핵심 구성요소인 자동 페일오버(failover) 메커니즘(방식에 따라 '승격' 또는 '선출'이라고도 함)이 등장합니다. 복제만으로 여러 개의 정상적인 데이터 사본을 확보할 수 있지만, 문제가 발생했을 때 다음 작업도 준비되어 있어야 합니다.
- 프라이머리에서 발생한 예기치 않은 장애 감지
- 새로운 프라이머리로 승격할 적절한 복제본 선택
- 장애가 발생한 프라이머리 복구 및 새 복제본 생성(승격된 복제본 대체)
마지막 구성요소는 경우에 따라 두 번째 요소와 결합되기도 하는데, 바로 요청을 올바른 노드로 라우팅하는 장치입니다. 애플리케이션이 데이터 쓰기를 위해 프라이머리를 직접 바라보고 있다가(모범 사례) 해당 프라이머리에 장애가 발생하면, 애플리케이션은 새로 승격된 프라이머리를 자동으로 찾지 못합니다. 이 문제를 해결하는 방법도 다양하지만, 가장 널리 쓰이는 방식은 프록시(proxy) 또는 로드 밸런서(load balancer)를 사용하는 것입니다. 애플리케이션을 프록시나 로드 밸런서에 연결하면, 해당 장치가 트래픽을 보낼 올바른 위치를 판단해 주므로 데이터베이스 서버로 직접 연결할 필요가 없습니다.
이 모든 것을 하나로 묶으면, 페일오버 발생 시 자동 페일오버 시스템과 프록시/로드 밸런서가 함께 작동합니다. 새 마스터가 승격되면 프록시나 로드 밸런서가 트래픽을 새 마스터로 향하게 합니다. 애플리케이션 쪽에서는 아무것도 변경할 필요가 없으며, 승격 과정 중 일시적인 응답 지연이 있을 수는 있지만, 애플리케이션은 승격이 일어났다는 사실조차 알 필요가 없습니다. 여기까지가 핵심 구성요소를 아우르는 개괄적인 설명입니다. 이제 저희 솔루션의 각 구성요소에 사용된 기술을 자세히 살펴보겠습니다.
저희가 사용하는 기술
핵심 구성요소를 살펴봤으니, 이제 앞서 언급한 각 구성요소를 어떻게 구현했는지 알아보겠습니다.
복제(Replication)
Postgres는 기본적으로 다양한 복제 방식을 지원합니다. 현재는 복제본을 한 개 또는 두 개 구성할 수 있으며, 향후 선택지를 더 확대할 계획입니다. 복제의 또 다른 중요한 개념은 동기식(synchronous)과 비동기식(asynchronous) 복제입니다. 동기식 복제에서는 프라이머리가 각 복제본의 쓰기 완료 확인을 받은 후에야 해당 쓰기를 완료된 것으로 간주합니다. 반면 비동기식 복제에서는 프라이머리가 복제본에 쓰기를 전달하기만 할 뿐, 애플리케이션이나 클라이언트의 쓰기를 확인하기 전에 복제본의 완료 여부를 검증하지 않습니다. 저희 솔루션은 동기식과 비동기식 복제를 모두 지원합니다. 기본값으로는 동기식 복제를 활성화하며, 쓰기가 프라이머리의 WAL(Write-ahead Log)과 최소 한 개의 복제본에 기록되었는지 확인하도록 환경을 구성합니다. 이 설정은 트랜잭션 단위 또는 세션 단위로 변경할 수 있습니다.
페일오버와 승격(Failover & Promotion)
페일오버와 승격 기능을 제공하는 오픈소스 및 서드파티 도구는 여럿 있지만, 저희는 다음과 같은 뛰어난 특성 때문에 Patroni를 선택했습니다.
- Kubernetes® 네이티브 지원: 저희 새 플랫폼은 Kubernetes 기반이므로, 별도의 상태 관리나 컨센서스 메커니즘 없이 Kubernetes에 바로 통합되는 도구를 채택할 수 있다는 점이 핵심이었습니다.
- 활발한 개발과 커뮤니티: Patroni 커뮤니티는 매우 활발해서, 기능이 확장됨에 따라 저희도 직접 기여하고 협업할 수 있습니다. 또한 컨퍼런스 발표와 기술 문서부터 Zalando의 오퍼레이터 예제까지 학습에 활용할 만한 자료가 풍부합니다.
- 간결한 아키텍처: 다른 많은 도구들은 로드 밸런싱과 프라이머리 승격을 처리하기 위해 Postgres 인스턴스 외부에 전용 리소스를 추가로 요구합니다. 반면 Patroni는 Postgres를 감싸고(wrap) 나머지 기능은 Kubernetes 네이티브 구성요소로 처리하기 때문에, HA를 위해 추가 리소스를 할당할 필요가 없었습니다.
환경에 따라 결과는 다를 수 있지만, 저희 경험상 Patroni는 설정과 유지관리가 쉬우면서 우리 환경에 매우 잘 맞는 훌륭한 도구였습니다. 노드가 어느 HA 그룹에 속하는지만 Patroni에 알려주면 나머지는 알아서 처리합니다. 복제 구성, 장애 프라이머리 감지, 복제본 승격, 새 복제본 생성은 물론, 프로세스의 마지막 단계에서 Kubernetes와의 연동까지 모두 수행합니다.
요청 라우팅(Request Routing)
마지막 퍼즐 조각은 Patroni와 서비스(service)라 불리는 Kubernetes 네이티브 구성요소입니다. Kubernetes의 서비스는 컨테이너 그룹(파드, pod)의 레이블을 기준으로 트래픽을 라우팅하는 프록시처럼 작동합니다. Patroni는 활성 프라이머리에 master 레이블을 붙이고, Kubernetes는 master 레이블이 있는 파드로만 데이터베이스 트래픽을 라우팅합니다. 물론 이는 개념적인 설명이지만, 실제로도 이런 식으로 작동합니다. 이 프로세스는 확장하여, 데이터베이스 읽기 요청을 복제본으로 분산(프라이머리 부하 감소)하는 보조 포트를 제공하는 데에도 활용할 수 있습니다.
여기까지가 저희가 사용한 기술들의 간략한 소개입니다. 더 궁금한 점이 있다면 언제든 말씀해 주세요. 고객님들과 기술 이야기를 나누는 것을 무척 좋아합니다.
지금 바로 사용해 보세요!
Mission Control의 Create an Instance 화면에는 2단계에 Customize Your Instance 섹션이 있습니다. 거기서 체크박스를 클릭해 화살표를 초록색으로 바꾸면, PostgreSQL 인스턴스의 HA 복제본이 추가됩니다. 또한 우측 상단의 줄임표(...)를 클릭하여 복제본 수(1개 또는 2개)를 선택할 수도 있습니다.
기본적으로는 장애 상황에서도 이중화를 유지할 수 있도록 복제본 2개를 권장하지만, 1개를 선택할 수도 있습니다.
Feedback 탭을 통해 의견을 남기거나 질문할 수 있습니다. Sales Chat을 클릭하면 지금 바로 상담을 시작할 수도 있습니다.