본문 바로가기
WIKI 기술 지식 베이스

자주 묻는 질문

원문 보기 위키 갱신

자주 묻는 질문 (Frequently Asked Questions, FAQ)

출처: 문서

본문

Kubernetes에서 PostgreSQL 실행하기 (Running PostgreSQL in Kubernetes)

PostgreSQL 같은 상태를 가진(stateful) 워크로드는 Kubernetes에서 실행할 수 없다는 게 상식 아닌가요? 왜 그 반대라고 말하나요?

2021년 9월에 Data on Kubernetes Community가 의뢰한 독립 연구 조사에 따르면, 응답자의 절반이 대부분의 프로덕션 워크로드를 Kubernetes에서 실행하고 있어요. 그중 90%는 Kubernetes가 상태를 가진 워크로드에 준비가 됐다고 믿고, 70%는 프로덕션에서 데이터베이스를 실행하죠. Postgres 같은 데이터베이스 말이에요. 다만 그들에 따르면 지식 격차(Kubernetes와 Cloud Native 전체적으로 가파른 학습 곡선이 있음)와 Kubernetes 연산자의 품질 같은 중요한 과제가 여전히 남아 있어요. 후자가 우리가 CloudNativePG 같은 연산자가 여러분 프로젝트의 성공에 크게 기여한다고 믿는 이유예요.

우리 같은 데이터베이스 마니아에게 진짜 게임 체인저는 Kubernetes 1.14(2019년 4월)의 로컬 persistent volume 지원 도입이었어요.

CloudNativePG는 불변 애플리케이션 컨테이너(immutable application container) 위에 구축됐다고 하는데, 그게 무슨 뜻인가요?

마이크로서비스 아키텍처 패턴에 따르면, 컨테이너는 단일 애플리케이션 또는 프로세스를 실행하도록 설계돼요. 그 결과 그러한 컨테이너 이미지는 주 애플리케이션을 단일 진입점(소위 PID 1 프로세스)으로 실행하도록 만들어져요.

Kubernetes 용어에서 애플리케이션을 워크로드라고 불러요. 워크로드는 웹 애플리케이션 서버 같은 무상태(stateless)일 수도 있고, 데이터베이스 같은 상태 유지(stateful)일 수도 있어요. 이 개념을 PostgreSQL에 매핑하면, 불변 애플리케이션 컨테이너는 불변 컨테이너 이미지 안의 특정 버전에 묶여 실행되는 단일 "postgres" 프로세스를 의미해요.

SSH, systemd, syslog 같은 다른 프로세스는 허용되지 않아요.

불변 애플리케이션 컨테이너는 여전히 컨테이너를 해석하고 사용하는 매우 흔한 방식인 가변 시스템 컨테이너(Mutable System Containers)와 대조돼요.

불변(Immutable)은 컨테이너가 수명 중에 수정되지 않음을 의미해요: 업데이트도, 패치도, 구성 변경도 없어요. 애플리케이션 코드를 갱신하거나 패치를 적용해야 한다면 새 이미지를 빌드해 재배포해야 해요. 불변성은 배포를 더 안전하고 반복 가능하게 만들어요.

자세한 내용은 "Why EDB chose immutable application containers"를 참조하세요.

Cloud Native란 무슨 뜻인가요?

Cloud Native Computing Foundation은 "Cloud Native"라는 용어를 정의했어요. 그러나 2ndQuadrant에서 Cloud Native PostgreSQL/CloudNativePG 연산자를 시작한 이래, 개발 팀은 Cloud Native를 세 가지 주요 개념으로 해석해 왔어요:

  1. 사람과 원칙·프로세스에 기반한, 건전하고 진정성 있으며 번영하는 DevOps 문화. 팀과 조직(팀의 팀으로서)이 지속적으로 변화해 혁신하고 결과물 전달을 가속화하며 비즈니스에 더 안전하고 효율적이며 더 몰입적인 방식으로 가치를 창출할 수 있게 함
  2. 불변 애플리케이션 컨테이너에 기반한 마이크로서비스 아키텍처
  3. Kubernetes 같은 이러한 컨테이너를 관리하고 오케스트레이션하는 방식

현재 컨테이너 오케스트레이션의 사실상 표준은 Kubernetes이며, Cloud Native 애플리케이션의 배포·관리·확장성을 자동화해요.

우리에게 공명하는 또 다른 Cloud Native 정의는 Ibryam과 Huß가 "Kubernetes Patterns", O'Reilly 출판에서 정의한 것이에요:

컨테이너화된 마이크로서비스를 대규모로 자동화하는 원칙, 패턴, 도구

베어메탈 Kubernetes에서 CloudNativePG를 실행할 수 있나요?

네, 확실히요. Kubernetes를 베어메탈에서 실행할 수 있어요. 그리고 로컬 연결 스토리지가 있는 물리적 워커 노드 하나 이상을 Postgres 워크로드에 전용으로 할당해 최대이고 예측 가능한 I/O 성능을 얻을 수 있어요.

CloudNativePG의 원천인 실제 Cloud Native PostgreSQL 프로젝트는 2019년 파일럿 프로젝트에서 태어났어요. 이 프로젝트는 스토리지와 PostgreSQL을 같은 베어메탈 서버에서 먼저 Linux에서 직접, 그다음 Kubernetes 안에서 벤치마킹했죠. 예상대로 실험은 로컬 persistent volume을 통한 Kubernetes 컨테이너 실행이 도입하는 성능 영향이 무시할 만하다는 것을 보여줘 Cloud Native 이니셔티브가 계속될 수 있었어요.

파일 시스템 복제 대신 PostgreSQL 복제를 써야 하는 이유는 무엇인가요?

"아키텍처: 상태 동기화(Synchronizing the state)" 섹션을 읽어보세요.

컨테이너로 PostgreSQL을 실행하는 대신 연산자를 써야 하는 이유는 무엇인가요?

Kubernetes에서 PostgreSQL을 실행하는 가장 기본적인 접근 방식은 레플리카 없이 Postgres 컨테이너를 실행하는 pod를 두는 것이에요. pod는 Kubernetes의 최소 배포 단위예요. Postgres 데이터 디렉터리를 호스팅하는 볼륨은 pod에 마운트되며, 보통 네트워크 스토리지에 위치해요. 이 경우 문제가 생기면 Kubernetes가 pod를 재시작하거나 다른 Kubernetes 노드로 옮겨요.

가장 정교한 접근 방식은 연산자(operator)로 PostgreSQL을 실행하는 것이에요. 연산자는 Kubernetes 컨트롤러의 확장이며, 비즈니스 연속성 맥락에서 복잡한 애플리케이션이 어떻게 동작하는지 정의해요. 연산자 패턴은 현재 Kubernetes에서 이 목적을 위한 최신 기법이에요. 연산자는 인간 연산자의 작업을 자동화되고 프로그램적인 방식으로 시뮬레이션해요.

Postgres는 복잡한 애플리케이션이고, 연산자는 클러스터를 배포하는 것(첫 단계)뿐 아니라 예기치 않은 이벤트 후에 제대로 대응해야 해요. 대표적인 예가 페일오버예요.

연산자는 자기 치유, 확장성, 복제, 고가용성, 백업, 복구, 업데이트, 접근, 리소스 제어, 스토리지 관리 같은 능력을 Kubernetes에 의존해요. 또한 PostgreSQL 클러스터를 로그 관리 및 모니터링 인프라에 통합하는 것을 용이하게 해요.

CloudNativePG는 선언적 구성을 통해 PostgreSQL 클러스터의 원하는 상태(desired state) 정의를 가능하게 해요. Kubernetes는 Kubernetes 컨트롤러가 시작한 리컨실레이션 루프를 통해 인프라의 현재 상태가 원하는 상태와 일치하도록 지속적으로 보장해요. 원하는 상태와 실제 상태가 일치하지 않으면, 리컨실레이션 루프가 자기 치유 절차를 트리거해요. 여기서 CloudNativePG 같은 연산자가 등장하는 거죠.

다른 Postgres 연산자도 있나요?

네, 당연하죠. 그리고 우리 조언은 결정을 내리기 전에 모든 연산자를 살펴보고 CloudNativePG와 비교해보라는 것이에요. 대부분의 연산자가 외부 페일오버 관리 도구(Patroni 등)를 사용하고 StatefulSet에 의존하는 것을 볼 수 있을 거예요.

GitHub에 공개된 시간순의 비망라적이지 않은 목록입니다:

Star History Chart

누락된 관련 항목이 있으면 PR로 알려주세요.

:::info Data on Kubernetes Community (우리 관리자 일부 포함)는 Operator Feature Matrix라는 연산자 목록을 위한 독립적이고 벤더 중립적인 프로젝트를 진행 중이에요. :::

CloudNativePG는 완전히 선언적(declarative) 연산자라고 말하는데, 그게 무슨 뜻인가요?

선언적 구성을 설명하는 가장 쉬운 방법은 명령적(imperative) 구성과의 차이를 강조하는 예시를 드는 거예요. 명령적 맥락에서 상태는 순서대로 실행할 일련의 작업으로 정의돼요. 즉, 첫 번째 인스턴스를 만들고 복제를 구성하고 두 번째, 세 번째 인스턴스를 클론해서 3노드 PostgreSQL 클러스터를 얻을 수 있어요.

선언적 접근 방식에서 시스템의 상태는 구성으로 정의돼요. 즉 "레플리카 두 개를 가진 PostgreSQL 18 클러스터가 있다"고요. 이 접근 방식은 변경 관리 작업을 크게 단순화하고, 이것을 Git 같은 소스 제어 시스템에 저장하면 Infrastructure as Code 기능을 가능하게 해요. 그리고 Kubernetes는 배포를 넘어서 우리의 요청이 항상 충족되도록 보장해요.

Kubernetes에서 PostgreSQL을 실행하려면 어떤 기술이 필요한가요?

Kubernetes에서 PostgreSQL을 실행하려면 DevOps 팀에 PostgreSQL과 Kubernetes 기술이 모두 필요해요. 데이터베이스 관리자가 Kubernetes 핵심 개념에 익숙해지고 Kubernetes 관리자와 상호작용할 수 있을 때 최상의 경험을 얻을 수 있어요.

Cloud Native PostgreSQL을 완전히 활용하려는 모든 사람에게 CNCF 인증 프로그램의 "Certified Kubernetes Administrator (CKA)" 자격을 취득하는 것을 권장해요.

CloudNativePG는 왜 StatefulSet을 사용하지 않나요?

CloudNativePG는 StatefulSet 리소스에 의존하지 않고, 동적 프로비저닝을 위해 선택된 스토리지 클래스를 활용해 기반 PVC를 직접 관리해요. 이 결정의 세부 사항과 이유는 "Custom Pod Controller" 섹션을 참조하세요.

고가용성 (High availability)

연산자 pod가 죽거나 일정 시간 사용할 수 없으면 PostgreSQL 클러스터는 어떻게 되나요?

CloudNativePG 연산자는 무엇보다 자기 치유 능력을 담당해요. 그렇기에 연산자가 다운되는 동안 자기 치유 기능은 사용하지 못할 수 있어요.

그러나 정전이 PostgreSQL 클러스터가 실행 중인 노드에 영향을 미치지 않는다고 가정하면, 데이터베이스는 관련 Kubernetes 서비스를 통해 정상 운영을 계속 제공할 거예요. 게다가 각 PostgreSQL pod 안에서 실행되는 인스턴스 매니저도 계속 작동해, 로깅, 메트릭 내보내기, WAL 파일 연속 아카이빙 같은 부가 서비스를 포함해 데이터베이스 서버가 살아 있는지 보장해요.

요약하면:

연산자의 정전이 반드시 PostgreSQL 데이터베이스 정전을 의미하지는 않아요. DBA나 시스템 관리자 없이 데이터베이스를 운영하는 것과 같다고 볼 수 있어요.

CloudNativePG가 Patroni, repmgr, Stolon 같은 페일오버 관리 도구에 의존하지 않는 이유는 무엇인가요?

CloudNativePG를 개발하는 팀의 일부가 과거에 repmgr에 깊이 관여했지만, 우리는 다른 접근 방식을 택해 Kubernetes 컨트롤러를 직접 확장하고 Postgres 클러스터의 상태를 보관하기 위해 Kubernetes API 서버에 의존하기로 결정했어요. 그리고 이를 다음을 위한 유일한 진실 원천으로 사용해요:

  • 주로 자동화된 페일오버와 스위치오버를 통해 Postgres 클러스터의 고가용성을 제어하며 인스턴스 매니저와 스스로 조정
  • 애플리케이션의 진입점인 Kubernetes 서비스 제어

페일오버 후 이전 프라이머리를 새 프라이머리와 수동으로 재동기화해야 하나요?

아니요. 연산자가 자동으로 그 작업을 해주며, 이전 프라이머리를 새 프라이머리와 동기화하기 위해 pg_rewind에 의존해요.

데이터베이스 관리 (Database management)

왜 PostgreSQL을 써야 하나요?

PostgreSQL은 데이터베이스 영역에서 Linux가 운영체제 공간에서 차지하는 것과 동등한 위치라고 우리는 믿어요. 현재 최신 Postgres 메이저 버전은 16이며, 기본으로 다음을 제공해요:

  • 물리·논리 모두의 네이티브 스트리밍 복제
  • 연속 핫 백업과 시점 복구(point in time recovery)
  • 단일 인스턴스의 수직 확장성을 개선하는 데이터베이스 영역에서 잘 알려진 기법인 수평 테이블 파티셔닝을 위한 선언적 파티셔닝
  • 지리 데이터베이스용 PostGIS 같은 확장으로 확장성
  • 수직 확장성을 위한 병렬 쿼리
  • 표준 SQL로 조회되는 구조적·비구조적 데이터 모두를 위한 멀티모델 하이브리드 데이터베이스를 펼치는 JSON 지원

등등...

단일 PostgreSQL 인스턴스에 몇 개의 데이터베이스를 호스팅해야 하나요?

우리 권장사항은 단일 PostgreSQL 클러스터(프라이머리 + 여러 스탠바이 서버로 의도)를 단일 데이터베이스에 전용으로, 단일 마이크로서비스 애플리케이션이 완전히 관리하게 하는 것이에요. 그러나 "postgres" 슈퍼유저를 활용하면 원하는 만큼 사용자와 데이터베이스를 만들 수 있어요(가용 리소스에 따라).

이 권장사항의 이유는 마이크로서비스에 기반한 Cloud Native 개념에 있어요. 순수 마이크로서비스 아키텍처에서 마이크로서비스 자체가 관리하는 데이터를 단독으로 소유해야 해요. 이것은 플랫 파일, 큐, 키-값 저장소, 또는 우리의 경우 구조적·비구조적 데이터를 모두 담은 PostgreSQL 관계형 데이터베이스일 수 있어요. 일반적인 생각은 스키마 관리와 마이그레이션을 포함해 오직 마이크로서비스만 데이터베이스에 접근할 수 있다는 것이에요.

CloudNativePG는 기본적으로 애플리케이션 사용자와 그 사용자가 소유한 애플리케이션 데이터베이스를 생성해서 이런 방식으로 동작하도록 설계됐어요.

PostgreSQL 인스턴스를 단일 마이크로서비스가 소유한 데이터베이스에 전용으로 예약하면 다음이 향상돼요:

  • 리소스 관리: PostgreSQL에서 CPU·메모리 제한 리소스는 일반적으로 데이터베이스 레벨이 아니라 인스턴스 레벨에서 처리되므로, pod 레벨의 Kubernetes 리소스 관리 정책과 통합하기 더 쉬워요
  • 물리적 연속 백업과 PITR(Point-In-Time-Recovery): PostgreSQL이 인스턴스 레벨에서 연속 백업과 복구를 처리하므로, 인스턴스당 데이터베이스 하나가 있으면 PITR 작업을 단순화하고 보존 정책 관리를 차별화하며 백업의 데이터 보호를 높여요
  • 애플리케이션 업데이트: 각 애플리케이션이 다른 애플리케이션이 소유한 데이터베이스에 영향을 주지 않고 자신의 업데이트 정책을 결정할 수 있게 해요
  • 데이터베이스 업데이트: 각 애플리케이션이 어떤 PostgreSQL 버전을 사용할지, 그리고 독립적으로 언제 어떤 조건(예: 컷오버 시각)에서 다른 PostgreSQL 메이저 버전으로 업그레이드할지 결정할 수 있어요

Kubernetes를 고려하지 않아도 될 데이터베이스 크기 상한이 있나요?

아니요. 이 점에서는 Kubernetes가 가상 머신이나 베어메탈과 다를 바가 없기 때문이에요. 다만 실제로는 Kubernetes 클러스터의 가용 리소스에 달려 있어요. 매우 큰 데이터베이스(VLDB)에 대한 우리 조언은 shared nothing 아키텍처를 고려하는 것이에요. 여기서 Kubernetes 워커 노드는 전용 스토리지와 함께 단일 Postgres 인스턴스에 전용으로 할당돼요. 우리는 로컬 persistent volume으로 베어메탈 Kubernetes에서 PostgreSQL 실행을 벤치마킹했을 때 이 극단적인 아키텍처 패턴이 동작함을 입증했어요. 테이블스페이스와 수평 파티셔닝은 데이터베이스의 수직 확장성을 개선하는 데 사용할 수 있는 데이터 모델링 기법이에요.

PostgreSQL 클러스터에서 시간대를 어떻게 지정할 수 있나요?

PostgreSQL은 공식 문서에 설명된 대로 시간대를 광범위하게 지원해요:

PostgreSQL에서 시간대는 세션, 트랜잭션, 심지어 쿼리의 일부로도 사용될 수 있지만, 매우 일반적인 방법은 전역적으로 설정하는 것이에요. CloudNativePG에서는 다음 예시처럼 .spec.postgresql.parameters 섹션에서 클러스터 레벨 시간대를 구성할 수 있어요:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-italy
spec:
  instances: 1

  postgresql:
    parameters:
      timezone: "Europe/Rome"

  storage:
    size: 1Gi

시간대는 다음으로 확인할 수 있어요:

$ kubectl exec -ti pg-italy-1 -c postgres -- psql -x -c "SHOW timezone"
-[ RECORD 1 ]---------
TimeZone | Europe/Rome

최상의 비즈니스 연속성 결과를 위한 권장 아키텍처는 무엇인가요?

"아키텍처" 섹션에서 다뤘듯이, 주요 권장사항은 단일 클러스터에서 Kubernetes가 제공하는 네이티브 능력과 리소스를 활용해 가능한 한 shared nothing 아키텍처를 채택하는 것이에요:

  • 가용성 영역: 같은 Kubernetes 클러스터 안에서 인스턴스를 여러 가용성 영역에 분산
  • 워커 노드: 결과적으로 Postgres 인스턴스가 서로 다른 Kubernetes 워커 노드에 있게 함
  • 스토리지: Postgres를 실행하는 각 워커 노드에 전용 스토리지 사용

스탠바이를 최소 하나, 가급적 두 개 이상 사용해서 클러스터에서 동기 복제를 구성할 수 있고, 고가용성을 위해 RPO=0을 도입할 수 있어요.

가용성 영역이 없다면(보통 온프레미스 설치의 경우) 워커 노드와 스토리지에서 분리하세요.

로컬/리전 객체 스토어에 연속 백업을 제대로 설정하세요.

단일 Kubernetes 클러스터에 있는 동일한 아키텍처를 레플리카 클러스터 기능을 통해 다른 Kubernetes 클러스터(보통 다른 지리적 영역이나 리전)에 복제해, 글로벌 규모의 재해 복구와 고가용성을 제공할 수 있어요.

기본 최대 RPO 5분으로 충분하다면, 스트리밍 연결을 제공할 필요 없이 프라이머리 객체 스토어의 WAL 아카이브를 사용해 다른 리전의 레플리카에 공급할 수 있어요.

인스턴스를 어떻게 중지하거나 시작하나요?

"Fencing" 또는 "Hibernation"을 참조하세요.

CloudNativePG가 자동으로 생성하는 역할·데이터베이스 같은 전역 객체는 무엇인가요?

연산자는 애플리케이션용 사용자(기본적으로 app이라는 이름)와 그 사용자가 소유한 애플리케이션용 데이터베이스(기본적으로 app이라는 이름)를 자동으로 만들어요.

이 방식으로 데이터베이스는 마이크로서비스 도입 준비가 되고, 개발자는 superuser 접근 없이 app 사용자로 마이그레이션을 제어할 수 있어요.

그런 다음 팀은 "Declarative role management" 기능으로 읽기-쓰기 작업용 사용자를 더 만들고 테이블에 필요한 GRANT를 할당할 수 있어요.

더 알아보기 (Learn more)