26Y02b

Young-Kyoo Kim·3일 전

step2 파이프라인에서 추출되는 3가지 정산 파일들에 대해 MinIO AIStor Tables(Iceberg V3 규격) 스키마 생성 명령어를 정리했습니다.

이전 단계에서 공유해 드린 step2_pipeline.py 소스 코드의 Pandas 데이터 타입(Dtype) 및 집계 컬럼들과 1:1로 완벽히 대응되도록 ID와 타입을 매핑한 명세입니다.

사내 mc alias가 myaistor로 설정되어 있고, 웨어하우스명이 finops_warehouse, 네임스페이스가 storage인 상황을 기준으로 작성했습니다.


1. daily_enriched_STORAGE_20260630.parquet 스키마

가장 로우한 컨테이너 레벨의 11대 메트릭 집계 및 FinOps 가중치 연산 결과가 모두 담긴 마스터 원부 테이블입니다.

mc table create myaistor finops_warehouse storage daily_enriched_storage \
  --schema '{
    "type": "struct",
    "fields": [
      {"id": 1, "name": "date", "type": "string", "required": true},
      {"id": 2, "name": "cluster_type", "type": "string", "required": true},
      {"id": 3, "name": "workload_domain", "type": "string", "required": true},
      {"id": 4, "name": "namespace", "type": "string", "required": true},
      {"id": 5, "name": "workload_type", "type": "string", "required": true},
      {"id": 6, "name": "node", "type": "string", "required": true},
      {"id": 7, "name": "pod", "type": "string", "required": true},
      {"id": 8, "name": "container", "type": "string", "required": true},
      {"id": 9, "name": "minutes_running", "type": "long", "required": true},
      {"id": 10, "name": "cpu_request_max", "type": "double", "required": false},
      {"id": 11, "name": "cpu_limit_max", "type": "double", "required": false},
      {"id": 12, "name": "cpu_usage_p95", "type": "double", "required": false},
      {"id": 13, "name": "cpu_throttled_max", "type": "double", "required": false},
      {"id": 14, "name": "mem_request_max", "type": "double", "required": false},
      {"id": 15, "name": "mem_limit_max", "type": "double", "required": false},
      {"id": 16, "name": "mem_usage_p95", "type": "double", "required": false},
      {"id": 17, "name": "mem_rss_p95", "type": "double", "required": false},
      {"id": 18, "name": "oom_strike_sum", "type": "long", "required": false},
      {"id": 19, "name": "pv_capacity_max", "type": "double", "required": false},
      {"id": 20, "name": "pv_used_p95", "type": "double", "required": false},
      {"id": 21, "name": "cpu_allocated_core_hours", "type": "double", "required": false},
      {"id": 22, "name": "cpu_usage_core_hours", "type": "double", "required": false},
      {"id": 23, "name": "cpu_waste_core_hours", "type": "double", "required": false},
      {"id": 24, "name": "mem_allocated_gb_hours", "type": "double", "required": false},
      {"id": 25, "name": "mem_usage_gb_hours", "type": "double", "required": false},
      {"id": 26, "name": "mem_waste_gb_hours", "type": "double", "required": false},
      {"id": 27, "name": "pv_allocated_gb_hours", "type": "double", "required": false},
      {"id": 28, "name": "pv_usage_gb_hours", "type": "double", "required": false},
      {"id": 29, "name": "pv_waste_gb_hours", "type": "double", "required": false},
      {"id": 30, "name": "is_oom_killed", "type": "boolean", "required": false},
      {"id": 31, "name": "has_no_request", "type": "boolean", "required": false},
      {"id": 32, "name": "has_no_limit", "type": "boolean", "required": false},
      {"id": 33, "name": "cpu_shortage_cores", "type": "double", "required": false},
      {"id": 34, "name": "status", "type": "string", "required": false}
    ]
  }'

2. daily_ns_usage_STORAGE_20260630.parquet 스키마

네임스페이스(부서)별로 하루 동안 소모한 총량 자원 자산과 최종 FinOps 챠징 가중치 점수가 계산된 요약 테이블입니다. 주간/월간 리포트 롤업 속도를 극대화할 때 주로 스캔하게 됩니다.

mc table create myaistor finops_warehouse storage daily_ns_usage_storage \
  --schema '{
    "type": "struct",
    "fields": [
      {"id": 1, "name": "date", "type": "string", "required": true},
      {"id": 2, "name": "namespace", "type": "string", "required": true},
      {"id": 3, "name": "cpu_used_ch", "type": "double", "required": false},
      {"id": 4, "name": "cpu_alloc_ch", "type": "double", "required": false},
      {"id": 5, "name": "cpu_waste_ch", "type": "double", "required": false},
      {"id": 6, "name": "mem_used_gh", "type": "double", "required": false},
      {"id": 7, "name": "mem_alloc_gh", "type": "double", "required": false},
      {"id": 8, "name": "pv_used_gh", "type": "double", "required": false},
      {"id": 9, "name": "pv_alloc_gh", "type": "double", "required": false},
      {"id": 10, "name": "final_usage_score", "type": "double", "required": false}
    ]
  }'

3. pareto_ns_STORAGE_20260630.parquet 스키마

어떤 네임스페이스가 전사 낭비 지분의 몇 %를 차지하는지 파레토 분석(누적 비중) 점수가 기록된 등급 산출물 테이블입니다.

mc table create myaistor finops_warehouse storage pareto_ns_storage \
  --schema '{
    "type": "struct",
    "fields": [
      {"id": 1, "name": "date", "type": "string", "required": true},
      {"id": 2, "name": "namespace", "type": "string", "required": true},
      {"id": 3, "name": "total_waste_core_hours", "type": "double", "required": false},
      {"id": 4, "name": "minutes_running_sum", "type": "long", "required": false},
      {"id": 5, "name": "container_cnt", "type": "long", "required": false},
      {"id": 6, "name": "total_allocated_core_hours", "type": "double", "required": false},
      {"id": 7, "name": "waste_share_pct", "type": "double", "required": false},
      {"id": 8, "name": "waste_cumsum_pct", "type": "double", "required": false}
    ]
  }'

💡 아키텍처 고도화를 위한 한 가지 팁 (Pareto 테이블 보정)

현재 step2 파이프라인 내부의 파레토 요약 데이터프레임(df_ns) 결과물에는 내부 컬럼 데이터로 date가 포함되어 있지 않고 파일명에만 날짜 정보가 들어가 있습니다.

단일 Iceberg 테이블에 매일매일 이 파레토 데이터를 일렬로 누적하여 분산 조회(StarRocks 등) 시스템에서 시계열 추이를 쿼리하려면, step2_pipeline.py 소스 코드 내부에서 파레토 테이블을 파일로 써내기 직전에 아래 한 줄을 추가해 주시는 것이 데이터 모델링 측면에서 훨씬 유용합니다.

# step2_pipeline.py의 파레토 파일 쓰기 직전 구역 (약 140라인 부근)
df_ns["waste_share_pct"] = (df_ns["total_waste_core_hours"] / global_total_waste * 100).round(2)
df_ns["waste_cumsum_pct"] = df_ns["waste_share_pct"].cumsum().round(2)

# 💡 [팁 추가] 단일 Iceberg 테이블 시계열 적재를 위해 내부 컬럼에 날짜 강제 인젝션
df_ns["date"] = date_chunk 

df_ns.to_parquet(cluster_partition_dir / f"pareto_ns_{cluster_chunk}_{date_chunk}.parquet", index=False)

위 코드를 반영해 두면, 제공해 드린 3번 mc table 스키마 구조 그대로 하나의 테이블 안에서 날짜별 파레토 지분 변동 추이를 SQL 윈도우 함수 등으로 매끄럽게 추적할 수 있게 됩니다.

0개의 댓글