ClickHouse 관련 구성 요소
- OpenTelemetry Collector는 텔레메트리 데이터를 수신, 처리하고 내보내는 프록시입니다. ClickHouse 기반 솔루션은 이 구성 요소를 로그 수집과 이벤트 처리에 사용하며, 배칭 및 삽입 전에 이러한 작업을 수행합니다.
- 텔레메트리 데이터의 사양, API, 내보내기를 구현하는 Language SDKs입니다. 이러한 SDK는 애플리케이션 코드에서 트레이스가 올바르게 기록되도록 하고, 이를 구성하는 스팬을 생성하며, 메타데이터를 통해 서비스 간 Context가 전파되도록 보장합니다. 그 결과 분산 트레이스가 형성되고 스팬을 서로 연관시킬 수 있습니다. 또한 이러한 SDK는 일반적인 라이브러리와 프레임워크에 계측을 자동으로 적용하는 에코시스템으로 보완되므로, 코드를 변경하지 않고도 즉시 사용할 수 있는 계측을 활용할 수 있습니다.
배포판
- collector의 크기를 줄여 배포 시간을 단축할 수 있습니다
- 노출되는 공격 표면을 줄여 collector의 보안을 강화할 수 있습니다
OTel을 사용한 데이터 수집
Collector 배포 역할
- Agent - Agent 인스턴스는 서버나 Kubernetes 노드와 같은 에지에서 데이터를 수집하거나, OpenTelemetry SDK로 계측된 애플리케이션에서 이벤트를 직접 수신합니다. 후자의 경우 Agent 인스턴스는 애플리케이션과 함께 또는 애플리케이션과 동일한 호스트에서 실행됩니다(예: 사이드카 또는 데몬셋). Agent는 데이터를 ClickHouse로 직접 전송하거나 gateway 인스턴스로 전송할 수 있습니다. 전자의 방식은 Agent 배포 패턴이라고 합니다.
- Gateway - Gateway 인스턴스는 독립적으로 실행되는 서비스를 제공합니다(예: Kubernetes의 배포). 일반적으로 cluster별, 데이터 센터별 또는 region별로 배포됩니다. 이러한 인스턴스는 단일 OTLP endpoint를 통해 애플리케이션(또는 agent 역할의 다른 collector)에서 이벤트를 수신합니다. 일반적으로 여러 gateway 인스턴스를 배포하고, 기본 제공 로드 밸런서를 사용해 이들 사이에 부하를 분산합니다. 모든 agent와 애플리케이션이 이 단일 endpoint로 신호를 전송하는 경우, 이를 흔히 Gateway 배포 패턴이라고 합니다.
로그 수집
- Filelog 수신기를 통한 스크레이핑 - 이 수신기는 디스크의 파일을 tail하여 로그 메시지를 구성한 뒤 ClickHouse로 전송합니다. 이 수신기는 여러 줄 메시지 감지, 로그 롤오버 처리, 재시작 내구성을 위한 체크포인팅, 구조 추출과 같은 복잡한 작업을 처리합니다. 또한 Docker 및 Kubernetes 컨테이너 로그도 tail할 수 있으며, Helm 차트로 배포할 수 있고, 여기서 구조를 추출한 뒤 파드 세부 정보로 보강할 수도 있습니다.
팁:
otelbin.iootelbin.io는 구성을 검증하고 시각화하는 데 유용합니다.구조화 vs 비구조화
예시
filelog 수신기로 읽고, 결과 메시지를 stdout으로 출력하는 OTel collector의 간단한 구성입니다. 로그가 구조화되어 있으므로 json_parser 연산자를 사용합니다. access-structured.log 파일의 경로에 맞게 수정하십시오.
파싱에는 ClickHouse 사용을 고려하십시오아래 예시에서는 로그에서 timestamp를 추출합니다. 이를 위해
json_parser 연산자를 사용하며, 이 연산자는 전체 로그 줄을 JSON 문자열로 변환한 뒤 그 결과를 LogAttributes에 저장합니다. 이 작업은 계산 비용이 클 수 있으며, ClickHouse에서 더 효율적으로 처리할 수 있습니다. 자세한 내용은 SQL로 구조 추출을 참조하십시오. 동일한 작업을 regex_parser로 수행하는 비구조화 예시는 여기에서 확인할 수 있습니다.filelog 수신기가 포함되어 있음). 예를 들어 사용자는 otelcol_0.102.1_darwin_arm64.tar.gz 대신 otelcol-contrib_0.102.1_darwin_arm64.tar.gz를 다운로드해야 합니다. 릴리스는 여기에서 확인할 수 있습니다.
설치가 완료되면 다음 명령으로 OTel collector를 실행할 수 있습니다:
Body 필드에 문자열로 저장되지만, json_parser 덕분에 JSON이 Attributes 필드로 자동 추출된다는 점입니다. 동일한 연산자를 사용해 타임스탬프도 적절한 Timestamp 컬럼으로 추출했습니다. 로그를 OTel로 처리하는 방법에 대한 권장 사항은 Processing을 참조하십시오.
연산자연산자는 로그 처리의 가장 기본적인 단위입니다. 각 연산자는 파일에서 줄을 읽거나 필드에서 JSON을 파싱하는 등 하나의 역할만 수행합니다. 그런 다음 원하는 결과를 얻기 위해 연산자들을 파이프라인으로 연결합니다.
TraceID 또는 SpanID 필드가 없습니다. 이러한 필드가 있는 경우, 예를 들어 분산 추적을 구현할 때는 위에서 보여준 동일한 기법을 사용해 JSON에서 추출할 수 있습니다.
로컬 또는 Kubernetes 로그 파일을 수집해야 하는 경우, filelog 수신기에서 사용할 수 있는 구성 옵션과 offset, 그리고 멀티라인 로그 파싱이 처리되는 방식을 충분히 숙지할 것을 권장합니다.
Kubernetes 로그 수집
ResourceAttributes 컬럼에 저장될 수 있습니다. 현재 ClickHouse는 이 컬럼에 Map(String, String) 유형을 사용합니다. 이 유형을 처리하고 최적화하는 방법에 대한 자세한 내용은 Using Maps 및 Extracting from maps를 참조하십시오.
트레이스 수집
예시
telemetrygen 도구를 사용합니다. 설치 방법은 여기의 지침을 따르십시오.
다음 구성은 OTLP 수신기에서 트레이스 이벤트를 수신한 뒤 stdout으로 전송합니다.
config-traces.xml
telemetrygen을 사용해 trace 이벤트를 collector로 전송합니다:
처리 - 필터링, 변환 및 보강
-
Processors - Processors는 수신기(receivers)가 수집한 데이터를 수정하거나 변환한 뒤 exporter로 보내기 전에 처리합니다. Processors는 collector 구성의
processors섹션에 정의된 순서대로 적용됩니다. 이는 선택 사항이지만, 최소한의 구성은 일반적으로 권장됩니다. ClickHouse와 함께 OTel collector를 사용할 때는 processors를 다음과 같이 제한하는 것을 권장합니다.- memory_limiter는 collector에서 메모리 부족 상황을 방지하는 데 사용됩니다. 권장 사항은 리소스 추정을 참조하십시오.
- 컨텍스트 기반 보강을 수행하는 processor. 예를 들어 Kubernetes Attributes Processor는 k8s 메타데이터를 사용해 스팬, 메트릭, 로그의 리소스 속성을 자동으로 설정할 수 있으며, 예를 들어 이벤트에 원본 파드 ID를 추가할 수 있습니다.
- traces에 필요할 경우 Tail 또는 head 샘플링
- 기본 필터링 - 연산자로 처리할 수 없는 경우(아래 참조) 불필요한 이벤트를 삭제합니다.
- 배칭 - ClickHouse와 함께 사용할 때 데이터가 배치 단위로 전송되도록 보장하는 데 필수적입니다. “Exporting to ClickHouse”를 참조하십시오.
- Operators - Operators는 수신기에서 사용할 수 있는 가장 기본적인 처리 단위를 제공합니다. 기본적인 parsing을 지원하므로 Severity 및 Timestamp와 같은 필드를 설정할 수 있습니다. 여기서는 JSON 및 regex parsing과 함께 이벤트 필터링과 기본 변환도 지원됩니다. 이벤트 필터링은 이 단계에서 수행하는 것을 권장합니다.
예시
regex_parser를 사용해 로그 줄에서 구조를 추출하고 이벤트를 필터링하는 연산자와, 이벤트를 배치로 묶고 메모리 사용량을 제한하는 프로세서를 함께 사용한다는 점에 유의하십시오.
config-unstructured-logs-with-processor.yaml
ClickHouse로 내보내기
OpenTelemetry Collector Contrib 사용ClickHouse exporter는 코어 배포판이 아니라 OpenTelemetry Collector Contrib의 일부입니다. contrib 배포판을 사용하거나 자체 collector를 빌드할 수 있습니다.
- pipelines - 위 구성은 pipelines의 사용을 보여줍니다. pipelines는 수신기, processor, exporter의 집합으로 구성되며, logs와 traces용 파이프라인이 각각 하나씩 포함됩니다.
- endpoint - ClickHouse와의 통신은
endpoint매개변수로 구성합니다. 연결 문자열tcp://localhost:9000?dial_timeout=10s&compress=lz4&async_insert=1을 사용하면 TCP를 통해 통신합니다. 트래픽 전환 등의 이유로 HTTP를 선호한다면, 여기에 설명된 대로 이 연결 문자열을 수정하십시오. 이 연결 문자열 안에서 사용자 이름과 비밀번호를 지정하는 방법을 포함한 전체 연결 정보는 여기에 설명되어 있습니다.
- ttl - 여기의 값은 데이터를 얼마나 오래 보관할지 결정합니다. 자세한 내용은 “데이터 관리”를 참조하십시오. 이 값은 시간 단위로 지정해야 하며, 예를 들어 72h와 같이 설정합니다. 아래 예시에서는 데이터가 2019년 데이터이므로, 삽입하면 ClickHouse가 즉시 제거하므로 TTL을 비활성화합니다.
- traces_table_name 및 logs_table_name - logs 및 traces 테이블의 이름을 결정합니다.
- create_schema - 시작 시 기본 스키마(schema)로 테이블을 생성할지 결정합니다. 시작하기에서는 기본값이 true입니다. 실제 환경에서는 false로 설정하고 스키마를 직접 정의해야 합니다.
- database - 대상 데이터베이스입니다.
- retry_on_failure - 실패한 batch를 재시도할지 결정하는 설정입니다.
- batch - batch processor는 이벤트가 batch 단위로 전송되도록 합니다. 최소 10,000, timeout은 5s 이상을 권장합니다(메모리가 허용되면 최대 100,000까지 사용할 수 있습니다). 이 값들 중 하나에 먼저 도달하면 exporter로 플러시할 batch가 시작됩니다. 이 값을 낮추면 지연 시간이 더 짧은 파이프라인이 되어 데이터를 더 빨리 쿼리할 수 있지만, 그만큼 ClickHouse로 더 많은 연결과 batch를 전송하게 됩니다. 비동기 삽입를 사용하지 않는 경우에는 ClickHouse에서 too many parts 문제가 발생할 수 있으므로 권장하지 않습니다. 반대로 비동기 삽입을 사용하는 경우, 데이터가 쿼리 가능해지는 시점은 asynchronous insert 설정의 영향도 받지만 데이터 자체는 connector에서 더 빨리 플러시됩니다. 자세한 내용은 Batching을 참조하십시오.
- sending_queue - 전송 큐의 크기를 제어합니다. 큐의 각 항목에는 하나의 batch가 들어 있습니다. 예를 들어 ClickHouse에 연결할 수 없는 상태에서도 이벤트가 계속 들어와 이 큐 한도를 초과하면 batch는 삭제됩니다.
telemetrygen 도구를 사용해 다음 명령을 실행하세요:
기본 제공 스키마
create_schema 설정으로 비활성화할 수 있습니다. 또한 위에서 언급한 설정을 통해 logs 및 traces 테이블 이름을 기본값인 otel_logs와 otel_traces에서 변경할 수 있습니다.
아래 스키마에서는 TTL이 72h로 활성화되어 있다고 가정합니다.
otelcol-contrib v0.102.1):
- 기본적으로 테이블은
PARTITION BY toDate(Timestamp)를 통해 날짜 기준으로 파티션됩니다. 이렇게 하면 만료된 데이터를 효율적으로 삭제할 수 있습니다. - TTL은
TTL toDateTime(Timestamp) + toIntervalDay(3)로 설정되며, collector 구성에 설정된 값과 일치합니다.ttl_only_drop_parts=1은 포함된 모든 행이 만료되었을 때 전체 파트만 삭제함을 의미합니다. 이는 파트 내부의 행을 삭제하는 방식보다 더 효율적이며, 이 경우 비용이 큰 삭제 작업이 발생합니다. 이 설정은 항상 적용할 것을 권장합니다. 자세한 내용은 TTL을 사용한 데이터 관리를 참고하십시오. - 이 테이블은 표준
MergeTree엔진을 사용합니다. 이는 logs와 traces에 권장되며, 일반적으로 변경할 필요가 없습니다. - 테이블은
ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId)로 정렬됩니다. 즉,ServiceName,SeverityText,Timestamp,TraceId에 대한 필터를 사용하는 쿼리에 최적화됩니다. 목록에서 앞쪽에 있는 컬럼일수록 뒤쪽 컬럼보다 더 빠르게 필터링됩니다. 예를 들어ServiceName으로 필터링하는 것은TraceId로 필터링하는 것보다 훨씬 빠릅니다. 예상되는 액세스 패턴에 맞게 이 정렬 순서를 조정해야 합니다. 자세한 내용은 프라이머리 키 선택을 참고하십시오. - 위 스키마는 컬럼에
ZSTD(1)을 적용합니다. 이는 logs에 가장 적합한 압축을 제공합니다. 더 높은 압축률을 위해 ZSTD 압축 수준을 기본값인 1보다 높일 수 있지만, 실제로 큰 이점이 있는 경우는 드뭅니다. 이 값을 높이면 삽입 시점의 CPU 오버헤드(압축 중)는 커지지만, 압축 해제 성능(따라서 쿼리 성능)은 대체로 비슷한 수준을 유지합니다. 자세한 내용은 여기를 참고하십시오. 또한 디스크 사용량을 줄이기 위해 Timestamp에는 추가 delta 인코딩이 적용됩니다. ResourceAttributes,LogAttributes,ScopeAttributes가 맵이라는 점에 유의하십시오. 이들 사이의 차이를 이해하는 것이 중요합니다. 이러한 맵에 접근하는 방법과 내부 키 접근을 최적화하는 방법은 “맵 사용”을 참고하십시오.- 여기서 대부분의 다른 타입도 최적화되어 있습니다. 예를 들어
ServiceName은 LowCardinality로 저장됩니다. 예시 logs에서 JSON인Body는 String으로 저장된다는 점에 유의하십시오. - 블룸 필터는 맵의 키와 값, 그리고
Body컬럼에 적용됩니다. 이는 이러한 컬럼에 접근하는 쿼리의 쿼리 시간을 개선하기 위한 것이지만, 일반적으로 꼭 필요한 것은 아닙니다. 자세한 내용은 보조/데이터 스킵 인덱스를 참고하십시오.
삽입 최적화
배칭
- (1) 데이터를 수신하는 노드에 문제가 발생하면 삽입 쿼리가 timeout되거나(또는 더 구체적인 오류가 반환되거나) 확인 응답을 받지 못합니다.
- (2) 데이터가 노드에 기록되었지만 네트워크 중단으로 인해 쿼리 전송자에게 확인 응답을 반환할 수 없는 경우, 전송자는 timeout 또는 네트워크 오류를 받게 됩니다.
timeout에 도달하기 전에 batch를 플러시하므로, 파이프라인의 엔드투엔드 지연 시간은 낮게 유지되고 batch 크기도 일관되게 유지됩니다.
비동기 삽입 사용
timeout이 만료되면 작은 배치가 전송됩니다. 이는 문제를 일으킬 수 있으며, 이런 상황에서는 비동기 삽입(asynchronous inserts)이 필요합니다. 이러한 상황은 일반적으로 에이전트 역할의 collector가 ClickHouse로 직접 전송하도록 구성된 경우에 발생합니다. 게이트웨이는 집계기 역할을 하므로 이 문제를 완화할 수 있습니다. 자세한 내용은 게이트웨이를 사용한 스케일링을 참조하십시오.
큰 배치를 보장할 수 없다면 Asynchronous Inserts를 사용해 ClickHouse에 배칭을 맡길 수 있습니다. 비동기 삽입을 사용하면 데이터는 먼저 버퍼에 삽입된 뒤, 나중에 또는 비동기적으로 데이터베이스 저장소에 기록됩니다.
비동기 삽입이 활성화된 상태에서는 ClickHouse가 ① 삽입 쿼리를 수신하면 해당 쿼리의 데이터가 먼저 ② 즉시 인메모리 버퍼에 기록됩니다. 이후 ③ 다음 버퍼 플러시가 발생하면 버퍼의 데이터가 정렬된 뒤 파트(part)로 데이터베이스 저장소에 기록됩니다. 데이터는 데이터베이스 저장소로 플러시되기 전까지는 쿼리로 검색할 수 없으며, 버퍼 플러시는 구성할 수 있습니다.
collector에서 비동기 삽입을 활성화하려면 connection string에 async_insert=1을 추가하십시오. 전달 보장을 위해 wait_for_async_insert=1(기본값)을 사용하는 것을 권장합니다. 자세한 내용은 여기를 참조하십시오.
비동기 삽입으로 전송된 데이터는 ClickHouse 버퍼가 플러시되면 삽입됩니다. 이는 async_insert_max_data_size를 초과했거나 첫 번째 INSERT 쿼리 이후 async_insert_busy_timeout_ms밀리초가 경과한 경우에 발생합니다. async_insert_stale_timeout_ms가 0이 아닌 값으로 설정되어 있으면 마지막 쿼리 이후 async_insert_stale_timeout_ms milliseconds가 지나면 데이터가 삽입됩니다. 이러한 설정을 조정해 파이프라인의 엔드투엔드 지연 시간을 제어할 수 있습니다. 버퍼 플러시를 조정하는 데 사용할 수 있는 추가 설정은 여기에 문서화되어 있습니다. 일반적으로는 기본값이 적절합니다.
적응형 비동기 삽입을 고려하십시오사용 중인 에이전트 수가 적고 처리량은 낮지만 엔드투엔드 지연 시간 요구 사항이 엄격한 경우에는 적응형 비동기 삽입이 유용할 수 있습니다. 일반적으로 이는 ClickHouse에서 볼 수 있는 고처리량 관측성 사용 사례에는 적용되지 않습니다.
async_insert_deduplicate 설정을 참조하십시오.
이 기능 구성에 대한 전체 내용은 여기에서 확인할 수 있으며, 자세한 설명은 여기에서 확인할 수 있습니다.
배포 아키텍처
에이전트 전용
- 연결 스케일링 - 각 에이전트는 ClickHouse에 연결을 설정합니다. ClickHouse는 수백 개, 많게는 수천 개의 동시 삽입 연결도 유지할 수 있지만, 결국 이는 제한 요인이 되어 삽입 효율을 떨어뜨립니다. 즉, ClickHouse가 연결 유지에 더 많은 리소스를 사용하게 됩니다. 게이트웨이를 사용하면 연결 수를 최소화하고 삽입 효율을 높일 수 있습니다.
- 엣지에서의 처리 - 이 아키텍처에서는 모든 변환이나 이벤트 처리를 엣지 또는 ClickHouse에서 수행해야 합니다. 이는 제약이 될 뿐 아니라, 복잡한 ClickHouse materialized view가 필요해지거나 중요한 서비스에 영향을 줄 수 있고 리소스도 부족한 엣지로 상당한 계산 작업을 넘기게 될 수 있습니다.
- 작은 배치와 지연 시간 - 에이전트 collector는 각각 매우 적은 이벤트만 수집할 수 있습니다. 따라서 일반적으로 전송 SLA를 충족하기 위해 정해진 인터벌마다 플러시하도록 구성해야 합니다. 그 결과 collector가 작은 배치를 ClickHouse로 전송할 수 있습니다. 이는 단점이지만 비동기 삽입으로 완화할 수 있습니다. 자세한 내용은 삽입 최적화를 참조하십시오.