ReferenceKubernetes
OpenTelemetry, ClickStack과 SeaweedFS cold tier 운영 참조
외부 OTLP 인증, node agent 재수집 차단, gateway 내구성 queue, ClickHouse cold S3 보존과 SeaweedFS S3 audit 경로를 조회합니다.
핵심 요약
- 외부 OTLP는
otel.jamie.kr:443에서만 받고 Gateway APIExternalAuth가 Bearer token을 검증한 뒤 내부otel-gateway로 전달함 - node agent는
clickstack-ingest자신의 container log만 제외해 순환 수집을 차단하고 나머지 workload 로그 수집을 유지함 otel-gateway는 OTLP4317·4318과 Fluent Forward8006을 수신하고 file-backed queue로 downstream 장애를 흡수함- ClickHouse는 2일 재압축, 3일
cold_s3이동, 14일 삭제 TTL로 hot storage와 SeaweedFS S3 cold tier를 분리함 - SeaweedFS S3 access audit은 Fluent Forward로 gateway에 전달되고
seaweedfs.s3.accessdataset으로 일반 telemetry와 구분됨
수집 경로와 책임을 분리함
외부 Codex·SDK Kubernetes node
│ HTTPS :443 + Bearer token │ container·host·kubelet telemetry
▼ ▼
Gateway API ── ExternalAuth ──► otel-gateway ◄── otel-node DaemonSet
│ │
│ OTLP/gRPC :4317, HTTP :4318 ├── OTLP sink ──► clickstack-ingest
│ │ │
SeaweedFS S3 ── Fluent Forward :8006 ───┘ ▼
ClickHouse
├── hot disk
└── SeaweedFS S3 cold tier- Gateway API는 인터넷 노출, TLS 종료, hostname과 OTLP path allowlist를 소유함
otel.jamie.kr은 Gateway의 HTTPS listener에 연결됨- Collector Service와 Pod의
4317,4318은 외부에 직접 노출하지 않음
otel-gateway는 인증된 signal, node telemetry와 S3 audit을 하나의 exporter 경계에서 처리함- gateway는 sink retry와 disk-backed sending queue를 소유함
clickstack-ingest는 gateway의 OTLP sink만 ingress로 허용함
- ClickHouse는 telemetry 조회와 TTL 실행을 소유하고 SeaweedFS는 cold object와 S3 access audit을 소유함
- SeaweedFS Master·Volume data PVC는
object-rwoStorageClass로10.25.140.6:/s3_data에 배치됨 - object data의 물리 저장소와 ClickHouse hot disk를 분리해 NFS 용량 압박의 원인을 구분함
- SeaweedFS Master·Volume data PVC는
외부 OTLP를 인증 후 gateway로 전달함
- gRPC와 OTLP/HTTP는 path가 다르므로 같은 HTTPS endpoint에서 별도 backend port로 분기함
| 요청 종류 | 공개 경로 | 내부 backend |
|---|---|---|
| OTLP/gRPC logs·metrics·traces | /opentelemetry.proto.collector.*.v1.*Service/Export | otel-gateway:4317 |
| OTLP/HTTP logs | /v1/logs | otel-gateway:4318 |
| OTLP/HTTP metrics | /v1/metrics | otel-gateway:4318 |
| OTLP/HTTP traces | /v1/traces | otel-gateway:4318 |
- 각 route rule은
ExternalAuth로otel-external-auth:8080을 먼저 호출함- auth Service는 정확한
Authorization: Bearer <token>에만200을 반환함 - 토큰이 없거나 일치하지 않는 요청은 gateway에서
401으로 종료됨 - route는
Authorization만 auth backend로 전달하고 인증 성공 표시는X-OTLP-Authenticated로 제한함
- auth Service는 정확한
- token 원문은 Git에 두지 않고 SOPS ciphertext에서 Kubernetes Secret으로 materialize함
- 클라이언트는 표준
OTEL_EXPORTER_OTLP_HEADERS로Authorization=Bearer%20<token>을 주입함 - Codex 설정에는
/v1/logs,/v1/metrics,/v1/tracesendpoint만 유지함
- 클라이언트는 표준
- 공개 ingress와 내부 backend는 NetworkPolicy로 별도 제한함
- Gateway ingress entity는 gateway의
4317,4318에만 접근 가능해야 함 - auth Service는 ingress, host와 remote node의
8080만 허용함
- Gateway ingress entity는 gateway의
curl --silent --output /dev/null --write-out '%{http_code}\n' \
--request POST --header 'Content-Type: application/x-protobuf' \
--data-binary '' https://otel.jamie.kr/v1/logs
kubectl -n observability get httproute otel-external -o wide
kubectl -n observability rollout status statefulset/otel-gateway
kubectl -n observability rollout status deployment/otel-external-auth- 완료 조건은 무인증 요청
401, 유효 token 요청200, 그리고 signal이 ClickHouse에서 조회되는 상태임 - 세부 route와 Codex client 설정은 Gateway API ExternalAuth로 외부 OTLP 수집 보호하기에서 확인 가능함
node agent가 ClickStack 재수집을 막음
- 재수집은 collector가 쓴 로그를 node agent가 다시 읽어 같은 collector로 보내는 순환 구조에서 발생함
clickstack-ingest는 OTLP sink의 수신 workload이므로 자신의 container stdout·stderr를 다시 수집하면 volume이 증폭될 수 있음- 다른 application 로그를 넓게 제외하면 관측 공백이 생기므로 자기 자신만 정확히 제외해야 함
otel-nodeDaemonSet의 logs pipeline은 namespace와 Pod 이름을 함께 검사하는 filter를 적용함
processors:
filter/drop-clickstack-ingest-self:
error_mode: ignore
logs:
log_record:
- >-
resource.attributes["k8s.namespace.name"] == "observability" and
IsMatch(resource.attributes["k8s.pod.name"], "^clickstack-ingest-[0-9]+$")
service:
pipelines:
logs:
processors:
- memory_limiter
- resource/role
- filter/drop-clickstack-ingest-self
- batch- 정규식은 StatefulSet Pod만 대상으로 하므로
clickstack-ingest-*가 아닌 observability workload는 계속 수집됨- deployment 이름이나 namespace 단독 match로 확대하면 새 workload가 의도치 않게 제외될 수 있음
- collector release 이름이나 Pod 이름이 바뀌면 filter와 실제 metadata를 함께 갱신해야 함
- 검증은 수집량과 제외 대상을 함께 조회해야 함
SELECT
ResourceAttributes['k8s.pod.name'] AS pod,
count() AS records
FROM default.otel_logs
WHERE Timestamp >= now() - INTERVAL 3 MINUTE
AND ResourceAttributes['k8s.namespace.name'] = 'observability'
AND match(ResourceAttributes['k8s.pod.name'], '^clickstack-ingest-[0-9]+$')
GROUP BY pod
ORDER BY records DESC;- 기대 결과는 filter 적용 뒤 해당 query가
0record이고 다른 application 로그는 계속 유입되는 상태임
gateway가 신호별 pipeline과 내구성 queue를 제공함
otel-gateway는 두 replica StatefulSet으로 실행하고 각 replica에1Gi의 retained queue PVC를 제공함file_storageextension은/var/lib/otel/storage에 queue를 저장함- replica 축소나 StatefulSet 삭제 때 queue PVC가
Retain이므로 데이터 정리에는 별도 승인과 절차가 필요함
- 일반 OTLP와 S3 audit은 다른 receiver·resource processor·logs pipeline으로 처리함
| pipeline | receiver | 공통 exporter | 구분 attribute |
|---|---|---|---|
logs | OTLP | otlp/sink | platform.telemetry.role=gateway |
metrics | OTLP | otlp/sink | platform.telemetry.role=gateway |
traces | OTLP | otlp/sink | platform.telemetry.role=gateway |
logs/s3-audit | Fluent Forward 8006 | otlp/sink | event.dataset=seaweedfs.s3.access |
- gateway exporter는 queue
8192, retry 초기 간격5s, 최대 간격30s, 무기한 retry를 사용함- sink 장애가 장기화되면 PVC와 node storage가 채워질 수 있으므로 queue 사용량, retry와 exporter error를 함께 alert해야 함
- queue는 delivery resilience를 제공하지만 ClickHouse retention이나 백업 정책을 대체하지 않음
- gateway ingress는 telemetry namespace, SeaweedFS audit sender와 public ingress의 port별 흐름으로 제한함
4317,4318은 OTLP에만 사용함- TCP와 UDP
8006은 SeaweedFS와clickhouse-s3-gateway의 Fluent Forward audit에만 사용함
kubectl -n observability get svc otel-gateway
kubectl -n observability get pvc -l app.kubernetes.io/instance=otel-gateway
kubectl -n observability logs statefulset/otel-gateway --since=10mClickHouse cold S3를 최소 권한 계정으로 보존함
- ClickHouse는 local default disk와 SeaweedFS S3
cold_s3disk를 묶은hot_coldstorage policy를 사용함- internal
clickhouse-s3-gateway:8333이 SeaweedFS filer를 향하는 S3 endpoint임 - cold data bucket은
clickhouse-observability임 object-rwo는 SeaweedFS 자체 Master·Volume data의 physical storage 계약이며 ClickHouse S3 client가 NFS를 직접 mount하는 경로가 아님
- internal
- telemetry 테이블 TTL은 hot cost와 조회 기간을 명시적으로 분리함
| 데이터 연령 | ClickHouse 동작 | 저장 위치 |
|---|---|---|
| 0~2일 | 기본 codec·hot query | default disk |
| 2일 | ZSTD(3) 재압축 | default disk |
| 3일 | TO DISK 'cold_s3' | SeaweedFS S3 |
| 14일 | DELETE | 삭제됨 |
- cold tier는
otel-cold-tier전용 AccessKey와 SecretKey를 사용함- account는
clickhouse-observabilitybucket에만Admin,Read,List,Tagging,Writeaction을 가짐 - SeaweedFS 전역 admin identity를 ClickHouse 설정에 사용하지 않음
- credentials와 SeaweedFS identity configuration은 SOPS ciphertext로 Git에 보관함
- account는
- TTL과 storage policy 변경은 모든 replica에서 수렴했는지 확인한 뒤 오래된 part의 disk를 조회해야 함
SELECT disk_name, count() AS parts, formatReadableSize(sum(bytes_on_disk)) AS size
FROM system.parts
WHERE active AND disk_name = 'cold_s3'
GROUP BY disk_name;
SELECT name, create_table_query
FROM system.tables
WHERE database = 'default'
AND position(create_table_query, "storage_policy = 'hot_cold'") > 0;- NFS PVC request 용량은 NFS server quota가 아니므로 physical free space와 S3 object 증가량을 별도로 관측해야 함
SeaweedFS S3 access audit을 Fluent Forward로 수집함
- 공개 SeaweedFS S3와 internal
clickhouse-s3-gateway모두auditLogConfig를 사용해 동일 gateway로 audit을 전송함- destination은
otel-gateway.observability.svc.cluster.local:8006임 request_ack: true는 audit receiver의 acknowledgement를 기다려 network failure를 감지함- tag prefix는 public S3와 cold tier gateway를 구분하는 보조 정보임
- destination은
- gateway의
fluent_forward/s3-auditreceiver는 audit record에 표준 resource attribute를 추가함k8s.cluster.name=jamie-krservice.name=seaweedfs-s3-auditevent.dataset=seaweedfs.s3.access
- audit pipeline은 S3 요청의 성공·실패와 actor를 추적할 수 있지만 object payload를 보관하는 data pipeline이 아님
- audit record의 필드 구조는 SeaweedFS version과 operation에 따라 달라질 수 있으므로 query 전에 raw attribute를 확인해야 함
- audit retention은 일반 telemetry TTL을 따르므로 장기 보존·규제 요구가 있으면 별도 export와 retention 계약이 필요함
SELECT
Timestamp,
Body,
ResourceAttributes['service.name'] AS service,
ResourceAttributes['event.dataset'] AS dataset
FROM default.otel_logs
WHERE Timestamp >= now() - INTERVAL 15 MINUTE
AND ResourceAttributes['event.dataset'] = 'seaweedfs.s3.access'
ORDER BY Timestamp DESC
LIMIT 100;운영 검증과 변경 순서를 유지함
- 변경 전에는 route, receiver, identity, storage policy와 NetworkPolicy의 소유자가 모두 GitOps desired state인지 확인함
- runtime에서 Secret 또는 collector config를 직접 수정하면 다음 Argo CD sync에서 되돌아갈 수 있음
- SOPS plaintext를 terminal 출력, commit message, CI log와 debugging artifact에 남기지 않음
- 배포 후에는 경계별 acceptance check를 순서대로 실행함
- 외부 OTLP 무인증
401과 인증200확인 - gateway OTLP signal 및 exporter retry·queue 상태 확인
- node agent의
clickstack-ingest재수집0record 확인 - ClickHouse
hot_cold정책·TTL과cold_s3part 확인 otel-cold-tiercredential로 bucket scoped S3 요청 확인- S3 요청 뒤
seaweedfs.s3.accessaudit record 확인
- 외부 OTLP 무인증
- SeaweedFS PVC migration과 Released PV cleanup은 object integrity와 rollback 보존 기간을 분리해 승인함
- migration 직후의 이전
/k8s_dataPV를 삭제하면 storage path rollback 선택지가 사라짐 - data copy, SeaweedFS readiness, S3 access, cold part와 audit 수집을 모두 검증한 뒤 rollback PV 보존 종료를 진행함
- migration 직후의 이전
실행 제안
- 새 receiver·exporter·TTL 변경은 하나의 GitOps sync에 묶되 acceptance query는 signal, storage와 audit 경계별로 분리해 실행함
- S3 계정은 workload별로 분리하고 SeaweedFS global admin credentials를 telemetry runtime에 배포하지 않음
- queue, cold S3 object 증가량, 14일 TTL 삭제와 audit 유입량을 함께 대시보드화해 비용과 누락을 동시에 감시함