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은 없습니다.