
SigNoz의 docker-compose가 폐기된 주에 직접 셀프 호스팅을 해보며 — 가이드맵
요약
SigNoz의 docker-compose 지원 중단에 따라 새로운 배포 도구인 Foundry를 사용하여 셀프 호스팅하는 방법을 설명합니다. Foundry를 통해 선언적으로 매니페스트를 생성하고, 네트워크 설정 패치 및 초기 부팅 시 주의사항을 다룹니다.
핵심 포인트
- SigNoz는 이제 docker-compose 대신 Foundry 도구를 사용함
- Foundry의 forge/cast 명령어로 배포 매니페스트 생성 및 적용 가능
- 선언적 패치를 통해 생성된 docker-compose 파일의 호스트네임 수정 가능
- 초기 관리자 계정 생성 전까지는 텔레메트리 데이터가 흐르지 않음
나는 토요일에 SigNoz를 셀프 호스팅(self-host)하기 위해 앉았고, 저장소의 deploy/ 폴더에서 다음 내용을 발견했습니다:
참고:
install.sh스크립트와docker-compose매니페스트(manifests)는 더 이상 사용되지 않습니다 (deprecated).
내가 북마크해 두었던 모든 튜토리얼이 갑자기 틀린 것이 되어버렸습니다. 이제 SigNoz는 고정된 compose 파일을 제공하는 대신, 배포를 직접 *생성(generates)*해 주는 새로운 도구인 Foundry를 통해 설치됩니다. 이 포스트는 내가 가졌으면 좋았을 가이드맵입니다. Foundry로 SigNoz를 실행하고, 멀티 서비스 앱을 연결하며, 완전히 API를 통해 알림(alerts)과 대시보드(dashboards)를 프로비저닝(provisioning)하는 방법, 그리고 나에게 실제 몇 시간을 허비하게 만들었던 세 가지 주의사항(gotchas)을 정리했습니다. 여러분은 이 과정을 건너뛸 수 있습니다.
두 개의 명령어로 사용하는 Foundry
Foundry는 배포를 forge (스펙으로부터 매니페스트 생성)와 cast (매니페스트 적용)로 나눕니다. 스펙은 작은 casting.yaml 파일입니다:
apiVersion: v1alpha1
kind: Installation
metadata
...
foundryctl forge -f casting.yaml을 실행하면, 읽기 쉬운 일반적인 docker-compose.yaml이 포함된 pours/deployment/ 디렉토리가 생성됩니다. 여기에는 ClickHouse, ClickHouse Keeper, Postgres metastore, signoz 서버, 그리고 OTel-collector ingester가 포함되어 있습니다. 내가 가장 좋아하는 부분은 바로 이 점입니다. 출력 결과가 표준 Docker 아티팩트(artifacts)이므로 git에 커밋할 수 있고, 업그레이드 시 diff를 확인할 수 있으며, 직접 docker compose up -d를 실행할 수도 있습니다. 읽을 수 있는 인프라(Infrastructure)인 셈입니다.
한 가지 까다로운 점은, 생성된 네트워크 이름이 signoz-network이고 collector의 DNS 별칭이 signoz-ingester라는 점입니다. 반면 수년간의 습관(그리고 내 앱 설정들)은 signoz-otel-collector를 기대하고 있었습니다. 모든 것을 이름을 바꾸는 대신, Foundry를 사용하면 생성된 매니페스트를 선언적(declaratively)으로 패치(patch)할 수 있습니다:
patches:
- target: "deployment/compose.yaml"
operations:
...
다시 forge를 실행하면 두 호스트네임(hostnames)이 모두 해결됩니다. 나의 네 가지 FastAPI 서비스(gateway → checkout → payment → inventory 및 부하 생성기(load generator)로 구성된 작은 상점 모델)는 OTEL_EXPORTER_OTLP_ENDPOINT를 http://signoz-otel-collector:4317로 가리키고 있으며, 변경 사항을 전혀 인지하지 못합니다.
주의사항 #1: 첫 부팅 설정(first-boot setup)을 완료하기 전까지는 텔레메트리(telemetry) 흐름이 발생하지 않습니다. 제 서비스들은 정상적으로 가동 중이었고, OTLP를 내보내고 있었으며, 오류도 전혀 없었습니다. 하지만 ClickHouse는 비어 있는 상태였습니다. 컬렉터(collector)의 로그에는 OpAMP 연결 오류가 표시되었습니다. 확인 결과, 인제스터(ingester)는 OpAMP를 통해 SigNoz 서버로부터 실시간 설정을 가져오는데, 이는 초기 관리자 계정이 생성된 후에야 작동하기 시작합니다 (/api/v1/version을 확인하면 그때까지 `

로그 검색 (Log search)은 정규 표현식 (regex)을 포함하여 동일한 언어를 사용합니다. 이를 통해 프로덕션 로그로 유출되는 AWS 스타일의 액세스 키 (access keys)를 찾아낼 수 있습니다:
body REGEXP 'AKIA[0-9A-Z]{16}' AND deployment.environment = 'production'
저는 정확히 저 표현식을 경고 규칙 (alert rule)으로 변환했습니다 — 단 한 줄로 구성된 비밀 유출 탐지 장치 (secret-leak tripwire)가 된 것입니다.
주의사항 #2: 그룹화 (group-bys) 시 리소스 속성 (resource attributes)이 스팬 속성 (span attributes)을 가립니다. 저의 결제 서비스 (checkout service)는 동적으로 배포 모델을 변경합니다 — "배포된 버전 (deployed version)"은 컨테이너 재시작 없이 런타임에 변경되며, 각 스팬 (span)에 service.version 스팬 속성으로 찍힙니다. 하지만 저의 그룹화 결과에는 에러를 포함한 모든 스팬에 대해 이전 버전이 계속 표시되었습니다. 그 이유는 다음과 같습니다: 저는 또한 OTEL_RESOURCE_ATTRIBUTES에 service.version=1.4.1을 포함시켜 두었는데, 리소스 속성과 스팬 속성이 동일한 이름을 공유할 경우 쿼리에서는 리소스 속성이 우선권을 갖습니다. 해결책은 속성의 소유자를 하나로 정하는 것입니다. 저는 리소스 환경 변수에서 이를 제거하고, ASGI 미들웨어 (middleware)에서 요청마다 찍도록 설정했습니다:
@app.middleware("http")
async def stamp_service_version(request, call_next):
span = trace.get_current_span()
...
해결 후에도 두 번째 레이어가 존재합니다: 만약 특정 키가 두 컨텍스트 모두에 한 번이라도 존재했다면, SigNoz는 기본적으로 단순 이름을 리소스 (resource) 컨텍스트로 해석합니다 — 저의 그룹화는 쿼리 응답에 첨부된 경고를 읽기 전까지 조용히 빈 버전을 반환했습니다: "'service.version' 키가 모호합니다... 기본적으로 'resource' 컨텍스트를 사용합니다. 속성을 명시적으로 쿼리하려면 완전한 이름 (예: 'attribute.service.version')을 사용하십시오.""
그룹화에 완전한 이름 (qualified name)을 사용하자, 롤아웃 비교 (rollout comparison)가 광고된 대로 정확하게 작동했습니다. SigNoz가 결과에 첨부하는 경고를 읽으세요 — 제가 마주친 모든 경고에는 정확한 해결책이 포함되어 있었습니다.
코드로서의 경고 및 대시보드 (실제 API 대상)
I wanted the whole setup reproducible from a fresh clone, so no clicking around the UI: notification channels, five alert rules, and three dashboards all provisioned by a Python script hitting the REST API. Three things I learned that aren't in any tutorial yet:
새로운 클론(clone)으로부터 전체 설정을 재현 가능하게 만들고 싶었습니다. 즉, UI를 일일이 클릭하는 대신 REST API를 호출하는 Python 스크립트를 통해 알림 채널(notification channels), 5개의 경고 규칙(alert rules), 3개의 대시보드(dashboards)를 모두 프로비저닝(provisioning)하고자 했습니다. 아직 어떤 튜토리얼에도 나와 있지 않은 세 가지 사실을 배웠습니다:
경고 규칙은 v5 형태를 요구합니다. 저의 첫 번째 시도는 이전 방식인 builderQueries JSON을 사용했으나, `
왜 1분이 아니라 3분일까요? 저는 "비활성 (inactive)" 규칙들을 한참 동안 쳐다보다가 결국 서버 로그를 읽으러 가야 했습니다. 모든 평가 로그에는 eval_delay: 120000이 기록되어 있었습니다. 임계값 (Threshold) 규칙은 늦게 도착하는 스팬 (spans)이 잘못된 부정 결과 (false negatives)를 초래하지 않도록, 의도적으로 과거 2분 전에 종료된 윈도우 (window)를 평가합니다. 즉, 11:08에 설정한 저의 1분 윈도우는 장애가 시작되기 전인 11:05~11:06 사이의 트래픽을 점수화하고 있었던 것입니다. 이 사실을 알고 나면, "내 알람이 느리다"는 "내 알람이 정확하다"로 바뀌게 됩니다. 지연 시간 (latency) 계산을 할 때는 항상 평가 윈도우 (eval window) + 2분을 고려하세요.


과거의 나에게 해주고 싶은 말
- 마이그레이션 (migration)과 싸우지 말고 활용하세요. Foundry가 생성한 compose 파일은 과거의 거대한 단일 파일보다 훨씬 가독성이 좋으며,
pours/디렉토리를 커밋함으로써 검토 가능한 인프라 (infra)를 가질 수 있습니다. - 텔레메트리 (telemetry)가 비어 있다고 해서 엑스포터 (exporter)가 고장 난 것은 아닙니다. 애플리케이션을 건드리기 전에
setupCompleted와 OpAMP 로그를 먼저 확인하세요. - 속성 (attribute)당 소유자는 하나여야 합니다. 리소스 속성 (Resource attrs)은 정적인 사실을 위한 것이며, 동적인 모든 것은 스팬 (spans)에 속해야 합니다. 그리고 절대 양쪽 모두에 두지 마세요.
- API 에러는 오히려 좋습니다. 제가 저지른 모든 스키마 (schema) 실수는 메시지를 생성했으며, 주의 깊게 읽어보면 그 안에 해결책이 포함되어 있었습니다.
- 드롭다운 (dropdowns)보다 표현식 (Expressions)이 낫습니다. v5 필터 구문은 제가 사용해 본 쿼리 빌더 (query builder) 중 "빌더" 부분이 제 방해를 하지 않은 첫 번째 도구였습니다.
위의 모든 것은 이번 주 제 컴퓨터에서 실행되었습니다 — SigNoz v0.133, Foundry v0.2.14, Docker 기반의 Python 3.12 서비스들입니다. 다음 주는 Agents of SigNoz 해커톤이 예정되어 있습니다. 이 스택은 제가 그 위에서 구축할 토대이며, 이제 여러분은 주말 대신 오후 한때 만에 이를 구축할 수 있습니다.
참고 자료: signoz.io/docs/install/docker · Foundry: github.com/SigNoz/foundry
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기