← posts/b.log()

blog92@web:~$ cat posts/devops-patterns-06-operations.md

DEVOPS11 min read

DevOps 패턴과 안티패턴 6강 — 운영: 장애를 대하는 방식

Monitoring과 Observability의 차이, 로그·메트릭·트레이스와 OpenTelemetry, Golden Signals와 SLI/SLO·Error Budget, Alert Fatigue와 Circuit Breaker, 그리고 Blameless Postmortem.

이전 강의: 5강. 배포 전략: 빅뱅 릴리스를 쪼개는 법 이번 강의 키워드: Monitoring vs Observability, 로그·메트릭·트레이스, OpenTelemetry, Golden Signals, SLI/SLO, Error Budget, Alert Fatigue, Circuit Breaker, Retry Storm, Blameless Postmortem

들어가며

새벽 3시에 휴대폰이 울립니다. "CPU 사용률 85% 초과". 일어나 확인해 보니 서비스는 멀쩡합니다. 배치 작업이 돌았을 뿐이죠. 이런 알림이 일주일에 스무 번 옵니다. 그러다 어느 날 진짜 장애가 났는데, 알림은 그 스무 개 사이에 섞여 아무도 보지 못했습니다. 다음 날 회의에서 "왜 알림을 확인하지 않았느냐"는 질문이 나옵니다.

이 장면에는 이번 강의의 안티패턴이 모두 들어 있습니다. 의미 없는 알림, 증상이 아닌 원인에 대한 감시, 그리고 사람을 탓하는 문화입니다. 1강의 세 번째 질문, "피드백이 빠른가"는 운영 단계에서 "문제를 빨리, 정확하게 아는가"라는 질문이 됩니다.

Monitoring과 Observability

두 용어는 자주 섞여 쓰이지만 강조점이 다릅니다.

모니터링(Monitoring)은 미리 정해둔 질문에 답합니다. "CPU가 80%를 넘었나?", "디스크가 가득 찼나?" 같은 질문입니다. 우리가 이미 알고 있는 실패 방식을 감시하는 것이죠.

관측 가능성(Observability)은 원래 1960년 Rudolf Kálmán이 제어 이론에서 정의한 개념으로, 시스템의 외부 출력만으로 내부 상태를 추론할 수 있는 정도를 뜻합니다. 소프트웨어 운영에서는 Honeycomb의 Charity Majors 등이 이 개념을 대중화했는데, 핵심은 미리 예상하지 못한 질문에도 답할 수 있는가입니다. "왜 이 특정 고객의 요청만 느리지?", "어제 배포 이후 어떤 경로에서 오류가 늘었지?" 같은 질문이죠.

분산 시스템이 복잡해질수록 실패 방식은 미리 다 예상할 수 없습니다. 그래서 대시보드를 늘리는 것만으로는 부족하고, 임의의 질문을 던질 수 있는 데이터가 필요해졌습니다. 2강에서 "SSH로 들어가 확인하는" 습관을 버려야 한다고 했는데, 그 빈자리를 채우는 것이 바로 관측 가능성입니다.

세 가지 신호: 로그, 메트릭, 트레이스

관측 데이터는 흔히 세 종류로 나눕니다.

메트릭(Metrics)은 시간에 따른 숫자입니다. 초당 요청 수, 오류율, 응답 시간 분포 같은 것이죠. 저장 비용이 싸고 추세를 보기 좋지만, 개별 요청의 세부 사항은 담지 못합니다.

로그(Logs)는 개별 사건의 기록입니다. 세부 정보가 풍부하지만 양이 많고, 형식이 제각각이면 분석하기 어렵습니다.

트레이스(Traces)는 하나의 요청이 여러 서비스를 거쳐가는 경로를 기록합니다. 2010년 Google이 공개한 Dapper 논문이 분산 추적의 원형이 됐습니다. 마이크로서비스 환경에서 "어느 구간이 느린가"에 답하려면 트레이스가 필요합니다.

이 세 신호를 수집하는 방식이 도구마다 달라 생기던 혼란은, 2019년 OpenTracing과 OpenCensus 프로젝트가 합쳐진 OpenTelemetry가 표준으로 자리 잡으면서 크게 줄었습니다.

여기서 흔한 안티패턴은 로그만 쌓아두는 운영입니다. 서버마다 텍스트 로그 파일이 쌓이고, 장애가 나면 각 서버에 접속해 grep으로 뒤집니다. 이를 개선하는 첫걸음은 두 가지입니다. 로그를 사람이 읽는 문장 대신 JSON 같은 구조화된 형식으로 남기는 것, 그리고 요청마다 고유한 상관관계 ID(correlation ID)를 붙여 여러 서비스의 로그를 하나의 요청으로 엮을 수 있게 하는 것입니다.

text
# 비구조화 로그
2026-09-16 03:12:44 ERROR payment failed for user kim
 
# 구조화 로그
{"ts":"2026-09-16T03:12:44Z","level":"error","event":"payment_failed",
 "user_id":"u-1024","request_id":"req-7f3a","provider":"pg-a","latency_ms":3021}

두 번째 형식이라면 "지난 1시간 동안 pg-a 결제 실패 중 지연이 3초를 넘은 요청"을 바로 조회할 수 있습니다.

무엇을 측정할 것인가

측정할 수 있는 것은 무한하지만, 출발점으로 삼기 좋은 검증된 틀이 몇 가지 있습니다.

Google의 SRE 책(2016)은 사용자 대면 시스템에서 네 가지 황금 신호(Four Golden Signals)를 제시했습니다. 지연시간(latency), 트래픽(traffic), 오류(errors), 포화도(saturation)입니다. 서비스 관점에서는 Tom Wilkie가 정리한 RED(Rate, Errors, Duration)가, CPU·디스크 같은 자원 관점에서는 Brendan Gregg의 USE(Utilization, Saturation, Errors)가 자주 쓰입니다.

세 틀의 공통점은 사용자가 겪는 증상과 자원의 상태를 구분한다는 것입니다. 이 구분이 알림 설계의 핵심이 됩니다.

SRE와 Error Budget: 벽을 숫자로 허물기

1강에서 개발팀은 변경을, 운영팀은 안정성을 추구하다 충돌한다고 했습니다. Google은 2003년 Ben Treynor Sloss가 이끈 SRE(Site Reliability Engineering) 조직에서 이 충돌을 독특한 방식으로 풀었습니다. 안정성을 숫자로 합의하는 것입니다.

먼저 SLI(Service Level Indicator)를 정합니다. "성공한 요청의 비율", "300ms 안에 응답한 요청의 비율" 같은 사용자 경험 지표입니다. 그다음 SLO(Service Level Objective), 즉 목표치를 정합니다. 예를 들어 "30일 동안 요청의 99.9%가 성공한다"입니다. (외부 고객과 계약으로 맺어 위반 시 보상이 따르는 것은 SLA라고 부르며, 보통 SLO보다 느슨하게 잡습니다.)

SLO가 99.9%라면, 나머지 0.1%는 실패해도 되는 양입니다. 이것을 Error Budget(오류 예산)이라고 부릅니다. 이 발상의 힘은 여기서 나옵니다.

  • 예산이 남아 있으면 개발팀은 자유롭게 배포하고 실험합니다. 안정성은 합의된 수준을 지키고 있으니까요.
  • 예산을 다 쓰면 새 기능 배포를 멈추고 안정성 개선에 집중합니다.

"변경을 해도 되는가"라는 질문이 두 팀의 감정 싸움이 아니라 공유된 숫자로 결정됩니다. 1강의 혼돈의 벽을 제도로 허문 사례라고 할 수 있습니다. 또한 100%를 목표로 삼는 것 자체가 안티패턴이라는 점도 알려줍니다. 100% 가용성을 목표로 하면 모든 변경이 위험 요소가 되고, 결국 1강의 악순환으로 돌아갑니다. 사용자는 99.99%와 100%의 차이를 체감하기 어렵지만, 그 차이를 메우는 비용은 막대합니다.

Alert Fatigue: 알림이 많을수록 덜 안전하다

도입부의 새벽 3시 알림으로 돌아가 봅시다. 이 상황을 알림 피로(Alert Fatigue)라고 부릅니다. 의미 없는 알림이 반복되면 사람은 알림 자체를 무시하게 됩니다. 3강에서 본 Flaky Test와 똑같은 일탈의 정상화입니다.

알림 피로의 가장 흔한 원인은 원인(cause)에 알림을 거는 것입니다. CPU 85%는 원인일 수도 있지만, 사용자에게 아무 영향이 없을 수도 있습니다. 반대로 CPU는 멀쩡한데 외부 API 지연으로 사용자가 고통받을 수도 있습니다.

그래서 SRE의 원칙은 증상(symptom)에 알림을 걸라는 것입니다. 사용자가 겪는 오류율과 지연시간, 즉 SLO에 영향을 주는 신호에만 사람을 깨우는 알림을 겁니다. 더 정교하게는 오류 예산이 소진되는 속도(burn rate)를 기준으로 삼습니다. "이 속도로 가면 한 시간 안에 한 달치 예산을 다 쓴다"면 즉시 호출하고, "사흘 안에 다 쓴다"면 업무 시간에 확인할 티켓을 만드는 식입니다.

좋은 알림의 기준은 간단합니다. 사람을 깨우는 모든 알림은 즉시 행동이 필요하고, 그 행동이 무엇인지 알 수 있어야 합니다. 행동할 게 없는 알림은 대시보드로 옮기고, 매번 같은 행동을 반복하는 알림은 자동화 대상입니다. 각 알림에는 대응 절차를 담은 런북(runbook) 링크를 붙여 둡니다.

회복탄력성 패턴: 실패를 전제로 설계하기

관측이 "실패를 빨리 아는 것"이라면, 회복탄력성 패턴은 "실패가 번지지 않게 하는 것"입니다. Michael Nygard가 2007년 『Release It!』에서 정리한 패턴들이 지금도 표준처럼 쓰입니다.

타임아웃(Timeout). 외부 호출에는 반드시 제한 시간을 둡니다. 타임아웃이 없는 호출은 상대 서비스가 멈추면 호출한 쪽의 스레드와 커넥션까지 함께 붙잡혀, 장애가 연쇄적으로 번집니다.

재시도와 백오프(Retry with Backoff). 일시적인 실패는 재시도로 해결될 수 있습니다. 그런데 여기에 안티패턴이 숨어 있습니다. 상대 서비스가 과부하로 느려졌을 때 모든 클라이언트가 즉시, 여러 번 재시도하면 부하가 몇 배로 불어나 서비스가 완전히 무너집니다. 이를 재시도 폭풍(Retry Storm)이라고 부릅니다. 재시도 간격을 지수적으로 늘리고(exponential backoff), 여기에 무작위 편차(jitter)를 더해 재시도가 한 시점에 몰리지 않게 해야 합니다. 재시도 횟수에도 상한이 필요합니다.

서킷 브레이커(Circuit Breaker). 전기 차단기처럼 동작합니다. 특정 외부 서비스로의 호출이 일정 비율 이상 실패하면 회로를 열어 한동안 호출 자체를 하지 않고 즉시 실패(또는 대체 응답)를 반환합니다. 일정 시간 뒤 일부 호출만 시험적으로 흘려보내고, 성공하면 회로를 다시 닫습니다. 망가진 서비스에 계속 요청을 보내 서로를 더 망가뜨리는 것을 막아줍니다.

격벽(Bulkhead). 배의 격벽처럼 자원을 구획으로 나눕니다. 결제 API 호출용 커넥션 풀과 검색 API 호출용 커넥션 풀을 분리해 두면, 결제 쪽이 막혀도 검색은 계속 동작합니다.

한 걸음 더 나간 접근이 2010년대 초 Netflix가 대중화한 카오스 엔지니어링(Chaos Engineering)입니다. Chaos Monkey는 운영 환경에서 무작위로 인스턴스를 종료시킵니다. 실패를 일부러 일으켜서, 위 패턴들이 실제로 작동하는지 평소에 검증하는 것이죠. 2강의 Phoenix Server, 5강의 롤백 연습과 같은 철학입니다. 실패를 일상으로 만들어, 진짜 실패가 특별한 일이 되지 않게 한다.

Blameless Postmortem: 사람을 탓하지 않는 이유

장애가 끝나면 회고가 남습니다. 여기서 가장 파괴적인 안티패턴이 비난 문화(Blame Culture)입니다. "누가 이 설정을 바꿨나", "왜 확인하지 않았나"로 시작하는 회고입니다.

비난은 당장은 책임을 묻는 것처럼 보이지만 결과는 반대입니다. 사람들은 다음 장애에서 자기 실수를 숨기고, 정보는 사라지며, 같은 장애가 반복됩니다. 1강에서 안티패턴은 국소적으로 합리적인 선택이 쌓인 구조라고 했습니다. 사람을 바꿔도 구조가 그대로면 다음 사람이 같은 실수를 합니다.

2012년 Etsy의 John Allspaw(1강에서 Flickr 발표를 했던 바로 그 사람입니다)는 Blameless Postmortem(비난 없는 사후 검토)을 소개하는 글에서, 항공 안전 분야의 Sidney Dekker가 발전시킨 공정한 문화(Just Culture) 개념을 소프트웨어 운영에 가져왔습니다. 핵심 질문을 "누가 잘못했나"에서 "그 사람은 그 시점에 왜 그 행동이 합리적이라고 판단했나"로 바꾸는 것입니다.

이 질문을 던지면 구조가 보이기 시작합니다. 설정 화면에 운영과 스테이징이 구분되지 않았다거나, 경고가 평소에도 늘 떠 있어 무시하는 게 일상이었다거나, 새벽 시간에 혼자 대응해야 했다는 사실이 드러납니다. 개선 대상은 사람이 아니라 이런 조건들입니다.

좋은 회고가 피하는 함정이 두 가지 더 있습니다.

후견지명 편향(Hindsight Bias). 결과를 알고 나면 "그때 당연히 알았어야 했다"고 느끼기 쉽습니다. 하지만 당시 당사자는 결과를 몰랐고, 수많은 신호 중 어떤 것이 중요한지 알 수 없었습니다.

단일 근본 원인의 신화. "근본 원인은 X였다"로 끝나는 회고는 대개 너무 단순합니다. 복잡한 시스템의 장애는 여러 기여 요인(contributing factors)이 겹쳐서 일어납니다. 5강의 Knight Capital 사건만 봐도 플래그 재사용, 배포 누락, 감지 장치 부재가 모두 필요했습니다. 하나만 없었어도 사고는 일어나지 않았거나 훨씬 작았을 겁니다.

회고의 결과물은 반성문이 아니라 구체적인 개선 조치여야 하고, 각 조치에는 담당자와 기한이 있어야 합니다.

영웅 문화와 버스 팩터

운영 영역의 마지막 안티패턴은 영웅 문화(Hero Culture)입니다. 장애가 날 때마다 특정 한 사람이 새벽에 나타나 해결합니다. 조직은 그 사람을 칭찬하고, 그 사람은 점점 더 많은 시스템을 혼자 알게 됩니다.

이 상태의 위험을 표현하는 말이 버스 팩터(Bus Factor)입니다. "몇 명이 버스에 치이면 프로젝트가 멈추는가"라는 다소 섬뜩한 질문이죠. 영웅 문화의 버스 팩터는 1입니다. 그 사람이 휴가를 가거나 퇴사하면, 2강 도입부의 "아무도 모르는 서버"가 탄생합니다.

대응은 지식을 사람 밖으로 꺼내는 것입니다. 런북을 작성하고, 온콜을 여러 사람이 돌아가며 맡고, 장애 대응에 다른 사람을 참여시켜 함께 배우게 합니다. 반복되는 수작업은 SRE에서 말하는 토일(toil)로 규정해 자동화 대상으로 삼습니다. 영웅이 필요 없는 시스템이 좋은 시스템입니다.

안티패턴 한눈에 보기

안티패턴증상대응 패턴
로그만 쌓기장애 때 서버마다 grep구조화 로그, 상관관계 ID, 트레이스
100% 가용성 목표모든 변경이 위험 요소SLO, Error Budget
원인 기반 알림행동할 게 없는 새벽 호출증상 기반 알림, burn rate
Alert Fatigue진짜 알림을 놓침행동 가능한 알림만, 런북
무한 재시도장애가 몇 배로 증폭백오프와 지터, 서킷 브레이커
비난 문화실수 은폐, 같은 장애 반복Blameless Postmortem
영웅 문화버스 팩터 1런북, 온콜 순환, 토일 자동화

정리

운영에서의 빠른 피드백은 예상하지 못한 질문에도 답할 수 있는 관측 가능성에서 출발합니다. SLO와 Error Budget은 안정성을 공유된 숫자로 바꿔 1강의 개발·운영 갈등을 제도적으로 풀고, 알림은 사용자가 겪는 증상과 예산 소진 속도에만 걸어야 알림 피로를 피할 수 있습니다. 회복탄력성 패턴은 실패가 번지지 않게 막고, 카오스 엔지니어링은 그 패턴이 실제로 작동하는지 평소에 검증합니다. 그리고 장애 이후에는 사람이 아니라 구조를 고치는 Blameless Postmortem이 같은 장애의 반복을 막습니다.

이번 강의 후반부에서 이미 기술보다 문화의 비중이 커졌습니다. 다음 7강에서는 이 흐름을 이어 조직 구조 자체를 다룹니다. 왜 "DevOps 팀"을 만드는 것이 오히려 안티패턴일 수 있는지, Conway의 법칙과 Team Topologies가 무엇을 말하는지 살펴봅니다.

COMMENTS (…)

댓글을 불러오는 중이에요.

NEW COMMENT0 / 1000
⌘↵ 전송

blog92@web:~$ cd ..