관리 메뉴

근묵자흑

Coroot 코드로 읽는 LLM RCA 컨텍스트 — 한 인시던트가 Cloud 전송까지 가는 경로 본문

AIOps

Coroot 코드로 읽는 LLM RCA 컨텍스트 — 한 인시던트가 Cloud 전송까지 가는 경로

Luuuuu 2026. 5. 24. 14:20

본 문서는 Coroot OSS 코드를 직접 읽어 OSS가 Coroot Cloud로 보내는 RCA 컨텍스트의 구조와 경로를 코드 레벨에서 분석한 자료입니다. 운영 관점의 서술과 자료구조 레퍼런스를 한 문서에 함께 담습니다.

분석 기준

  • Repository: github.com/coroot/coroot (Apache-2.0)
  • Tag: v1.20.2
  • Commit SHA: 6f2da40b9bf4b795929f8ec3a6cfb1c31ccbc805 (short: 6f2da40)
  • 분석 시점: 2026-05-24

OSS 경계 선언: 본 문서가 다루는 모든 코드는 위 commit 기준 OSS만 해당합니다. Coroot Enterprise/Cloud 바이너리는 분석 대상이 아니며, LLM 호출(ai.go)은 OSS 트리에 없습니다. Enterprise 영역 언급 시 항상 [추정] 또는 [블로그] 태그를 명시합니다.


0. TL;DR & Key Findings

  • OSS가 Cloud에 전송하는 RCA 컨텍스트의 최외곽 형태cloud.RCARequest입니다. msgpack+lz4 → POST /integration/rca → Cloud → model.RCA 응답. Cloud 내부에서 LLM 프롬프트로 변환됩니다 — §8 [추정] (→ §2, §3)
  • 두 진입점: 수동 RCA() + 자동 IncidentRCA() — 동일 수집 체인, 동일 직렬화 경계 (→ §2, §4)
  • OSS 경계 명확: 직렬화+HTTP까지 OSS. 그 너머(LLM 프롬프트·모델 호출)는 Coroot Cloud 비공개 (→ §8)
  • auditor.Audit()은 RCA 흐름 밖 — application view 전용. AuditReport는 Cloud 전송 대상이 아닙니다 (→ §4, §5)
  • 자체 LLM PoC 가능: OSS가 컨텍스트 빌더를 완전 공개합니다 → cloud.RCARequest 재현으로 자체 LLM 직접 프롬프트 가능 (→ §8)

1. 시나리오로 시작 — "한 인시던트가 Coroot Cloud 전송까지 가는 경로"

에러율이 SLO 임계를 넘으면 Coroot watcher가 번레이트 규칙(1h/5m × 14.4x, 6h/15m × 6x — §6)을 평가해 인시던트를 open합니다. 이때 api.IncidentRCA() 콜백이 자동 발화해 메트릭·K8s 이벤트·SLO 위반 trace를 모아 cloud.RCARequest를 채웁니다. msgpack + lz4로 직렬화된 뒤 POST /integration/rca로 전송되며, Cloud가 응답한 model.RCAdb.UpdateIncidentRCA로 저장됩니다. 수동 요청(api.RCA())도 동일한 직렬화 경계로 수렴합니다.

flowchart LR
  A[SLO 위반<br/>model/alert.go: 14.4x / 6x] --> B[Incident open<br/>watchers + db.UpdateIncident]
  B --> C{어느 경로?}
  C -->|자동| D1[api/rca.go: IncidentRCA]
  C -->|수동| D2[api/rca.go: RCA handler]
  D1 --> E[raw 수집<br/>Metrics + K8sEvents + Traces + Configs + Deployments]
  D2 --> E
  E --> F[cloud.RCARequest 구성]
  F --> G[msgpack + lz4 직렬화]
  G --> H[POST /integration/rca<br/>Coroot Cloud]
  H -.->|OSS 경계| I[Cloud 내부:<br/>LLM 호출 비공개]:::cloud
  I -.-> J[model.RCA 응답<br/>ShortSummary + RootCause + ...]
  J --> K[db.UpdateIncidentRCA<br/>또는 HTTP 응답]
  classDef cloud stroke-dasharray:5 5,fill:#fafafa

OSS는 점선 박스 직전까지 담당하며, 그 너머는 Coroot Cloud 비공개 영역입니다.


2. 직렬화 경계 — cloud.RCARequest + api/rca.go

이 챕터는 본 글의 중심에 해당합니다. OSS가 Coroot Cloud로 RCA 컨텍스트를 어떻게 보내는지, 그 정확한 형태를 코드로 확인합니다.

2.1 라우트 등록

수동 RCA 진입점은 main.go L204, 자동 트리거는 L145(watchers.NewIncidents(database, a.IncidentRCA))에서 등록됩니다.

// main.go, tag v1.20.2, SHA 6f2da40
// permalink: github.com/coroot/coroot/blob/6f2da40b9bf4b795929f8ec3a6cfb1c31ccbc805/main.go#L204
// L204
r.HandleFunc("/api/project/{project}/app/{app}/rca", a.Auth(a.RCA)).Methods(http.MethodGet)

2.2 두 진입점

진입점 호출자 시그니처 응답
RCA() HTTP 요청 (수동) func (api *Api) RCA(w http.ResponseWriter, r *http.Request, u *db.User) JSON 또는 DB 저장
IncidentRCA() watcher (자동) func (api *Api) IncidentRCA(ctx context.Context, project *db.Project, world *model.World, incident *model.ApplicationIncident) DB 저장만

두 함수 모두 최종적으로 cloudAPI.RCA(ctx, rcaRequest) — 동일한 Cloud API 호출로 수렴합니다.

2.3 raw 데이터 수집 체인

# 호출 결과 → RCARequest 필드
1 getTimeContext / IncidentTimeContext Ctx (시간 윈도: from/to/step)
2 db.GetCheckConfigs CheckConfigs
3 db.GetApplicationDeployments ApplicationDeployments
4 ctr.QueryCache Metrics
5 ch.GetKubernetesEvents KubernetesEvents
6 ch.GetTracesViolatingSLOs (PR #672) ErrorTrace, SlowTrace
7 cloudAPI.RCA(ctx, rcaRequest) → 직렬화 → Cloud

IncidentRCA()world 객체를 호출자(watcher)로부터 주입받으므로 LoadWorldByRequest 단계가 없습니다. RCA() 핸들러는 api.db.GetCheckConfigs()를 별도로 호출하고 LoadWorldByRequestworld.GetApplicationGetTracesViolatingSLOs 순으로 진행합니다.

2.4 호출 흐름 — sequenceDiagram

sequenceDiagram
  participant W as watcher
  participant H as api/rca.go: RCA() / IncidentRCA()
  participant DB as db (Postgres)
  participant CTR as constructor
  participant CH as ClickHouse
  participant CAPI as cloud.Api.RCA()
  participant CLOUD as Coroot Cloud (비공개)

  Note over W,H: 두 진입점
  W->>H: incident 자동 트리거 (IncidentRCA)
  Note right of H: 또는 HTTP GET /rca (수동)

  H->>DB: GetCheckConfigs / GetApplicationDeployments
  DB-->>H: configs, deployments
  H->>CTR: QueryCache(metrics)
  CTR-->>H: Metrics
  H->>CH: GetKubernetesEvents
  CH-->>H: K8sEvents
  H->>CH: GetTracesViolatingSLOs (PR #672)
  CH-->>H: ErrorTrace, SlowTrace

  H->>H: cloud.RCARequest 구성

  H->>CAPI: cloudAPI.RCA(ctx, rcaRequest)
  CAPI->>CAPI: msgpack.Encode + lz4 압축
  CAPI->>CLOUD: POST /integration/rca
  Note over CLOUD: LLM 호출 비공개

  CLOUD-->>CAPI: model.RCA (응답)
  CAPI-->>H: *model.RCA
  H->>DB: UpdateIncidentRCA (또는 HTTP 응답)

2.5 cloud.RCARequest 자료구조

Cloud로 전송되는 RCA 컨텍스트의 형태입니다 (Cloud가 내부에서 LLM 프롬프트로 변환합니다 — §8 [추정]).

// cloud/rca.go, tag v1.20.2, SHA 6f2da40
// permalink: github.com/coroot/coroot/blob/6f2da40b9bf4b795929f8ec3a6cfb1c31ccbc805/cloud/rca.go#L15-L32
// L15–L32
type RCARequest struct {
    Ctx timeseries.Context

    ApplicationId model.ApplicationId

    CheckConfigs                model.CheckConfigs
    ApplicationDeployments      map[model.ApplicationId][]*model.ApplicationDeployment
    ApplicationCategorySettings map[model.ApplicationCategory]*db.ApplicationCategorySettings
    CustomApplications          map[string]model.CustomApplication
    CustomCloudPricing          *db.CustomCloudPricing

    Metrics map[string][]*model.MetricValues

    KubernetesEvents []*model.LogEntry

    ErrorTrace *model.Trace
    SlowTrace  *model.Trace
}

2.6 직렬화 + HTTP 호출

// cloud/rca.go, tag v1.20.2, SHA 6f2da40
// permalink: github.com/coroot/coroot/blob/6f2da40b9bf4b795929f8ec3a6cfb1c31ccbc805/cloud/rca.go#L34-L49
// L34–L49
func (api *Api) RCA(ctx context.Context, req RCARequest) (*model.RCA, error) {
    buf := bytes.NewBuffer(nil)
    lw := lz4.NewWriter(buf)
    if err := msgpack.NewEncoder(lw).Encode(req); err != nil {
        return nil, err
    }
    if err := lw.Close(); err != nil {
        return nil, err
    }

    var rca model.RCA
    if err := api.request(ctx, http.MethodPost, "/integration/rca", "application/msgpack", "lz4", buf, &rca); err != nil {
        return nil, err
    }
    return &rca, nil
}

3단계 파이프라인으로 정리됩니다: ① msgpack.Encode(req) — RCARequest를 MessagePack 바이너리로 직렬화. ② lz4.NewWriter로 감싸 압축. ③ POST /integration/rca로 전송 (content-type application/msgpack, encoding lz4). 응답은 model.RCA로 디코딩합니다.

2.7 PR #672 — SLO 위반 trace 추가의 의미

PR #672("Add trace data to RCA context", commit 36f5f20, 2025-09-10)는 4개 파일 변경(+89/-1줄)으로 LLM 컨텍스트의 정합성을 보강한 변경입니다.

// cloud/rca.go L30–L31
ErrorTrace *model.Trace
SlowTrace  *model.Trace

clickhouse/traces.go에 신규 추가된 GetTracesViolatingSLOsStatusCode=ERROR span 포함 trace 1건(ErrorTrace)과 latency SLO 초과 trace 1건(SlowTrace)을 조회합니다. PR 이전에는 메트릭 시계열 + K8s 이벤트만 전달됐으나, PR 이후에는 실제 위반 trace의 span 계층 데이터까지 LLM 컨텍스트에 합류합니다. LLM의 인과 추론에 추가 단서가 들어간 셈입니다.

2.8 OSS 경계 박스 (1차 선언)

OSS 경계 선언 (1차): 위 api.request(ctx, POST, /integration/rca, msgpack, lz4, buf, &rca) 한 줄이 OSS 코드가 LLM 컨텍스트 전송에 대해 담당하는 마지막 단계입니다. 그 다음에 Coroot Cloud 내부에서 일어나는 일(요청 파싱, LLM 프롬프트 변환, 모델 호출, 응답 후처리)은 비공개입니다. 챕터 8에서 더 다룹니다.


3. RCA 컨텍스트 자료구조 — Cloud 전송 컨텍스트의 형태

OSS가 Cloud에 넘기는 RCA 컨텍스트는 cloud.RCARequest이며, Cloud 내부에서 LLM 프롬프트로 변환되는 단계는 비공개입니다([추정] §8). Cloud 응답은 model.RCA이며, 둘 다 OSS에 정의되어 있습니다.

classDiagram
  class RCARequest {
    +Ctx Context
    +ApplicationId ApplicationId
    +CheckConfigs CheckConfigs
    +ApplicationDeployments Map
    +Metrics Map
    +KubernetesEvents List
    +ErrorTrace Trace
    +SlowTrace Trace
  }
  class RCA {
    +Status string
    +Error string
    +ShortSummary string
    +RootCause string
    +ImmediateFixes string
    +DetailedRootCause string
    +PropagationMap PropagationMap
  }
  class ApplicationIncident {
    +Details IncidentDetails
    +RCA RCA
  }
  class IncidentDetails {
    +AvailabilityBurnRates List
    +LatencyBurnRates List
    +AvailabilityImpact Impact
  }
  class IncidentView {
    +AvailabilitySLO SLODetails
    +LatencySLO SLODetails
  }
  class MetricValues {
    +Values TimeSeries
  }
  class LogEntry {
    +Timestamp Time
    +Severity string
    +Body string
  }
  class Trace {
    +Spans List
  }
  ApplicationIncident --> IncidentDetails : Details
  ApplicationIncident --> RCA : RCA
  RCARequest --> MetricValues : Metrics
  RCARequest --> LogEntry : KubernetesEvents
  RCARequest --> Trace : ErrorTrace_SlowTrace
  IncidentView ..> ApplicationIncident

Diagram source: cloud/rca.go:L15-L32, model/application_incident.go:L10-L76, api/views/incident/incident.go:L57-L65, model/labels.go:L44, model/log.go:L32, model/trace.go:L15 (tag v1.20.2, SHA 6f2da40). 위 다이어그램은 전송 경계에 등장하는 타입들의 관계도이며, 각 타입의 핵심 필드만 표시했습니다. 전체 시그니처는 아래 카드에서 다룹니다.

메인 카드 (Cloud 전송 직접 관여, 4장)

cloud.RCARequest (cloud/rca.go:L15-L32) — OSS→Cloud RCA 요청 페이로드. Cloud 전송 컨텍스트의 결정적 형태입니다 (Cloud가 내부에서 LLM 프롬프트로 변환 — §8 [추정]). 핵심 필드는 Metrics map[string][]*model.MetricValues / KubernetesEvents []*model.LogEntry / ErrorTrace+SlowTrace *model.Trace (PR #672)입니다. writer→reader: api/rca.gocloudAPI.RCA → Cloud. 직렬화는 msgpack+lz4 방식입니다.

model.RCA (model/application_incident.go:L21-L30) — Cloud LLM 응답. Status 값은 OK/Failed/In progress/AI disabled/Out of credits 중 하나입니다. 핵심 필드는 ShortSummary/RootCause / ImmediateFixes / PropagationMap *PropagationMap입니다. JSON 키는 "short_summary", "root_cause", "immediate_fixes", "detailed_root_cause_analysis", "propagation_map". writer→reader: cloudAPI.RCAdb.UpdateIncidentRCA() 또는 utils.WriteJson.

model.ApplicationIncident+IncidentDetails (model/application_incident.go:L10-L76) — watcher가 SLO 위반 시 생성합니다. RCA *RCA 임베드로 영속화됩니다. 핵심 필드는 AvailabilityBurnRates/LatencyBurnRates []BurnRate / AvailabilityImpact Impact / RCA *RCA. RCA==nil이면 미분석 상태입니다.

api/views/incident.View (api/views/incident/incident.go:L57-L65) — 인시던트 페이지 응답. AvailabilitySLO/LatencySLO *SLODetails / Widgets []*model.Widget. spec v1의 incident.Summary는 존재하지 않으며, 이 타입이 그 역할을 담당합니다.

Cloud 전송 컨텍스트의 모양 — msgpack 직렬화 전 JSON 표현

msgpack 바이너리는 사람이 직접 읽을 수 없으므로 RCARequest를 JSON으로 표현하면 다음과 같습니다 (필드명은 Go struct 그대로):

{
  "Ctx": {"From": "2026-05-24T03:00:00Z", "To": "2026-05-24T03:05:00Z", "Step": "15s"},
  "ApplicationId": "default/Deployment/order-service",
  "CheckConfigs": { "..." : "..." },
  "ApplicationDeployments": { "default/Deployment/order-service": [ { "StartedAt": "...", "Version": "v1.2.3" } ] },
  "Metrics": { "container_cpu_usage_seconds_total": [ "..." ] },
  "KubernetesEvents": [ { "Timestamp": "...", "Reason": "OOMKilled", "Body": "..." } ],
  "ErrorTrace": { "Spans": [ { "TraceId": "...", "StatusCode": "ERROR", "Name": "...", "Duration": "..." } ] },
  "SlowTrace": null
}

[추정]: Coroot Cloud는 이 컨텍스트를 받아 내부적으로 LLM 프롬프트로 변환합니다. model.RCA 응답의 고정 6필드 구조(status/short_summary/root_cause/immediate_fixes/detailed_root_cause_analysis/propagation_map)를 보면 LLM에 출력 스키마가 강제되었음을 추정할 수 있습니다. RCACopilot(EuroSys '24) 패턴과 유사한 방식입니다.

보조 카드 (간접 관여, 4장)

model.AuditReport+model.Check (model/audit_report.go:L43-L56, model/check.go:L510-L528)

  • 역할: auditor 19-stage 실행 결과. UI의 AuditReport/Check 표시용. Cloud 전송 대상이 아니며 — CheckConfigs(설정)만 RCARequest에 포함됩니다
  • 핵심 필드: AuditReport.Checks []*Check, Check.Status, Check.Threshold, Check.Widgets (json:"-" 직렬화 제외)
  • writer → reader: auditor.Audit()api/views/application.View.Reports (UI 표시)
  • JSON 키 / 한계: AuditReport.name/status/checks · Check.Widgets는 직렬화 제외 대상이며, RCA 흐름과 분리된 별도 경로입니다.

model.World+model.Application (model/world.go:L18-L33, model/application.go:L14-L52)

  • 역할: 전체 토폴로지 컨테이너. RCA는 world.GetApplication(appId) + world.CheckConfigs만 사용합니다.
  • 핵심 필드: World.CheckConfigs, World.Applications map[ApplicationId]*Application, Application.LatencySLIs, Application.AvailabilitySLIs
  • writer → reader: constructor.LoadWorld()api/rca.go: RCA()/IncidentRCA() (CheckConfigs, GetApplication만 추출)
  • JSON 키 / 한계: World/Application 자체는 직렬화되지 않으며, Application.Reports []*AuditReport는 RCARequest에 포함되지 않습니다.

model.AlertRules (model/alert.go:L10-L28)

  • 역할: 번레이트 임계 정의. 인시던트 open 조건 + RCA 시간 윈도 결정에 사용됩니다.
  • 핵심 필드: AlertRule.LongWindow, ShortWindow, BurnRateThreshold (Rule1=14.4x 1h/5m, Rule2=6x 6h/15m), IncidentTimeOffset=1h+5m
  • writer → reader: model/alert.go 상수 → watchers/incidents.go (번레이트 평가) → api/rca.go (시간 윈도 from=openedAt-offset)
  • JSON 키 / 한계: 상수 정의이며, 임계값은 CheckConfigs를 통해 간접적으로 Cloud에 전달됩니다.

MetricValues+LogEntry+Trace (model/labels.go:L44, model/log.go:L32, model/trace.go:L15)

  • 역할: RCARequest raw 데이터 원소 타입. msgpack+lz4 직렬화 대상입니다.
  • 핵심 필드: MetricValues.Values *TimeSeries, LogEntry.Body+Severity+Timestamp, Trace.Spans []*TraceSpan
  • writer → reader: ctr.QueryCache / ch.GetKubernetesEvents / ch.GetTracesViolatingSLOscloud.RCARequest.Metrics / KubernetesEvents / ErrorTrace+SlowTrace
  • JSON 키 / 한계: msgpack 직렬화 (사람이 읽는 JSON이 아님) · lz4 압축이 전송 비용을 결정 · Trace는 SLO 위반 1건 단위로 조회합니다.

[추정]: Cloud는 수신 컨텍스트를 LLM 프롬프트로 변환하는 것으로 보입니다. model.RCA 고정 5필드 구조는 출력 스키마가 강제되었음을 시사합니다. 정확한 프롬프트는 비공개입니다.


4. 컨텍스트 빌더 — RCA 핸들러 안쪽

이 챕터에서 짚어둘 사실 하나가 있습니다. RCA 핸들러는 auditor.Audit()을 호출하지 않습니다. spec v1이 가정했던 "deep AuditReport → LLM" 흐름은 코드에 존재하지 않습니다. 대신 api/rca.go의 두 핸들러가 raw 데이터를 직접 모아 cloud.RCARequest를 구성합니다. 이것이 Phase 3 분석에서 정정된 지점입니다.

4.1 두 핸들러의 호출 트리 비교

공통 파이프라인: getTimeContext → GetCheckConfigs → GetApplicationDeployments → QueryCache(Metrics) → GetKubernetesEvents → GetTracesViolatingSLOs → cloudAPI.RCA(rcaRequest).

차이 RCA() (수동) IncidentRCA() (자동)
시간 윈도 getTimeContext (URL 파라미터) IncidentTimeContext (incident.OpenedAt + IncidentTimeOffset)
world 주입 LoadWorldByRequest (요청 시 생성) watcher가 인자로 전달
결과 처리 DB 저장 또는 HTTP 응답 DB 저장만
스킵 조건 없음 incident.RCA.Status == "OK"이면 재분석 안 함
멀티클러스터 사용자 요청 단위 "RCA is not supported for mult-cluster projects" 조기 종료

4.2 시간 윈도 결정 로직

IncidentTimeOffset = timeseries.Hour + 5*timeseries.Minute — 첫 번째 AlertRule의 LongWindow(1h) + ShortWindow(5m) 합계입니다. IncidentRCA()incident.OpenedAt으로부터 이 오프셋만큼 이전을 from으로 설정해, LLM이 최대 1시간 5분 치 컨텍스트를 받도록 합니다. Coroot는 가장 빠른 번레이트 윈도(1h) 기준을 채택해 토큰 비용과 인과 추론 정보 사이의 균형을 잡았습니다.

4.3 auditor.Audit() 분기는 RCA가 아닌 application view 영역

auditor.Audit() 시그니처: func Audit(w *model.World, p *db.Project, generateDetailedReportFor *model.Application, prof *Profile). generateDetailedReportFor=app이면 해당 앱에 한해 Chart/Heatmap 위젯 포함 deep 리포트를 만듭니다. RCA 핸들러(RCA(), IncidentRCA())는 auditor.Audit()을 단 한 번도 호출하지 않습니다. RCA 흐름에서 AuditReport/Check가 LLM에 도달하는 경로는 OSS 코드에 없습니다.

4.4 자체 LLM PoC 가능성

위 호출 트리 7단계 모두 OSS에서 가시화 가능합니다. PoC 구성 경로는 다음과 같습니다: ① OSS의 ctr.QueryCache, ch.GetKubernetesEvents, ch.GetTracesViolatingSLOs를 재사용해 cloud.RCARequest 호환 구조를 채웁니다. ② 자체 프롬프트 + 자체 모델로 LLM을 호출합니다. ③ 응답을 model.RCA 스키마에 맞춰 반환하면 기존 db.UpdateIncidentRCA + RCA.vue UI 재사용까지 가능합니다.


5. 룰 기반 처리 — auditor/Audit()

5.0 재포지셔닝

이 챕터는 RCA 흐름의 일부가 아닙니다. auditor.Audit()은 application view 경로에서 호출되어 UI 진단 정보를 만듭니다. RCA 입력으로는 CheckConfigs(설정)만 전달되고, Check 결과(Status/Message)는 LLM에 도달하지 않습니다. 이 챕터의 목적은 두 가지입니다: ① Coroot가 LLM 없이 어떻게 진단하는가를 보여주는 기준선, ② 자체 LLM PoC 시 룰 결과를 추가 컨텍스트로 넣을지 판단하는 재료.

5.1 stage 파이프라인

// auditor/auditor.go, tag v1.20.2, SHA 6f2da40
// permalink: github.com/coroot/coroot/blob/6f2da40b9bf4b795929f8ec3a6cfb1c31ccbc805/auditor/auditor.go#L36-L105
func Audit(w *model.World, p *db.Project, generateDetailedReportFor *model.Application, prof *Profile) {
    for _, app := range w.Applications {
        a := &appAuditor{w: w, p: p, app: app, detailed: app == generateDetailedReportFor}
        stages.stage("slo", a.slo)
        // ... 19 stages
        stages.stage("deployments", a.deployments)
    }
}

stage 순서: slo → instances → cpu → memory → storage → gpu → network → dns → postgres → mysql → redis → mongodb → memcached → jvm → dotnet → python → nodejs → logs → deployments

5.2 다이어그램 — stage 파이프라인

flowchart LR
  Start([Audit world, p, ...]) --> R[for app in w.Applications]
  R --> S1[slo]
  S1 --> S2[instances]
  S2 --> S3[cpu / memory / storage / gpu]
  S3 --> S4[network / dns]
  S4 --> S5[postgres / mysql / redis / mongodb / memcached]
  S5 --> S6[jvm / dotnet / python / nodejs]
  S6 --> S7[logs]
  S7 --> S8[deployments]
  S8 --> End([Reports per app])
  Note["application view UI 표시용<br/>RCA 흐름과 분리"]:::note
  End -.-> Note
  classDef note fill:#ffeaa7,stroke:#fdcb6e

5.3 깊게 보는 3 stage

slo.go — Stage 1: check.SetStatus(br.Severity, "%s", br.FormatSLOStatus())로 번레이트 메시지를 생성합니다(auditor/slo.go:L39, SHA 6f2da40). UI 표시용이며 Cloud 전송 대상에는 포함되지 않습니다.

logs.go — Stage 18: a.app.LogMessages(logparser 클러스터링 결과)에서 에러 패턴 해시를 check.AddItem()으로 등록합니다(auditor/logs.go:L20-L22). UI 패턴별 빈도 표시용입니다.

deployments.go — Stage 19: model.CalcApplicationDeploymentStatuses()로 배포 전후 30분 메트릭을 비교해 배포 상관관계를 판단합니다(auditor/deployments.go:L21). Cloud 전송에는 배포 목록(raw)만 들어가고 auditor의 상관 결과는 빠집니다.

5.4 RCA와의 관계

AuditReport는 Cloud 전송 컨텍스트에 직접 포함되지 않습니다. CheckConfigs(설정)만 cloud.RCARequest.CheckConfigs에 포함됩니다. 룰 설정만 Cloud에 전달되고, 룰 결과 자체는 전달되지 않습니다. 자체 LLM PoC라면 룰 결과까지 컨텍스트에 추가하는 선택지가 열려 있습니다.


6. 트리거 — 번레이트 규칙

구글 SRE 4-window 모델의 2-rule 구현입니다(model/alert.go:L10-L28, SHA 6f2da40).

AlertRules = []AlertRule{
    {LongWindow: Hour,   ShortWindow: 5*Minute,  BurnRateThreshold: 14.4, Severity: CRITICAL},
    {LongWindow: 6*Hour, ShortWindow: 15*Minute, BurnRateThreshold: 6,   Severity: CRITICAL},
}
IncidentTimeOffset = Hour + 5*Minute

두 Rule이 동시에 임계를 초과할 때 인시던트가 열립니다. FormatSLOStatus() 메시지("error budget burn rate is %.1fx within %s")의 배율은 14.4x 임계를 초과한 실시간 관측치(동적 값)입니다. 저트래픽 서비스에서는 번레이트 계산이 유의미하지 않아 인시던트가 열리지 않을 수 있습니다([이슈] #694). 두 윈도가 동시에 초과되면 ApplicationIncident가 열리고, IncidentRCA() 자동 트리거 핸들러가 호출됩니다 (→ §2.2).


7. SLI 입력 (간략)

RCA 컨텍스트의 Metrics 필드는 ctr.QueryCache(...)로 채워지지만, 그 원천은 constructor.LoadWorld()(constructor/constructor.go:L60)가 Prometheus/ClickHouse에서 모은 시계열입니다. SLI는 그 시계열의 부분집합에 해당합니다.

어노테이션 기반 SLO 정의(coroot.com/slo-availability-objective, coroot.com/slo-latency-objective, coroot.com/slo-latency-threshold)는 constructor/queries.go(L791, L823, L829)에서 파싱됩니다. 파싱 결과는 model.AvailabilitySLI.Configmodel.LatencySLI.Config로 저장되고, watchers가 이를 읽어 번레이트를 계산합니다.

SLO 수집 파이프라인의 자세한 분석은 병행 산출물 docs/coroot-slo-rca-architecture.md를 참조합니다.


8. LLM 통합 경계 — OSS가 끝나는 지점

8.1 OSS 경계 명확화

OSS에서 코드로 확인되는 것:

  • cloud.RCARequest 자료구조 (cloud/rca.go:L15-L32)
  • msgpack + lz4 직렬화 (cloud/rca.go:L34-L49)
  • POST /integration/rca HTTP 요청
  • 응답 타입 model.RCA (6 필드 고정 스키마)

OSS에서 보이지 않는 것 [추정]:

  • Cloud 측의 RCARequest 파싱/변환
  • LLM 프롬프트 (시스템/사용자)
  • LLM 모델 호출 (어느 API, 어떤 파라미터)
  • 응답 후처리

8.2 ai.go 부재 확인

$ find . -name 'ai.go' | grep -v '\./front' | head -5
(출력 없음)

OSS 트리에 ai.go 부재 확인 — Enterprise 바이너리 전용입니다.

8.3 v1.14 릴리즈 [릴리즈노트]

Coroot v1.14.0에서 Cloud AI 통합이 도입되었습니다. OSS 인스턴스에서 Cloud 연결 없이 /integration/rca를 호출하면 "AI disabled" 상태가 반환됩니다.

8.4 Issue #647 로그 라인 [이슈]

GitHub Issue #647 재현 로그: ai.go:116] POST "https://api.openai.com/v1/chat/completions": 429 Too Many Requests — Cloud 측이 OpenAI API를 직접 호출한 흔적입니다.

8.5 지원 LLM 프로바이더 [블로그]

Coroot 블로그 "We built AI-powered Root Cause Analysis that actually works" (Nikolay Sivko, 2025-05-06): Claude 3.7 Sonnet (권장), GPT-4o, OpenAI-compatible API (DeepSeek, Gemini 등 셀프호스팅 가능).

8.6 [추정] LLM 프롬프트 변환

추정: cloud.RCARequest의 raw 데이터(metrics + traces + k8s events + configs + deployments)가 Coroot Cloud 측에서 LLM 프롬프트로 어떻게 변환되는지는 비공개입니다. 응답 타입 model.RCA의 6 필드(status / short_summary / root_cause / immediate_fixes / detailed_root_cause_analysis / propagation_map) 구조를 보면, LLM에 고정 출력 스키마가 요구되었음을 추정할 수 있습니다. RCACopilot(EuroSys '24)의 structured output 패턴과 유사한 방식입니다.

8.7 자체 LLM PoC 가능성

OSS 코드만으로 cloud.RCARequest를 그대로 재현할 수 있습니다. ctr.QueryCache, ch.GetKubernetesEvents, ch.GetTracesViolatingSLOs를 그대로 재사용해 컨텍스트를 채운 뒤, 자체 프롬프트 + 자체 모델(Claude/GPT/DeepSeek 등)로 LLM을 호출하고 응답을 model.RCA 스키마에 맞춰 반환하면 기존 db.UpdateIncidentRCA + RCA.vue UI까지 재사용 가능합니다.

Coroot OSS는 자체 LLM RCA를 위한 컨텍스트 빌더로 활용할 수 있는 형태입니다.

8.8 OSS 경계 선언 (2차 재선언)

OSS 경계 선언 (2차, 재선언): 본 문서의 모든 코드 인용은 github.com/coroot/coroot (Apache-2.0) commit 6f2da40 기준 OSS만 해당합니다. Coroot Cloud HTTP 엔드포인트(/integration/rca) 너머는 비공개입니다. LLM 호출 ai.go는 OSS 트리에 부재합니다 — find 결과로 확인되었습니다.


9. 종합 — 증거·산출물 매핑

flowchart LR
  E2["§2 /rca route"] --> R["cloud.RCARequest"]
  E4R["§4 RCA() 수동"] --> R
  E4I["§4 IncidentRCA() 자동"] --> R
  E2P["§2.7 PR#672 traces"] -->|fills| R
  E6["§6 14.4x/6x"] -->|triggers| INC[ApplicationIncident]
  INC --> E4I
  R -->|msgpack+lz4| H["§2.6 POST /integration/rca"]
  H -.->|OSS 경계| CL["§8 Cloud LLM"]:::cloud
  CL -.-> RESP[model.RCA]
  RESP --> DB[UpdateIncidentRCA]
  E5["§5 auditor.Audit 별도"]:::aside
  classDef cloud stroke-dasharray:5 5,fill:#fafafa
  classDef aside fill:#ffeaa7,stroke:#fdcb6e

트리거는 수동 RCA() 또는 자동 IncidentRCA() (§4)입니다. 둘 다 동일한 파이프라인(msgpack+lz4)으로 /integration/rca에 도달합니다 (§2). 그 너머는 OSS 경계 밖입니다 (§8). 응답 model.RCA는 DB에 영속화됩니다. auditor.Audit()은 분리된 별도 경로입니다 (§5).


10. Caveats & 운영 한계

아래 항목은 본 문서를 읽거나 재활용할 때 염두에 둘 범위·신뢰도 제약입니다.

  • 버전 한계: 코드 인용은 v1.20.2 / SHA 6f2da40 시점 기준입니다. 이후 릴리즈에서 필드·경로·직렬화 방식이 변동될 수 있습니다.
  • LLM 호출 비공개: LLM 프롬프트 구조 / 프롬프트 엔지니어링 / LLM 모델 호출은 Coroot Cloud 엔드포인트 너머에 위치합니다 — [추정] 표기만 가능하며 코드 확인은 불가합니다.
  • RCARequest ≠ LLM 직접 입력: cloud.RCARequest는 Cloud로 전송되는 것이지 LLM에 직접 입력되는 것이 아닙니다. Cloud 측이 msgpack 역직렬화 후 추가 변환을 거쳐 LLM에 보낸다는 추정만 가능하며, 변환 단계는 비공개입니다.
  • eBPF 내부 범위 밖: coroot-node-agent eBPF 계층의 내부 구현은 본 문서 분석 범위 밖입니다.
  • 외부 분석: 본 문서는 공식 Coroot 입장이 아닌 외부 코드 분석입니다. Coroot 팀의 설계 의도와 다를 수 있습니다.
  • SLO 파이프라인 상세: SLO 수집 파이프라인(PromQL 쿼리·SLI 계산·번레이트 평가)의 상세는 병행 산출물 docs/coroot-slo-rca-architecture.md를 참조합니다.