본문으로 건너뛰기

5.1 bootstrap과 initdb

Patroni를 시작한 노드의 데이터 디렉토리가 비어 있다면, 그 노드가 할 일은 둘 중 하나입니다. 클러스터의 첫 PostgreSQL 인스턴스를 새로 만들거나(bootstrap), 이미 있는 클러스터에서 데이터를 복사해 replica가 되거나. 어느 쪽인지는 노드가 스스로 정하지 않습니다. DCS(Distributed Configuration Store)에 남은 흔적이 정합니다.

initialize 키가 가르는 분기​

판단 기준은 DCS의 initialize 키입니다.

  • 키가 없으면 아직 아무도 이 클러스터를 초기화하지 않은 상태입니다. 노드들은 initialize lock을 놓고 경쟁하고, lock을 잡은 첫 번째 노드만 bootstrap을 실행합니다.
  • 키가 있으면 이미 초기화된 클러스터이므로 replica 생성 절차만 수행합니다(5.3 replica 생성).

실패 경로가 중요합니다. bootstrap이 중간에 실패하거나 non-zero exit code를 반환하면 Patroni는 만들다 만 결과물을 정리하고 initialize lock을 해제해, 다른 노드가 다시 시도할 기회를 넘깁니다.

기본 initdb bootstrap​

bootstrap.method를 생략하거나 initdb로 지정하면, lock을 잡은 노드가 initdb를 실행해 클러스터를 만듭니다. initdb에 넘길 옵션은 bootstrap.initdb 아래에 list로 적습니다.

bootstrap:
initdb:
- encoding: UTF8
- locale: C.UTF-8
- data-checksums

data-checksums처럼 값이 없는 플래그 옵션은 단독 항목으로 적습니다. pg_rewind는 data checksums가 켜져 있거나 wal_log_hints가 on이어야 동작하므로, use_pg_rewind를 쓸 계획이라면 initdb 시점에 data-checksums를 켜 두는 편이 낫습니다. 다만 Patroni는 wal_log_hints를 기본 on으로 두고(4.4), PostgreSQL 18부터는 initdb가 data checksums를 기본으로 켭니다. 따라서 data-checksums를 명시해야 하는 경우는 PostgreSQL 17 이하에서 checksums를 원할 때입니다. pg_rewind가 failover 후 구 primary 재합류에서 하는 역할은 Part VI에서 다룹니다.

bootstrap.dcs는 최초 1회만 기록된다​

bootstrap.dcs 아래 내용은 클러스터 초기화 직후 DCS의 /config 키에 기록되어 global dynamic configuration이 됩니다. 여기서 자주 걸리는 함정이 있습니다. 공식 문서는 "Patroni가 클러스터를 최초 한 번 초기화하고 설정이 DCS에 저장된 이후에는, YAML의 bootstrap.dcs 섹션을 아무리 고쳐도 효력이 없다"고 note로 명시합니다. 초기화가 끝난 클러스터의 dynamic configuration은 patronictl edit-config 또는 REST API의 /config 엔드포인트로만 바꿉니다. 구성 3계층의 전체 그림은 Part IV 참고.

post_bootstrap 스크립트​

초기화 직후 한 번 실행할 스크립트를 bootstrap.post_bootstrap(또는 post_init)에 지정합니다. 스크립트는 superuser 계정이 지정된 connection string URL을 인자로 받고, PGPASSFILE 환경변수가 pgpass 파일 위치로 설정된 채 실행됩니다. 애플리케이션 계정이나 database처럼 클러스터 생성 직후 한 번만 만들면 되는 객체를 준비하는 자리입니다.

bootstrap:
post_bootstrap: /usr/local/bin/post_bootstrap.sh
#!/bin/bash
# $1 = superuser connection string, 비밀번호는 PGPASSFILE 경유로 전달된다
psql "$1" -c "CREATE DATABASE app"

custom bootstrap method​

initdb 대신 임의의 스크립트로 첫 인스턴스를 만들도록 바꿀 수도 있습니다. 백업에서 복원해 새 클러스터를 여는 PITR 시나리오가 대표적인 용도입니다. bootstrap.method에 이름을 적고, 같은 이름의 서브섹션에 계약을 채웁니다.

사용자 정의 부트스트랩 계약

bootstrap:
method: pitr_restore
pitr_restore:
command: /usr/local/bin/restore_cluster.sh
keep_existing_recovery_conf: False
no_params: False
recovery_conf:
recovery_target_action: promote
recovery_target_timeline: latest
restore_command: cp /backup/wal/%f %p

계약은 다음과 같습니다.

  • command는 절대 경로 또는 Patroni 명령 위치 기준 상대 경로입니다.
  • Patroni가 인자 두 개를 추가로 전달합니다. --scope(클러스터 이름)와 --datadir(data directory 경로)입니다. no_params: True로 전달을 끕니다.
  • 서브섹션에 적은 나머지 key: value는 전부 --name=value 형식으로 command에 붙습니다.
  • 스크립트가 exit code 0을 반환하면 Patroni는 그 결과물로 PostgreSQL을 구성하고 시작을 시도합니다. 중간 단계가 실패하거나 non-zero를 반환하면 bootstrap 실패로 간주하고, 정리 후 initialize lock을 해제합니다.
  • recovery_conf 블록이 있으면 인스턴스 시작 전에 recovery 설정을 만듭니다. PostgreSQL 12 이상에서는 recovery.conf 파일 대신 설정 파라미터와 signal 파일 방식으로 처리됩니다. 보통 recovery_target_* 하나 이상과 recovery_target_action: promote를 함께 둡니다.
  • keep_existing_recovery_conf: True면 기존 recovery.conf(PostgreSQL 11 이하) 또는 recovery.signal/standby.signal(PostgreSQL 12 이상)을 지우지 않고 recovery 설정도 덮어쓰지 않습니다. pgBackRest처럼 recovery 설정을 스스로 만드는 도구와 함께 쓸 때 필요합니다.
경고

bootstrap method는 체이닝되지 않고, 실패해도 기본 initdb로 fallback하지 않습니다. 공식 문서가 note로 명시해 둔 제약입니다. replica 생성의 create_replica_methods가 목록을 순서대로 시도하는 것과 대비되는 지점으로, custom bootstrap이 계속 실패하면 클러스터는 노드들이 lock을 잡았다 놓기를 반복하며 초기화되지 못한 상태에 머뭅니다.

정리​

  • 빈 데이터 디렉토리의 노드는 DCS의 initialize 키 유무에 따라 bootstrap과 replica 생성으로 갈리고, bootstrap은 lock을 잡은 첫 노드만 수행합니다.
  • 기본 bootstrap은 initdb입니다. pg_rewind를 염두에 두면 data-checksums를 initdb 옵션에 넣어 둡니다.
  • bootstrap.dcs는 최초 1회만 /config에 기록됩니다. 이후에는 patroni.yml을 고쳐도 무효이며 patronictl edit-config로 바꿉니다.
  • custom bootstrap은 exit 0 계약으로 성패를 알리고, 실패하면 lock을 해제해 다른 노드에 기회를 넘깁니다. 체이닝과 fallback은 없습니다.