새벽 3시에 아무도 보지 않는 동안 일하는 에이전트의 가치는, 나중에 그것이 무엇을 했는지 재구성할 수 있는 능력만큼입니다. "밤새 토큰으로 38 $를 태웠다"는 나쁜 아침입니다. "검색 도구가 빈 페이지를 돌려줬고 플래너가 400번 재시도해서 38 $를 태웠다"는 점심 전에 고칠 수 있는 버그입니다. 두 번째 문장을 쓰려면 트레이스가 있어야 합니다.
Langfuse는 모든 LLM 호출, 프롬프트, 도구 실행 단계, 토큰 수, 지연 시간을 클릭해서 따라갈 수 있는 트리로 기록합니다. 직접 호스팅하면 고객 데이터, API 출력, 가끔은 잘못 섞여 든 비밀값까지 담긴 프롬프트와 응답이 남의 대시보드가 아닌 내가 관리하는 서버에 남습니다.
운영 비용
Langfuse v4는 작은 바이너리 하나가 아니라 6개의 컨테이너입니다. 웹 앱, 백그라운드 worker, Postgres, 이벤트용 ClickHouse, Redis, 파일용 MinIO입니다. 테스트 서버에 설치해 보니 빈 프로젝트에서 약 3.5 GB RAM을 쓰고 있었습니다. 커지는 쪽은 ClickHouse로, 보내는 span이 모두 그곳에 쌓입니다.
| 부하 | 요금제 |
|---|---|
| 에이전트 몇 개, 몇 주 치 트레이스 | Medium-IP: 6 vCPU, 6 GB, 45 GB (월 $20) |
| 에이전트 무리, 몇 달 치 기록 | Pro 요금제, RAM 32 GB부터 — 문의 |
더 작은 요금제에도 설치는 되지만, ClickHouse와 worker가 동시에 메모리를 원하는 순간부터 버거워합니다. 저라면 6 GB 아래로는 내려가지 않겠습니다.
에이전트는 어디에 있나요? 다른 머신에서 실행된다면 전용 IPv4가 있는 요금제를 고르세요. 에이전트에는 HTTPS 엔드포인트가 필요한데, NAT 요금제에서는 나가는 주소가 내 것이 아니고 외부에서는 SSH 포트만 접속됩니다. 에이전트가 바로 이 서버에서 실행된다면 Langfuse를 인터넷에 노출할 필요가 전혀 없습니다. 시험용이라면 NAT Medium(월 $12)으로 충분합니다. 위치는 독일과 핀란드 두 곳이며, 사용 가능 여부는 주문할 때 표시됩니다. 내가 아니라 에이전트에 가까운 곳을 고르세요. 서버 요금은 암호화폐로 결제하며 KYC는 없습니다.
설치
새로 만든 Ubuntu 24.04 또는 Debian 12 서버에서 root로 진행합니다. 먼저 Docker:
curl -fsSL https://get.docker.com | sh
다음은 Langfuse입니다. compose 파일의 기본값 대신 실제 비밀값을 씁니다:
git clone --depth 1 https://github.com/langfuse/langfuse.git
cd langfuse
PG=$(openssl rand -hex 16); MINIO=$(openssl rand -hex 16)
cat > .env <<EOF
NEXTAUTH_URL=https://langfuse.example.com
NEXTAUTH_SECRET=$(openssl rand -hex 32)
SALT=$(openssl rand -hex 32)
ENCRYPTION_KEY=$(openssl rand -hex 32)
POSTGRES_PASSWORD=$PG
DATABASE_URL=postgresql://postgres:$PG@postgres:5432/postgres
CLICKHOUSE_PASSWORD=$(openssl rand -hex 16)
MINIO_ROOT_PASSWORD=$MINIO
LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY=$MINIO
LANGFUSE_S3_MEDIA_UPLOAD_SECRET_ACCESS_KEY=$MINIO
LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY=$MINIO
REDIS_AUTH=$(openssl rand -hex 16)
TELEMETRY_ENABLED=false
EOF
chmod 600 .env
이 중 두 비밀번호는 짝이 있습니다. Postgres 비밀번호는 DATABASE_URL 안에도 들어가고, MinIO 비밀번호는 S3_..._SECRET_ACCESS_KEY 세 줄과 같아야 합니다. 그래서 각각을 하나의 변수에서 가져옵니다.
이제 함정입니다. compose 파일은 웹 앱을 3000번, MinIO를 9090번 포트로 모든 인터페이스에 공개하고, Docker가 공개한 포트는 ufw를 우회합니다. 둘 다 localhost에 묶어 리버스 프록시만 입구로 남기세요:
sed -i 's/- 3000:3000/- 127.0.0.1:3000:3000/; s/- 9090:9000/- 127.0.0.1:9090:9000/' docker-compose.yml
docker compose up -d
curl -s http://127.0.0.1:3000/api/public/health
마지막 명령은 {"status":"OK", ...}를 출력해야 합니다. 첫 실행 때 데이터베이스 마이그레이션이 돌아가며, 저희 테스트에서는 컨테이너가 뜬 지 약 15초 뒤에 헬스 체크가 통과했습니다.
앞단에 HTTPS
langfuse.example.com을 서버 IP로 지정한 다음:
apt install -y caddy
cat > /etc/caddy/Caddyfile <<'EOF'
langfuse.example.com {
reverse_proxy 127.0.0.1:3000
}
EOF
systemctl reload caddy
ufw allow 22/tcp && ufw allow 80/tcp && ufw allow 443/tcp && ufw enable
인증서는 Caddy가 알아서 받습니다. 사이트를 열고 계정을 만든 뒤(첫 조직이 됩니다) 프로젝트를 추가하고, 그 공개 키와 비밀 키를 복사하세요.
그다음 문을 닫아 두세요. Langfuse는 AUTH_DISABLE_SIGNUP을 이해하지만 기본 compose 파일은 이를 web 컨테이너에 넘기지 않으므로 langfuse-web 아래에 한 줄이 더 필요합니다:
sed -i 's|^ NEXTAUTH_SECRET: \(.*\)$| NEXTAUTH_SECRET: \1\n AUTH_DISABLE_SIGNUP: ${AUTH_DISABLE_SIGNUP:-false}|' docker-compose.yml
echo "AUTH_DISABLE_SIGNUP=true" >> .env
docker compose up -d
이제부터 가입 시도에는 «Sign up is disabled»가 돌아갑니다. 동료는 프로젝트 안에서 추가하세요.
에이전트에서 트레이스 보내기
Python SDK는 함수를 감싸고, 중첩된 호출은 중첩된 span이 됩니다:
python3 -m venv venv && . venv/bin/activate
pip install langfuse
export LANGFUSE_PUBLIC_KEY=pk-lf-...
export LANGFUSE_SECRET_KEY=sk-lf-...
export LANGFUSE_BASE_URL=https://langfuse.example.com
from langfuse import get_client, observe
langfuse = get_client() # LANGFUSE_* 변수를 읽음
@observe(name="fetch-prices")
def fetch_prices(pair: str) -> dict:
return {"pair": pair, "bid": 64210.5}
@observe(name="nightly-agent-run")
def run() -> str:
data = fetch_prices("BTC/USDT")
return f"done: {data['pair']}"
print(run())
langfuse.flush() # 짧은 스크립트는 백그라운드 전송이 끝나기 전에 종료됨
LANGFUSE_BASE_URL을 건너뛰지 마세요. 이 변수가 없으면 SDK는 조용히 모든 것을 Langfuse 클라우드로 보내고, 그곳에는 내 키가 없으니 빈 대시보드만 보게 됩니다.
터미널에서 span이 도착했는지 확인하려면:
curl -s -u "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" \
"$LANGFUSE_BASE_URL/api/public/v2/observations?limit=5"
v4에서는 예전 엔드포인트 /api/public/traces가 events-only 모드에 관한 오류로 응답합니다. 예상된 동작이며 설치가 잘못된 것이 아닙니다. 실제 에이전트에서도 LLM 호출과 각 도구를 같은 방식으로 감쌉니다. 또 Langfuse에는 널리 쓰이는 LLM SDK와 에이전트 프레임워크용 통합이 있어서, 곳곳에 데코레이터를 붙이는 대신 import 한 줄만 바꾸면 되는 경우가 많습니다.
지켜볼 것
- 디스크. ClickHouse와 MinIO는 보낸 모든 것을 보관합니다. 공간이 어디에 쓰였는지는
docker system df -v로 볼 수 있습니다. 긴 프롬프트와 응답을 트레이스하면 Medium-IP의 45 GB는 몇 년이 아니라 몇 주 만에 찹니다. 한 달에 한 번 확인하고, 부족해지면 400 GB 이상의 Pro 요금제를 문의하세요. - 업데이트. compose 파일은 메이저 버전(
:4)을 고정합니다.docker compose pull && docker compose up -d로 그 안의 수정 사항을 받습니다. 메이저 버전을 바꾸기 전에는 changelog를 읽으세요. 트레이스 엔드포인트가 사라진 것도 바로 v4였습니다. - 백업. 중요한 것은 Docker 볼륨에 있습니다. Postgres에는 사용자, 프로젝트, 키가, ClickHouse와 MinIO에는 트레이스 자체가 있습니다. 일정에 따라 서버 밖으로 복사하세요.
월 $20의 가치가 있을까요? 에이전트가 토큰에 그보다 많이 쓴다면(무인 에이전트는 대부분 그렇습니다), 토큰이 어디로 갔는지 아는 것은 루프가 처음 폭주하는 날 바로 본전을 뽑습니다. 하루 한 번 도는 작은 에이전트에는 이 중 어느 것도 필요 없습니다. 로그 파일과 토큰 카운터면 충분합니다.
관련 글
- AI 에이전트용 VPS: 에이전트 자체가 실행되는 곳
- 멀티 에이전트 시스템용 VPS: 에이전트 무리 전체의 자원 산정
- LiteLLM 프록시용 VPS: 모든 모델 키를 위한 하나의 게이트웨이, 지출 한도 포함
- Pro 요금제 문의: 에이전트 무리를 위한 RAM 32~80 GB
- ufw 방화벽 설정 방법
댓글
아직 댓글이 없습니다. 첫 번째가 되세요.