Web Operations 실전 훈련북

이 문서는 읽는 문서가 아니라 쓰면서 확인하는 훈련 문서입니다.

  • 운영 상황에서 먼저 확인할 증거를 직접 고른다.
  • 답안을 쓴 뒤 해설과 비교한다.
  • 연결 문서로 돌아가 판단 기준을 다시 다진다.

이 문서의 사용법

이 문서는 기존 Web Operations 문서의 요약본이 아니다.

이미 만든 91. Web Operations 실전 가이드북이 “이 상황에서는 어떤 문서를 다시 열어야 하는가”를 알려주는 지도라면, 이 문서는 그 지도를 보고 직접 판단을 써보는 문제집이다.

사용 순서는 단순하다.

  1. 훈련 상황과 Evidence를 읽는다.
  2. 먼저 써보기에 자기 답안을 적는다.
  3. 힌트를 보고 빠진 관점이 있는지 확인한다.
  4. 해설과 비교한다.
  5. 연결 문서를 다시 열어 기준을 보강한다.
  6. 반복 기록표에 놓친 관점과 다음 액션을 남긴다.

답안은 길 필요가 없다.

중요한 것은 “나는 무엇을 먼저 확인했고, 왜 그 순서가 안전한가”를 남기는 것이다.

훈련 방식

훈련은 세 가지 형식으로 나뉜다.

형식목적결과물
Lab작은 운영 산출물을 직접 작성한다표, 체크리스트, 확인 순서, 판단표
Incident장애 상황에서 첫 판단과 복구 순서를 쓴다5분 triage, 원인 후보, 복구 절차, 완료 검증
Review그럴듯하지만 부족한 설계를 검토한다숨은 리스크, 빠진 운영 계약, 승인 조건

각 훈련에는 일부러 완성되지 않은 정보가 들어 있다.

실제 운영도 처음부터 모든 정보가 정리되어 있지 않기 때문이다.

반복 학습 루프

한 번 풀고 끝내지 않는다.

같은 훈련을 다음 순서로 다시 푼다.

  1. 첫 번째 반복: 연결 문서를 보지 않고 답안을 쓴다.
  2. 두 번째 반복: 연결 문서를 열고 답안을 고친다.
  3. 세 번째 반복: 해설을 가리고 5분 안에 첫 대응 순서를 쓴다.
  4. 네 번째 반복: 비슷한 개인 프로젝트 상황으로 바꿔 다시 쓴다.

반복할수록 답안은 짧아져야 한다.

짧아진 답안이 근거를 잃지 않는다면 판단 기준이 몸에 들어온 것이다.

훈련 목록

구분훈련핵심 산출물주로 다시 볼 문서
LabLab 1. 배포 전 운영 증거표 만들기release evidence table01. Build Artifact와 Release 단위 상세, 05. 배포 전 설정 체크리스트 상세, 91. Web Operations 실전 가이드북
LabLab 2. Linux 서비스 실행 점검표 만들기systemd/service checklist01. User Permission Process 기본 상세, 02. Service systemd 관리 상세, 91. Web Operations 실전 가이드북
LabLab 3. Nginx와 Spring 요청 경로 확인표 만들기request path checklist01. Reverse Proxy와 Upstream 상세, 05. Nginx 로그와 장애 디버깅 상세, 91. Web Operations 실전 가이드북
LabLab 4. Restore Drill 체크리스트 만들기restore drill checklist01. Backup 종류와 복구 목표 상세, 02. Restore Drill과 검증 상세, 91. Web Operations 실전 가이드북
IncidentIncident 1. 배포 직후 5xx와 migration 의심first 5 minutes and branch decision01. 배포 실패 대응 Runbook 상세, 03. Schema Migration 운영 상세, 91. Web Operations 실전 가이드북
IncidentIncident 2. Nginx 502와 Spring 500 구분layer isolation05. Nginx 로그와 장애 디버깅 상세, 02. API 장애 대응 Runbook 상세, 91. Web Operations 실전 가이드북
IncidentIncident 3. Health는 UP인데 주문 API timeouthealth, APM, dependency triage02. Liveness Readiness Health Check 상세, 01. APM이 보는 지표 상세, 91. Web Operations 실전 가이드북
IncidentIncident 4. Pod는 Running인데 Ingress 404Kubernetes request path01. Pod Deployment Service Ingress 상세, 03. Probe Resource Limit 상세, 91. Web Operations 실전 가이드북
ReviewReview 1. Canary 배포 설계안 리뷰stop condition and rollback contract03. Canary와 점진적 트래픽 전환 상세, 05. 배포 전후 검증 체크리스트 상세, 91. Web Operations 실전 가이드북
ReviewReview 2. 관측 설계안 리뷰log, metric, trace, alert contract05. 로그 메트릭 트레이스 연결 분석 상세, 03. Alert Rule과 Noise 관리 상세, 91. Web Operations 실전 가이드북
ReviewReview 3. Managed Service 도입 설계안 리뷰remaining responsibility map05. Managed Service 선택 기준 상세, 02. VPC Subnet Security Group 상세, 91. Web Operations 실전 가이드북
Final최종 종합 훈련end-to-end incident response00. Web Operations Runbook 압축 정리, 91. Web Operations 실전 가이드북

Part 1. 실습 랩

Lab 1. 배포 전 운영 증거표 만들기

상황

주문 API order-api 새 버전을 오늘 17시에 운영 배포하려 한다.

개발자는 기능 테스트가 끝났다고 말하고, 배포 담당자는 “pipeline이 초록이면 바로 진행해도 되지 않나”라고 묻는다.

당신은 배포 전에 장애 대응자가 볼 수 있는 운영 증거표를 작성해야 한다.

제공된 Evidence

배포 정보는 다음과 같다.

service: order-api
env: prod
git_sha: 8f32ab1
image_tag: order-api:2026-07-01.3
image_digest: sha256:9be7...
active_profile: prod
changed_config:
  ORDER_TIMEOUT_MS: 3000 -> 2000
  PAYMENT_CLIENT_RETRY: 2 -> 1
migration:
  V20260701_01__add_payment_attempt_column.sql
smoke_test:
  GET /actuator/health -> 200
  GET /orders/123 -> 200
  POST /orders -> not tested
rollback_note:
  "previous image exists"

해야 할 일

다음 열을 가진 배포 전 운영 증거표를 작성한다.

항목현재 값확인 위치기대 결과실패 시 중단 조건
artifact
config/profile
migration
health/smoke
rollback

먼저 써보기

힌트

  • previous image exists만으로 rollback 가능하다고 말할 수 있는가?
  • 쓰기 API smoke test가 빠져도 배포를 진행할 수 있는가?
  • migration이 있으면 이전 버전과 schema 호환성을 확인해야 한다.
  • timeout과 retry 변경은 사용자가 느끼는 실패율과 latency를 바꿀 수 있다.

해설

좋은 답안은 “배포할 수 있다/없다”보다 “어떤 증거를 더 확인해야 배포를 진행할 수 있는가”를 먼저 적는다.

artifact는 tag뿐 아니라 digest와 Git SHA까지 맞아야 한다.

config는 active profile과 실제 environment source에서 확인해야 한다.

migration은 실행 여부보다 rollback 시 이전 버전이 새 schema를 읽을 수 있는지가 중요하다.

health는 process 생존 확인이고, smoke test는 핵심 사용자 경로 확인이다. 주문 생성 같은 쓰기 경로가 핵심이라면 사전에 정한 테스트 계정과 정리 방법이 있어야 한다.

연결 문서

가져갈 판단 기준

배포 전 증거표는 배포를 빠르게 하기 위한 문서가 아니라, 실패했을 때 안전하게 멈추기 위한 문서다.

Lab 2. Linux 서비스 실행 점검표 만들기

상황

order-api가 VM에서 systemd 서비스로 실행된다.

지난주에는 log directory 권한 문제로 서비스가 시작되지 않았고, 누군가 임시로 권한을 넓혀 해결했다.

이번에는 같은 일이 반복되지 않도록 운영 서버 실행 점검표를 작성해야 한다.

제공된 Evidence

$ systemctl status order-api
Loaded: loaded (/etc/systemd/system/order-api.service; enabled)
Active: failed (Result: exit-code)
 
$ ls -l /opt/order-api/app.jar
-rw-r----- 1 deploy deploy 92818342 Jul  1 16:42 app.jar
 
$ ls -ld /var/log/order-api
drwxr-x--- 2 root root 4096 Jul  1 16:10 /var/log/order-api
 
$ grep -E 'User|EnvironmentFile|WorkingDirectory' /etc/systemd/system/order-api.service
User=app
WorkingDirectory=/opt/order-api
EnvironmentFile=/etc/order-api/order-api.env

해야 할 일

다음 질문을 포함한 실행 점검표를 작성한다.

  • 어떤 사용자가 서비스를 실행하는가?
  • jar, env file, log directory를 그 사용자가 읽거나 쓸 수 있는가?
  • 먼저 실행할 읽기 명령은 무엇인가?
  • 변경 명령을 실행한다면 범위를 어디까지 좁힐 것인가?
  • 변경 후 완료 검증은 무엇인가?

먼저 써보기

힌트

  • app 사용자가 jar를 읽을 수 있는가?
  • log directory는 app 사용자가 쓸 수 있는가?
  • chmod -R 777은 문제를 빨리 숨기지만 다음 배포를 더 위험하게 만든다.
  • systemd 서비스는 process, 파일 권한, 환경 파일, restart 정책을 함께 봐야 한다.

해설

좋은 점검표는 변경 명령보다 읽기 명령이 먼저 나온다.

먼저 id app, ls -l, namei -l, journalctl -u order-api 같은 명령으로 현재 상태를 좁힌다.

그다음 필요한 권한만 고친다. 예를 들어 jar 소유자나 group을 service 실행 사용자 기준으로 맞추고, log directory는 app이 쓸 수 있게 제한적으로 변경한다.

완료 검증은 systemctl restart 성공만으로 끝내지 않는다. journal, application log, port listen, health, 핵심 API smoke test까지 확인한다.

연결 문서

가져갈 판단 기준

운영 서버 권한 문제는 넓게 열어 해결하는 것이 아니라 실행 주체와 필요한 접근 범위를 좁혀 해결한다.

Lab 3. Nginx와 Spring 요청 경로 확인표 만들기

상황

신규 API /api/orders가 배포됐다.

브라우저에서는 502가 보이지만, 개발자는 Spring 로그에 에러가 없다고 말한다.

Nginx, upstream, Spring port, DB까지 요청 경로를 따라 확인표를 만들어야 한다.

제공된 Evidence

# nginx access log
10.0.1.23 - - [01/Jul/2026:17:12:04 +0900] "POST /api/orders HTTP/1.1" 502 157 "-" "mobile" "req-7f3a"
 
# nginx error log
connect() failed (111: Connection refused) while connecting to upstream,
upstream: "http://127.0.0.1:8080/api/orders"
 
# server
$ ss -lntp | grep java
LISTEN 0 100 0.0.0.0:8081 0.0.0.0:* users:(("java",pid=4821))

해야 할 일

요청 경로 확인표를 작성한다.

구간확인할 증거기대 상태현재 단서다음 행동
Client Nginx
Nginx upstream
upstream Spring
Spring dependency

먼저 써보기

힌트

  • 502는 Spring 비즈니스 예외가 아닐 수 있다.
  • error log의 Connection refused는 upstream port와 실제 listen port를 비교해야 한다.
  • Spring log가 비어 있는 것은 요청이 Spring까지 도달하지 않았다는 증거일 수 있다.
  • Nginx 설정을 바꾸기 전에 현재 upstream과 process port를 먼저 확인한다.

해설

이 Evidence에서는 Nginx가 127.0.0.1:8080으로 보내고 있지만 Java process는 8081에서 listen 중이다.

따라서 첫 원인 후보는 Spring 코드가 아니라 upstream port 불일치다.

좋은 답안은 Nginx access log, error log, Spring application log를 같은 request id와 같은 시간대로 묶는다.

수정 후에는 단순히 200 한 번만 보지 말고 access log status, Spring request log, 핵심 API smoke test를 함께 확인한다.

연결 문서

가져갈 판단 기준

Spring 로그가 조용한 502는 애플리케이션이 아니라 요청 경로의 앞단에서 멈춘 신호일 수 있다.

Lab 4. Restore Drill 체크리스트 만들기

상황

운영 DB는 매일 새벽 snapshot을 만들고 있다.

팀은 “백업이 있으니 괜찮다”고 말하지만, 실제로 복구를 해본 적은 없다.

당신은 주문 데이터와 업로드 영수증 파일까지 포함하는 Restore Drill 체크리스트를 작성해야 한다.

제공된 Evidence

backup:
  db_snapshot: daily 03:00 KST
  pitr: enabled, 7 days
  object_storage_versioning: enabled
  receipt_bucket_backup: weekly
 
target:
  orders table
  payment_attempts table
  receipt object key stored in orders.receipt_key
 
known risk:
  DB row and receipt object can be restored to different points in time.

해야 할 일

다음을 포함한 Restore Drill 체크리스트를 작성한다.

  • 복구 목표: RPO, RTO
  • 복구 범위: DB, object storage, config
  • 별도 환경 복구 절차
  • 애플리케이션 검증
  • 데이터 검증 SQL 또는 확인 기준
  • 완료 선언 조건

먼저 써보기

힌트

  • 백업 성공과 복구 가능성은 다르다.
  • DB만 복구하면 object key가 가리키는 파일 시점이 맞지 않을 수 있다.
  • drill은 운영에 직접 덮어쓰는 절차가 아니라 별도 환경에서 검증하는 절차다.
  • 완료 검증에는 애플리케이션이 실제로 데이터를 읽을 수 있는지까지 들어가야 한다.

해설

좋은 체크리스트는 “snapshot이 존재한다”에서 멈추지 않는다.

복구 시점, 복구 대상, 검증 환경, 애플리케이션 기동, 핵심 API 조회, 파일 다운로드, count/sum 검증을 모두 포함한다.

특히 DB row와 object storage 파일은 다른 저장소이므로 같은 사고 시점으로 맞출 방법을 미리 적어야 한다.

Restore Drill의 성공 기준은 restore job 성공이 아니라 서비스가 복구된 데이터를 안전하게 읽는 것이다.

연결 문서

가져갈 판단 기준

백업은 존재로 증명되지 않고, 복구된 서비스를 검증할 때 증명된다.

Part 2. 장애 대응 훈련

Incident 1. 배포 직후 5xx와 migration 의심

Incident 상황

오늘 16:50에 주문 API 새 버전을 100% 배포했다.

17:05부터 주문 생성 5xx가 증가했고, 동시에 payment 관련 migration이 실행된 것으로 보인다.

팀은 rollback을 말하고 있지만, migration이 이미 실행되어 이전 버전 호환성이 불확실하다.

현재 보이는 증상

  • POST /orders 5xx가 0.2%에서 8.5%로 증가
  • GET /orders/{id}는 정상
  • 새 버전 Pod는 readiness 통과
  • DB migration log에 payment_attempt column 추가 이력 존재
  • 고객센터에 주문 실패 문의가 들어오기 시작함

로그/지표/명령 단서

17:04:58 ERROR order-api PaymentAttemptRepository
SQLGrammarException: column payment_attempt_id does not exist
 
deploy:
  previous_image: order-api:2026-06-30.8
  current_image: order-api:2026-07-01.3
  migration: V20260701_01__add_payment_attempt_column.sql
 
apm:
  endpoint=/orders method=POST error_rate=8.5% p95=2300ms
  endpoint=/orders/{id} method=GET error_rate=0.1% p95=180ms

5분 안에 확인할 것

원인 후보 분류

하면 안 되는 조치

  • migration 호환성 확인 전 전체 rollback
  • 실패 로그와 migration 이력 보존 전 재배포
  • 주문 쓰기 실패 범위 확인 전 장애 종료 선언
  • DB column을 즉흥적으로 삭제하거나 이름 변경

복구 절차

  1. 추가 배포를 멈추고 현재 artifact, migration version, 장애 시작 시각을 기록한다.
  2. 쓰기 API 영향 범위와 정상 조회 API를 분리한다.
  3. migration이 실제 어떤 DB에 적용됐는지 확인한다.
  4. 이전 버전이 현재 schema와 호환되는지 확인한다.
  5. 불확실하면 feature flag, 쓰기 차단, forward fix 중 가장 피해가 작은 선택지를 고른다.
  6. 조치 후 주문 생성 성공률, error rate, DB error log를 확인한다.

완료 검증

  • POST /orders error rate가 정상 범위로 회복
  • 실패 주문의 재시도 또는 보정 대상 범위 확정
  • migration version과 실행 결과 기록
  • 배포 실패 보고서에 rollback 가능 여부와 선택 이유 기록

회고 질문

  • migration PR에 이전 버전 호환성 확인이 있었는가?
  • smoke test에 쓰기 API가 포함되어 있었는가?
  • 배포 전 중단 조건이 error rate만이 아니라 DB schema 호환성까지 포함했는가?

연결 문서

가져갈 판단 기준

데이터 구조가 바뀐 배포에서는 rollback 버튼보다 schema 호환성 질문이 먼저다.

Incident 2. Nginx 502와 Spring 500 구분

Incident 상황

사용자는 결제 화면에서 간헐적으로 실패를 본다.

관제 화면에는 5xx가 증가한다고만 보이고, 팀 안에서는 Nginx 문제인지 Spring 문제인지 말이 갈린다.

현재 보이는 증상

  • /payments/confirm 요청 중 일부가 502
  • /orders 조회는 정상
  • Spring error log에는 같은 시간대 500 stacktrace가 적다
  • Nginx error log에도 upstream timeout이 있다

로그/지표/명령 단서

# nginx access log
17:22:01 "POST /payments/confirm" 502 request_id=req-a
17:22:03 "POST /payments/confirm" 500 request_id=req-b
 
# nginx error log
17:22:01 upstream timed out while reading response header from upstream, request: req-a
 
# spring log
17:22:03 ERROR request_id=req-b PaymentClientException: PG declined
 
# metric
payment_client_latency_p95=4800ms
nginx_upstream_response_time_p95=5100ms

5분 안에 확인할 것

원인 후보 분류

하면 안 되는 조치

  • 502와 500을 같은 원인으로 묶어 단정
  • request id 없이 로그 샘플만 비교
  • timeout만 크게 늘리고 외부 의존성 지연을 보지 않음
  • Spring stacktrace가 있으니 Nginx는 무관하다고 결론

복구 절차

  1. 같은 시간대의 Nginx access log, error log, Spring log를 request id로 묶는다.
  2. 502 요청이 Spring까지 도달했는지 확인한다.
  3. 500 요청은 Spring 내부 예외인지 외부 PG 응답 오류인지 분리한다.
  4. 외부 PG latency와 timeout 설정을 비교한다.
  5. 임시 완화로 결제 재시도 제한, timeout 조정, 장애 공지, traffic 제한 중 필요한 조치를 고른다.

완료 검증

  • 502와 500의 원인 후보가 분리됨
  • 결제 API error rate와 p95 회복
  • Nginx upstream timeout 감소
  • Spring payment client 예외율 감소
  • 장애 보고서에 request id 샘플과 시간대가 남음

회고 질문

  • Nginx와 Spring log에 같은 request id가 항상 남는가?
  • timeout 기준이 endpoint별로 분리되어 있는가?
  • 외부 결제 의존성 지연 alert가 있는가?

연결 문서

가져갈 판단 기준

같은 5xx라도 request id와 시간대를 맞추기 전에는 같은 장애라고 부르지 않는다.

Incident 3. Health는 UP인데 주문 API timeout

Incident 상황

Load Balancer target은 healthy이고 /actuator/health도 UP이다.

하지만 주문 생성 API p95가 급격히 오르고 일부 요청은 timeout으로 실패한다.

DB 콘솔은 available로 표시된다.

현재 보이는 증상

  • /actuator/health 200
  • /orders POST p95 7.8s
  • DB connection pool active가 max에 가까움
  • thread dump에 JDBC 대기 thread 증가
  • error rate는 낮지만 timeout 문의 증가

로그/지표/명령 단서

HikariPool-1 - Connection is not available, request timed out after 3000ms.
 
metric:
  http.server.requests{uri="/orders",status="200",quantile="0.95"} 7.8
  hikaricp.connections.active 30
  hikaricp.connections.max 30
  jvm.threads.live 220
 
health:
  status: UP

5분 안에 확인할 것

원인 후보 분류

하면 안 되는 조치

  • health가 UP이라는 이유로 장애가 아니라고 판단
  • connection pool만 크게 늘림
  • DB available만 보고 애플리케이션 의존성을 정상으로 결론
  • timeout 요청의 사용자 영향 범위를 확인하지 않음

복구 절차

  1. 핵심 API의 latency, timeout, error rate를 확인한다.
  2. connection pool active, pending, timeout을 본다.
  3. 느린 query 또는 외부 API 지연과 같은 하위 의존성을 확인한다.
  4. 필요하면 traffic 제한, 기능 제한, read-only 전환, pool 조정 중 피해가 작은 완화를 고른다.
  5. readiness가 실제 요청 가능 상태를 대표하는지 점검한다.

완료 검증

  • 주문 생성 p95가 정상 범위로 회복
  • connection pool pending과 timeout 감소
  • 사용자 실패율 감소
  • health check와 핵심 SLI의 차이를 보고서에 기록

회고 질문

  • readiness에 어떤 의존성을 포함해야 하는가?
  • pool 포화 alert와 API latency alert가 연결되어 있는가?
  • health UP과 사용자 성공률을 구분해 보고했는가?

연결 문서

가져갈 판단 기준

Health UP은 장애가 없다는 증거가 아니라 다음 확인을 시작할 수 있다는 신호다.

Incident 4. Pod는 Running인데 Ingress 404

Incident 상황

Kubernetes로 옮긴 order-api 새 Deployment가 Running 상태다.

readiness도 통과하지만 외부 URL /api/orders는 404를 반환한다.

Pod log에는 요청이 거의 없고, Ingress controller log에는 backend를 찾지 못했다는 메시지가 있다.

현재 보이는 증상

  • kubectl get pod는 Running
  • readiness probe 통과
  • 외부 URL은 404
  • Service endpoint가 비어 있음
  • Ingress rule은 새 host/path를 가리킴

로그/지표/명령 단서

$ kubectl get pod --show-labels
order-api-7f9c8d app=order-api,version=v2
 
$ kubectl describe svc order-api
Selector: app=orders-api
Endpoints: <none>
 
$ kubectl describe ingress order-api
Rules:
  host: api.example.com
  path: /api/orders -> service order-api:80

5분 안에 확인할 것

원인 후보 분류

하면 안 되는 조치

  • Pod가 Running이라는 이유로 애플리케이션 문제라고 단정
  • 원인 확인 전 Deployment 전체 재시작
  • selector 변경의 영향 범위 확인 없이 운영 Service 수정
  • Ingress 404와 Spring 404를 구분하지 않음

복구 절차

  1. Ingress가 어떤 Service를 가리키는지 확인한다.
  2. Service selector와 Pod label을 비교한다.
  3. Endpoint가 비어 있는지 확인한다.
  4. selector 수정 또는 label 수정 중 더 안전한 조치를 고른다.
  5. 외부 요청이 Pod log에 도달하는지 확인한다.
  6. readiness와 실제 application route를 함께 검증한다.

완료 검증

  • Service endpoint가 채워짐
  • Ingress 요청이 Pod log에 남음
  • /api/orders smoke test 성공
  • target health와 readiness가 모두 정상
  • 잘못된 selector를 배포 전 검증하는 체크리스트 추가

회고 질문

  • Deployment label과 Service selector를 배포 전 검증했는가?
  • Running, Ready, Reachable을 분리해서 보고했는가?
  • Ingress 404와 application 404를 로그로 구분할 수 있는가?

연결 문서

가져갈 판단 기준

Kubernetes에서 Running은 시작점이고, Service endpoint와 Ingress 도달이 운영 증거다.

Part 3. 설계 리뷰 훈련

Review 1. Canary 배포 설계안 리뷰

제안된 설계안

새 주문 할인 기능을 canary로 배포한다.

트래픽은 5%, 25%, 50%, 100% 순서로 10분마다 올린다.

각 단계에서 /actuator/health가 UP이면 다음 단계로 진행한다.

문제가 생기면 이전 image로 rollback한다.

겉보기 장점

  • 한 번에 100% 배포하지 않는다.
  • 단계별로 트래픽을 올린다.
  • health check를 본다.
  • rollback image가 있다.

숨은 리스크

  • health UP만으로 주문 성공률을 대표하지 못한다.
  • 할인 기능은 특정 사용자군과 특정 상품에서만 실패할 수 있다.
  • DB schema나 cache key 변경이 있으면 image rollback만으로 복구되지 않을 수 있다.
  • 각 단계의 중단 조건이 숫자로 정의되어 있지 않다.
  • 관측 창 10분이 충분한지 근거가 없다.

리뷰 질문

  • 어떤 SLI가 canary 중단 조건인가?
  • 새 기능을 사용하는 사용자군과 endpoint를 따로 볼 수 있는가?
  • rollback하면 DB와 cache가 이전 코드와 호환되는가?
  • 5%에서 볼 sample 수가 충분한가?
  • 누가 다음 단계 진행을 승인하는가?

빠진 운영 계약

  • canary별 error rate, p95, saturation 중단 기준
  • feature flag 또는 traffic 고정 방법
  • DB migration 호환성 확인
  • 단계별 관측 시간과 승인자
  • rollback 후 검증 항목

개선된 설계 방향

단계별 트래픽 비율보다 중단 조건을 먼저 둔다.

health UP 외에 주문 생성 성공률, 할인 적용 오류율, latency, DB error, cache hit 변화 같은 사용자 영향 지표를 본다.

DB 변경이 있다면 Expand and Contract 방식과 rollback 가능성을 별도로 검토한다.

승인 조건

  • 각 단계별 중단 조건이 숫자로 적혀 있다.
  • rollback 또는 feature flag off 절차가 검증되어 있다.
  • canary 대상 사용자군과 endpoint 지표가 분리되어 있다.
  • 배포 후 검증 체크리스트가 owner와 함께 준비되어 있다.

연결 문서

가져갈 판단 기준

Canary의 핵심은 천천히 올리는 것이 아니라 멈출 이유를 미리 합의하는 것이다.

Review 2. 관측 설계안 리뷰

제안된 설계안

새 결제 API를 출시하면서 다음 관측 계획을 세웠다.

로그는 기존처럼 text log로 남긴다.

metric은 전체 API 5xx 비율만 본다.

trace는 나중에 필요하면 붙인다.

alert는 5xx가 5분 이상 1%를 넘으면 Slack으로 보낸다.

겉보기 장점

  • 최소한의 로그와 5xx alert가 있다.
  • 기존 시스템을 크게 바꾸지 않는다.
  • alert 기준이 숫자로 적혀 있다.

숨은 리스크

  • 결제 요청 단위 correlation id가 없으면 Nginx, Spring, PG client 로그를 연결하기 어렵다.
  • 전체 5xx 비율은 결제 API 장애를 늦게 보여줄 수 있다.
  • latency와 timeout은 5xx보다 먼저 나빠질 수 있다.
  • alert owner와 runbook이 없다.
  • trace가 없으면 외부 PG, DB, 내부 로직 중 어디가 느린지 보기 어렵다.

리뷰 질문

  • 결제 요청 하나를 로그, metric, trace에서 같은 id로 찾을 수 있는가?
  • 5xx 외에 p95, timeout, PG latency, pool saturation을 보는가?
  • alert를 받은 사람은 어떤 runbook을 열어야 하는가?
  • 민감 정보가 로그에 남지 않게 필드 설계가 되어 있는가?

빠진 운영 계약

  • request id/correlation id 필드
  • endpoint별 error rate와 latency metric
  • PG client span 또는 외부 의존성 metric
  • alert owner, severity, runbook link
  • 민감 정보 masking 기준

개선된 설계 방향

결제 API는 전체 API 평균이 아니라 endpoint별 SLI로 본다.

로그에는 request id, user id hash, endpoint, status, latency, error code를 구조화해 남긴다.

metric은 error rate, p95/p99, throughput, saturation을 분리한다.

alert는 “누가 무엇을 할지”와 runbook이 연결되어야 한다.

승인 조건

  • Nginx와 Spring log에 같은 request id가 남는다.
  • 결제 API 전용 dashboard가 있다.
  • alert에 owner와 연결 runbook이 있다.
  • 장애 보고서에 남길 수 있는 metric query와 log filter가 정해져 있다.

연결 문서

가져갈 판단 기준

관측 설계의 목표는 화면을 많이 만드는 것이 아니라 같은 사건을 여러 신호로 설명하게 만드는 것이다.

Review 3. Managed Service 도입 설계안 리뷰

제안된 설계안

팀은 VM의 MySQL을 Managed DB로 이전하려 한다.

설계안에는 “DB 운영 책임은 클라우드가 처리하므로 애플리케이션은 endpoint만 바꾸면 된다”라고 적혀 있다.

connection pool과 subnet, security group, backup restore drill은 별도 항목으로 빠져 있다.

겉보기 장점

  • DB patch와 장애 조치 부담이 줄어든다.
  • snapshot과 monitoring 기능을 사용할 수 있다.
  • VM 디스크 관리 부담이 줄어든다.

숨은 리스크

  • connection pool 합계가 DB max connection을 넘을 수 있다.
  • private subnet, route, security group이 잘못되면 애플리케이션만 timeout을 본다.
  • managed 상태가 available이어도 query latency나 pool 대기는 생길 수 있다.
  • backup이 있어도 restore drill이 없으면 복구 가능성을 증명하지 못한다.
  • 책임이 사라지는 것이 아니라 cloud metric과 app metric을 함께 봐야 하는 구조로 바뀐다.

리뷰 질문

  • 모든 instance의 pool 합계가 DB limit 안에 있는가?
  • DB 접근 경로의 source, target, port가 문서화되어 있는가?
  • 장애 시 app log와 DB metric을 같은 시간대로 볼 수 있는가?
  • restore drill과 애플리케이션 검증 절차가 있는가?
  • 누가 cloud console metric과 application metric을 함께 볼 책임이 있는가?

빠진 운영 계약

  • connection pool budget
  • subnet/security group 변경 절차
  • DB metric dashboard와 alert owner
  • backup retention, PITR, restore drill
  • 장애 시 임시 완화 방법

개선된 설계 방향

Managed DB 도입 설계에는 줄어드는 책임과 남는 책임을 따로 적는다.

애플리케이션은 endpoint만 바꾸는 것이 아니라 pool, timeout, retry, readiness, alert, restore drill을 함께 조정해야 한다.

네트워크 경로와 권한은 장애 중 가장 먼저 확인할 수 있도록 표로 남긴다.

승인 조건

  • pool 합계와 DB max connection 비교표가 있다.
  • VPC, subnet, security group 경로가 문서화되어 있다.
  • backup restore drill이 별도 환경에서 검증되어 있다.
  • DB available과 API success rate를 함께 보는 dashboard가 있다.

연결 문서

가져갈 판단 기준

Managed Service 도입은 책임 삭제가 아니라 책임 위치와 관측 경계의 변경이다.

최종 종합 훈련

종합 시나리오

전날 18시에 할인 기능 canary 배포가 있었다.

오늘 오전 10시부터 주문 API p95가 상승했고, 일부 요청은 Nginx 502로 실패한다.

Load Balancer와 /actuator/health는 UP이다.

DB connection active는 평소보다 높고, 영수증 이미지 업로드 중 일부는 object storage 403을 반환한다.

팀은 canary를 중단할지, 전체 rollback을 할지, DB pool을 늘릴지, object storage 권한을 먼저 고칠지 의견이 갈린다.

제공된 Evidence

deploy:
  yesterday 18:00 canary 10%
  feature: discount-coupon-v2
  image: order-api:2026-07-01.3
  migration: none
 
nginx:
  10:03 "POST /orders" 502 request_id=req-901 upstream_response_time=5.0
  10:04 "POST /receipts" 403 request_id=req-914 upstream_response_time=0.2
 
spring:
  req-901 HikariPool timeout after 3000ms
  req-914 ObjectStorageAccessDenied bucket=receipt-prod key=2026/07/01/...
 
metric:
  order_post_p95: 8200ms
  order_post_error_rate: 3.2%
  hikaricp_active: 30/30
  receipt_upload_403_rate: 6.1%
  canary_traffic: 10%
 
health:
  actuator: UP
  lb_target: healthy

해야 할 일

다음을 직접 작성한다.

  • 5분 triage
  • 영향 범위
  • 원인 후보 분류
  • 확인할 로그/지표/명령
  • 하면 안 되는 조치
  • 복구 절차
  • 완료 검증
  • 회고 action
  • 연결 문서

먼저 써보기

힌트

  • 이 상황은 하나의 원인으로만 설명되지 않을 수 있다.
  • Health UP은 핵심 API 성공을 보장하지 않는다.
  • canary 배포, DB pool saturation, Nginx 502, object storage 403을 각각 분리해 본다.
  • 전체 rollback 전에 데이터 변경 여부와 canary traffic 고정 가능성을 확인한다.
  • object storage 403은 DB pool 문제와 별개 경로일 수 있다.

해설

좋은 대응은 먼저 canary traffic을 고정하고 추가 배포를 멈춘다.

그다음 주문 생성 장애와 영수증 업로드 장애를 같은 장애로 묶지 않고 request id, endpoint, metric 기준으로 나눈다.

주문 API는 DB pool saturation과 Nginx upstream timeout을 함께 본다. 502는 Spring이 느려져 Nginx timeout으로 보일 수도 있고, upstream 연결 문제일 수도 있으므로 access log, error log, Spring log를 같은 request id로 묶는다.

영수증 403은 object storage 권한, signed URL, bucket policy, key prefix를 본다.

Health가 UP이어도 readiness와 핵심 API SLI가 실패하면 사용자 영향 장애로 다룬다.

복구는 canary 중단 또는 traffic 고정, pool 임시 조정, feature flag off, object storage 권한 rollback 중 사용자 피해를 줄이는 순서로 고른다.

완료 검증은 p95, error rate, 403 rate, pool active, request log, 핵심 주문 smoke test, 영수증 업로드 확인까지 포함한다.

연결 문서

가져갈 판단 기준

복합 장애에서는 원인을 하나로 줄이기 전에 사용자 영향과 요청 경로를 먼저 나눠야 한다.

반복 기록표

날짜훈련처음 판단놓친 관점다시 볼 문서다음 액션
Lab 1
Lab 2
Lab 3
Lab 4
Incident 1
Incident 2
Incident 3
Incident 4
Review 1
Review 2
Review 3
최종 종합 훈련