Member
incident-responseai-agentclaudeawslambdacognitografanadevopsautomation

장애를 스스로 분석하고, 승인받아 조치하는 파이프라인 구축기

· 39 min read · by Member

지난 글에서 여러 클러스터에 흩어져 있던 메트릭, 로그, Trace를 하나의 Grafana로 모으는 관측(Observability) 환경을 정리했습니다. 신호를 한곳에 모으고 나니 "무슨 일이 일어났는지"는 한 화면에서 볼 수 있게 되었습니다.

그런데 실제로 장애가 나면 그다음부터는 여전히 사람의 일이었습니다.

알람이 울리면 Grafana를 열고, 어느 서비스인지 확인하고, Pod 상태를 보고, 최근 배포가 있었는지 Argo CD를 뒤지고, 로그와 Trace를 대조하고, 그제서야 "아 이거네" 하고 조치를 합니다. 이 과정은 익숙해지면 빠르지만, 문제는 매번 사람이 붙어 있어야 한다는 점이었습니다. 특히 새벽이나 주말에 알람이 오면, 원인 파악의 시작점에 도달하기까지 걸리는 시간이 곧 장애 시간이 됩니다.

이 글은 그 "알람이 온 다음부터 조치까지"의 앞단을 자동화한 기록입니다. 정확히는 분석은 AI 에이전트가 스스로 하게 하고, 실제 조치는 사람이 승인 버튼을 눌러야만 실행되게 만든 파이프라인입니다.

다만 지금 만든 것은 완성된 운영 시스템이 아니라 PoC(개념 검증) 단계입니다. 우선 Grafana 알람 하나를 입구로 삼아, "알람 → 자동 분석 → 승인 → 조치 → 복구 검증"이라는 전체 고리가 실제로 끝까지 도는지를 확인하는 것이 목표였습니다. 이 접근이 동작한다는 것을 확인한 다음, 신호원과 조치 범위를 넓혀 가는 순서로 발전시킬 계획입니다. 그래서 이 글은 그 PoC의 구조와, 여기서 어떻게 확장해 나갈지를 함께 정리한 기록입니다.

무엇을 자동화하고, 무엇을 사람이 쥘 것인가

시작하기 전에 한 가지를 분명히 정했습니다. 읽는 것은 자동화하되, 쓰는 것은 사람이 쥔다.

장애 조사는 대부분 조회입니다. Pod를 describe하고, 이벤트를 보고, 로그를 읽고, 배포 이력을 확인하는 일에는 위험이 없습니다. 반면 조치는 상태를 바꿉니다. Pod를 지우고, 롤백을 하고, replica를 조정하는 일은 잘못되면 장애를 키울 수 있습니다.

그래서 파이프라인을 두 단계로 나눴습니다.

  • 분석 단계: AI 에이전트가 읽기 전용으로 원인을 조사하고, 조치안 하나를 제안한다. 아무것도 바꾸지 않는다.
  • 조치 단계: 사람이 Google Chat에 온 카드에서 승인 버튼을 누르면, 그때 승인된 그 조치 하나만 실행된다.

여기서 중요한 판단이 하나 있었습니다. "AI가 위험한 조치를 하면 어쩌지"라는 걱정은, 승인 게이트가 있으면 상당 부분 사라진다는 점입니다. 어떤 조치도 사람의 클릭 없이는 실행되지 않기 때문에, AI는 "이건 위험하니 사람에게 넘기겠다"며 발을 빼는 대신 근본 원인을 실제로 해소할 조치를 자신 있게 제안하면 됩니다. 위험 판단과 최종 결정은 승인하는 사람이 합니다.

전체 아키텍처

전체 흐름은 세 계층으로 나뉩니다. 신호가 발생하는 입구, 그것을 받아 넘기는 얇은 진입 계층, 그리고 실제 조사와 조치가 일어나는 무거운 실행 계층입니다.

[ 신호 발생 ]
  Grafana Alerting   (K8s·애플리케이션 메트릭/로그/Trace 기반 알람)
     │  알람 webhook   (기존 Dooray 알림도 그대로 병행 발송)

[ 진입부 — 얇고 공개된 계층 ]
  API Gateway   (공개 HTTPS 엔드포인트)
     │  POST /analyze   · Bearer 토큰 인증

  Lambda (Go)   — 이벤트가 있을 때만 잠깐 도는 라우팅 계층
     │  SSM SendCommand   (아웃바운드 풀)

[ 실행부 — 사설망의 무거운 계층 ]
  EC2 · incident-worker
     └─ [analyze]  claude -p   (ro 폴더 · 읽기 전용 MCP)
          · 여러 MCP를 스스로 순회하며 근본 원인 조사
          · Google Chat 카드 게시  →  원인 · 조치안 · 승인 버튼
 
           ⋯  담당자가 승인 버튼 클릭 (휴대폰 · 어디서나)  ⋯
 
  API Gateway   GET /approve
     │  Cognito(Google OIDC) · 허용목록 · HMAC 서명 · DynamoDB 1회용

  Lambda (Go)  →  SSM SendCommand  →  EC2 · incident-worker
     └─ [act]  claude -p   (rw 폴더 · 쓰기 MCP)
          · 승인된 조치 하나만 실행 + 복구 검증
          · Google Chat 결과 카드  →  완료 · 보류 · 실패

핵심은 세 가지입니다. 첫째, 분석(ro)과 조치(rw)가 물리적으로 분리되어 있습니다. 둘째, AI가 사람처럼 여러 도구(MCP)를 스스로 골라 조사합니다. 셋째, 조치로 넘어가는 문에는 승인 게이트가 있고, 그 문은 인터넷에 열려 있지만 회사 계정 인증으로 잠겨 있습니다.

하나씩, 왜 그렇게 했는지 정리합니다.

claude -p — 헤드리스로 실행하고, 구독으로 비용을 아낀다

분석과 조치를 수행하는 것은 EC2에서 도는 Claude Code입니다. 다만 사람이 터미널 앞에 앉아 대화하는 방식이 아니라, claude -p "프롬프트" 형태의 헤드리스(print) 모드로 실행합니다. 이 모드는 프롬프트를 한 번 받아 처리하고 결과를 출력한 뒤 종료합니다. 대화형 세션이 아니라 한 번의 배치 실행이라, 프로그램(worker)이 알람마다 호출하기에 적합합니다.

이 방식에는 비용 측면의 이점도 있습니다. EC2에는 Claude Code가 하나의 Claude 플랜에 로그인된 상태로 떠 있고, claude -p는 그 세션을 그대로 사용합니다. 즉 알람이 올 때마다 수행되는 반복적인 분석이 토큰 단위로 과금되는 API 호출로 쌓이지 않고, 구독 플랜 안에서 처리됩니다. 장애가 잦은 시기에 분석을 아무리 많이 돌려도 비용이 선형으로 늘지 않습니다.

또한 조사 기준이 되는 런북은 각 작업 폴더의 CLAUDE.md에 담겨 있어, 매 호출마다 거대한 지시문을 다시 실어 보낼 필요가 없습니다. MCP 도구의 정의도 필요할 때 로드되므로, 한 번의 실행이 무겁지 않게 유지됩니다.

왜 훅이 아니라 두 번의 호출인가

처음에는 Claude Code의 훅(hook) 으로 이 문제를 풀 수 있을지 검토했습니다. 훅은 도구 호출 전후에 지정한 스크립트를 끼워 넣는 기능이라, 예를 들어 "쓰기 도구를 호출하려 하면 일단 막고 승인을 받는다" 같은 흐름을 한 세션 안에서 만들 수 있습니다.

하지만 우리 승인은 성격이 달랐습니다.

  • 승인이 비동기이고 채널이 다르다. 승인은 같은 프로세스 안에서 즉시 일어나지 않습니다. 담당자가 Google Chat 카드를 보고, 때로는 몇 분 뒤 휴대폰으로 버튼을 누릅니다. 하나의 claude 세션이 그 시간 동안 멈춰 서서 외부의 사람 클릭을 기다리게 하는 것은 부자연스럽고 취약합니다.
  • 분석과 조치는 애초에 다른 실행 환경이어야 한다. 분석은 읽기 전용 환경에서, 조치는 쓰기 가능한 환경에서 돌아야 합니다. 이걸 훅으로 한 세션 안에서 전환하기보다, 처음부터 두 개의 독립된 헤드리스 실행으로 나누는 편이 경계가 훨씬 분명합니다.

그래서 훅으로 한 세션을 중간에 붙잡는 대신, 분석은 끝나면 종료하고, 조치는 승인 이벤트가 새로 트리거하는 별개의 실행으로 설계했습니다. 분석 결과는 카드로 남고, 승인이 들어오면 그 카드에 실린 조치 하나만 새 실행이 수행합니다. 두 실행 사이의 유일한 연결 고리는 뒤에서 설명할 서명된 승인 링크입니다.

왜 분석과 조치를 물리적으로 분리했나

가장 먼저 고민한 것은 "읽기 전용을 어떻게 보장하느냐"였습니다.

AI 에이전트에게 프롬프트로 "절대 아무것도 바꾸지 마"라고 적을 수는 있습니다. 하지만 프롬프트는 지시일 뿐 강제가 아닙니다. 정말로 쓰기를 막으려면, 지시가 아니라 환경 자체가 쓰기를 할 수 없어야 합니다.

그래서 에이전트를 실행하는 작업 디렉토리를 두 개로 나눴습니다.

test/
├── ro/   — 분석 단계 전용
│   ├── .mcp.json   읽기 전용 MCP만 연결
│   └── CLAUDE.md   조사 런북 (읽기 전용)
└── rw/   — 승인된 조치 실행 전용
    ├── .mcp.json   쓰기 가능한 MCP 연결
    └── CLAUDE.md   조치 실행 런북

ro 폴더에서 에이전트를 실행하면 연결되는 도구가 전부 읽기 전용입니다. 클러스터 조회 도구는 조회 기능만, Argo CD 도구는 read-only 모드, GitLab 도구도 읽기 전용 모드로 뜹니다. 게다가 이 폴더에서는 셸이나 파일 편집 도구 자체가 차단되어 있습니다. 즉 분석 에이전트는 바꾸고 싶어도 바꿀 수단이 없습니다.

쓰기는 오직 rw 폴더에서만 가능합니다. 그리고 rw 폴더의 에이전트는 승인 단계, 즉 사람이 버튼을 누른 다음에만 호출됩니다.

이렇게 폴더와 권한으로 나눠 두면, "AI가 실수로 프로덕션을 건드리는" 시나리오가 프롬프트 한 줄이 아니라 구조로 막힙니다. 분석은 아무리 많이 돌려도 안전하고, 위험한 동작은 승인이라는 좁은 문 하나로만 지나갑니다.

왜 MCP를 붙였나 — 에이전트가 SRE처럼 조사하게

분석 에이전트가 원인을 찾으려면, 사람이 조사할 때 쓰는 도구들이 필요합니다. 여기서 MCP(Model Context Protocol)를 사용했습니다. MCP는 에이전트에게 외부 시스템을 조회할 수 있는 도구를 붙여 주는 표준입니다.

분석 폴더에는 읽기 전용으로 다음 도구들을 연결했습니다.

  • Kubernetes/EKS — Pod 상태, 이벤트, 스케줄링 실패 사유 조회
  • Grafana — 대시보드와 메트릭 조회
  • Argo CD (dev/live) — 배포 동기화 상태, 리비전 이력
  • GitLab — 파이프라인 결과, 최근 커밋
  • CloudWatch — AWS 리소스 지표
  • AWS API — 관리형 리소스 상태

여기서 자주 오해하는 부분이 있습니다. "어떤 알람에는 어떤 도구를 써라"를 런북에 일일이 적어야 하는 것 아니냐는 것입니다. 그렇지 않습니다. 도구를 쥐어 주면, 에이전트가 상황을 보고 스스로 필요한 도구를 골라 순회합니다. 이것이 에이전트 방식의 핵심입니다.

실제로 배포 직후 깨진 알람에는 에이전트가 알아서 Argo CD의 리비전을 확인하고, Pod가 뜨지 않는 알람에는 이벤트와 스케줄링 사유를 조회하고, DB 관련 질문에는 CloudWatch를 호출했습니다. 어느 것도 "이 알람엔 이 도구"라고 지정하지 않았습니다.

그래서 런북(뒤에서 다시 설명합니다)은 "증상 X면 도구 Y를 써라"는 정답표가 아니라, 어떻게 깊게 파고들 것인가라는 조사 원칙을 담는 방향으로 썼습니다.

알람은 어떻게 파이프라인에 들어오나

입구는 Grafana Alerting입니다. 기존에도 장애 알람은 Dooray(사내 메신저)로 보내고 있었는데, 이 흐름을 끊지 않으면서 AI 분석 파이프라인을 병행으로 붙였습니다.

Grafana의 notification policy에서 라우팅을 이렇게 구성했습니다.

severity =~ critical|warning  →
   ├─ receiver: incident-claude   → AI 분석 파이프라인  (continue: true 로 계속)
   └─ receiver: dooray            → 기존 Dooray 알림 유지

continue: true가 핵심입니다. 첫 번째 규칙(AI 파이프라인)에 매칭되어도 멈추지 않고 다음 규칙(Dooray)까지 계속 매칭되게 해서, 기존 Dooray 알림은 그대로 두고 AI 분석만 추가로 얹었습니다. 새 시스템을 붙일 때 기존 운영 흐름을 건드리지 않는 것은 중요합니다. AI 파이프라인이 잘못되어도 사람은 여전히 Dooray로 알람을 받습니다.

incident-claude receiver는 webhook이고, 그 대상이 API Gateway의 /analyze 엔드포인트입니다. 이 webhook에는 Bearer 토큰 인증을 걸어서, 허가되지 않은 요청이 이 엔드포인트를 두드려 분석 파이프라인을 발동시키지 못하게 했습니다.

왜 API Gateway · Lambda · Cognito인가

진입 계층을 관리형 서버리스로 구성한 데에는 각각 이유가 있습니다.

API Gateway — 이 파이프라인에는 공개된 HTTPS 진입점이 필요합니다. Grafana webhook이 알람을 보낼 곳도, 담당자가 휴대폰으로 누르는 승인 링크도 인터넷에서 접근 가능해야 합니다. API Gateway는 TLS 종료, 경로별 라우팅(/analyze, /approve), 뒤단 Lambda 연결을 관리형으로 제공합니다. 직접 웹서버를 세우고 인증서를 관리할 이유가 없습니다.

Lambda — 진입부가 하는 일은 이벤트가 들어왔을 때 아주 잠깐 도는 글루 코드입니다. 요청을 받고, 인증을 확인하고, SSM으로 명령을 넘기는 것이 전부입니다. 이런 일에 서버를 상시 켜 두는 것은 낭비입니다. Lambda는 이벤트가 있을 때만 실행되고, 유휴 시에는 0으로 축소되며, 운영할 인스턴스가 없습니다. 알람이나 승인이라는 드문 이벤트에 반응하는 계층으로는 이보다 잘 맞는 선택이 없었습니다. 구현은 Go 단일 바이너리로 해서 콜드 스타트도 짧습니다.

Cognito — 승인자의 신원 인증을 직접 구현하고 싶지 않았습니다. 로그인, OAuth 코드 교환, 토큰 발급 같은 일은 잘못 만들면 그 자체가 취약점이 됩니다. Cognito는 Google OIDC 페더레이션을 관리형으로 제공하므로, 이미 회사가 쓰는 Google 계정 체계에 인증을 그대로 얹을 수 있었습니다. 우리는 "누가 승인할 수 있는가"라는 정책만 정하고, 신원 확인의 무거운 부분은 Cognito에 맡겼습니다.

왜 실행은 EC2에서 하나 — 얇은 진입부와 무거운 실행부의 분리

진입부를 Lambda로 두면서도, 실제 조사와 조치는 EC2에서 돌게 했습니다. 이 분리가 이 아키텍처의 뼈대입니다.

이유는 두 계층의 성격이 정반대이기 때문입니다.

  • 진입부(Lambda) 는 공개되어 있고, 상태가 없고, 아주 짧게 돕니다.
  • 실행부(EC2) 는 사설망 안에 있고, 상태가 있고, 상대적으로 오래 돕니다. Claude Code 프로세스와 여러 MCP 서버 프로세스를 함께 띄우고, 하나의 Claude 플랜에 로그인된 세션을 유지하고, ro/rw 폴더와 런북 같은 파일시스템을 쓰고, 한 번의 분석이 1~2분에 이르기도 합니다. 이런 무겁고 상태 있는 작업은 수명이 짧고 상태가 없는 Lambda와 맞지 않습니다.

그래서 무거운 실행은 이미 클러스터와 AWS 접근 권한을 가진 상시 EC2에 두고, Lambda는 그 앞에서 요청을 받아 넘기는 얇은 계층으로만 남겼습니다.

이 분리는 보안 경계이기도 합니다. 인터넷에 노출되는 것은 얇은 API Gateway와 Lambda뿐입니다. 클러스터와 AWS 리소스를 실제로 만질 수 있는 강력한 실행 환경(EC2)은 인터넷에서 들어오는 접속 경로를 두지 않습니다. 대신 필요한 명령을 스스로 밖으로 나가 가져옵니다. 이 방식이 다음 절의 주제입니다.

SSM — 사설망에 접속 경로를 열지 않고 명령을 전달하는 법

여기서 현실적인 문제가 하나 있었습니다. 실행부 EC2는 사설망 안쪽에 있습니다. Lambda가 이 EC2에 직접 명령을 넣으려면 밖에서 안으로 들어오는 접속 경로가 필요한데, 사설 리소스에 외부에서 도달하는 통로를 여는 것은 피하고 싶었습니다.

그래서 SSM(AWS Systems Manager) 을 사용했습니다. SSM은 방향이 반대입니다. Lambda가 EC2로 들어가는 것이 아니라, EC2에 떠 있는 SSM 에이전트가 밖으로 나가서 명령을 가져옵니다(아웃바운드 풀). Lambda는 "이 인스턴스에서 이 명령을 실행해줘"라고 SSM에 등록만 하면, EC2가 알아서 그 명령을 당겨와 실행합니다.

Lambda
   │  ① "이 인스턴스에서 이 명령을 실행" 을 SSM에 등록

SSM  (명령 큐)

   │  ② EC2의 SSM 에이전트가 밖으로 나가 명령을 당겨옴 (아웃바운드)

EC2 · incident-worker   →   ③ 당겨온 명령 실행

이 방식의 장점은 사설 리소스에 외부에서 들어오는 접속 경로를 두지 않아도 된다는 것입니다. 실행부는 오직 밖으로 나가는 연결만 사용하고, 인바운드는 전혀 허용하지 않습니다.

EC2에서 실제로 도는 것은 incident-worker라는 단일 Go 바이너리입니다. analyze 서브커맨드로 호출되면 분석 에이전트를, act 서브커맨드로 호출되면 조치 에이전트를 실행합니다. 분석이 끝나면 Google Chat에 원인·조치안·승인 버튼이 담긴 카드를 올립니다.

승인 게이트를 인터넷에 여는 문제

가장 신경 쓴 부분이 승인 흐름입니다.

승인 버튼은 어디서나 눌릴 수 있어야 합니다. 새벽에 알람이 오면 담당자가 휴대폰으로 카드를 보고 바로 승인할 수 있어야 하니까요. 그래서 승인 엔드포인트는 인터넷에 열려 있어야 합니다. 다만 인터넷에 열린 URL을 허가되지 않은 사람이 눌러도 조치가 실행되어서는 안 됩니다.

그래서 승인 링크 하나에 네 겹의 검증을 걸었습니다.

승인 클릭


 [1] Cognito + Google OIDC 로그인
      회사 Google 계정으로 로그인해야 통과


 [2] 승인자 허용목록(allowlist) 확인
      로그인 이메일이 승인 권한 목록에 있는가


 [3] HMAC 서명 검증
      (incident + cluster + action)을 서명한 값과 일치하는가
      → URL의 조치 내용을 위조하면 서명이 깨져서 거부


 [4] DynamoDB 1회용 처리
      같은 incident가 이미 조치됐으면 거부 (중복 실행 방지)


승인된 조치를 rw 에이전트로 실행

[1] Cognito + Google OIDC — 승인 링크를 누르면 먼저 Cognito를 거쳐 Google 로그인으로 리다이렉트됩니다. 회사 Google 계정으로 로그인해야만 다음으로 넘어갑니다. 신원 인증을 직접 구현하지 않고, 이미 회사가 쓰는 Google 계정 체계에 얹은 것입니다.

[2] 허용목록 — 로그인만 했다고 끝이 아닙니다. 로그인한 이메일이 승인 권한이 있는 사람 목록에 있어야 합니다. 회사 계정이어도 아무나 조치를 승인할 수는 없습니다.

[3] HMAC 서명 — 카드의 승인 링크에는 어떤 조치를(action) 어느 클러스터에(cluster) 어떤 장애에 대해(incident) 실행할지가 들어 있습니다. 이 값들을 비밀 키로 서명해 둡니다. 만약 누군가 URL의 조치 내용을 몰래 바꾸면 서명이 맞지 않아 거부됩니다. 즉 승인할 수 있는 것은 AI가 카드에 제안한 바로 그 조치뿐이고, 임의로 다른 조치를 끼워 넣을 수 없습니다.

[4] DynamoDB 1회용 — 마지막으로, 같은 장애에 대한 조치가 이미 실행되었는지 확인합니다. 조건부 쓰기로 "이 incident는 처음 처리되는 것"임을 원자적으로 보장해서, 버튼을 두 번 눌러도 조치는 한 번만 실행됩니다.

이 네 겹을 모두 통과해야 비로소 조치 에이전트가 호출됩니다. 인터넷에 열려 있지만, 열려 있는 것은 "회사의 허가된 사람이, 위조되지 않은, 아직 처리되지 않은 조치를, 한 번" 실행할 수 있는 문입니다.

조치 실행과 검증

승인이 통과하면 rw 폴더의 조치 에이전트가 호출됩니다. 이 에이전트에게는 딱 한 가지만 시킵니다. 승인된 조치 하나를 정확히 실행하고, 서비스가 실제로 복구됐는지 확인하라.

여기서 중요한 원칙이 두 가지입니다.

첫째, 조치 에이전트는 다시 판단하지 않습니다. 원인 분석과 조치 선택은 이미 앞 단계에서 끝났고 사람이 승인했습니다. 조치 에이전트가 "내가 다시 보니 다른 것도 해야겠는데" 하고 범위를 넓히면 승인의 의미가 사라집니다. 그래서 조치 런북은 분석 런북과 완전히 다르게, 짧고 엄격하게 썼습니다. 승인된 그 조치만, 무관한 리소스는 절대 건드리지 말고.

둘째, 명령이 성공했다고 복구된 것이 아닙니다. 롤백 명령이 성공 응답을 반환해도, 실제로 Pod가 뜨고 트래픽을 받는지는 별개입니다. 그래서 조치 후에는 다시 상태를 조회해서 복구를 검증하고, 한 번의 정상값이 아니라 안정적으로 유지되는지 확인한 다음에야 "완료"로 보고합니다. 결과는 완료/보류/실패로 구분해 Google Chat 카드로 다시 올라옵니다.

런북(CLAUDE.md)이 사실상 코드다

이 시스템에서 에이전트의 행동을 결정하는 것은 코드가 아니라 런북입니다. 분석 폴더와 조치 폴더에 각각 CLAUDE.md라는 런북이 있고, 에이전트는 이 문서를 판단 기준으로 삼습니다.

처음에는 런북을 "증상별 대응표"로 쓰려고 했습니다. Pending이면 이걸 보고, CrashLoop이면 저걸 보고… 하지만 이렇게 쓰면 에이전트가 딱 그 항목만 보고 멈춥니다. 표에 없는 장애 유형(파이프라인 실패, 인프라 이상, 노드 문제)은 다루지 못하고, 표에 있는 것도 "왜"까지 파고들지 않습니다.

그래서 방향을 바꿨습니다. 런북에는 정답표 대신 조사 원칙을 담았습니다.

  • 알람은 증상일 뿐 원인이 아니다. "왜"의 사슬을 최초 원인까지 추적하라.
  • 가설을 세우고 반증하라. "배포 때문인 것 같다"는 시간적 상관관계만으로 확정하지 마라.
  • 하나의 지표로 단정하지 말고 메트릭·로그·트레이스·배포 이력을 교차 검증하라.
  • 상관관계와 인과관계를 구분하라.
  • 조회에 실패한 것을 "이상 없음"으로 해석하지 마라.
  • 명령 성공을 복구로 간주하지 마라.

이렇게 원칙 중심으로 쓰면, 어떤 도구를 어떤 순서로 쓸지는 에이전트가 상황에 맞게 정하고, 런북은 판단의 품질만 붙잡아 줍니다. 그리고 실제로 해결한 사례를 "증상 → 조사 과정 → 배제한 가설 → 근본 원인 → 조치 → 검증 → 결과" 형식으로 런북 하단에 케이스북으로 쌓아, 비슷한 장애가 왔을 때 참고하되 정답으로 쓰지는 않도록 했습니다.

런북을 이렇게 다루다 보니, 이 문서를 고치는 일이 사실상 시스템의 행동을 프로그래밍하는 일과 같아졌습니다. 프롬프트가 코드인 셈입니다.

앞으로 — PoC에서 어떻게 넓혀 갈까

이번 PoC의 목표는 Grafana 알람 하나를 입구로, 전체 고리가 실제로 도는지 확인하는 것이었습니다. 그 검증이 끝났으니, 여기서부터는 입구를 넓혀 가며 실제 운영에 쓸 수 있는 형태로 발전시키는 단계입니다. 대략 이런 순서를 생각하고 있습니다.

  • 1단계 (현재 · PoC) — Grafana 알람 → 분석 → 승인 → 조치 → 복구 검증 고리 검증
  • 2단계 — CloudWatch Alarm → SNS로 AWS 데이터·인프라 계층(RDS 등) 붙이기
  • 3단계 — GitLab 파이프라인 실패 webhook 붙이기
  • 이후 — 케이스북 축적, 조치 범위 확대

장애의 신호는 Grafana에서만 오지 않습니다. 다행히 진입부(/analyze) 뒤로는 완전히 동일한 흐름(분석 → 승인 → 조치)을 재사용할 수 있어서, 앞단에 신호를 하나씩 붙이기만 하면 됩니다.

신호원 — 어디서 발생하든 같은 입구로 모인다
  · Grafana Alerting            (K8s·애플리케이션 알람)
  · CloudWatch Alarm → SNS      (AWS DB·관리형 인프라 알람)
  · GitLab 파이프라인 webhook    (CI 실패)


  API Gateway  /analyze   (각 신호를 공통 페이로드로 정규화)


  Lambda  →  SSM(아웃바운드 풀)  →  EC2 · incident-worker


  분석  →  승인 게이트  →  조치      (앞서 설명한 동일 흐름)

CloudWatch → SNS로 AWS 데이터 계층 붙이기. RDS 같은 관리형 리소스의 상태는 Grafana가 아니라 CloudWatch에 쌓입니다. 여기에 CloudWatch Alarm을 걸어(예: DB 커넥션 수, ACU 상한 도달, 스토리지, 데드락) 알람이 울리면 SNS 토픽으로 보내고, 그 SNS 구독이 /analyze를 호출하게 만들 계획입니다. SNS는 알람을 JSON으로 전달하므로, 얇은 어댑터가 이를 파이프라인이 기대하는 공통 페이로드로 변환해 넣습니다. 이렇게 하면 DB·인프라 알람도 K8s 알람과 똑같이 AI가 조사하고 조치안을 제안하게 됩니다.

GitLab 파이프라인 실패 붙이기. 배포·빌드 파이프라인이 깨지는 것도 대응이 필요한 사건입니다. GitLab은 파이프라인 이벤트에 대해 webhook을 보낼 수 있으므로, 파이프라인이 실패하면 그 webhook을 파이프라인 입구로 보낼 계획입니다. 분석 에이전트는 이미 GitLab MCP를 쥐고 있어서, 실패한 잡의 로그를 열어 어느 단계가 왜 깨졌는지 조사하고, 재실행이 답인지 코드·설정의 문제인지까지 짚어 조치안을 제안할 수 있습니다.

두 경우 모두 뒷단은 손댈 것이 없습니다. 분석·승인·조치의 흐름은 그대로 두고, 앞에 신호를 하나씩 더 연결하는 방식입니다. 입구를 표준화해 둔 것이 여기서 값을 합니다.

정리

지금의 파이프라인은 이렇게 동작합니다.

  1. Grafana 알람이 뜨면 Dooray 알림과 함께 AI 분석 파이프라인이 발동한다.
  2. 읽기 전용 에이전트가 여러 MCP를 스스로 순회하며 근본 원인을 조사하고, 조치안 하나와 승인 버튼을 Google Chat 카드로 올린다.
  3. 담당자가 카드에서 승인을 누르면, 회사 계정 인증과 서명·중복 검증을 통과한 경우에만 조치 에이전트가 그 조치 하나를 실행하고 복구를 검증한다.

여기까지 오면서 얻은 가장 큰 교훈은, 자동화의 안전은 AI를 얼마나 잘 타이르느냐가 아니라, 위험한 행동이 지나갈 문을 얼마나 좁게 만드느냐에 달려 있다는 것입니다. 읽기와 쓰기를 폴더로 나누고, 진입부와 실행부를 분리하고, 조치는 승인이라는 좁은 문 하나로만 지나가게 하고, 그 문에 네 겹의 잠금을 걸었기 때문에, 분석 에이전트는 오히려 마음 놓고 과감하게 원인을 파고들 수 있습니다.

관측을 한곳에 모은 것이 지난 글의 마무리였다면, 이번 글은 그 관측 위에서 "그다음"을 사람 손에서 조금씩 덜어내기 시작한 기록입니다. 다음은 CloudWatch와 파이프라인까지 같은 입구로 모으는 일이 될 것입니다.