CI 실패 분류 에이전트 구축하기: 실패한 GitHub Actions 실행 자동 진단
요약
GitHub Actions의 CI 실패 로그를 자동으로 분석하여 실패 원인을 분류하는 AI 에이전트 구축 가이드입니다. 인프라 문제, 테스트 오류, 실제 회귀 등을 진단하고 PR에 코멘트를 남겨 개발자의 컨텍스트 스위칭 비용을 줄입니다.
핵심 포인트
- CI 실패 로그를 분석해 인프라, 테스트, 회귀, 설정 오류로 자동 분류
- workflow_run 이벤트를 활용해 보안성을 높인 에이전트 아키텍처 설계
- 결정론적 규칙과 LLM을 결합하여 진단의 정확도와 효율성 확보
- 에이전트의 권한을 제한하여 코드 수정 없이 로그 읽기 및 코멘트 작성에 집중
💡 원문은 devtocash.com에 게시되었습니다 — 이 가이드의 최신 정보가 유지되는 곳입니다. 저는 그곳에서 매주 실무 중심의 DevOps/SRE 심층 분석 글을 작성합니다.
"다시 실행하고 기도하기"라는 세금
진지한 CI (Continuous Integration) 파이프라인을 운영하는 모든 팀은 동일한 세금을 지불합니다. 실행이 실패하면, 개발자는 4,000줄의 로그를 훑어보며 "아마 일시적인 문제(flaky)겠지"라고 중얼거린 뒤, 다시 실행(re-run)을 클릭하고 20분 동안 다른 업무로 컨텍스트 스위칭 (context-switch)을 합니다. 모든 빌드 실패 상황에 이를 곱해보면, 아마도 6가지 정도의 반복되는 카테고리에 속하는 실패들에 매일 수 시간을 허비하고 있는 셈입니다. 이 가이드는 GitHub Actions를 위한 **CI 실패 분류 에이전트 (CI failure triage agent)**를 구축합니다. 이 에이전트는 첫 번째 검토 단계를 자동으로 수행합니다. 실행이 실패하면, 실패한 작업(job)의 로그를 가져와서 해당 실패를 일시적인 인프라 문제(flaky infrastructure), 일시적인 테스트 문제(flaky test), 실제 회귀(real regression), 또는 설정 오류(config error)로 분류합니다. 그런 다음 증거가 되는 라인을 인용하여 진단 내용을 PR (Pull Request) 코멘트로 게시하며, 신뢰도가 높은 인프라 문제인 경우에만 실패한 작업을 정확히 한 번 재시도합니다.
설계 원칙은 Terraform plan review agent를 PR마다 안전하게 실행할 수 있게 만든 것과 동일합니다. 즉, 에이전트는 로그를 읽고 코멘트를 작성할 뿐입니다. 코드를 푸시할 수 없고, 클라우드에 손을 댈 수 없으며, 유일한 쓰기 작업(실패한 작업 재실행)은 멱등성(idempotent)을 가지며 횟수가 제한되어 있습니다. 잘못된 진단이 내려질 경우의 최악의 상황은 잘못된 코멘트가 달리고 재실행 한 번이 낭비되는 것뿐입니다.
아키텍처: 첫 번째 워크플로우를 감시하는 두 번째 워크플로우
분류(triage) 에이전트는 그 자체로 하나의 워크플로우이며, CI 워크플로우가 실패로 완료될 때 workflow_run에 의해 트리거됩니다. 이러한 구조는 공짜로 얻게 되는 보안 특성을 가집니다: workflow_run은 PR(Pull Request) 브랜치가 아닌 **기본 브랜치(default branch)**에서 분류 코드를 실행합니다. 기여자는 분류 스크립트나 프롬프트를 자신을 트리거하는 동일한 PR 내에서 수정할 수 없습니다. 이는 매우 중요한데, CI 로그는 공격자의 영향을 받는 입력값(테스트 이름, 에러 문자열, 에코된 사용자 데이터 등이 모두 로그에 포함됨)이기 때문입니다(이 주입 공격 범위는 DevOps 에이전트를 위한 프롬프트 인젝션(prompt injection for DevOps agents)에서 다룹니다).
흐름은 4단계로 구성되며, 마지막 두 단계에만 모델이 관여합니다:
- GitHub API를 통해 실패한 작업(jobs)을 가져오고, 실패한 단계(step)의 로그 끝부분(tail)을 추출합니다.
- 이미 알고 있는 실패 유형에 대해 결정론적 패턴 규칙(deterministic pattern rules)을 실행합니다.
- 규칙이 일치하지 않으면, 분류 도구 스키마(classification tool schema)를 강제하여 LLM(대규모 언어 모델)에게 질문합니다.
- PR에 판정 결과를 코멘트로 남깁니다. 안전하게 재시도 가능한 경우에만 자동 재시도를 수행합니다.
1단계: 중요한 로그 가져오기
실패한 실행은 메가바이트 단위의 로그를 생성하지만, 대부분은 통과한 단계에서 발생한 것입니다. GitHub API를 사용하면 실패한 작업으로 범위를 좁힐 수 있으며, 각 작업의 로그 엔드포인트는 토큰을 소비하기 전에 다듬을 수 있는 일반 텍스트를 반환합니다:
# scripts/ci_triage.py
import os, re, httpx
...
실패한 작업의 중복을 제거한 마지막 300줄을 가져오는 것은 의도적으로 투박한 휴리스틱(heuristic)이지만 효과적입니다. 실패한 단계가 마지막에 실행되며, 스택 트레이스(stack traces), 어설션 차이(assertion diffs), 종료 코드(exit codes)가 로그의 끝부분에 존재하기 때문입니다. 또한 이는 분류당 토큰 소비량을 예측 가능한 수치로 제한해 줍니다. 이 에이전트는 빌드가 실패할 때마다 실행되므로, DevOps 에이전트 토큰 경제학(DevOps agent token economics)에서 언급된 예산 관리 원칙이 여기서도 적용됩니다.
2단계: 지루한 실패를 위한 결정론적 규칙
신뢰할 수 있는 모든 Ops 에이전트와 동일한 계층 구조를 따릅니다. 정규 표현식 (Regex)으로 분류할 수 있는 실패에 모델 호출 비용을 절대 낭비하지 마십시오. 이러한 패턴들은 GitHub 호스팅 러너 (GitHub-hosted runners)에서 반복적으로 발생하는 인프라 불안정성 (Infrastructure flakes)을 다루며, LLM을 완전히 건너뛰고(short-circuit) 처리합니다.
RULES = [
("flaky_infra", "runner-oom",
r"Process completed with exit code 137|Killed\b"),
...
종료 코드 (Exit code) 137에 대해서는 부연 설명이 필요합니다. 러너에서 이 코드는 거의 항상 작업이 표준 호스팅 러너의 7GB 메모리 제한에 도달했음을 의미합니다. 이는 Kubernetes 클러스터에서 추적하던 OOM (Out of Memory) 시그니처와 동일하며, 단지 CI의 모습을 하고 있을 뿐입니다. 재시도 시 통과할 수도 있다는 점에서 "flaky"하다고 할 수 있지만, 동일한 규칙이 하나의 작업에서 반복적으로 발생한다면, 코멘트에는 "재시도됨"이 아니라 "러너 크기를 키우거나 빌드를 축소하십시오"라고 명시해야 합니다.
3단계: 스키마를 통해 강제되는 LLM 트리아지 (Triage)
규칙이 포착하지 못하는 모든 것—단언 실패 (Assertion failures), 테스트 스위트 내부의 타임아웃 (Timeouts), 의존성 해결 (Dependency resolution)의 이상 현상 등—은 모델로 전달됩니다. 출력은 도구 스키마 (Tool schema)를 통해 강제되므로, 파이프라인은 산문(Prose)이 아닌 검증된 JSON만을 처리합니다.
import anthropic, json
TRIAGE_TOOL = {
...
"확실하지 않을 때는 실제 호출을 수행하라"는 비대칭적 지침 (Asymmetric instruction)은 프롬프트에서 가장 중요한 문구입니다. 회귀 (Regressions)를 낙관적으로 "flaky"라고 라벨링하는 트리아지 에이전트는 팀이 실패한 빌드를 무시하도록 학습시키며, 이는 에이전트가 없는 것보다 훨씬 더 나쁩니다. 도구 호출이 누락되었을 때의 폴백 (Fallback) 처리 또한 동일하게 안전한 방향으로 실패하도록 설계되어야 합니다.
4단계: 판결을 코멘트로 남기고, 엄격한 제한 하에 재시도
판결은 PR 코멘트로 전달되며 ( workflow_run 페이로드가 동일 리포지토리 브랜치에 대한 PR 번호를 전달합니다), 재시도 결정은 모델이 덮어쓸 수 없는 세 가지 조건을 가진 명확한 코드로 구현됩니다.
def maybe_retry(verdict: dict) -> bool:
return (verdict["classification"] == "flaky_infra"
and verdict["confidence"] >= 0.8
...
RUN_ATTEMPT 체크는 루프를 끊는 역할을 합니다. 2번째 시도(attempt 2)가 실패했다는 것은 그것이 일시적인 오류(flaky)가 아니었음을 의미하며, 에이전트는 실행(run)이 재시도 폭풍(retry storm)으로 이어지도록 핑퐁(ping-pong)해서는 안 됩니다. 일시적인 오류가 발생하는 _테스트(tests)_는 의도적으로 자동 재시도 대상에서 제외됩니다. 재시도된 일시적 오류 테스트가 통과되어 버리면 해당 오류(flake)를 그냥 묻어버리게 되기 때문입니다. 대신, 댓글에 해당 테스트 이름을 명시하여 누군가가 격리(quarantine)하거나 수정할 수 있도록 합니다.
트리아지(triage) 워크플로우 자체는 다음과 같습니다:
# .github/workflows/ci-triage.yml
name: ci-triage
on:
...
만약 메인 파이프라인이 아직 깨끗하게 분리되어 실패하는 작업(jobs) 구조로 설계되어 있지 않다면, 그것을 먼저 수정할 가치가 있습니다. GitHub Actions 프로덕션 설정 가이드에서는 작업별 트리아지(triage)를 모호하지 않고 날카롭게 만들어주는 작업 분할(job-splitting) 패턴을 다룹니다.
점수를 기록하세요, 그렇지 않으면 그저 추측하는 것뿐입니다
모든 판정(verdict)은 쿼리가 가능한 어딘가에 기록되어야 합니다. 실행(run)당 하나의 JSON 아티팩트(artifact)를 생성하거나, 분류 및 규칙별로 라벨링된 카운터를 Prometheus에 푸시하는 방식입니다. 이를 통해 두 가지 결과가 도출됩니다. 첫째, 실제 일시적 오류율(flake-rate) 수치입니다. 일시적 오류로 분류된 실패 실행의 비율은 파이프라인의 노이즈 플로어(noise floor)이며, 이를 낮추는 것은 측정 가능한 작업입니다. 둘째, 정직한 DORA 지표(DORA metrics)입니다. 자동 재시도를 통해 성공(green)으로 돌아온 인프라의 일시적 오류는 변경 실패율(change-failure rate)에 포함되지 않는 것이 타당하며, 이제는 대충 넘기는 대신 해당 항목을 제외할 수 있는 라벨을 갖게 되었습니다.
에이전트를 신뢰하기 전에, 파이프라인에 권한을 가진 일반적인 운영(ops) 에이전트를 평가하는 것과 동일한 방식으로 에이전트를 평가하십시오. 이미 진단한 20개의 실제 실패 실행 로그(이미 알고 있는 일시적 오류 테스트, 레지스트리 429 오류, 실제 회귀(regression), 누락된 비밀값(secret) 등)를 수집하여 분류 결과가 일치하는지 확인하십시오. 프롬프트(prompt)를 수정할 때마다 다시 측정하십시오. DevOps AI 에이전트를 위한 평가(evals for DevOps AI agents)에 나오는 방법론과 일대일로 매칭됩니다. 여러분이 라벨링한 실패 사례들이 바로 테스트 세트(fixture set)가 됩니다.
정직한 한계
이 에이전트는 진단(triages)을 수행할 뿐, 디버깅(debug)은 하지 않습니다. 인과관계를 증명하기에는 여러분의 diff 의미론(diff semantics)을 깊이 있게 볼 수 없기 때문에, '실제 실패(real_failure)'라는 것은 '이 변경 사항에 의해 그럴듯하게 발생했을 가능성'을 의미할 뿐, 확정적인 판결은 아닙니다. 이 에이전트는 때때로 새로운 인프라 실패를 실제 문제로 오분류(annoying, safe)하고 — 더 드물지만 더 심각한 경우 — 비결정적 회귀(nondeterministic regression)를 일시적 오류(flaky)로 분류합니다. 이것이 바로 비대칭 프롬프트(asymmetric prompt), 단일 재시도 제한(single-retry cap), 그리고 flaky 테스트에 대한 자동 재시도 금지 규칙이 존재하는 이유입니다. 재시도를 비활성화한 상태에서 주석 전용 모드(comment-only mode)로 2주 동안 실행해 보고, 그 결과 라벨을 팀이 실제로 내린 결론과 비교하세요. 그런 다음만 재실행 버튼을 누르게 하십시오. 이처럼 보수적인 신뢰 수준에서도, 이는 '재실행하고 기도하기'를 분당 몇 분 안에 증거가 있는 진단으로 바꿔주는데 — 이것이 빨간 빌드(red build)가 단순한 중단인지 아니면 추적 가능한 이슈 티켓인지를 결정하는 차이입니다.
📌 DevOps, SRE, Kubernetes, 관측 가능성(observability), 클라우드 비용 가이드 전체 라이브러리와 함께 이 가이드의 최신 버전을 devtocash.com에서 확인하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기