3.5 cargo neon 개발 환경과 Neon Local
Docker Compose 외에도 Neon을 로컬에서 사용하는 방법이 두 가지 더 있습니다. 하나는 저장소를 직접 빌드해 cargo neon 명령으로 구성 요소를 실행하는 개발 환경입니다. 다른 하나는 Neon이 배포하는 neon_local 컨테이너입니다. 이름이 비슷해 혼동하기 쉽지만 성격은 정반대입니다. 전자는 완전히 오프라인으로 작동하는 개발자용 미니 클러스터이고, 후자는 Neon 클라우드에 연결하는 프록시입니다.
cargo neon: 소스 빌드 개발 환경
upstream docker-compose/README.md는 "실험용 미니 Neon을 돌리려면 컨테이너 이미지보다 cargo neon을 쓰라"고 안내합니다. 이 명령은 저장소 안의 control_plane 크레이트가 제공하는 neon_local 바이너리입니다. 이름은 neon_local이지만 아래에서 설명하는 Neon Local 컨테이너와는 다른 도구입니다.
빌드
Rust 툴체인과 PostgreSQL 빌드 의존성이 필요합니다. macOS 기준 준비물은 README에 정리되어 있습니다.
xcode-select --install
brew install protobuf openssl flex bison icu4c pkg-config m4 libpq
brew link --force libpq
# neon_local 이 Ed25519 키를 만들 때 Homebrew OpenSSL 이 PATH 앞에 있어야 한다
echo 'export PATH="$(brew --prefix openssl)/bin:$PATH"' >> ~/.zshrc
exec zsh -l
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone --recursive https://github.com/neondatabase/neon.git
cd neon
make -j"$(sysctl -n hw.logicalcpu)" -s # 패치된 PostgreSQL 여러 버전 + Rust 구성 요소
경로에 공백이 있으면 빌드가 실패합니다. debug 빌드가 기본이며, release 빌드는 BUILD_TYPE=release를 붙입니다. 이 문서에서는 빌드를 실행하지 않았습니다. 아래 명령과 출력은 README에 실린 내용입니다. 빌드 시간은 머신에 따라 수십 분 단위입니다.
cargo neon start가 기동하는 구성 요소 목록은 저장소 버전에 따라 늘어납니다. 최근 control_plane 소스는 storage controller와 endpoint storage까지 함께 시작하고, storage controller는 자신의 상태를 담을 로컬 PostgreSQL을 요구합니다. 실제 목록은 실행 출력으로 확인하고, 문서와 다르면 소스 기준을 따릅니다.
실행 흐름
cargo neon init # .neon 디렉터리에 저장소 초기화
cargo neon start # broker, storage controller, pageserver, safekeeper, endpoint storage 기동
cargo neon tenant create --set-default # tenant 와 첫 timeline 생성
cargo neon endpoint create main
cargo neon endpoint start main # postgresql://cloud_admin@127.0.0.1:55432/postgres
branch를 만들고 해당 branch에서 compute를 실행하는 명령이 compose 실습의 branch.sh + compute2에 대응합니다.
cargo neon timeline branch --branch-name migration_check
cargo neon timeline list
cargo neon endpoint create migration_check --branch-name migration_check
cargo neon endpoint start migration_check # 다른 포트(예: 55434)로 두 번째 postgres
cargo neon endpoint list
(L) main [de200bd42b49cc1814412c7e592dd6e9]
(L) ┗━ @0/16F9A00: migration_check [b3b863fa45fa9e57e615f9f2d944e601]
ENDPOINT ADDRESS TIMELINE BRANCH NAME LSN STATUS
main 127.0.0.1:55432 de200bd42b49cc1814412c7e592dd6e9 main 0/16F9A38 running
migration_check 127.0.0.1:55434 b3b863fa45fa9e57e615f9f2d944e601 migration_check 0/16F9A70 running
timeline list의 트리 출력은 compose 실습의 status.sh가 재현한 것입니다. 여기서는 --branch-name처럼 사람이 읽을 수 있는 이름을 사용합니다. 이 이름은 .neon 아래의 로컬 메타데이터에만 있습니다. pageserver는 여전히 timeline id만 인식합니다.
compose와 비교
| 항목 | docker compose (neon-lab) | cargo neon |
|---|---|---|
| 준비 | 이미지 pull 9 GB | 소스 빌드(Rust, PostgreSQL 의존성) |
| 구성 요소 | 컨테이너 8개 | 프로세스 여러 개, 같은 머신. storage controller 포함 |
| branch 생성 | pageserver API 직접 호출 | timeline branch 명령 |
| compute 추가 | compose 서비스 정의 | endpoint create/start |
| 코드 수정 반영 | 이미지 재빌드 필요 | make 후 재시작 |
| object storage | MinIO 컨테이너 | 로컬 파일시스템 또는 설정한 S3 |
아키텍처를 직접 확인하고 API를 익히는 데는 compose가 빠릅니다. pageserver나 safekeeper 코드를 수정하려면 cargo neon이 적합합니다.
Neon Local: 클라우드 branch를 로컬 포트로
neondatabase/neon_local 이미지는 로컬에서 PostgreSQL을 실행하지 않습니다. 컨테이너가 시작될 때 Neon 클라우드 프로젝트에 API로 임시 branch를 만듭니다. 그런 다음 localhost:5432로 들어오는 연결을 해당 branch로 전달합니다. 컨테이너를 종료하면 branch도 삭제합니다.
services:
db:
image: neondatabase/neon_local:latest
ports:
- "5432:5432"
environment:
NEON_API_KEY: ${NEON_API_KEY}
NEON_PROJECT_ID: ${NEON_PROJECT_ID}
PARENT_BRANCH_ID: ${PARENT_BRANCH_ID:-} # 생략하면 프로젝트 기본 branch
DELETE_BRANCH: "true"
접속 문자열은 postgres://neon:npg@localhost:5432/<db>?sslmode=require로 고정입니다. 실제 인증은 프록시가 API key로 처리합니다. 따라서 애플리케이션 설정에는 클라우드 비밀번호가 들어가지 않습니다.
프록시가 제시하는 인증서는 self-signed입니다. 인증서를 엄격하게 검증하는 클라이언트는 연결을 거부하므로 검증을 완화하는 설정이 필요합니다. Node.js의 pg와 postgres 라이브러리라면 ssl: { rejectUnauthorized: false }를 넣고, psql이라면 sslmode=no-verify로 바꿉니다.
| 변수 | 역할 |
|---|---|
| NEON_API_KEY | Neon API 인증 |
| NEON_PROJECT_ID | 대상 프로젝트 |
| PARENT_BRANCH_ID | 임시 branch의 부모. 기본은 프로젝트 기본 branch |
| BRANCH_ID | 새로 만들지 않고 기존 branch에 연결. PARENT_BRANCH_ID와 동시 사용 불가 |
| DELETE_BRANCH | false면 컨테이너 종료 후에도 branch 유지 |
.neon_local/ 디렉터리와 .git/HEAD를 컨테이너에 마운트하면 git branch마다 다른 Neon branch를 유지하도록 매핑할 수도 있습니다. git branch를 바꾸면 프록시가 대응하는 데이터베이스 branch로 연결을 전환합니다.
오프라인 동작 여부
Neon Local은 인터넷과 Neon 계정이 필요합니다. 소개 글에는 오프라인 모드를 검토 중이라고만 적혀 있습니다. 비행기 안에서 개발하거나 사내 망에서 외부 연결이 차단된 환경이라면 이 장 앞부분의 compose나 cargo neon이 대안입니다. 두 방식은 반대로 클라우드 branch와 아무 관계가 없습니다.
언제 무엇을 쓰는가
- 팀이 이미 Neon 클라우드를 사용하고 개발자마다 격리된 데이터베이스가 필요할 때는 Neon Local을 사용합니다. 클라우드 데이터를 그대로 branch로 받으므로 seed 스크립트가 필요 없습니다.
- Neon 내부 구조를 공부하거나 self-hosting 가능성을 검토할 때는 compose 또는
cargo neon을 사용합니다. - CI에서 PR마다 데이터베이스가 필요할 때는 Neon Local 컨테이너를 서비스로 실행하거나, 4.2의 GitHub Actions 방식을 사용합니다.
연습 문제
- compose 실습의
branch.sh와cargo neon timeline branch가 pageserver에 보내는 요청이 같은 엔드포인트를 사용하는지control_plane크레이트 소스에서 확인합니다. - Neon Local 컨테이너를
DELETE_BRANCH=false로 두 번 기동했을 때 Neon 콘솔에 branch가 몇 개 생길지 예상하고 문서에서 확인합니다. - Neon Local의 git 매핑 기능을 Docker Desktop for Mac에서 사용하려면 파일 공유 방식을 gRPC FUSE로 바꿔야 합니다. 문서 주석이 그렇게 안내하는 이유를 생각해 봅니다.