10.2 로깅
전통적인 PostgreSQL 운영에서는 로그가 서버 안 log/ 디렉토리에 파일로 쌓였다. Kubernetes에서는 방식이 다르다. CloudNativePG는 PostgreSQL 로그를 포함한 모든 로그를 JSON 형식으로 표준 출력(stdout)에만 흘려보내고, 파일로 저장하지 않는다. 보관과 검색은 컨테이너 로그를 수집하는 Kubernetes 계층(로그 수집기 + 저장소)이 담당한다. 즉 애플리케이션은 로그를 뿜기만 하고, 어디에 얼마나 오래 둘지는 인프라가 정하는 구조다.
flowchart TD
PG["postgres"]
IM["instance-manager"]
STDOUT["stdout<br/>(JSON 한 줄)"]
COLL["로그 수집기<br/>(Fluent Bit 등)"]
STORE[("로그 저장소<br/>Loki·ES")]
PG --> IM
IM -->|JSON 변환| STDOUT
STDOUT -->|컨테이너 로그| COLL
COLL --> STORE
classDef node fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
class PG,IM,STDOUT,COLL,STORE node
JSON 로그 구조
로그는 한 줄에 JSON 객체 하나다. 사람이 읽기엔 다소 빽빽하지만, 기계가 파싱하고 필드로 검색하기에는 훨씬 낫다. 모든 레코드는 공통 필드를 갖는다.
| 필드 | 의미 |
|---|---|
level | 심각도 — error, warning, info, debug, trace |
ts | 타임스탬프 |
logger | 로그를 낸 프로세스 종류 |
msg | 메시지. 구조화된 경우 "record" |
record | logger 종류에 따라 달라지는 실제 내용 |
logging_pod | 로그를 낸 Pod 이름 |
PostgreSQL 자체 로그도 이 틀 안에 들어간다. 이때 logger는 postgres이고, record 안에 PostgreSQL의 CSV 로그 필드가 그대로 담긴다.
{
"level": "info",
"ts": "2026-07-05T09:12:34.567Z",
"logger": "postgres",
"msg": "record",
"record": {
"log_time": "2026-07-05 09:12:34.567 UTC",
"user_name": "app",
"database_name": "app",
"process_id": "1234",
"error_severity": "LOG",
"sql_state_code": "00000",
"message": "connection authorized: user=app database=app"
},
"logging_pod": "cluster-example-1"
}logger 종류
logger 필드를 보면 로그가 어느 단계에서 나왔는지 알 수 있다. 문제를 쫓을 때 이 값으로 먼저 걸러 내면 범위가 크게 좁혀진다.
| logger | 출처 |
|---|---|
postgres | PostgreSQL 인스턴스 |
instance-manager | Pod 안에서 postgres를 지키는 인스턴스 매니저 |
initdb | 클러스터 초기화 |
wal-archive / wal-restore | WAL 아카이브·복원 |
barman-cloud-wal-archive / barman-cloud-wal-restore | Barman 기반 WAL 작업 |
pg_basebackup, pg_ctl, pg_rewind | PostgreSQL 유틸리티 |
pgaudit | PGAudit 감사 로그 |
로그 레벨
로그 레벨은 인스턴스와 Operator에서 따로 정한다.
클러스터의 .spec.logLevel로 지정한다. 값은 error·warning·info(기본)·debug·trace다.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 3
logLevel: infopgaudit 감사 로그
CloudNativePG는 감사 로깅 확장인 PGAudit를 기본으로 통합한다. postgresql 설정에 pgaudit 파라미터를 주기만 하면, Operator가 shared_preload_libraries에 pgaudit를 넣고 빼는 일과 모든 데이터베이스에서 확장을 만들고 지우는 일을 알아서 처리한다.
spec:
postgresql:
parameters:
pgaudit.log: "all, -misc"
pgaudit.log_catalog: "off"감사 레코드는 logger: "pgaudit"로 나오며, record.audit 안에 감사 메타데이터(audit_type, statement_id, class, command, statement, parameter)가 담긴다. 로그 수집기에서 logger == "pgaudit"로 필터링하면 감사 로그만 따로 모을 수 있다.
로그 수집
로그가 stdout으로 나오므로, 컨테이너 로그를 다루는 어떤 도구와도 자연스럽게 맞물린다. 가볍게 실시간으로 볼 때는 stern이 편하다.
# cluster-example로 시작하는 모든 Pod의 로그를 한 번에 tail
stern cluster-example프로덕션에서는 Fluent Bit·Fluentd 같은 수집기를 노드마다 두어 컨테이너 로그를 긁고, Loki나 Elasticsearch에 적재해 검색·보관한다.
log-field-level·log-field-timestamp 플래그로 level·ts 필드 이름을 바꿀 수 있다.정리
- 모든 로그는 파일이 아니라 stdout으로, JSON 한 줄씩 나온다
- 공통 필드(
level·ts·logger·msg·record)로 파싱·검색한다 logger값으로 postgres·instance-manager·wal-archive 등 출처를 구분한다- 로그 레벨은 시작 시점에만 적용되고, 감사는 pgaudit로 통합된다
- 보관·검색은 stern·Fluent Bit·Loki 같은 Kubernetes 계층 도구가 맡는다
다음 절(10.3)에서는 이 모든 리소스에 Operator가 붙이는 label과 annotation을 읽는다.