개발자 여러분, 이런 경험 없으신가요? 애플리케이션에 열심히 메트릭을 심어놨는데, 막상 운영을 시작하니 "아, 이 메트릭 이름 바꾸고 싶다" 혹은 "히스토그램 버킷을 더 세분화해야겠어" 같은 요구사항이 생깁니다. 보통 이런 경우 어떻게 하시나요? 😭
- 코드를 수정한다.
- 새로운 버전을 빌드한다.
- 애플리케이션을 재배포하고 재시작한다.
이 과정은 너무 번거롭고 시간도 오래 걸립니다. 특히 긴급하게 변경이 필요할 때는 정말 답답하죠. 만약, 실행 중인 애플리케이션을 재시작하지 않고, 코드 한 줄 건드리지 않고 이 모든 설정을 동적으로 변경할 수 있다면 어떨까요?
오늘은 바로 그 마법 같은 방법을 알려드리겠습니다.

🤔 전체 그림부터 이해하기: 지휘자와 오케스트라
이 마법을 이해하려면 먼저 두 가지 핵심 요소를 알아야 합니다: MeterProvider와 Meter.
- MeterProvider (지휘자 🏢): 오케스트라의 지휘자라고 생각해보세요. 어떤 악기를(Exporter) 통해 연주를 외부에 들려줄지, 어떤 악보(View)를 보고 연주할지 모든 규칙과 구성을 결정하는 중앙 통제실입니다.
- Meter (연주자 🎻): 실제 연주자입니다. 개발자는 이 Meter를 통해 "카운터를 1 증가시켜줘", "이 값으로 히스토그램을 기록해줘" 와 같은 연주(계측)를 요청합니다.
중요한 점은, 모든 연주자(Meter)는 단 한 명의 지휘자(MeterProvider)를 바라보고 있다는 것입니다.
[ MeterProvider (지휘자) ] 🏢
│
├── 설정 A (View, Exporter 등)
│
└── 생성 및 관리
│
├── Meter "A" (연주자 1) 🎻
├── Meter "B" (연주자 2) 🎷
└── Meter "C" (연주자 3) 🎺
지휘자가 중간에 "자, 지금부터 템포를 빠르게 갑시다!"라고 지시를 바꾸면 어떻게 될까요? 연주자들은 즉시 그 지시에 따라 연주 방식을 바꿉니다. 연주자들을 일일이 해고하고 새로운 지시를 숙지한 연주자들로 교체할 필요가 없죠.
바로 이 원리가 우리가 이야기할 동적 설정의 핵심입니다.
✨ 마법의 순간: MeterProvider의 설정 변경
MeterProvider는 Meter 인스턴스들의 중앙 구성 및 관리 지점 역할을 합니다. MeterProvider의 설정(예: 새로운 View 등록, Exporter 변경 등)이 업데이트되면, 해당 프로바이더를 통해 생성된 모든 기존 Meter들은 이 새로운 설정을 자동으로 상속받아 적용합니다.
이게 왜 중요할까요?
애플리케이션 코드에 박혀있는 meter.counter('request_count').add(1) 같은 계측 코드는 그대로 둔 채, 외부에서 MeterProvider의 설정만 바꿔주면 메트릭이 수집되고 표현되는 방식이 실시간으로 바뀌기 때문입니다!
Meter를 수동으로 다시 생성하거나, 계측 코드를 수정하고 재배포할 필요가 전혀 없습니다.
👨💻 코드로 직접 확인해보기
백문이 불여일견이죠. 간단한 Python 코드를 통해 이 과정이 어떻게 동작하는지 직접 보여드리겠습니다.
시나리오:
- my_request_counter라는 이름의 카운터를 만듭니다.
- 5초 동안 1초마다 카운터를 1씩 증가시키며 메트릭을 출력합니다.
- 애플리케이션 실행 중에 my_request_counter의 이름을 http_server_request_total로 바꾸는 View를 동적으로 등록합니다.
- 이후 5초 동안 카운터가 어떻게 출력되는지 확인합니다.
import time
from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader
from opentelemetry.sdk.metrics.view import View
# 1. 초기 설정: 콘솔로 메트릭을 출력하는 Exporter와 Reader 설정
exporter = ConsoleMetricExporter()
reader = PeriodicExportingMetricReader(exporter, export_interval_millis=1000)
provider = MeterProvider(metric_readers=[reader])
metrics.set_meter_provider(provider)
# 2. Meter 인스턴스 생성 및 카운터 생성
# 이 meter 객체는 한번만 생성되고 계속 사용됩니다.
meter = metrics.get_meter("my-app-meter")
request_counter = meter.create_counter(
"my_request_counter",
description="The number of requests received",
unit="1"
)
print("🚀 초기 설정으로 5초간 카운터 증가 시작...")
for i in range(5):
request_counter.add(1)
print(f"[{i+1}/10] my_request_counter 증가!")
time.sleep(1)
# --- ✨ 마법이 일어나는 지점! ---
print("\n🪄 동적으로 View를 등록하여 메트릭 이름 변경을 시도합니다...")
new_view = View(
instrument_name="my_request_counter",
name="http_server_request_total" # 새로운 메트릭 이름
)
provider.add_view(new_view)
print("✅ View 등록 완료! Meter를 재생성하지 않았습니다.\n")
# --------------------------------
print("🚀 변경된 설정으로 5초간 카운터 증가 계속...")
for i in range(5, 10):
# 코드는 전혀 바뀌지 않았습니다!
request_counter.add(1)
print(f"[{i+1}/10] my_request_counter 증가!")
time.sleep(1)
print("\n🏁 실행 완료. 출력된 메트릭을 확인하세요.")
time.sleep(2) # 마지막 메트릭이 출력될 시간을 줍니다.
📊 실행 결과 (Raw Data)
[초기 5초 동안의 콘솔 출력] my_request_counter라는 이름으로 메트릭이 출력됩니다.
// ... (초기 출력 중 일부)
{
"resource_metrics": [
{
"resource": { ... },
"scope_metrics": [
{
"scope": { "name": "my-app-meter", "version": "" },
"metrics": [
{
"name": "my_request_counter",
"description": "The number of requests received",
"unit": "1",
"data": {
"aggregation_temporality": 2,
"is_monotonic": true,
"data_points": [
{
"attributes": {},
"start_time_unix_nano": "...",
"time_unix_nano": "...",
"value": 5
}
]
}
}
]
}
]
}
]
}
[View 등록 후 5초 동안의 콘솔 출력] 보세요! request_counter.add(1) 코드는 그대로인데, 출력되는 메트릭의 이름이 http_server_request_total로 바뀌었습니다.
// ... (View 등록 후 출력 중 일부)
{
"resource_metrics": [
{
"resource": { ... },
"scope_metrics": [
{
"scope": { "name": "my-app-meter", "version": "" },
"metrics": [
{
"name": "http_server_request_total", // <--- 이름이 변경되었습니다!
"description": "The number of requests received",
"unit": "1",
"data": {
"aggregation_temporality": 2,
"is_monotonic": true,
"data_points": [
{
"attributes": {},
"start_time_unix_nano": "...",
"time_unix_nano": "...",
"value": 10
}
]
}
}
]
}
]
}
]
}
이처럼 기존에 생성된 meter와 request_counter는 provider의 설정 변경을 자동으로 감지하고 새로운 규칙(View)을 적용했습니다.
❌ 이건 왜 잘못된 방법일까? (Anti-Patterns)
이 강력한 기능을 이해했다면, 왜 다음과 같은 방법들이 좋지 않은지도 명확해집니다.
1. 필요할 때마다 Meter를 새로 생성하는 경우 🤦♂️
만약 설정을 바꿀 때마다 Meter 객체를 다시 get_meter()로 가져와야 한다고 생각했다면, 이는 잘못된 접근입니다.
- 배경: Meter는 특정 스코프(라이브러리, 모듈 등)를 대표하는 오래 지속되는 객체로 설계되었습니다. 매번 새로 만드는 것은 비효율적이며, MeterProvider가 제공하는 중앙 관리의 이점을 완전히 무시하는 행위입니다. 마치 지휘자가 바뀔 때마다 모든 연주자를 해고하고 새로 뽑는 것과 같습니다.
2. 각 Meter에 개별적으로 설정을 주입하려는 경우 🙅♀️
MeterProvider를 통하지 않고, 각각의 Meter에 직접 설정을 전달하려고 시도할 수도 있습니다.
- 배경: 이는 중앙 통제라는 아키텍처의 핵심을 무너뜨립니다. 애플리케이션 전체에 흩어져 있는 수십 개의 Meter 설정을 일일이 찾아다니며 수정해야 한다면, 유지보수는 재앙이 될 것입니다. 지휘자 없이 연주자들이 각자 알아서 연주하는 혼란스러운 오케스트라와 같습니다.
결론: 현명하게 관찰하고 유연하게 대응하기
MeterProvider와 Meter의 관계는 단순한 코드 구조를 넘어, 계측 코드와 설정을 분리하여 유연하고 확장 가능한 모니터링 시스템을 구축하기 위한 철학을 담고 있습니다.
이제 여러분은 애플리케이션을 중단하지 않고도 실시간으로 메트릭 수집 방식을 바꾸는 강력한 도구를 갖게 되었습니다. 히스토그램 버킷을 조정하거나, 특정 메트릭을 필터링하거나, 이름을 바꾸는 등의 작업을 동적으로 처리하여 변화하는 요구사항에 민첩하게 대응해보세요! ✨
'클라우드 > opentelemetry' 카테고리의 다른 글
| 당신의 서버 로그, 개인정보 유출의 시한폭탄일 수 있습니다 💣 | OpenTelemetry 민감정보 안전하게 처리하는 법 (0) | 2025.11.08 |
|---|---|
| 🤯 "서버 터졌대!" 하이브리드 환경, 모니터링 때문에 머리 아프셨죠? 정답 알려드립니다 (0) | 2025.11.06 |
| '이 버튼' 아무도 안 누르네? 과감히 삭제! 개발자들이 앱을 개선하는 비밀 (0) | 2025.11.05 |
| OpenTelemetry 메트릭 속성, 이름 짓기에도 규칙이 있다? 🧐 네임스페이스 완전 정복! (0) | 2025.11.05 |
| 제로 노력으로 텔레메트리 구현? 🚀 OpenTelemetry eBPF Instrumentation (OBI) 첫 릴리스 발표! (0) | 2025.11.04 |