장애 자동 대응의 안전장치 (Guarded Alert Remediation)
"경보를 받아 컨테이너 재시작 같은 조치를 자동으로 실행하면, 잘못된 명령이나 위조된 요청, 연쇄 실행은 무엇으로 막는가?" 이 페이지는 그 질문에 답한다. Guarded alert remediation은 Alertmanager의 webhook receiver 뒤에서 조치를 실행하는 시스템이 요청 인증, 반복 방지, 허용 목록, 시험 실행, 사람 승인, 도구 권한 분리 같은 장치를 여러 겹으로 두는 패턴이다. Source는 규칙 기반 webhook 앱과 AI 장애 관제 Agent 두 가지이며, 두 구현은 판단 주체가 다르지만 "실행 전에 무엇을 통과해야 하는가"라는 같은 문제를 다룬다. 이 패턴은 Agent Safety Boundary의 Authorization 경계에 해당하는 운영 사례로 볼 수 있다.
두 가지 구현 형태
| 구분 | 규칙 기반 webhook 앱 | AI 장애 관제 Agent |
|---|---|---|
| 판단 주체 | 코드가 alertname과 severity를 매칭한다[1] |
Agent가 metric·로그·과거 사례를 조회해 분석하고 조치를 제안한다[2] |
| 실행 시점 | 조건이 맞으면 바로 실행한다. dryRun이 켜져 있으면 로그만 남긴다[1:1] |
승인 기반 조치는 Discord 확인 뒤에 실행하고, playbook의 AUTO 조치는 코드가 즉시 실행한다[3] |
| 실행 예시 | docker:restart leafy_api[1:2] |
docker restart <container-name>[4] |
- 규칙 기반 앱은 경보 조건과 조치를 코드에 미리 묶어 두고, AI 장애 관제 시스템은 Agent 분석과 playbook의 실행 정책을 결합한다. 사람 승인은 승인 기반 경로에 적용되며 AUTO 경로에는 없다.[3:1] 이 차이는 AI Workflow vs Agent의 control boundary 관점에서 경로별로 읽어야 한다.
흐름
규칙 기반 webhook 앱은 경보 하나가 다음 순서로 검사를 통과해야 실행된다.[1:3]
flowchart TB
A[Alertmanager] -->|POST /alert| T{토큰 검증}
T -->|불일치| X1[401 invalid token]
T -->|일치| C{쿨다운 확인}
C -->|쿨다운 중| X2[cooldown 응답, 실행 안 함]
C -->|통과| M{경보 매칭과 허용 목록}
M -->|불일치| X3[ok 응답, 실행 안 함]
M -->|일치| D{dryRun}
D -->|true| L[audit 로그만 기록]
D -->|false| R[docker / ssh / script 실행]AI 장애 관제 시스템의 로컬 코드에는 분석 뒤 playbook 조치와 대화 중 모델 제안을 처리하는 세 경로가 있다.[3:2]
flowchart TB
A[Alertmanager webhook] --> PB[알람 검증과 playbook 조회]
PB --> RA[ReAct 또는 Orchestrator 분석: 조회 도구]
RA --> C{playbook category}
C -->|AUTO| F[고정 playbook 명령]
C -->|NEEDS_APPROVAL| T[playbook 템플릿과 알람 label]
C -->|READ_ONLY 또는 NONE| D[분석 결과만 알림]
T --> P[PendingApprovalStore: 명령과 채널, TTL]
U[Discord 사용자 대화] --> CA[ConversationAgent: 로그 조회와 실행 제안]
CA --> V{isAllowed: 형식 검사}
V -->|통과| P
V -->|거부| X[거부]
P --> Y{listener 조건 통과와 yes 또는 approve}
Y -->|확인| E{실행 서비스의 공통 형식 검사}
Y -->|취소 또는 만료| N[실행 안 함]
F --> E
E -->|통과| R[docker restart 실행과 결과 전송]
E -->|거부| X안전장치별로 막는 위험
| 안전장치 | 막는 위험 | 규칙 기반 webhook 앱 | AI 장애 관제 Agent |
|---|---|---|---|
| 요청 인증 | 위조된 경보 요청 | X-Alert-Token 공유 비밀, 불일치 시 401[1:4] |
source에 언급 없음 |
| 반복 방지 | 같은 조치의 연쇄 실행 | cooldownSeconds: 120[1:5] |
source에 언급 없음 |
| 허용 목록 | 예상하지 못한 명령, prompt injection으로 생성된 명령 | allowedActions 화이트리스트[1:6] |
isAllowed()가 docker restart <container-name> 형식만 통과시킨다[4:1] |
| 경보별 허가 조치 | 경보와 무관한 조치 | 코드에서 ApiDown·critical일 때만 재시작[1:7] |
알람 경로는 AlertPlaybook으로 조치를 정하지만, 대화의 execute_command는 playbook을 검사하지 않는다[3:3] |
| 시험 실행 | 정책 검증 전의 실제 변경 | dryRun: true가 기본이며 실행 대신 로그만 남긴다[1:8] |
source에 언급 없음 |
| 사람 승인 | 분석 오판에 따른 실행 | 없음 | NEEDS_APPROVAL과 대화 제안은 대기시킨 뒤 확인받고, AUTO는 승인 없이 실행한다[3:4] |
| 도구 권한 분리 | 분석 단계의 과잉 판단에 따른 실행 | 해당 없음 | ReActAgent는 조회 도구만 갖고, 실행 제안 도구는 ConversationAgent 전용이다[4:2] |
| 승인 경로 분리 | 대화 Agent 출력이 스스로 승인됨 | 해당 없음 | listener가 대화와 yes/no·approve를 분리한다. 채널·role 검사는 설정값이 있을 때 적용한다[3:5] |
| 실행 환경 제한 | docker.sock을 통한 컨테이너 탈출 |
Docker socket proxy 같은 제한 래퍼를 권장한다[1:9] | source에 언급 없음 |
설계 포인트
- 대화의 실행 제안에서는 형식 검사와 사람 승인이 두 겹의 경계다. 형식에 맞지 않는 명령은 거부되지만,
docker restart postgres처럼 형식에 맞는 잘못된 대상은 검사만으로 차단되지 않는다. 이 경로는 playbook이나 별도 restart 대상 목록을 검사하지 않는다.[3:6] - Agent의 실행 도구
execute_command는 실제로 명령을 실행하지 않고 "제안"만 한다. 실행은 운영자 확인 뒤 별도 경로에서 일어난다.[4:3] - 대기 기록은 명령과 채널로 색인하고 기본 TTL은 30분이며,
yes는 해당 채널의 대기 명령을 실행한다.approve <command>는 명령으로 대기를 찾는다. listener의 채널·role 필터는 값이 설정된 경우에만 동작하고, 대기 기록에 요청자나 승인자 user ID는 없다. 따라서 채널 구분을 개인별 승인 권한 검증으로 보아서는 안 된다. 실제 운영 설정은 이번에 확인하지 않았다.[3:7] - AUTO에서는 현재
GPUDcgmExporterDown에 등록된docker restart dcgm-exporter가 승인 없이 실행된다. 분석 결론이 아니라 playbook의 명령을 사용하므로, 읽기 전용 분석 Agent가 실행 도구를 갖지 않는 것과 시스템에 자동 실행 정책이 있는 것은 양립한다.[3:8] - 실행 도구 객체는 생성 시점에 채널과 알림 정보를 고정한다. 그래서 Agent가 호출 때 채널 ID를 잘못 넘길 여지가 없다.[4:4]
- 규칙 기반 앱은
dryRun을 기본값으로 켜 두고, 실제 실행은 명시적으로 끄는 방식이다.[1:10] 새 정책을 운영에 넣기 전에 로그로 매칭 결과부터 확인하는 단계로 쓸 수 있다고 보인다.
한계와 주의
- 허용 명령을 늘릴수록
isAllowed()검증 로직이 복잡해지고 실수 가능성도 커진다. 새 명령 유형을 추가하려면 정규식과 파싱 로직을 함께 고쳐야 한다.[4:5] - 규칙 기반 앱 예시 코드는 마지막 실행 시각을
AtomicLong하나로 관리한다.[1:11] 예시 코드를 그대로 쓰면 쿨다운이 경보·조치 종류와 관계없이 전체에 하나만 걸린다고 읽힌다. - 예시의 토큰 검증은 원문 스스로 "단순 토큰 검증"이라고 표시한다.[1:12]
docker.sock을 직접 마운트하면 컨테이너 탈출 위험이 있다.[1:13]- 문서와 코드의 범위 차이: 개요·도구 분리 글의 승인 기반 설명[4:6][2:1]과 파이프라인 문서의 AUTO 분기[5]는 로컬 코드
a1ca1b7에서 서로 다른 경로로 구현되어 있다. 이 페이지는 그 로컬 버전의 정적 검사이며, 실제 서버의 배포 버전·설정이나 인젝션 성공을 검증한 기록은 아니다.[3:9]
관련
- Alertmanager: 경보를 묶고 라우팅해 webhook receiver로 보내는 앞단이다. 이 패턴은 그 receiver 뒤에서 무엇을 실행할지를 다룬다.
- Agent Safety Boundary: 실행 전 권한 확인이 필요하다는 Authorization 경계의 운영 사례다.
- Typed Decision Model: 모델의 판단과 실행 권한을 분리한다는 같은 원칙을 판단 계층 쪽에서 다룬다.
- Agent Tool Use: 조회 도구와 실행 도구를 Agent별로 나누는 설계가 tool 권한 범위의 구체 예다.
테스트 질문
- Agent가 제안한
docker rm -f postgres를 운영자가yes로 승인하면 실행되는가? - 허용 목록이 있는데 사람 승인을 또 두는 이유는 무엇인가?
- 규칙 기반 자동 대응에서
dryRun과 쿨다운은 각각 어떤 위험을 막는가? - 자동 분석 Agent에 실행 도구가 없는데도
dcgm-exporter가 승인 없이 재시작될 수 있는 이유는 무엇인가? - ConversationAgent의 로그 조회 대상 검사와 재시작 대상 검사는 같은 경계인가?
출처
자동 대응 시스템.md — "Alertmanager가 보낸 경보를 수신하여 화이트리스트/쿨다운 정책으로 Docker 재시작 등의 자동 대응 액션을 실행하는 Spring Boot 앱.", 구성 흐름 "토큰 검증 → 쿨다운 확인 → 화이트리스트 매칭 → docker / ssh / script 실행 (dryRun 모드 시 로그만)",
token: "SHARED_SECRET" # 단순 토큰 검증,dryRun: true # true 시 실행 안 하고 로그만,cooldownSeconds: 120 # 같은 액션 반복 방지,allowedActions화이트리스트, Controller 코드의ResponseEntity.status(401).body("invalid token"),"cooldown"응답,"ApiDown"·"critical"매칭,"docker:restart leafy_api",private final AtomicLong lastActionTs, "Docker 소켓 직접 마운트 (/var/run/docker.sock) → 컨테이너 탈출 위험", "프록시/제한 래퍼(Docker socket proxy) 사용 권장" ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎01-프로젝트-개요.md — "Alertmanager 웹훅을 수신해 AI가 자율적으로 분석하고 Discord를 통해 결과와 조치를 제안하는 에이전트", "Alertmanager에서 웹훅이 오면 에이전트가 자동으로 Prometheus 메트릭, Loki 로그, 과거 유사 사례(RAG)를 조회하고 근본 원인 및 조치를 분석한다.", "AlertPlaybook (알람별 허가된 조치 목록 조회)", "에이전트가
docker restart <container>실행이 필요하다고 판단하면 직접 실행하지 않고 Discord로 확인 요청을 보낸다. 운영자가yes를 입력해야 실행된다. 허가된 명령 패턴(docker restart <name>)만 실행 가능하다." ↩︎ ↩︎monitoring-agent-authorization-code-2026-10-09-a1ca1b7.md — local clean commit
a1ca1b7의 발췌.AlertAnalysisChain.javaL66–99·L116–133,AlertPlaybook.javaL16–25·L88–89는 playbook과 AUTO 직접 실행,DiscordNotificationService.javaL73–92는 NEEDS_APPROVAL의 대기 저장,ConversationAgent.javaL59–80과AgentTools.javaL112–150은 대화 Agent의 조회 도구 등록과 Loki 조회 대상 검사,ConversationTools.javaL14–50은 형식 검사 뒤 대기 저장,CommandExecutorService.javaL22–32·L53–88은 공통 형식 검사와 실행,DiscordBotListener.javaL49–90·L92–126 및PendingApprovalStore.javaL14–58은 조건부 role·채널 필터와 명령·채널별 승인이다. 실제 배포·설정값은 확인하지 않았다. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎03-도구를-두-종류로-나눈-이유.md — "
AgentTools는 읽기 전용으로 유지했다.", "ConversationTools는 ConversationAgent 전용으로 만들고,execute_command하나만 넣었다.", "중요한 건execute_command가 실제로 명령을 실행하지 않는다는 점이다. 실행이 아니라 "제안"이다.", "docker restart <container-name>형식만 허용한다. 에이전트가 프롬프트 인젝션을 당하거나 예상치 못한 명령을 생성하더라도 이 검증을 통과하지 못하면 거부된다.", "PendingApprovalStore에 30분 TTL로 저장되고, Discord에서 "yes"가 들어와야 실행된다.", "같은 채널에서 "yes"를 입력한 사람만 해당 채널의 대기 명령을 실행할 수 있다.", ""yes/no" 처리 경로와 대화 처리 경로가 분리되어 있다.", "에이전트의 출력이 스스로 실행을 트리거하는 건 불가능하다.", "ConversationTools가 생성되는 시점에 컨텍스트(어느 채널, 어떤 알림)가 이미 고정된다.", "명령이 늘수록isAllowed()로직이 복잡해지고 검증 실수 가능성도 높아진다." ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎03-에이전트-파이프라인.md — "AlertPlaybook — 알람명으로 허가된 조치 조회", DiscordNotificationService 분기 "[NEEDS_APPROVAL] → 분석 결과 + 명령 실행 승인 요청", "[AUTO] → 명령 자동 실행 후 결과 전송", "[READ_ONLY/NONE] → 분석 결과만 전송" ↩︎