7.2 명령 치트시트
실습과 운영에서 반복해서 쓰는 명령을 도구별로 모았습니다. 각 절에는 명령만 담았습니다. 동작 원리와 주의점은 해당 장으로 연결했습니다. 명령 앞의 설명은 한두 문장으로 제한했습니다.
neon-lab (Docker Compose)
Part III에서 사용하는 실습 구성입니다. 파일은 compose.yml과 scripts/ 아래의 셸 스크립트 세 개입니다. 호스트에 psql이 없어도 compute 컨테이너의 psql을 사용합니다.
# 기동과 상태
docker compose up -d # MinIO, storage_broker, pageserver, safekeeper x3, compute1
docker compose ps
scripts/status.sh # tenant, timeline 트리, ancestor_lsn, logical/physical size
# 접속 (compute1: 55433, compute2: 55434)
scripts/psql.sh compute1 -c 'select count(*) from t;'
scripts/psql.sh compute1 # 대화형
# branch 생성. 부모의 현재 LSN 또는 --at-lsn으로 과거 LSN 지정
scripts/branch.sh feature-a
scripts/branch.sh before-delete --at-lsn 0/1A2B3C4
docker compose --profile branch up -d compute2
scripts/psql.sh compute2 -c 'select count(*) from t;'
# 정리. -v를 붙이면 MinIO와 pageserver 데이터까지 지운다
docker compose --profile branch down -v
scripts/branch.sh는 결과를 .env에 기록합니다. compose.yml은 그 값을 compute1(TIMELINE_ID)에 넘깁니다. compute2(BRANCH_TIMELINE_ID)에도 같은 값을 넘깁니다. 원리는 3.2와 3.3에 있습니다.
pageserver HTTP API
pageserver의 관리 API는 9898 포트를 사용합니다. branch를 만들 때는 ancestor_timeline_id를 지정한 timeline 생성 요청을 보냅니다. ancestor_start_lsn을 생략하면 부모의 현재 위치에서 분기합니다.
.env는 Docker Compose가 읽는 파일이고 현재 셸에는 자동으로 들어오지 않습니다. 아래 명령을 그대로 쓰려면 변수를 먼저 설정합니다.
PS=http://localhost:9898
# 변수 설정: neon-lab 디렉터리라면 .env 를 읽고, 아니면 API 로 뽑는다
. ./.env # TENANT_ID, TIMELINE_ID, BRANCH_TIMELINE_ID
TENANT=${TENANT_ID:-$(curl -s $PS/v1/tenant | jq -r '.[0].id')}
PARENT=${TIMELINE_ID:-$(curl -s $PS/v1/tenant/$TENANT/timeline \
| jq -r '[.[] | select(.ancestor_timeline_id == null)][0].timeline_id')}
TIMELINE=$PARENT # 조회와 삭제 예제의 대상
# 목록과 상세
curl -s $PS/v1/tenant | jq .
curl -s $PS/v1/tenant/$TENANT/timeline | jq '.[] | {timeline_id, ancestor_timeline_id, ancestor_lsn, last_record_lsn}'
curl -s $PS/v1/tenant/$TENANT/timeline/$TIMELINE | jq .
# branch 생성 (새 timeline id는 16바이트 hex를 호출자가 만든다)
NEW_CURRENT=$(python3 -c 'import secrets; print(secrets.token_hex(16))')
curl -s -X POST -H 'Content-Type: application/json' \
-d "{\"new_timeline_id\":\"$NEW_CURRENT\",\"ancestor_timeline_id\":\"$PARENT\",\"pg_version\":17}" \
$PS/v1/tenant/$TENANT/timeline/
# 과거 LSN 에서 분기. id 를 새로 만들지 않으면 인자가 달라 409 가 된다
NEW_PITR=$(python3 -c 'import secrets; print(secrets.token_hex(16))')
curl -s -X POST -H 'Content-Type: application/json' \
-d "{\"new_timeline_id\":\"$NEW_PITR\",\"ancestor_timeline_id\":\"$PARENT\",\"ancestor_start_lsn\":\"0/1A2B3C4\",\"pg_version\":17}" \
$PS/v1/tenant/$TENANT/timeline/
# 삭제 (자식 timeline이 있으면 실패한다)
curl -s -X DELETE $PS/v1/tenant/$TENANT/timeline/$NEW_CURRENT
응답 코드 201은 생성됐거나 같은 인자로 이미 존재함을 뜻합니다. 409는 다른 인자로 이미 존재함을 뜻합니다. 406은 재시도해도 성공할 수 없는 요청을 뜻합니다. 저장소 문서는 durable해질 때까지 호출자가 생성을 재시도해야 한다고 명시합니다.
cargo neon 개발 환경
소스를 빌드해 한 워크스테이션에서 pageserver, safekeeper, broker, compute를 실행합니다. 3.5에서 이 방법을 다룹니다.
git clone --recursive https://github.com/neondatabase/neon.git && cd neon
make -j"$(nproc)" -s # macOS: make -j"$(sysctl -n hw.logicalcpu)" -s
cargo neon init # .neon 디렉터리에 repository 생성
cargo neon start # broker, pageserver, safekeeper 기동
cargo neon tenant create --set-default
cargo neon endpoint create main
cargo neon endpoint start main # postgresql://cloud_admin@127.0.0.1:55432/postgres
cargo neon endpoint list
cargo neon timeline branch --branch-name migration_check
cargo neon timeline list # 트리 형태로 ancestor 표시
cargo neon endpoint create migration_check --branch-name migration_check
cargo neon endpoint start migration_check
cargo neon endpoint stop main
cargo neon stop
Neon CLI
CLI는 neon으로 호출합니다. neonctl은 별칭입니다. --project-id를 생략하면 현재 문맥의 프로젝트를 사용합니다. 세부 옵션은 4.1에 있습니다.
node -v # npm 패키지 문서 기준 Node.js 22.20 이상 필요
npm i -g neon
neon auth # 브라우저 로그인. login 별칭
neon projects list
# branch
neon branches list --project-id <id>
neon branches create --name feature-a --parent main
neon branches create --name pitr-check --parent main@2026-09-07T03:00:00Z
neon branches create --name schema-only --schema-only
neon branches create --name ci-123 --expires-at 2026-09-08T00:00:00Z
neon branches get feature-a
neon branches reset feature-a --parent --preserve-under-name feature-a-old
neon branches restore main '^self@2026-09-07T03:00:00Z' --preserve-under-name main-before-restore
neon branches schema-diff main feature-a --database appdb
neon branches set-expiration ci-123 --expires-at 2026-09-09T00:00:00Z
neon branches add-compute feature-a --type read_only
neon branches set-default feature-a
neon branches delete feature-a
# 접속 문자열과 snapshot
neon connection-string feature-a --database-name appdb
neon snapshots list
neon snapshots create --branch main --timestamp 2026-09-07T03:00:00Z
neon snapshots get <snapshot-id>
Neon API
Neon은 CLI의 모든 기능을 REST API로도 제공합니다. GitHub Actions나 자체 도구에서는 API를 직접 호출합니다.
export NEON_API_KEY=...
PROJECT=<project_id>
# branch 생성 (parent_id 생략 시 기본 branch)
curl -s -X POST https://console.neon.tech/api/v2/projects/$PROJECT/branches \
-H "Authorization: Bearer $NEON_API_KEY" -H 'Content-Type: application/json' \
-d '{"branch":{"name":"feature-a","parent_id":"br-xxxx"},"endpoints":[{"type":"read_write"}]}'
# schema-only branch
curl -s -X POST https://console.neon.tech/api/v2/projects/$PROJECT/branches \
-H "Authorization: Bearer $NEON_API_KEY" -H 'Content-Type: application/json' \
-d '{"branch":{"parent_id":"br-xxxx","init_source":"schema-only"}}'
# branch 목록
curl -s https://console.neon.tech/api/v2/projects/$PROJECT/branches \
-H "Authorization: Bearer $NEON_API_KEY" | jq '.branches[] | {id, name, parent_id, parent_lsn}'
DBLab Engine CLI
DBLab의 dblab 명령은 서버(DBLab Engine)에 HTTP로 접속합니다. DBLab은 branch, switch, commit, log를 4.0부터 제공합니다. 설치와 데이터 소스 설정은 5.2에 있습니다.
dblab init --environment-id dev --url "http://127.0.0.1:2345" --token "SECRET_TOKEN" --insecure
dblab instance status
# clone
dblab clone create --username app --password secret --branch main --id test-clone
dblab clone create --username app --password secret --id test-clone --protected 8h
dblab clone reset test-clone
dblab clone reset --latest test-clone
dblab --forwarding-server-url "ssh://user@host:22" --forwarding-local-port 8888 clone port-forward test-clone
# branch (4.0+)
dblab branch # 목록
dblab branch test
dblab branch --parent-branch dev test
dblab branch --snapshot-id <snapshot_id> test
dblab switch test
dblab commit --clone-id test-clone --message "index rebuilt"
dblab log test
dblab branch --delete test
# snapshot
dblab snapshot list
dblab snapshot create --pool dblab_pool
dblab snapshot delete "dblab_pool/dataset_1@snapshot_20241028174127"
Doltgres SQL 함수
Doltgres는 Git 방식 CLI 대신 SQL 함수와 시스템 테이블로 버전 관리 기능을 제공합니다. 5.3에서 Docker로 실행합니다. 아래 함수는 Doltgres 1.3.1 이미지에서 실행해 확인한 것입니다.
docker run -d --name doltgres -e DOLTGRES_PASSWORD=password -p 55440:5432 dolthub/doltgresql:latest
PGPASSWORD=password psql -h localhost -p 55440 -U postgres
# branch 로 작업할 때는 database/branch 형식으로 접속한다
PGPASSWORD=password psql -h localhost -p 55440 -U postgres -d 'postgres/feature'
-- commit
select dolt_add('-A');
select dolt_commit('-m', 'seed accounts');
select * from dolt.status;
-- branch
select dolt_branch('feature');
select * from dolt.branches;
select active_branch();
-- branch 에서 변경하고 이력 확인
select dolt_commit('-a', '-m', 'change on feature');
select * from dolt_log('feature');
-- 비교와 머지
select * from dolt_diff('main', 'feature', 'accounts');
select * from dolt_diff_stat('main', 'feature');
select dolt_merge('feature');
-- 삭제
select dolt_branch('-d', 'feature');
충돌이 있는 머지는 autocommit 상태에서 실패하고 롤백됩니다. 트랜잭션으로 감싸면 충돌 테이블을 읽고 해소할 수 있습니다.
begin;
select dolt_merge('feature');
select * from dolt_conflicts;
select * from dolt_conflicts_accounts;
select dolt_conflicts_resolve('--ours', 'accounts');
commit;
ZFS snapshot과 clone
Linux 서버에서 PGDATA를 ZFS dataset에 둘 때 사용하는 thin clone 절차입니다. ZFS는 macOS 기본 기능이 아니고 이 절차는 Linux를 기준으로 씁니다. 일관성 확보 방법과 놓치기 쉬운 조건은 5.1에 있습니다.
기본 절차는 원본을 정상 종료한 뒤 snapshot을 만드는 것입니다. 이 방법에서는 clone에 남는 상태가 없습니다.
# PGDATA 밖에 있는 것을 먼저 확인한다. 결과가 별도 dataset 이면 함께 snapshot 해야 한다
readlink -f /var/lib/pgsql/17/data/pg_wal
ls -l /var/lib/pgsql/17/data/pg_tblspc/
sudo -u postgres pg_ctl -D /var/lib/pgsql/17/data stop -m fast
zfs snapshot tank/pgdata@before-migration # 여러 dataset 이면 zfs snapshot -r
sudo -u postgres pg_ctl -D /var/lib/pgsql/17/data start
zfs list -t all -o name,used,refer,origin -r tank
zfs clone tank/pgdata@before-migration tank/pgdata-clone
zfs set mountpoint=/var/lib/pgsql/clone tank/pgdata-clone
# clone 위에서 두 번째 PostgreSQL 기동. 포트와 설정을 명시하고 archive 는 끈다
sudo -u postgres pg_ctl -D /var/lib/pgsql/clone \
-o "-c port=5433 -c archive_mode=off" start
원본을 멈출 수 없으면 원자적 snapshot으로 만들고 clone 쪽의 잔여 상태를 지웁니다. 기동 시 crash recovery가 정합성을 맞춥니다.
sudo -u postgres psql -c 'CHECKPOINT;'
zfs snapshot -r tank/pgdata@online # 관련 dataset 을 한 번에
zfs clone tank/pgdata@online tank/pgdata-clone
rm -f /var/lib/pgsql/clone/postmaster.pid # 원본 PID 가 복제되면 기동이 거부된다
sudo -u postgres pg_ctl -D /var/lib/pgsql/clone \
-o "-c port=5433 -c archive_mode=off" start
정리는 두 경로 중 하나만 고릅니다. 단순 폐기는 clone을 먼저 지웁니다.
sudo -u postgres pg_ctl -D /var/lib/pgsql/clone stop -m fast
zfs destroy tank/pgdata-clone
zfs destroy tank/pgdata@before-migration
clone을 새 원본으로 쓰려면 승격합니다. 승격하면 snapshot 소유가 clone으로 넘어가므로 이후 정리 대상도 바뀝니다.
zfs promote tank/pgdata-clone
zfs rename tank/pgdata tank/pgdata-old
zfs rename tank/pgdata-clone tank/pgdata
zfs destroy tank/pgdata-old # snapshot 소유가 이동한 뒤에 지운다
Aurora clone (AWS CLI)
Aurora clone은 point-in-time restore 명령에 --restore-type copy-on-write를 추가해 만듭니다. 먼저 클러스터를 만들고 DB 인스턴스는 따로 추가합니다.
aws rds restore-db-cluster-to-point-in-time \
--source-db-cluster-identifier prod-cluster \
--db-cluster-identifier prod-clone \
--restore-type copy-on-write \
--use-latest-restorable-time
aws rds describe-db-clusters --db-cluster-identifier prod-clone --query '*[].[Status]' --output text
aws rds create-db-instance \
--db-instance-identifier prod-clone-instance \
--db-cluster-identifier prod-clone \
--db-instance-class db.r6g.large \
--engine aurora-postgresql
Tiger Cloud fork
Tiger Cloud의 fork는 시점 옵션 세 개 중 정확히 하나를 지정합니다.
tiger service fork <service_id> --now --name analytics-fork
tiger service fork <service_id> --last-snapshot --name quick-fork
tiger service fork <service_id> --to-timestamp 2026-09-07T03:00:00Z --name pitr-fork --cpu 4 --memory 16