본문으로 건너뛰기

관측 신호는 Collector 없이 앱이 OTLP로 직접 push 한다

  • 상태: 채택됨
  • 작성일: 2026-07-28
  • 관련 문서: ADR-0009, APP-248, APP-254, APP-255, APP-405

맥락

Grafana Cloud로 로그·메트릭·트레이스를 보내려면 수집 계층이 필요하다. 표준 구성은 Collector(Grafana Alloy)를 호스트에 두고 앱이 로컬로 보내는 것이다. 그런데 서비스가 둘뿐이고 EC2도 두 대다. 이 규모에서 수집 계층을 하나 더 운영할 만한지가 갈림길이었다.

결정

  • Collector를 두지 않고 두 앱이 OTLP 게이트웨이로 직접 push 한다.
  • 프로토콜은 http/protobuf다. 게이트웨이가 gRPC를 받지 않는다.
  • 인증 헤더는 base64("인스턴스ID:glc_토큰")glc_ 토큰을 그대로 넣으면 401이다.
  • 히스토그램은 base2_exponential_bucket_histogram으로 고정한다. 기본 explicit bucket은 (라우트 × 메서드 × 코드)마다 18 series를 만들어 Free 한도(10K active series)를 태운다. Java는 대문자, Python은 소문자여야 한다.
  • Alloy를 다시 볼 기준은 숫자로 정해 둔다: 파일에만 로그가 있는 상황 2회 / egress 월 $20 초과 / 서비스 3개 이상 또는 k8s / 호스트 메모리·디스크 장애 / 에러 트레이스 보존이 병목.

대안

대안 1 — Grafana Alloy(Collector) 도입. 표준 구성이고 tail sampling과 호스트 메트릭을 얻는다. 전송 설정도 한 곳에 모인다. 기각한 이유: 서비스 2개·EC2 2대 규모에서는 운영할 것을 하나 더 늘릴 만큼의 값이 안 보였다. 대신 언제 다시 볼지를 조건으로 남겼다.

결과

  • 운영할 컴포넌트는 그대로다. 다시 볼 기준을 숫자로 남겨 뒀으니 재검토는 관측이 열어 준다. 누가 판단해서 여는 자리가 아니다.
  • 로그 유실 가능성, tail sampling 불가(= "에러만 100% 보존"을 구현할 수 없다), 호스트 메모리·디스크 메트릭 공백, 크레덴셜 분산, EC2별 egress 개별 부담을 함께 받았다.
  • 전송 설정이 앱마다 흩어졌다. Java와 Python이 서로 다른 표기(대문자/소문자)를 요구하고 게이트웨이 인증·프로토콜·히스토그램 종류를 두 앱이 각자 맞게 들고 있어야 한다.
  • 전송이 앱 프로세스 안에 있다. 전송이 죽어도 앱은 멀쩡히 돌고 그것을 알려 줄 중간 계층이 없다. 실제로 두 번 깨졌다. 메트릭 temporality를 delta로 두자 배치 하나가 400이 되면서 그 안의 HTTP 메트릭까지 통째로 버려졌다. OTEL_INSTRUMENTATION_MICROMETER_ENABLED 기본값이 false라 손으로 만든 계측 15종은 열흘간 한 건도 도달하지 않았다(APP-405).
  • GRAFANA_CLOUD_OTLP_TOKENdocker restart로 안 바뀐다. 이 값을 읽는 것은 앱이 아니다. deploy.sh가 읽어 컨테이너 환경변수로 넣고 opentelemetry-instrument는 프로세스 시작 시점에 그것을 읽는다. 그래서 OTLP가 401로 계속 실패하는데 앱은 멀쩡히 도는 모양이 된다. 관측이 죽은 것을 관측할 방법이 없다.