3.4 Kubernetes 백엔드
Kubernetes 위에서 Patroni를 돌릴 때는 etcd를 따로 배포할 필요가 없다. kubernetes 섹션을 정의하면 Patroni가 클러스터 상태와 leader key를 Kubernetes API 오브젝트(Endpoints 또는 ConfigMaps)의 annotation에 저장한다. 합의와 저장의 어려운 부분은 Kubernetes 컨트롤 플레인이 이미 해결해 두었으므로, Patroni는 그 위에 올라타는 구조다.
Endpoints 모드와 ConfigMaps 모드
상태를 어디에 저장할지는 use_endpoints로 갈린다. 두 모드의 차이는 leader가 바뀔 때 필요한 갱신 횟수에 있다.
flowchart TD
L["leader 전환"]
subgraph EP["Endpoints 모드"]
E1["annotation과 주소<br/>동시 갱신 (1회)"]
end
subgraph CM["ConfigMaps 모드"]
C1["leader ConfigMap<br/>갱신 (1회차)"]
C2["leader Endpoint<br/>갱신 (2회차)"]
C1 --> C2
end
L --> EP
L --> CM
Endpoints 모드(use_endpoints: true)에서는 Patroni가 만든 Endpoints 오브젝트의 annotation에 leader 정보를 저장한다. 공식 문서가 이 모드를 권장하는 이유는 원자성이다. leader 정보를 담은 annotation과 실제 leader pod를 가리키는 주소가 한 번의 갱신으로 동시에 바뀌므로, ConfigMaps 모드보다 leader 전환이 안전하다. 다만 권장 모드임에도 기본값은 꺼짐이므로 직접 켜야 한다.
Endpoints 모드에는 pod_ip가 필수다. promote 시점에 leader Endpoint의 subsets를 자기 pod 주소로 채워야 하기 때문이다. Service에 port 이름이 정의되어 있다면 ports에 같은 이름을 지정해야 Service가 정상 동작한다.
ConfigMaps 모드(기본)에서는 leader 정보를 ConfigMap의 metadata에 저장한다. 이 경우 leader 전환에 최소 두 번의 갱신이 필요하다. leader ConfigMap 한 번, 해당 Endpoint 한 번이다. 공식 문서는 OpenShift처럼 ConfigMaps 외에 대안이 없는 환경도 있다고 언급한다.
scope: pg-ha
name: pg-ha-0
kubernetes:
namespace: databases # 오브젝트를 만들 K8s namespace (기본 default)
labels:
application: patroni # 클러스터 소속 오브젝트 검색/부여 라벨
use_endpoints: true
pod_ip: 10.244.1.15 # 보통 PATRONI_KUBERNETES_POD_IP 환경변수로 주입
ports:
- name: postgresql
port: 5432pod_ip처럼 pod마다 달라지는 값은 YAML에 고정하기 어려우므로, 실제 배포에서는 PATRONI_KUBERNETES_POD_IP 같은 PATRONI_KUBERNETES_* 환경변수로 주입하는 형태가 일반적이다.
라벨 체계
Patroni는 라벨로 자기 클러스터의 오브젝트를 찾고, 각 pod에 현재 role을 라벨로 새긴다. 라우팅용 Service의 selector가 이 role 라벨을 바라보는 구성이 Kubernetes에서의 기본 패턴이다.
| 키 | 기본값 | 내용 |
|---|---|---|
labels | - | 클러스터 소속 오브젝트를 찾는 기준이자 생성 오브젝트에 부여하는 라벨 |
scope_label | cluster-name | 클러스터 이름을 담는 라벨 키 |
role_label | role | 노드 role을 담는 라벨 키 |
leader_label_value | primary | leader pod의 role 라벨 값 |
follower_label_value | replica | replica pod의 role 라벨 값 |
standby_leader_label_value | primary | standby leader pod의 role 라벨 값 |
bootstrap_labels | - | bootstrap이나 replica 생성 중인 pod에 붙일 라벨 |
master에서 primary로 바뀌었다. 4.0.0 릴리스 노트는 구 동작을 유지해 다운타임이나 복잡한 마이그레이션을 피하려면 leader_label_value와 standby_leader_label_value를 master로 설정하라고 안내한다. role=master를 selector로 쓰는 Service가 남아 있는 상태로 업그레이드하면 primary로 향하는 트래픽이 끊기므로, 라벨 값과 selector를 함께 점검해야 한다.RBAC
Patroni pod의 ServiceAccount에는 오브젝트를 만들고 갱신할 권한이 필요하다. 공식 저장소의 예시 매니페스트(kubernetes/patroni_k8s.yaml)가 정의하는 Role은 다음과 같다.
kind: Role
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["create", "get", "list", "patch", "update", "watch", "delete", "deletecollection"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "patch", "update", "create", "list", "watch", "delete", "deletecollection"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "patch", "update", "watch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["create"]예시 매니페스트의 주석에 따르면 delete와 deletecollection은 patronictl remove에만 필요한 권한이다. 클러스터 삭제를 Kubernetes 매니페스트 정리로만 수행할 방침이라면 이 두 verb를 빼는 선택지도 있다.
예시 매니페스트에는 kubernetes Endpoint를 get하는 ClusterRole(patroni-k8s-ep-access)도 함께 정의되어 있다. 매니페스트 주석에 따르면 이 권한은 default namespace 밖에 배포하면서 아래의 bypass_api_service를 쓸 때만 필요하다.
bypass_api_service
Patroni는 평소 pod에 노출된 KUBERNETES_SERVICE_HOST 환경변수, 즉 kubernetes service를 경유해 API 서버와 통신한다. bypass_api_service: true(기본 false)를 설정하면 service 뒤에 있는 API 노드 목록을 직접 해석해 그 노드들로 바로 연결한다.
Kubernetes 백엔드로 실제 클러스터를 구성하고 운영하는 생태계(Spilo 이미지 등)와 배포 패턴은 Part XII 심화에서 다룬다.
정리
- Kubernetes 백엔드는 별도 DCS 배포 없이 Endpoints 또는 ConfigMaps의 annotation에 클러스터 상태를 저장한다.
- 권장은 Endpoints 모드다. leader annotation과 주소가 한 번에 갱신되어 전환이 안전하다. 단 기본값은 꺼져 있고
pod_ip가 필수이며, OpenShift는 ConfigMaps 모드만 가능하다. - role 라벨 기본값이 4.0에서
master에서primary로 바뀌었다. 업그레이드 시 Service selector와leader_label_value를 함께 점검한다. - RBAC은 configmaps/endpoints의 생성과 갱신, pods 조회가 뼈대이고, delete 계열 verb는
patronictl remove를 쓸 때만 필요하다.