DevOps · Infra

AI가 전사 알림을 두 번 죽이다

AI 에이전트의 실수, guardrail, 시스템 방어까지 120일의 기록, 그리고 99.981% 가용성.

김용현
·
# DevOps# Infra
AI가 전사 알림을 두 번 죽이다
date
slug
author
status
tags(최대 3개)
summary
type
thumbnail
category
updatedAt
 
인프라팀 전원이 같은 CLAUDE.md 정책 파일 위에서 Claude Code를 씁니다. 팀 공통 가이드라인(AWS SSO 규칙, Git workflow, 승인 정책)은 함께 쓰고, 파트별 전문 정책(DevOps, SRE, Security, DBA)은 따로 둡니다. 팀원들의 세션은 자동 기록돼, 누가 언제 무엇을 Claude와 했는지 볼 수 있습니다.
Notion 세션 기록 — 인프라팀 전원의 Claude Code 작업 이력이 카테고리별로 자동 기록된다
Notion 세션 기록 — 인프라팀 전원의 Claude Code 작업 이력이 카테고리별로 자동 기록된다
핵심은 한 사람이 겪은 실수가 CLAUDE.md에 들어가는 순간 다른 팀원의 Claude도 같은 실수를 하지 않습니다.
더 중요한 건 세션을 닫을 때 도는 회고입니다. 세션이 끝나면 Claude가 그날 작업을 돌아보고 아쉬웠던 점과 개선할 점을 정리해 줍니다. "이번에 WAF 로그 쿼리를 세 번 다시 던졌는데, 필드명 매핑이 MEMORY에 없어서였다" 같은 피드백이 나오면 그 자리에서 guardrail 한 줄이 추가됩니다.
세션정리 회고
세션정리 회고
세션정리 → 회고 → guardrail 추가. 이 사이클이 매일 돌면서 정책이 자랍니다. guardrail 15개는 한 번에 짠 게 아니라 매 세션 회고에서 한 줄씩 쌓인 결과입니다. 한 사람의 경험이 팀 전체의 시스템이 되는 것, 이게 guardrail의 진짜 힘입니다.

TL;DR

  • 120일간 AI 사고 15건을 겪었고, 그때마다 CLAUDE.md guardrail로 바꿨습니다.
  • 3개월 간격으로 Datadog Slack Integration API를 잘못 불러 전사 알림이 두 번 끊겼습니다.
  • Target 5xx(애플리케이션 에러) 발생률이 90% 줄었습니다.
  • 가용성은 99.963% → 99.981%, 모니터는 75개 → 191개.
  • 프로덕션 장애 median 다운타임 11분, 10분 내 복구 45%.
  • AI는 산수를 틀리고, 같은 API를 두 번 죽이고, 네이버 크롤러까지 막습니다. 그 실수를 시스템으로 막는 게 결국 DevOps의 일입니다.

도입: 모니터링 시스템이 없습니다

지난해 10월, 100만 개의 웹사이트가 돌아가는 플랫폼의 인프라팀에 리드로 합류했습니다. 미션은 단순했습니다.
서비스 장애를 10분 안에 인지하고 해결하라.
Datadog에 로그인했습니다. 모니터 75개, 대시보드는 기본 구성, 애플리케이션 로그 수집은 거의 안 되어 있었습니다. 5xx 에러율이 정상인지 아닌지 판단할 기준 조차 없었습니다.
그로부터 120일. 이 글은 데이터로 증명하고, 실수로 배우고, 시스템으로 막은 기록입니다. 성공담이 아니라 AI 에이전트와 함께 삽질하며 만든 guardrail 15개의 전쟁일지입니다.
120일 전쟁 timeline
120일 전쟁 timeline

1부: 120일 전쟁일지

Day 1~18: 기반을 세우다

입사 첫 주는 Datadog 모니터 75개를 하나하나 열어보고, AWS 리소스 맵을 그리고, 알림 체계의 빈틈을 찾았습니다.
Claude Code와 손발을 맞추기 시작한 것도 이때입니다. 처음엔 단순한 질문과 답이었는데, 곧 CLAUDE.md에 팀의 맥락(AWS 프로파일, Datadog API 키 관리, Slack 채널 구조)을 하나씩 넣었습니다. 이 기초 작업이 이후 120일의 속도를 갈랐습니다.
  • 모니터 신규: 20개 (75 → 95)
  • 가용성: 99.963% (baseline 설정)
  • Target 5xx 에러율: baseline 설정

Day 19~49: 첫 전쟁들

입사 한 달 차, 진짜 전쟁이 시작됐습니다.

Redis CPU 99.8% 포화: 범인은 누구인가

어느 날 Redis CPU가 99.8%를 찍고 서비스 전체가 느려졌습니다.
첫 용의자는 batch 서버였습니다. 정시 배치 시간대와 겹쳤거든요. 그런데 Claude Code와 Datadog metric을 파보니 진짜 범인은 OMS(Order Management System) 부하테스트였습니다. QA가 아니라 프로덕션 Redis를 바라보고 있었던 겁니다.
batch는 누명을 벗었습니다. 원인은 추정하지 말고 데이터로 증명하라 — 이 한 줄을 남긴 사건입니다.

AWS Support Slack 자동화

AWS Support Case가 열리면 담당자가 손으로 Slack에 공유하던 걸 자동화했습니다(EventBridge + Lambda + Slack API). 작은 자동화였지만 "자동화할 수 있는 건 다 자동화한다"는 기조를 굳힌 출발점이었습니다.

Nginx 설정 파일 손상: 프록시 4대로 전파

간담이 서늘했던 날입니다. SSL 인증서 배치 스크립트의 버그로 Nginx 설정 파일이 깨진 채 rsync로 프록시 4대에 그대로 퍼졌습니다. 다행히 reload 전에 잡았는데, 더 무서웠던 건 백업도 없이 자동 rsync가 돌고 있었다는 사실이었습니다.
이후 Nginx Auto Recovery를 만들었습니다. 설정 변경 전 자동 백업, 문법 검증(nginx -t), 실패 시 자동 rollback. 이게 guardrail #13입니다.

퇴사자 SSH 키 / PAT 토큰 발견

보안 감사 중 퇴사자의 SSH 키와 Personal Access Token이 아직 살아 리포지토리 239개에 접근 가능한 걸 발견했습니다. 즉시 무효화하고 credential 정기 감사 프로세스를 세웠습니다.
  • 모니터 신규: 35개 (최다, 95 → 130)
  • 가용성: 99.969% (+0.006%p)
  • PHP-FPM 평균 Active: 9.0 → 7.1 (21% 개선)
11월 사건 타임라인
11월 사건 타임라인

Day 50~80: 최악의 실수, 최고의 교훈

12월은 AI 에이전트의 가장 치명적인 실수가 나온 달입니다.

Datadog Slack Integration API: 전사 알림 3일 중단 (1차)

Datadog에 Slack 채널을 새로 추가하려 했습니다. 간단한 일이라 Claude에게 맡겼고, Claude는 문서를 보고 PUT /api/v1/integration/slack을 호출했습니다. 스펙상 흠잡을 데 없는 호출이었습니다. 문서에도 "Update your Slack integration"이라 적혀 있었고 파라미터도 맞았습니다.
결과: 전사 Slack 알림이 전부 끊겼습니다.
이 API를 부르면 기존 Slack workspace의 OAuth 인증이 통째로 무효화됩니다. 문서 어디에도 사이드이펙트는 없었습니다. 복구하려면 Datadog UI에서 Slack Integration을 지우고 OAuth를 다시 연결해야 했습니다. 마침 실제 장애가 없어 망정이지, 3일간 알림이 깜깜했고 결국 MSP까지 에스컬레이션해 겨우 살렸습니다.
CLAUDE.md에 첫 "절대 금지" 정책이 박힌 순간입니다.
## CRITICAL: Datadog Slack Integration API 절대 사용 금지 `PUT /api/v1/integration/slack` API 절대 사용 금지! 이 API로 채널을 추가/수정하면 Slack workspace 인증이 무효화되어 전체 Slack 알림이 중단됨. - 사고 이력: 2025-12-08 API로 채널 추가 → 전체 알림 중단 3일 - 복구: Datadog UI > Integrations > Slack 타일 > Delete > Re-add - 채널 추가 올바른 방법: Datadog UI에서만 수동 추가
이 정도면 됐다고 생각했습니다.
실제 Claude.md의 금지 명령어 섹션
실제 Claude.md의 금지 명령어 섹션

PHP-FPM 30대 서버 동시 포화

정오 무렵 PHP-FPM Active Process가 서버 30대에서 동시에 치솟았습니다. 프로세스 720개 포화, max_children reached 카운트가 2,255까지 올라갔습니다. 특정 slow query가 PHP 워커를 연쇄로 잡아먹은 게 원인이었습니다. 주문 테이블의 index miss가 요청마다 몇 초씩 빨아들였고, 워커 풀이 순식간에 말랐습니다.
이후 PHP-FPM 모니터링을 강화했습니다. Active Process 비율 알림, max_children reached 알림, 서버별 워커 사용률 대시보드를 붙였습니다.

보안을 조이다

내부 보안 감사도 강화했습니다. 보안 그룹 inbound 규칙 점검, 관리 포트 외부 노출 확인, 안 쓰는 API 키 rotation을 체계로 만들었습니다.
  • 모니터 신규: 31개 (130 → 161)
  • 가용성: 99.979% (+0.010%p, 최대 개선폭)
  • 응답 시간 평균: 65.8ms

Day 81~111: 시스템을 세우다

이벤트 긴급 대응 시스템 + Slack /event

아임웹은 쇼핑몰 이벤트(타임세일, 한정판매 등)로 트래픽이 튀는 일이 잦습니다. 이벤트 시작에 맞춰 CloudFront origin을 이벤트 전용 ALB로 돌렸다가 끝나면 되돌려야 합니다. 이 과정을 통째로 자동화했습니다.
  • DynamoDB: devops-event-monitoring 테이블에 이벤트 정보 저장
  • Lambda: devops-event-site-manager가 CloudFront origin 자동 전환
  • Slack Bot: /event 명령어로 이벤트 등록·조회·삭제
  • EventBridge: 이벤트 시작 60분 전, 30분 전 자동 알림
 
매일 이벤트 자동 대응·모니터링 현황 알림 발송 — 각 단계 진행 여부는 thread로 표시
매일 이벤트 자동 대응·모니터링 현황 알림 발송 — 각 단계 진행 여부는 thread로 표시
이 시스템은 이후 2월의 여러 사고에서 제 몫을 톡톡히 합니다.
Slack /event 커맨드 — 이벤트 88건을 자동 관리, 긴급 대응부터 통계까지
Slack /event 커맨드 — 이벤트 88건을 자동 관리, 긴급 대응부터 통계까지
 

Postmortem Lambda: 이모지 하나로 postmortem 자동 생성

장애 thread에 :postmortem: 이모지를 누르면 이렇게 흘러갑니다.
  1. Lambda가 Slack thread 전체 메시지를 모음
  1. Bedrock Claude가 timeline 추출 + 5 Whys 분석
  1. Notion API가 postmortem 페이지 생성
  1. 완료 알림이 thread에 답글로 달림
Before에는 장애가 끝나면 2~3시간을 손으로 정리했지만, After는 이모지 하나, 5분입니다. 이 자동화 하나로 "귀찮아서 안 쓴다"는 말이 사라졌습니다.
  • 모니터 신규: 24개 (161 → 185)
  • 가용성: 99.964% (소폭 하락 — 1월 트래픽 급증)
 
1월 자동화 아키텍처 — 이벤트 모니터링 Lambda + postmortem 자동 생성 파이프라인
1월 자동화 아키텍처 — 이벤트 모니터링 Lambda + postmortem 자동 생성 파이프라인
 

Day 112~139: 폭풍의 달

2월은 사고의 달이자 guardrail이 가장 많이 붙은 달입니다. 15개 중 10개가 이 한 달에 나왔습니다.

AI가 산수를 틀리다: Unix timestamp 하드코딩

DDoS 공격이 한창이었습니다. 공격 패턴을 보려고 Slack API로 특정 시간대 메시지를 긁어와야 했습니다. Claude에게 "2026년 2월 15일 00:00 KST의 Unix timestamp를 구해 Slack API를 호출해 줘"라고 했더니 이렇게 했습니다.
# Claude가 작성한 코드 oldest = "1739548800" # 2025-02-15 00:00:00 KST... 가 아니라 UTC
2026년이 아니라 2025년 timestamp를 넣었습니다. 결과는 데이터 0건. 급한 대응 중에 "왜 메시지가 없지?" 하며 세 번을 다시 돌렸습니다.
guardrail:
## CRITICAL: Unix timestamp 계산은 반드시 Python datetime 사용 절대 하드코딩 금지. 직접 계산하거나 박아 넣으면 연도가 틀어짐. # 올바른 방법 from datetime import datetime, timezone, timedelta KST = timezone(timedelta(hours=9)) dt = datetime(2026, 2, 15, 0, 0, 0, tzinfo=KST) unix_ts = str(int(dt.timestamp())) # 절대 금지 oldest = "1739548800" # 이렇게 직접 쓰면 연도 틀림
AI는 코드는 기막히게 짜지만 산수는 직접 시키면 틀립니다. LLM의 본질적 한계라, 계산은 무조건 코드로 돌리게 해야 합니다.

CloudFront origin 전환 500 에러: TG에 인스턴스가 0대

이벤트 대비로 CloudFront default origin을 프리미엄 ALB로 돌려야 했습니다. Claude가 AWS CLI로 origin을 바꿨습니다. 절차상 문제는 없었습니다. 문제는 그 ALB의 target group에 EC2 인스턴스가 0대였다는 것. ASG(Auto Scaling Group)가 안 떠 있는 상태에서 origin을 돌려버린 거죠. 모든 요청이 500을 뱉었습니다. 바로 rollback하고 ASG를 먼저 띄운 뒤 재전환했습니다.
guardrail:
## CloudFront origin 전환 체크리스트 전환 전 필수 확인 (하나라도 실패 시 전환 금지): - [ ] Target group에 healthy 타겟이 1개 이상 존재 - [ ] ALB listener 규칙이 완전한지 확인 - [ ] Origin Protocol이 http-only인지 확인 - [ ] 전환 후 모니터링 체계 준비

TargetGroupAssociationLimit: 놓치기 쉬운 AWS 제약

같은 날 또 한 건. path-gateway-site-event-tg를 프리미엄 ALB에서 공유하려다 AWS CLI가 짧게 에러를 던졌습니다.
TargetGroupAssociationLimit
target group은 ALB 하나에만 붙습니다. AWS Quotas 문서에 Load balancers per target group: 1 (조정 불가)로 나와 있지만 실무에서 모른 채 공유를 시도하는 경우가 흔합니다. TG 이름은 32자 제한이라 path-gateway-site-event-premium-tg처럼 길게 지을 수도 없었습니다. 결국 pathgw-site-event-premium-tg라는 줄인 이름으로 새 TG를 만들고 EKS TargetGroupBinding으로 연결해 풀었습니다.
guardrail:
## AWS 리소스 제약 사전 확인 | 제약 | 설명 | 대안 | |------|------|------| | TargetGroupAssociationLimit | TG는 한 ALB에만 연결 가능 | 동일 설정의 새 TG 생성 | | TG 이름 32자 제한 | 이름 길이 초과 불가 | 약어 사용 |

WAF HostingProviderIPList Block: 네이버 봇을 막다

DDoS가 계속됐습니다. 공격 트래픽 출처에 호스팅 프로바이더 IP 대역이 많아, Claude에게 "HostingProviderIPList를 Block 모드로 바꿔"라고 했고 Claude는 그대로 했습니다.
30분 뒤 고객 이슈가 들어왔습니다. 네이버 검색 결과에서 사이트가 사라지기 시작한 겁니다. 네이버 크롤러(Yeti Bot) IP 대역이 NAVER Cloud Corp.(AS23576, 110.93.144.0/20)에 속하는데, AWS는 이걸 "호스팅 프로바이더"로 분류합니다. Block하면 네이버 크롤러가 403으로 막히는 거죠. 까보니 호스팅 IP 트래픽의 91%가 정상이었습니다. 즉시 Count 모드로 되돌렸습니다.
guardrail:
## WAF HostingProviderIPList 운영 정책 CRITICAL: Block 모드 전환 절대 금지. Count 모드 유지 필수. 사유: 호스팅 IP 트래픽의 91%가 정상 트래픽 - 네이버 크롤러: NAVER Cloud Corp. (AS23576) - Google 크롤러, 일반 사이트 접근 등 - Block 시 네이버 SEO 전체 장애 발생
WAF 규칙의 영향 범위를 모르면 손대지 마라. 대응의 조급함이 더 큰 장애를 부릅니다.

WAF 로그 source:waf vs service:waf: 3번 삽질

같은 날, DDoS 대응 중 WAF 로그를 보려 했습니다. Claude가 Datadog Log Search API를 호출했습니다.
# 1차 시도 query = "source:waf @httpRequest.host:[고객사 도메인]" # 결과: 0건 # 2차 시도 (쿼리 수정) query = "source:waf @httpRequest.headers.host:[고객사 도메인]" # 결과: 0건 # 3차 시도 (service로 변경) query = "service:waf @httpRequest.host:[고객사 도메인]" # 결과: 12,847건 !!
WAF 로그는 source:waf가 아니라 service:waf로 색인됩니다(우리 환경 기준). 공격이 진행 중인데 세 번을 헛돌린 건 뼈아팠고, 이 경험은 CLAUDE.md MEMORY 섹션에 박아뒀습니다.

Datadog Slack Integration API 2차: 같은 실수, 3개월 뒤

이 사건이 글 제목이 된 이유입니다. 12월 8일에 전사 알림을 3일간 죽인 바로 그 API를, 3개월 뒤에 또 호출했습니다.
CLAUDE.md에는 분명히 "절대 사용 금지"가 적혀 있었습니다. 범인은 context window였습니다. 세션이 길어지면서 초반에 로드된 CLAUDE.md가 context 밖으로 밀려났고, Claude는 처음 보는 API인 양 똑같이 호출했습니다.
## 추가된 강화 정책 - GET /api/v1/integration/slack (읽기)는 안전 - PUT (쓰기)만 금지 - Memory(영구 기억)에도 중복 기록 - 세션 시작 시 자동 로드되는 위치로 정책 이동
CLAUDE.md에 적는 것만으로는 부족합니다. 정책은 context window에서 밀려나지 않는 자리에 있어야 하고, Memory에도 겹쳐 기록해야 합니다.
사람은 한 번 데이면 몸이 기억하지만, AI는 매 세션이 백지에서 시작합니다. 기억을 시스템으로 떠먹여 줘야 합니다.

모니터 108개 대규모 고도화

사고만 있었던 건 아닙니다. 기존 모니터 108개를 한꺼번에 손봤습니다.
  • 알림 임계치를 실데이터 P50/P75/P95 기반으로 재설정
  • 모든 모니터에 runbook 링크 추가
  • 알림 메시지에 대시보드 직링크와 관련 로그 쿼리 포함
  • Composite Monitor로 복합 조건 알림 구성
P3~P4 모니터가 겹쳐 터지면 P2로 escalation
P3~P4 모니터가 겹쳐 터지면 P2로 escalation
WAF가 차단한 호스트 분석을 위한 링크 연결
WAF가 차단한 호스트 분석을 위한 링크 연결
손으로 하면 모니터당 15분, 108개면 27시간인데 Claude Code와 한 시간 만에 끝냈습니다.

/event delete가 CloudFront origin을 안 되돌리다

/event delete에 치명적 버그가 있었습니다. 이벤트를 지우면 DynamoDB 레코드는 사라지는데 CloudFront origin은 이벤트 ALB에 묶인 채 방치됐습니다. 이벤트가 끝나도 트래픽이 이벤트 인프라로 계속 흘렀던 거죠.
여기서 나온 게 Destructive Command Side Effect Checklist 정책입니다.
## Destructive Command Side Effect Checklist 삭제/비활성화/제거 명령을 구현할 때 반드시 확인: | 확인 항목 | 설명 | |-----------|------| | 연관 리소스 상태 복원 | 삭제 대상이 바꾼 외부 리소스를 원래대로 되돌리는가? | | 참조 데이터 정리 | 삭제 대상을 참조하는 다른 테이블/파라미터가 정리되는가? | | 비동기 프로세스 영향 | 삭제 후 주기적 Lambda/cron이 stale 데이터를 참조하지 않는가? | | rollback 가능성 | 삭제 실패 시 부분 삭제 상태에서 복구할 수 있는가? |일일 모니터링 메시지 삭제 사고
Slack 채널을 정리하다 "오래된 봇 메시지를 지워"라고 Claude에게 시켰습니다. Claude가 chat.delete로 지운 메시지 중 하나가 일일 모니터링 현황 메시지였습니다. Lambda가 매일 chat.update로 갱신하던 메시지였고, 그 timestamp가 SSM Parameter Store에 있었습니다. 메시지가 사라지자 timestamp가 무효화돼 이후 모든 일일 업데이트가 실패했습니다. SSM 파라미터를 리셋하고 Lambda를 수동 트리거해 복구했습니다.
guardrail:
## Bot Message Protection Policy 삭제 전 필수 확인: 1. 주기적으로 업데이트되는 봇 메시지인지 확인 2. 메시지 ts가 SSM 파라미터나 DynamoDB에 저장돼 있는지 확인 3. 삭제 시 이후 업데이트가 깨지지 않는지 확인 삭제 금지 대상: - 일일/주간 모니터링 현황 메시지 - 자동 업데이트되는 상태 메시지 - 하위 thread가 달린 root 메시지

Serverless 형상관리 61% → 100%

Lambda 함수들의 IaC(Infrastructure as Code) 커버리지가 61%밖에 안 됐습니다. 나머지 39%는 콘솔에서 손으로 만들어 Git에 코드가 없었죠. Claude Code와 함께 모든 Lambda를 SAM(Serverless Application Model) 템플릿으로 옮겨 100% 형상관리를 맞췄습니다.
  • 모니터 신규: 26개 (165 → 191)
  • 가용성: 99.981% (역대 최고)
  • Target 5xx: 10월 대비 90% 감소
  • 응답 시간 평균: 58.5ms (역대 최저)
2월 사건 밀도 히트맵 — guardrail 15개 중 10개가 2월에 집중된, 가장 치열했던 달
2월 사건 밀도 히트맵 — guardrail 15개 중 10개가 2월에 집중된, 가장 치열했던 달
 

2부: 데이터가 말하는 것

서비스 가용성: 99.963% → 99.981%

"겨우 0.018%p?" 싶을 수 있습니다. 풀어보면 다릅니다. 월 수백억 건을 처리하는 서비스에서 분 단위로 환산하면, 월간 장애 허용 시간이 약 16분에서 약 8분으로 반토막 났습니다. SRE 용어로 3.4 nines → 3.7 nines, SLO 99.95% 기준으로는 에러 버짓을 절반만 쓰는 셈입니다.
월 가용성 (전월 대비)
월 가용성 (전월 대비)
 
1월에 떨어진 건 설날 연휴 전후 트래픽이 역대 최대였기 때문입니다. 에러율이 잠깐 올랐지만 트래픽 증가에 비례한 수준이었고, 2월에 99.981%로 반등한 건 1월의 교훈을 반영한 인프라 최적화 덕입니다.
가용성 추이 — 트래픽이 늘어도 안정성은 올라감
가용성 추이 — 트래픽이 늘어도 안정성은 올라감
 

Target 5xx: 90% 감소

가장 극적인 지표입니다. Target 5xx는 CloudFront나 ALB가 아니라 애플리케이션 자체에서 난 에러, 즉 코드 레벨의 문제입니다.
모니터링이 제대로 돌면서 흐름이 만들어졌습니다. 문제가 일찍 보이고(모니터 75 → 191), 로그 수집과 slow query 모니터링으로 근본 원인을 짚고, 개발팀에 "이 쿼리가 P99 2초를 만든다"는 식으로 구체적 데이터를 넘기면 고쳐지고 재발이 막힙니다. 인프라팀이 코드를 직접 안 고쳐도 문제를 보이게 만드는 것만으로 절반은 해결됩니다.
10월: ██████████████████████████████████████████████████ 100% 11월: ████████████████████████████████████████ 80% 12월: █████████████████████████████████ 65% 1월: ███████████████████████████ 50% 2월: █████ 10% (-90%!)
Datadog Metric Explorer — Target 5xx / Total Requests 비율 추이 (10월~2월)
Datadog Metric Explorer — Target 5xx / Total Requests 비율 추이 (10월~2월)
 

응답 시간: 더 빠르게, 더 안정적으로

월 평균 P99
월 평균 P99
평균 응답 시간이 69.6ms → 58.5ms로 16% 좋아졌고, P99도 367.4ms → 342.7ms로 안정됐습니다. 1월에 설날 트래픽이 폭증했는데도 P99가 369.5ms로 크게 나빠지지 않은 게 핵심입니다. 인프라가 트래픽 증가를 흡수할 여유가 생겼다는 신호죠.

PHP-FPM: 숨은 성능 지표

월 평균 Active Process & Max Children Reached
월 평균 Active Process & Max Children Reached
PHP-FPM Active Process 평균이 9.0 → 4.4로 51% 줄었습니다. 워커가 절반만 바빠도 같은 트래픽을 소화한다는 뜻입니다.
"Max Children Reached가 2,837로 늘었으면 나빠진 거 아닌가?" 싶지만, 아닙니다. 예전엔 max_children 도달 이벤트를 못 잡았습니다. 2,837은 더 많이 터진 게 아니라 예전에 놓치던 걸 이제 잡는 숫자입니다.

모니터 성장: 75 → 191

notion image
11월에 35개로 가장 많이 만들었습니다. 입사 첫 달에 찾은 공백을 집중적으로 메운 결과죠. 1월부터 24~26개로 잦아든 건 기본 커버리지가 잡혔기 때문이고, 이제는 새 모니터보다 기존 모니터 고도화(임계치 튜닝, runbook 연결)에 더 많은 시간을 씁니다.
10월: ████████████████████ 75 11월: █████████████████████████████ 110 (+35) 12월: █████████████████████████████████████ 141 (+31) 1월: ████████████████████████████████████████████ 165 (+24) 2월: ██████████████████████████████████████████████████ 191 (+26)
Datadog 모니터 목록 — 192개 모니터가 우선순위별로 관리되고, created_by:claude_code 태그가 보인다
Datadog 모니터 목록 — 192개 모니터가 우선순위별로 관리되고, created_by:claude_code 태그가 보인다
종합 대시보드 — 가용성, 5xx 에러율, 응답 시간, 모니터 수의 5개월 추이
종합 대시보드 — 가용성, 5xx 에러율, 응답 시간, 모니터 수의 5개월 추이

장애 대응 속도: MTTR / MTTD

미션이 "장애를 10분 안에 인지·해결하라"였으니 이것도 데이터로 증명해야 합니다. 120일간 프로덕션 장애 11건을 분석했습니다.
10월: ██████████████████████████ Avg 24.3분 (4건, baseline 설정 기간) 11월: ████████████ Avg 11분 (2건) 12월: █████████████████ Avg 32분 (1건, EFS 아키텍처 이슈) 1월: ██ Avg 1.5분 (2건, 외부 벤더 제외) 2월: █████ Avg 5분 (1건)
10월에는 WAF 룰 적용 사고(52분)처럼 체계가 서기 전의 긴 다운타임이 있었습니다. 1~2월에는 대부분 5분 안에 복구됩니다. "10분 이내 인지·해결" 미션에 절반 가까이 닿았고, 나머지 절반은 auto-remediation으로 채워야 합니다.
MTTR 추이 — 월별 평균 다운타임 24.3분 → 5분, 1월부터 Target(10분 이내) 달성
MTTR 추이 — 월별 평균 다운타임 24.3분 → 5분, 1월부터 Target(10분 이내) 달성
 

3부: AI 에이전트 실수 카탈로그

실수 15건이 guardrail 15개가 됐습니다. 단순한 목록이 아니라, AI 에이전트를 실무에 넣으면 반드시 마주치는 함정의 패턴입니다.

패턴 A: AI는 API 문서를 믿는다 (side effect를 모른다)

notion image
문서에 적힌 게 전부가 아닙니다. 문서에 없는 side effect, 시스템 간 의존 관계, 비동기 프로세스의 영향을 AI는 모릅니다. 경험으로만 아는 걸 정책으로 적어둬야 합니다.

패턴 B: AI는 산수를 틀린다 (계산은 코드에 맡겨라)

notion image
LLM은 추론 엔진이지 계산기가 아닙니다. 날짜 계산, IP 대역 계산, 포트 매핑은 무조건 코드로 돌리게 해야 합니다.

패턴 C: AI는 우리 환경을 모른다 (필드명, 제약, 관례)

notion image
어느 환경에나 "우리만의 관례"가 있습니다. Datadog의 index 이름, AWS의 숨은 제약, Jira의 커스텀 필드명은 공식 문서에 없습니다. 환경 특화 지식을 정책 파일에 쌓아야 합니다.

패턴 D: AI는 영향 범위를 모른다 (blast radius)

notion image
AI는 "이 명령이 어디까지 영향을 주는가"를 가늠하지 못합니다. WAF 규칙 하나가 네이버 크롤러를 막고, CloudFront origin 변경 하나가 전체 서비스를 내립니다. 변경 전 영향 범위를 명시적으로 확인하는 체크리스트가 필요합니다.
실수 카탈로그 패턴 매트릭스 — AI 사고 15건을 4가지 패턴(Side Effects, Bad Math, Unknown Env, Blast Radius)으로 분류
실수 카탈로그 패턴 매트릭스 — AI 사고 15건을 4가지 패턴(Side Effects, Bad Math, Unknown Env, Blast Radius)으로 분류
 

왜 같은 실수를 두 번 할까

Datadog Slack Integration API 사고가 3개월 만에 반복된 이유를 깊게 파봅니다.
12월 08일: PUT /api/v1/integration/slack 호출 → 전사 알림 중단 ↓ CLAUDE.md에 "절대 사용 금지" 추가 ↓ (3개월간 문제없음) ↓ 2월 21일: 긴 세션에서 CLAUDE.md 정책이 context 밖으로 밀림 ↓ Claude가 같은 API를 "처음인 것처럼" 호출 ↓ 전사 알림 재중단
문제의 본질은 context window의 한계입니다. 세션이 길어지면 초반에 로드된 CLAUDE.md가 점점 뒤로 밀리다 결국 "없는 것"이 됩니다. 그래서 방어선을 여러 겹으로 깝니다.
방어선 1: CLAUDE.md 정책 (세션 초기에 로드) 방어선 2: Memory (영구 기억, 세션과 무관하게 유지) 방어선 3: 가장 중요한 정책은 context 시작 부분에 배치
💡 AI 에이전트의 "기억"은 구조적으로 관리해야 합니다. Memory(Auto Memory)와 정책 파일을 겹쳐 두고, 치명적인 정책일수록 context 앞쪽에 배치합니다.

guardrail은 어떻게 작동하는가

CLAUDE.md: 세션에 로드되는 규칙서

Claude Code가 세션을 열 때 자동으로 읽는 프로젝트 설정 파일입니다. 여기 적힌 정책이 Claude의 행동을 직접 제어합니다.
# 실제 CLAUDE.md 발췌 ## CRITICAL: Datadog Slack Integration API 절대 사용 금지 `PUT /api/v1/integration/slack` API 절대 사용 금지! 이 API로 채널을 추가/수정하면 Slack workspace 인증이 무효화되어 전체 Slack 알림이 중단됨. - 사고 이력 1: 2025-12-08 → 전체 알림 중단 3일 - 사고 이력 2: 2026-02-21 → 전체 알림 재중단 - 복구: Datadog UI > Integrations > Slack > Delete > Re-add - GET (읽기)는 안전. PUT (쓰기)만 금지.

MEMORY.md: 세션을 넘어서는 영구 기억

Auto Memory 기능으로, 세션이 끝나도 유지되는 영구 메모입니다.
# 실제 MEMORY.md 발췌 ## WAF 관련 - Datadog WAF 로그 쿼리: 반드시 service:waf 사용 (source:waf 아님!) - 호스트 필터: @httpRequest.host:도메인 - 액션 필터: @system.action:BLOCK (@action:BLOCK 아님!) - 사고 이력: 2026-02-22 @action:BLOCK으로 로그 링크 생성 → 0건 반환
사고 이력과 복구 방법까지 영구 기억에 기록
사고 이력과 복구 방법까지 영구 기억에 기록
 

Incident History: 정책에 "왜?"를 더하다

모든 guardrail에는 사고 이력이 붙습니다. 이 규칙이 왜 있는지를 알아야 Claude가 맥락을 이해하고 비슷한 상황에서도 조심합니다.
## CloudFront origin 전환 체크리스트 사고 이력: - 2026-02-18: [고객사A 도메인] CF를 premium ALB로 전환 시, TG에 EC2 인스턴스가 0대인 상태에서 전환하여 500 에러 발생. 즉시 rollback 후 ASG 생성 → 재전환으로 해결 - 2025-12: [고객사B 도메인] CF Origin Protocol을 https-only로 설정하여 504 에러 발생 (ALB는 port 80만 listening). http-only로 수정하여 해결
"하지 마"보다 "이렇게 했다가 이런 일이 났으니 하지 마"가 훨씬 셉니다.
CLAUDE.md 정책 → Auto Memory 패턴 → Incident History 기반 guardrail
CLAUDE.md 정책 → Auto Memory 패턴 → Incident History 기반 guardrail
 

결론: 실수를 시스템으로 막는 게 진짜 DevOps

notion image

세 가지 교훈

1. AI 200% 활용은 AI의 한계를 인정하고 메우는 것이다
AI를 맹신하면 전사 알림이 죽고, 불신하면 생산성이 10분의 1로 떨어집니다. 답은 가운데 있습니다. AI가 잘하는 것(코드 생성, 데이터 분석, 문서화)은 맡기고, 못하는 것(산수, side effect 예측, 환경 특화 지식)은 guardrail로 메웁니다. 모니터 108개를 한 시간 만에 고도화한 것도, 전사 알림을 두 번 죽인 것도 AI였습니다. 둘 다 인정해야 합니다.
2. 사람의 기억은 휘발성, 시스템의 기억은 영구적이다
"다음엔 조심하자"는 3개월 뒤에 같은 실수를 부릅니다. 실제로 그랬죠. CLAUDE.md에, Memory에, 사고 이력에 박아두면 다릅니다. 실수가 시스템에 쌓이면 같은 실수는 물리적으로 반복될 수 없습니다. 팀의 지식 관리, 온보딩, 표준화 — 어디든 똑같습니다. 사람은 바뀌어도 시스템은 남습니다.
3. 문서화가 곧 자동화, 정책이 곧 방어선이다
정책 파일 30개+, guardrail 15개는 "문서"가 아니라 실행되는 코드입니다. "HostingProviderIPList Block 금지"라고 적는 순간 Claude는 그 규칙을 어기지 않고, "Unix timestamp는 Python datetime으로"라고 적는 순간 산수를 틀리지 않습니다. AI는 모든 문서를 읽습니다.

다음은?

120일은 시작일 뿐입니다. median 다운타임을 11분에서 5분으로 줄이려면 auto-remediation이 핵심이고, 특정 패턴의 장애는 사람 개입 없이 자동 복구하게 만들 겁니다. 인프라팀 너머 개발팀·QA팀까지 AI 시스템을 넓히고, 여러 Claude Code 인스턴스가 context를 주고받으며 병렬로 일하게 하는 것도 목표입니다.
guardrail은 계속 늘어날 겁니다. 실수를 안 하는 게 목표가 아니라, 실수를 시스템으로 바꾸는 속도를 높이는 것이 목표입니다.
 

댓글