lakeFS 시작하기: Docker Compose, CLI, Python
lakeFS란?
lakeFS는 오브젝트 스토리지(S3, GCS, Azure Blob) 위에 올라가서 데이터 레이크에 Git 같은 시맨틱(브랜치, 커밋, 머지, 다이프)을 제공하는 오픈소스 데이터 버전 관리 시스템입니다. 읽기/쓰기 클라이언트를 원본 버킷 대신 lakeFS 엔드포인트(S3 호환 API)로 돌리면, lakeFS가 실제 오브젝트를 복사하지 않고 버전이 매겨진 레퍼런스를 관리해줍니다.
이 글에서 다루는 내용:
- GCS 버킷 백엔드로 Docker Compose를 써서 lakeFS를 로컬에서 실행하기.
- 관리자 계정과 저장소를 만드는 첫 설정.
lakectlCLI로 브랜치, 커밋, 머지하기.lakectl local(Git 같은clone/pull/commit)로 lakeFS 파일을 로컬에서 다루기.- 공식
lakefsSDK로 Python에서 읽고 쓰기. - 웹 UI에 내장된 SQL 콘솔로 parquet 파일에 바로 SQL 쿼리 날리기.
1. 설치: Docker Compose + GCS 버킷 백엔드
준비물
- Docker와 Docker Compose 설치
- 스토리지 네임스페이스로 쓸 GCS 버킷이 있는 GCP 프로젝트 (예:
gs://my-lakefs-bucket) - 그 버킷에 Storage Object Admin 권한이 있는 GCP 서비스 계정과 다운로드한 JSON 키
버킷 생성
# 고유한 버킷 이름을 정한다
export BUCKET=my-lakefs-bucket
export GCP_PROJECT=my-project
export REGION=us-central1
gsutil mb -p $GCP_PROJECT -l $REGION gs://$BUCKET
서비스 계정 키 만들기
export SA=lakefs-local@$GCP_PROJECT.iam.gserviceaccount.com
gcloud iam service-accounts create lakefs-local \
--project $GCP_PROJECT \
--display-name "lakeFS local"
gcloud projects add-iam-policy-binding $GCP_PROJECT \
--member "serviceAccount:$SA" \
--role "roles/storage.objectAdmin"
# presigned GCS URL을 위해 필요. lakectl과 많은 Python 클라이언트가
# 이걸로 업로드/다운로드를 lakeFS를 거치지 않고 GCS와 직접 주고받는다.
# 서비스 계정 자신에게 `signBlob` 권한을 부여하는 것.
gcloud iam service-accounts add-iam-policy-binding $SA \
--project $GCP_PROJECT \
--member "serviceAccount:$SA" \
--role "roles/iam.serviceAccountTokenCreator"
gcloud iam service-accounts keys create ./gcs-key.json \
--iam-account "$SA"
gcs-key.json은 docker-compose.yml 옆에 두고, .gitignore에 추가하세요.
두 번째 롤 바인딩이 왜 필요한가? 서비스 계정 자신에게 iam.serviceAccountTokenCreator가 없으면 lakeFS가 GCS URL을 서명하지 못해서, lakectl fs upload(그리고 presign이 켜진 Python 클라이언트)가 get physical address to upload object … giving up after 5 attempt(s) 에러로 실패합니다. 서명 권한을 부여하고 싶지 않다면 아래 compose env에 LAKEFS_BLOCKSTORE_GS_DISABLE_PRE_SIGNED_URL: "true"를 추가하세요 — 대신 모든 바이트가 GCS와 직접 오가지 않고 lakeFS 서버를 거치게 됩니다.
docker-compose.yml
lakeFS는 두 가지가 필요합니다: 메타데이터 DB(Postgres가 제일 쉬움)와 오브젝트 스토어 자격 증명. 최소 구성은 이렇습니다:
version: "3.8"
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: lakefs
POSTGRES_PASSWORD: lakefs
POSTGRES_DB: lakefs
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U lakefs"]
interval: 5s
retries: 10
lakefs:
image: treeverse/lakefs:latest
depends_on:
postgres:
condition: service_healthy
ports:
- "8000:8000"
environment:
LAKEFS_DATABASE_TYPE: postgres
LAKEFS_DATABASE_POSTGRES_CONNECTION_STRING: "postgres://lakefs:lakefs@postgres:5432/lakefs?sslmode=disable"
LAKEFS_AUTH_ENCRYPT_SECRET_KEY: "change-me-to-a-long-random-string"
LAKEFS_BLOCKSTORE_TYPE: gs
LAKEFS_BLOCKSTORE_GS_CREDENTIALS_FILE: /etc/lakefs/gcs-key.json
LAKEFS_LOGGING_LEVEL: INFO
volumes:
- ./gcs-key.json:/etc/lakefs/gcs-key.json:ro
volumes:
pgdata:
몇 가지 주의사항:
LAKEFS_BLOCKSTORE_TYPE: gs가 lakeFS에게 Google Cloud Storage를 쓰라고 알려줍니다.- 서비스 계정 키는 컨테이너에 read-only로 마운트됩니다.
LAKEFS_AUTH_ENCRYPT_SECRET_KEY는 길고 랜덤한 값으로 반드시 설정해야 합니다. 메타데이터 DB에 저장되는 자격 증명을 암호화합니다. 이 값을 바꾸면 기존 자격 증명이 모두 무효화됩니다.- 프로덕션이라면 DB를 매니지드 Postgres에 두고 lakeFS를 TLS 뒤에 두어야 합니다. 이 compose 파일은 로컬 탐색용입니다.
실행:
docker compose up -d
docker compose logs -f lakefs
로그에 listen on [::]:8000이 뜨면 브라우저에서 http://localhost:8000 을 열어보세요.
2. 간단한 시작
첫 실행 설정
lakeFS UI를 처음 열면 관리자 계정을 만들라고 합니다. 사용자명(예: admin)을 정하면 lakeFS가 access key ID와 secret access key를 출력합니다. 바로 복사해두세요 — secret은 한 번만 보여줍니다.
CLI와 Python 클라이언트가 읽어갈 수 있도록 ~/.lakectl.yaml에 저장합니다:
# ~/.lakectl.yaml
credentials:
access_key_id: AKIA...
secret_access_key: ...
server:
endpoint_url: http://localhost:8000
AWS 스타일 자격 증명을 기대하는 도구를 위해 환경 변수로도 내보내둡니다:
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export LAKEFS_ENDPOINT=http://localhost:8000
첫 저장소 만들기
UI에서: Create Repository → 이름은 quickstart, 스토리지 네임스페이스는 gs://my-lakefs-bucket/quickstart, 기본 브랜치는 main.
CLI로도 똑같이 할 수 있습니다 (다음 섹션 참고):
lakectl repo create lakefs://quickstart gs://my-lakefs-bucket/quickstart
스토리지 네임스페이스는 lakeFS가 이 저장소의 데이터를 저장할 버킷 내부 prefix입니다. 각 저장소마다 prefix가 다르기만 하면, 하나의 버킷에 여러 저장소를 둘 수 있습니다.
3. CLI 사용하기 (lakectl)
lakectl 설치
macOS:
brew tap treeverse/lakefs
brew install lakefs
Linux (릴리즈 페이지에서 바이너리 다운로드):
curl -L https://github.com/treeverse/lakeFS/releases/latest/download/lakeFS_Linux_x86_64.tar.gz \
| tar xz
sudo mv lakectl /usr/local/bin/
확인:
lakectl --version
lakectl repo list
업로드, 브랜치, 커밋, 머지
로컬 파일을 만들어 main 브랜치에 업로드합니다:
echo "id,name\n1,alice\n2,bob" > users.csv
lakectl fs upload \
-s users.csv \
lakefs://quickstart/main/data/users.csv
만약
get physical address to upload object … giving up after 5 attempt(s)에러가 난다면, 서버가 GCS URL을 서명하지 못하는 상황입니다 — 섹션 1의roles/iam.serviceAccountTokenCreator바인딩을 다시 확인하세요. 일회성 우회책으로는 업로드 명령에--pre-sign=false를 붙이면 됩니다. 그러면 바이트가 GCS로 직접 가지 않고 lakeFS를 거치게 됩니다.
커밋:
lakectl commit lakefs://quickstart/main \
-m "Add initial users.csv"
main에서 feature 브랜치 만들기:
lakectl branch create \
lakefs://quickstart/experiment \
-s lakefs://quickstart/main
새 브랜치에서 파일 수정:
echo "id,name\n1,alice\n2,bob\n3,carol" > users.csv
lakectl fs upload \
-s users.csv \
lakefs://quickstart/experiment/data/users.csv
lakectl commit lakefs://quickstart/experiment \
-m "Add carol"
main과 diff:
lakectl diff \
lakefs://quickstart/main \
lakefs://quickstart/experiment
feature 브랜치를 main에 머지:
lakectl merge \
lakefs://quickstart/experiment \
lakefs://quickstart/main
머지가 끝나면 feature 브랜치 삭제:
lakectl branch delete lakefs://quickstart/experiment
브랜치 삭제는 브랜치 레퍼런스만 제거합니다 — 아래에 있는 커밋과 그걸 가리키는 태그는 그대로 남습니다. 스크립트에서 쓸 때 확인 프롬프트를 건너뛰려면 -y를 추가하세요.
알아두면 좋은 명령어
# 저장소, 브랜치, 오브젝트 목록
lakectl repo list
lakectl branch list lakefs://quickstart
lakectl fs ls lakefs://quickstart/main/
# 커밋 히스토리 보기
lakectl log lakefs://quickstart/main
# 파일 다운로드
lakectl fs download \
lakefs://quickstart/main/data/users.csv \
./users.csv
# 브랜치를 이전 커밋으로 되돌리기
lakectl branch reset lakefs://quickstart/experiment \
--commit <commit-id>
# 커밋에 태그 달기 (데이터셋 버전을 재현 가능하게 하는 용도로 유용)
lakectl tag create \
lakefs://quickstart/v1.0 \
lakefs://quickstart/main
4. lakeFS 파일을 로컬에서 다루기 (lakectl local)
업로드/다운로드 코드를 짜지 않고, 텍스트 에디터·grep·pandas·Jupyter 노트북 같은 평범한 로컬 도구로 lakeFS 파일을 편집하거나 탐색하고 싶다면 **lakectl local**을 쓰세요. 로컬 디렉토리와 lakeFS 경로를 연결하고, Git 같은 느낌의 clone / pull / commit / status 명령을 제공합니다.
이건 파일을 다운로드합니다 (레이지 마운트가 아닙니다). 그래서 실제로 다루고 싶은 소·중규모 데이터셋에 적합합니다.
브랜치를 로컬 디렉토리로 clone
lakectl local clone \
lakefs://quickstart/main/data/ \
./data
이 명령은 ./data/를 lakefs://quickstart/main/data/의 미러로 만들고, 연결 정보를 ./data/.lakefs_ref.yaml에 기록해서 이후 명령이 이 디렉토리가 어느 저장소·브랜치·경로에 속해 있는지 알 수 있게 해줍니다.
pull, 편집, status, commit
# lakeFS에서 새로 추가되거나 바뀐 파일을 가져오기
lakectl local pull ./data
# 아무 도구로나 파일 편집
echo "id,name\n1,alice\n2,bob\n3,carol" > ./data/users.csv
# lakeFS 대비 무엇이 바뀌었는지 보기
lakectl local status ./data
# 로컬 변경사항을 브랜치에 커밋
lakectl local commit ./data \
-m "Update users.csv from local edits"
주의: Git과 달리
lakectl local commit은 로컬만의 작업이 아닙니다. 바뀐 파일을 lakeFS에 업로드하고 추적 중인 브랜치에 커밋을 생성하는 일을 한 번에 합니다 — 별도의push가 없습니다. 명령이 리턴되는 순간 변경사항은 이미 서버에 반영되어, 다른 머신에서도lakectl local pull로 받아볼 수 있습니다.
연결이 브랜치 단위로 묶여 있기 때문에 자연스러운 흐름이 생깁니다: 브랜치를 만들고, clone하고, 편집하고, commit한 뒤, lakectl merge로 main에 합친다 — 업로드/다운로드 API를 직접 건드릴 일이 없습니다.
다른 ref로 전환하기
다른 브랜치·태그·커밋을 대상으로 작업하고 싶다면 별도 디렉토리에 다시 clone하거나, 다른 위치로 cd해서 lakectl local clone을 다시 호출하세요. 하나의 로컬 디렉토리는 하나의 lakeFS ref와 매핑됩니다.
5. Python에서 사용하기
공식이자 가장 널리 쓰이는 lakeFS Python 패키지는 그냥 lakefs입니다 — Treeverse(lakeFS 팀)가 직접 관리하고 공식 문서에서 추천하는 그 패키지입니다. REST API를 저장소·브랜치·오브젝트 형태의 편한 객체 모델로 감싸주고, 읽기/쓰기와 브랜치/커밋/머지 흐름을 모두 이 하나로 해결할 수 있습니다.
pip install lakefs
바로 실행 가능한 예제: 아래 스니펫들을 전부 합쳐놓은 엔드-투-엔드 스크립트도 있습니다 — lakefs_quickstart.py. 자격 증명은 환경 변수에서 읽으므로 코드에 붙여넣을 필요가 없습니다:
export LAKEFS_HOST="http://localhost:8000" export LAKEFS_ACCESS_KEY="AKIA..." export LAKEFS_SECRET_KEY="..." export LAKEFS_REPO="quickstart" python lakefs_quickstart.py
연결
import lakefs
from lakefs.client import Client
client = Client(
host="http://localhost:8000",
username="AKIA...", # access key id
password="...", # secret access key
)
repo = lakefs.Repository("quickstart", client=client)
오브젝트 읽고 쓰기
main = repo.branch("main")
# 파일을 쓰고, 브랜치에 커밋.
# write 하나만으로는 오브젝트가 staging 상태에 있을 뿐, 커밋해야 영속화됨.
with main.object("data/from_sdk.txt").writer(mode="wb") as f:
f.write(b"hello from python\n")
main.commit(message="Add data/from_sdk.txt")
# 파일 읽기
with main.object("data/users.csv").reader(mode="r") as f:
print(f.read())
# prefix 하위 오브젝트 나열
for obj in main.objects(prefix="data/"):
print(obj.path, obj.size_bytes)
브랜치, 커밋, 머지
# main에서 브랜치 만들기
exp = repo.branch("python-experiment").create(source_reference="main")
# 브랜치에서 파일 쓰기
with exp.object("data/from_sdk.txt").writer(mode="wb") as f:
f.write(b"edited on the experiment branch\n")
# 커밋하고 main으로 머지
exp.commit(message="Update from python SDK")
exp.merge_into("main")
브랜치 이름 대신 태그나 커밋 ID를 써도 됩니다 — repo.ref("v1.0")이나 repo.ref("<commit-sha>")처럼 해서 특정 시점의 데이터를 읽을 수 있습니다.
전체 스크립트 다운로드
위 내용 전체가 하나의 실행 가능한 파일로 묶여 있습니다 — 엔드-투-엔드 (연결 → 쓰기 → 읽기 → 나열 → 브랜치 → 커밋 → 머지 → 검증):
↓ Download lakefs_quickstart.py
+ python lakefs_quickstart.py
writing data/from_sdk.txt on main...
reading data/from_sdk.txt on main...
contents: hello from python
listing data/ on main...
data/from_sdk.txt (18 bytes)
data/users.csv (37 bytes)
creating branch python-experiment-1776452154 off main...
editing data/from_sdk.txt on python-experiment-1776452154...
committing...
merging python-experiment-1776452154 -> main...
reading data/from_sdk.txt on main after merge...
contents: edited on the experiment branch
done.
6. 웹 UI에서 Parquet 파일에 SQL 쿼리 날리기
lakeFS v0.88.0부터 웹 UI에 내장 SQL 콘솔이 생겼습니다 — DuckDB를 WebAssembly로 컴파일해 브라우저 안에서 바로 실행합니다. UI에서 아무 parquet·CSV 오브젝트나 열면 쿼리 창이 같이 뜨고, DESCRIBE로 스키마를 보거나, SELECT로 샘플을 뽑거나, 같은 브랜치 안에서 파일 간 JOIN을 하거나, 심지어 두 브랜치의 같은 경로를 비교하는 쿼리까지 돌릴 수 있습니다.
설치도, 자격 증명 설정도, 아무 구성도 필요 없습니다 — 기본으로 동작합니다. 그리고 DuckDB가 lakeFS 서버가 아니라 브라우저에서 돌기 때문에 쿼리 자체는 클라이언트에 머물고, 서버는 오브젝트 바이트만 내려줍니다.
다음 단계
- Hooks: lakeFS는 pre-commit, pre-merge hook을 지원합니다 (Lua 스크립트나 외부 웹훅으로 스키마 검증, null 비율 임계값 등을 강제할 수 있음).
- Garbage collection: 브랜치를 지우거나 오브젝트를 덮어써도 GCS의 실제 데이터는
lakectl gc를 돌리기 전까지 삭제되지 않습니다. 리텐션 정책에 맞춰 주기적으로 실행하세요. - 인증: 오픈소스 버전은 단순 access key만 지원합니다 — 만든 관리자 계정으로 로컬 용도는 충분하고, UI에서 사용자별로 추가 access key를 발급할 수 있습니다. 세분화된 RBAC, SSO, IAM 연동은 오픈소스에 없습니다 — lakeFS Enterprise / Cloud로 옮겨졌고 “Fluffy”라는 클로즈드 소스 사이드카로 제공됩니다. Enterprise 가격은 공개되지 않아 Treeverse 세일즈에 문의해야 합니다. 혼자 쓰거나 작은 팀에서 직접 호스팅하는 경우라면, 대부분 VPN이나 사설 네트워크 뒤에서 단순 인증을 쓰는 게 현실적입니다.
- Spark / DuckDB / Trino: 모두 S3 게이트웨이를 통해 동작하며, 위에서 본 브랜치-경로 트릭을 똑같이 쓸 수 있습니다.