CloudWatch
AWS 리소스 기본 지표, CloudWatch Logs, Alarm, SNS/EventBridge 연동을 관리형으로 쓰고 싶을 때 적합하다.
AWS 중심 서비스이고, 관측 스택 운영 부담을 줄이며, 기본 운영 신호부터 빠르게 갖춰야 할 때분류: Layer 6 - 운영 심화: 관측성 & 복원력
Amazon CloudWatch는 AWS 리소스와 애플리케이션에서 나오는 로그, 지표, 알람, 대시보드를 모아 운영자가 서비스 상태를 판단하게 해주는 AWS 관리형 관측 서비스다.
여기서 관측은 “화면에 그래프를 띄우는 일”보다 넓다. 서비스가 느려졌는지 감지하고, 어느 리소스나 작업 경로가 의심스러운지 좁히고, 필요한 사람이나 자동화 경로로 알리는 과정까지 포함한다.
작은 애플리케이션은 서버 한 대의 stdout, 프로세스 상태, 간단한 에러 로그만 봐도 어느 정도 운영할 수 있다. 하지만 AWS 위에서 API 서버, Worker, SQS, RDS, Lambda, ALB, ECS 태스크가 같이 움직이면 상태가 여러 콘솔과 여러 파일로 흩어진다. 장애가 났을 때 “CPU가 높은가”, “큐가 밀렸는가”, “로그가 끊겼는가”, “알림 경로가 죽었는가”를 매번 서비스별로 찾아야 한다.
CloudWatch는 이 한계를 줄이기 위해 AWS 서비스가 내보내는 신호를 공통 형식으로 수집한다. EC2 CPU, RDS 연결 수, SQS backlog, ECS 서비스 CPU, 애플리케이션 로그처럼 출처가 다른 데이터를 같은 시간축 위에서 보게 만들고, 임계값을 넘으면 Alarm을 통해 SNS나 EventBridge로 넘긴다.
프론트엔드에서 Network 탭으로 느린 요청을 찾는다면, CloudWatch는 서버와 AWS 리소스 관점에서 그 요청 뒤의 CPU, 큐, 로그, 알람 상태를 보는 출발점이다.
CloudWatch의 핵심 철학은 “AWS 리소스와 서비스 신호를 관리형 방식으로 먼저 보이게 한다”에 가깝다.
이 철학 때문에 CloudWatch는 AWS 단일 환경의 기본 선택지로 강하다. 반대로 멀티클라우드 전체 관측, 복잡한 PromQL 기반 분석, 코드 수준 APM과 프로파일링까지 한 도구에서 끝내려는 요구에는 단독으로 부족할 수 있다.
CloudWatch 문서를 읽을 때는 서비스 이름보다 데이터 모델을 먼저 잡는 편이 쉽다.
| 용어 | 첫 정의 | 왜 필요한가 |
|---|---|---|
| CloudWatch Metrics | 시간에 따라 쌓이는 숫자 데이터다. CPU 사용률, 요청 수, 에러 수, 큐 길이처럼 그래프로 비교할 수 있는 값이다. | ”느린가”, “늘었는가”, “줄었는가”를 빠르게 감지한다. |
| CloudWatch Logs | 애플리케이션이나 AWS 서비스가 남긴 텍스트 이벤트다. | 숫자가 이상해졌을 때 원인 후보와 요청 단서를 찾는다. |
| CloudWatch Alarms | 특정 지표가 조건을 만족하는지 주기적으로 평가하는 상태 기계다. | 사람이 계속 대시보드를 보지 않아도 상태 전환을 알림이나 자동화로 보낸다. |
| Dashboard | 여러 지표와 로그 위젯을 한 화면에 배치한 운영 화면이다. | 배포 직후나 장애 중에 공통 상황판을 만든다. |
| SNS(Simple Notification Service) | Alarm 알림을 이메일, Slack 연동, 온콜 시스템 같은 구독자에게 fan-out하는 AWS 메시징 서비스다. | Alarm 상태 변화가 사람에게 도달하는 기본 알림 경로가 된다. |
| EventBridge | AWS 서비스의 상태 변화 이벤트를 규칙으로 받아 다른 서비스나 자동화로 넘기는 이벤트 버스다. | Alarm, ECS 태스크 상태 변화, 배포 이벤트를 후속 진단이나 자동화와 연결한다. |
| Namespace | 지표가 속한 논리적 묶음이다. 예: AWS/EC2, AWS/ECS, MyApp/Search. | 지표 이름 충돌을 막고 출처를 구분한다. |
| Dimension | 지표를 더 세부적으로 나누는 key-value다. 예: ServiceName=api, Environment=prod. | 같은 지표를 서비스, 환경, 인스턴스별로 필터링한다. |
| Period | 지표를 집계하는 시간 간격이다. 예: 60초, 5분. | 너무 짧으면 노이즈가 많고, 너무 길면 감지가 느려진다. |
| Statistic | Period 안의 여러 샘플을 어떻게 요약할지 정하는 방식이다. 예: Average, Sum, Maximum, p95. | CPU 평균, 에러 합계, 지연 p95처럼 질문에 맞는 숫자를 만든다. |
| Evaluation window | Alarm이 판단에 사용하는 시간 범위다. 보통 Period x Evaluation periods로 생각한다. | 일시 스파이크와 지속 장애를 구분한다. |
| Datapoints to alarm | N개 평가 지점 중 M개가 조건을 위반해야 ALARM으로 바꿀지 정하는 값이다. | 1 out of 1은 빠르지만 시끄럽고, 3 out of 5는 느리지만 안정적이다. |
| High-resolution metric | 1초 단위로 저장할 수 있는 커스텀 지표다. | 빠른 감지가 필요할 때 쓰지만 비용과 노이즈가 늘어난다. |
| Metric Filter | 로그 패턴을 숫자 지표로 바꾸는 CloudWatch Logs 기능이다. | worker_job_failed 로그를 WorkerJobFailureCount 지표로 바꿔 Alarm에 연결한다. |
| Log Group / Log Stream | Log Group은 서비스나 앱 단위 로그 묶음이고, Log Stream은 컨테이너, 태스크, 인스턴스 같은 실행 단위의 로그 흐름이다. | ”어느 서비스의 어느 실행 단위에서 난 로그인가”를 나눈다. |
| Retention | 로그를 얼마나 오래 보관할지 정하는 기간이다. | 보관 기간이 없으면 장애 분석에는 편하지만 비용이 계속 쌓인다. |
| Logs Insights | CloudWatch Logs를 쿼리하는 분석 기능이다. | 시간 범위, 필터, 집계로 에러 로그와 패턴을 빠르게 찾는다. |
| Container Insights | ECS, EKS, 컨테이너 워크로드의 CPU, 메모리, 네트워크, 태스크 수 같은 세부 지표를 수집하는 기능이다. | 기본 서비스 지표로는 안 보이는 컨테이너 단위 병목을 본다. |
| Missing data | Alarm 평가 시 해당 Period에 데이터가 없는 상태다. | 서비스가 멈춘 것인지, 트래픽이 없는 것인지, 수집이 끊긴 것인지 해석해야 한다. |
| Cardinality | Dimension 조합의 고유 개수다. | userId 같은 값을 Dimension에 넣으면 지표 수와 비용이 폭증한다. |
처음에는 이 표를 외우기보다 “숫자 신호는 Metrics, 텍스트 단서는 Logs, 자동 판단은 Alarms, 전체 상황판은 Dashboards”라고 잡으면 된다.
CloudWatch는 한 번에 모든 것을 마법처럼 이해하는 도구가 아니다. 신호를 수집하고, 저장하고, 집계하고, 조건을 평가하고, 상태 변화를 외부로 전달하는 파이프라인이다.
flowchart TD Source["AWS 서비스 또는 애플리케이션"] --> Metrics["Metrics: 숫자 지표"] Source --> Logs["Logs: 텍스트 이벤트"] Metrics --> Store["리전별 지표 저장소"] Logs --> Group["Log Group / Log Stream"] Group --> Insights["Logs Insights 검색"] Group --> Filter["Metric Filter"] Filter --> CustomMetric["로그에서 커스텀 지표 생성"] Store --> Eval["Alarm 평가: Period, Statistic, M out of N"] CustomMetric --> Eval Eval --> State["OK / ALARM / INSUFFICIENT_DATA"] State --> SNS["SNS 알림"] State --> EventBridge["EventBridge 상태 변경 이벤트"] SNS --> Human["Slack / 이메일 / 온콜"] EventBridge --> Automation["자동화 또는 후속 워크플로"]
이 흐름에서 중요한 점은 Alarm이 “로그 한 줄을 보고 즉시 울리는 기능”이 아니라는 것이다. Alarm은 지표를 일정 Period마다 집계하고 조건을 평가한 뒤 상태를 바꾼다. 로그에서 바로 알람을 만들고 싶다면 먼저 Metric Filter로 로그 패턴을 지표로 바꾸어야 한다.
CloudWatch는 직접 Prometheus 서버, 로그 수집기, 알림 시스템을 운영하지 않아도 AWS 기본 신호를 빠르게 볼 수 있게 해준다. 이 장점은 L6에서 특히 중요하다. Queue, Worker, Redis, 이벤트 기반 구조가 늘어나면 문제가 하나의 요청 처리 함수 안에 머물지 않는다. API 응답 지연, SQS backlog, ECS 태스크 재시작, RDS 연결 수, 애플리케이션 ERROR 로그를 같은 시간대에 놓고 봐야 한다.
다만 CloudWatch가 “관측성 전체”를 대신하지는 않는다. 요청 하나가 여러 서비스와 함수를 거쳐 어디서 느려졌는지 보려면 분산 추적이 필요하고, 코드 함수별 CPU 병목을 보려면 프로파일링 도구가 필요하다. CloudWatch는 AWS 신호의 기본 수집과 운영 판단의 시작점이다.
Metric은 시간에 따라 기록되는 숫자다. CPUUtilization=78%, RequestCount=1200, ApproximateNumberOfMessagesVisible=450, SearchSuccess=840 같은 값이 Metric이 된다.
CloudWatch Metric은 보통 아래 네 가지를 같이 이해해야 한다.
| 구성 요소 | 예시 | 읽는 법 |
|---|---|---|
| Namespace | AWS/ECS, AWS/RDS, MyApp/Search | 어느 서비스나 앱에서 온 지표인가 |
| Metric name | CPUUtilization, DatabaseConnections, SearchSuccess | 무엇을 측정하는가 |
| Dimension | ClusterName=prod, ServiceName=api, Environment=production | 어떤 하위 단위로 나누어 볼 것인가 |
| Timestamp와 value | 2026-04-07T10:00:00Z, 82.5 | 어느 시각에 값이 얼마였는가 |
팀이 검색 API의 성공 건수를 보고 싶다고 하자. CloudWatch 기본 AWS 지표에는 “우리 서비스에서 검색이 몇 건 성공했는지”가 없다. 이때 애플리케이션이 커스텀 지표를 보낸다.
import { CloudWatchClient, PutMetricDataCommand,} from "@aws-sdk/client-cloudwatch";
const client = new CloudWatchClient({ region: "ap-northeast-2" });
await client.send( new PutMetricDataCommand({ Namespace: "MyApp/Search", MetricData: [ { MetricName: "SearchSuccess", Dimensions: [ { Name: "Environment", Value: "production" }, { Name: "ServiceName", Value: "search-api" }, ], Value: 1, Unit: "Count", }, ], }),);이 예제에서 Namespace는 앱의 지표 묶음이고, MetricName은 측정 이름이며, Dimensions는 조회 축이다. Environment=production과 ServiceName=search-api 정도는 안전한 축이다. 반대로 userId, searchQuery, requestId를 Dimension에 넣으면 고유 조합이 너무 많이 생긴다.
CloudWatch에서 Dimension 조합이 달라지면 별도 시계열로 취급된다. 그래서 Dimension은 “나중에 필터링하고 싶은 값”이 아니라 “장기적으로 관리할 수 있는 낮은 개수의 축”이어야 한다.
| 설계 | 조합 수 감각 | 문제 |
|---|---|---|
Environment 3개 x ServiceName 10개 x Status 2개 | 60개 시계열 | 관리 가능하다. |
위 조합에 Region 3개 추가 | 180개 시계열 | 여전히 예측 가능하다. |
위 조합에 userId 100,000개 추가 | 18,000,000개 시계열 가능성 | 비용, 조회, 알람 관리가 폭발한다. |
requestId를 Dimension에 추가 | 요청 수만큼 계속 증가 | 지표가 아니라 로그나 트레이스에서 찾아야 할 값이다. |
가격표의 숫자는 바뀔 수 있으므로 금액은 공식 가격 페이지에서 확인해야 한다. 하지만 학습할 때 중요한 직관은 안정적이다. Dimension은 곱셈으로 늘어나고, 높은 Cardinality는 비용과 운영 복잡도를 같이 키운다.
같은 데이터라도 Period와 Statistic에 따라 다른 이야기가 된다.
| 질문 | 적합한 Statistic | 이유 |
|---|---|---|
| ”요청 수가 늘었는가” | Sum | Period 안의 총량이 중요하다. |
| ”CPU가 지속적으로 높았는가” | Average 또는 Maximum | 평균 부하와 순간 피크를 구분해야 한다. |
| ”사용자 지연이 나빠졌는가” | p95, p99 | 평균은 일부 느린 사용자를 숨긴다. |
| ”에러가 한 번이라도 있었는가” | Sum 또는 Maximum | Count 지표의 합계나 최대값이 유용하다. |
예를 들어 1분 동안 응답시간 샘플이 80ms, 90ms, 100ms, 120ms, 3000ms라면 평균은 약 678ms다. 평균만 보면 “조금 느림”처럼 보일 수 있지만, 한 요청은 3초가 걸렸다. 사용자 영향이 tail latency에 몰리는 API라면 p95나 p99가 더 맞는 질문일 수 있다.
CloudWatch의 일반적인 지표는 60초 단위로 보는 경우가 많다. High-resolution metric은 1초 단위로 커스텀 지표를 저장할 수 있게 해준다. 짧은 장애를 빨리 잡아야 하는 결제 승인, 실시간 입찰, 짧은 배치 작업 같은 경우에는 유용할 수 있다.
하지만 첫 선택으로 쓰면 안 된다. 1초 단위는 더 많은 데이터 포인트를 만들고, 더 빠른 Alarm은 노이즈도 같이 키운다. 대부분의 API와 Worker는 먼저 60초 Period로 시작하고, “1분 감지로는 실제 사용자 피해를 줄이기에 늦다”는 근거가 있을 때만 고해상도 지표를 검토한다.
EC2의 Basic Monitoring, Detailed Monitoring, high-resolution custom metric은 서로 다른 축이다. Basic Monitoring은 EC2 기본 지표를 더 느슨한 간격으로 보는 기본 모드이고, Detailed Monitoring은 EC2 지표를 더 촘촘한 1분 단위로 보는 옵션이다. High-resolution custom metric은 애플리케이션이 직접 보내는 커스텀 지표를 1초 단위까지 저장하는 기능이다. 처음에는 “AWS 기본 지표를 더 자주 볼 것인가”와 “애플리케이션 고유 신호를 직접 더 촘촘히 보낼 것인가”를 구분하면 된다.
Log는 텍스트 이벤트다. Metric이 “에러가 20건으로 늘었다”를 알려준다면, Log는 “어떤 요청에서 어떤 에러 메시지가 반복됐는가”를 알려준다.
CloudWatch Logs의 기본 구조는 Log Group과 Log Stream이다.
/ecs/order-api, /aws/lambda/payment-worker.ECS Fargate에서는 보통 Task Definition의 awslogs 설정으로 stdout/stderr를 CloudWatch Logs로 보낸다.
{ "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/order-api", "awslogs-region": "ap-northeast-2", "awslogs-stream-prefix": "ecs" } }}이 설정은 운영 명령어라기보다 구조를 이해하기 위한 예시다. 컨테이너가 로그를 파일에만 쓰고 CloudWatch로 보내지 않으면 Logs Insights에서 아무것도 검색되지 않는다.
Logs Insights는 Log Group을 대상으로 쿼리를 실행한다. 가장 자주 쓰는 학습용 쿼리는 아래 세 가지다.
-- 최근 에러 로그 보기fields @timestamp, @message| filter @message like /ERROR/| sort @timestamp desc| limit 20-- 특정 요청 또는 비즈니스 키워드 추적fields @timestamp, @message| filter @message like /payment_failed/| sort @timestamp desc| limit 50-- 시간대별 에러 발생 건수 집계filter @message like /ERROR/| stats count(*) as errorCount by bin(5m)| sort @timestamp descLog Insights에서 결과가 비어 있을 때는 “에러가 없다”고 바로 결론 내리면 안 된다. 시간 범위가 너무 좁거나, Log Group을 잘못 골랐거나, 애초에 로그가 CloudWatch로 들어오지 않았을 수 있다. 또한 쿼리는 스캔한 로그 양이 커질수록 비용과 시간이 늘어나므로 장애 중에도 시간 범위를 먼저 좁히는 습관이 필요하다.
Retention은 로그를 몇 일 동안 보관할지 정하는 설정이다. 보관 기간은 기술 선택이면서 비즈니스 계약이다. 결제 실패 로그는 감사와 고객 문의 때문에 더 오래 필요할 수 있고, staging debug 로그는 하루면 충분할 수 있다.
| 로그 종류 | 예시 보관 기간 | 판단 이유 |
|---|---|---|
| 결제, 정산, 보안 이벤트 | 90일 이상 또는 별도 감사 저장소 | 장애 분석보다 감사와 추적 가능성이 중요하다. |
| 프로덕션 API 에러 로그 | 14일에서 30일 | 최근 배포와 반복 장애를 분석하기에 충분한 기간을 둔다. |
| 액세스 로그, 상세 debug 로그 | 7일 또는 더 짧게 | 양이 많고 장기 분석 가치가 낮으면 비용을 줄인다. |
| staging, 개발 로그 | 1일에서 3일 | 개발 확인 목적이면 오래 둘 이유가 적다. |
수치 감각도 필요하다. 하루 2GB 로그를 남기는 Log Group을 7일 보관하면 대략 최근 14GB 규모를 다룬다. 90일 보관하면 180GB 규모가 된다. 실제 과금 방식은 세부 항목이 더 있지만, 보관 기간이 비용을 선형으로 키운다는 직관은 유효하다.
Metric Filter는 CloudWatch Logs에 들어오는 로그를 패턴으로 검사하고, 일치하면 커스텀 지표 값을 증가시키는 기능이다. 로그는 원인 분석에 좋지만 Alarm은 지표를 평가하므로, 로그 기반 알림에는 Metric Filter가 중간 다리 역할을 한다.
flowchart LR App["Worker 로그"] --> Group["Log Group: /ecs/image-worker"] Group --> Filter["Metric Filter: worker_job_failed"] Filter --> Metric["WorkerJobFailureCount +1"] Metric --> Alarm["Alarm: 5분 안에 10회 초과?"] Alarm --> SNS["SNS Topic"] SNS --> Notify["Slack / 이메일 / 온콜"]
중요한 경계가 있다.
ERROR와 { $.level = "ERROR" }는 다른 패턴이다.Metric Filter는 “모든 에러 로그를 다 알림으로 보낸다”보다 “운영 판단에 즉시 필요한 패턴만 지표화한다”에 가깝다. 예를 들어 worker_job_failed, image_resize_failed, dlq_message_received 같은 패턴은 Metric Filter 후보가 될 수 있다.
CloudWatch Alarm은 지표를 평가해서 OK, ALARM, INSUFFICIENT_DATA 중 하나의 상태를 가진다.
| 상태 | 의미 | 오해하기 쉬운 점 |
|---|---|---|
| OK | 최근 평가 데이터가 조건을 위반하지 않았다. | 서비스가 완전히 정상이라는 보장은 아니다. 알람으로 보지 않는 문제가 있을 수 있다. |
| ALARM | 설정한 조건을 만족했다. | 상태가 이미 ALARM이면 같은 조건이 계속되어도 매번 새 알림이 나가지 않을 수 있다. |
| INSUFFICIENT_DATA | 평가할 데이터가 부족하다. | ”문제없음”이 아니다. 지표 수집 중단, 리소스 종료, 트래픽 없음이 모두 가능하다. |
3 out of 5 알람 평가API p95 응답시간이 800ms를 넘으면 알람을 울리고 싶다고 하자.
설정:
TargetResponseTime최근 5개 데이터 포인트가 아래와 같다면:
| 분 | p95 응답시간 | 위반 여부 |
|---|---|---|
| 10:01 | 650ms | 정상 |
| 10:02 | 920ms | 위반 |
| 10:03 | 880ms | 위반 |
| 10:04 | 700ms | 정상 |
| 10:05 | 910ms | 위반 |
5개 중 3개가 임계값을 넘었으므로 Alarm은 ALARM으로 전환된다. 같은 조건을 1 out of 1로 만들면 10:02에 바로 울리지만 일시 스파이크에도 자주 울린다. 5 out of 5로 만들면 확실한 지속 장애만 잡지만 감지가 늦어진다.
이 예제의 핵심은 임계값 하나가 아니라 “얼마나 빨리 울릴 것인가”와 “얼마나 많은 노이즈를 허용할 것인가”의 선택이다.
CloudWatch Alarm에는 누락 데이터를 어떻게 처리할지 정하는 옵션이 있다. 하지만 정답은 지표 성격에 따라 다르다.
| 지표 성격 | Missing data 처리 감각 | 이유 |
|---|---|---|
| 항상 있어야 하는 인프라 지표 | breaching 후보 | ECS RunningTaskCount, heartbeat 지표가 사라진 것은 장애일 수 있다. |
| 요청이 없으면 자연스럽게 없는 지표 | notBreaching 또는 missing 후보 | 야간에 트래픽이 없어서 에러 카운트가 없는 것은 정상일 수 있다. |
| 간헐적 배치 작업 지표 | missing 후보 | 작업 실행 시간 외에는 데이터가 없는 것이 자연스럽다. |
| 커스텀 애플리케이션 heartbeat | breaching 후보 | 앱이 살아 있음을 의도적으로 보내는 지표라면 끊김 자체가 문제다. |
ECS 태스크가 모두 STOPPED되면 컨테이너 지표도 멈출 수 있다. 이때 “CPU가 낮다”가 아니라 “데이터가 없다”가 된다. 태스크 중단을 즉시 감지해야 하면 RunningTaskCount 같은 지표의 missing data 처리나 EventBridge의 ECS Task State Change 이벤트를 같이 봐야 한다.
Alarm이 ALARM으로 바뀌면 보통 SNS Topic으로 알림을 보낸다. SNS는 이메일, Slack 연동, 온콜 시스템 같은 구독자에게 메시지를 fan-out한다. 이메일 구독은 확인 링크를 눌러야 활성화되므로 PendingConfirmation이면 알람은 울렸지만 사람이 못 받을 수 있다.
EventBridge는 Alarm 상태 변경이나 ECS 태스크 상태 변경 같은 이벤트를 후속 자동화로 넘길 때 유용하다. 예를 들어 “결제 서비스 p95와 에러율이 같이 나빠지면 Slack 알림”은 SNS만으로 충분할 수 있지만, “특정 Alarm이 ALARM이면 자동으로 진단 Lambda를 실행”하려면 EventBridge가 더 자연스럽다.
Dashboard는 그래프를 많이 붙이는 화면이 아니라, 장애 중 첫 5분에 팀이 같은 질문을 보게 만드는 화면이다. 좋은 대시보드는 서비스의 정상 기준선과 장애 후보를 한 화면에 둔다.
예를 들어 주문 API 대시보드는 아래처럼 구성할 수 있다.
| 행 | 지표 | 질문 |
|---|---|---|
| 사용자 영향 | ALB RequestCount, 5xx, TargetResponseTime p95/p99 | 사용자 요청이 실제로 실패하거나 느려졌는가 |
| 애플리케이션 | ECS CPU, Memory, RunningTaskCount | API 컨테이너가 부족하거나 재시작 중인가 |
| 비동기 경로 | SQS backlog, DLQ 메시지 수, Worker CPU | 요청 밖 작업이 밀리거나 죽었는가 |
| 데이터 계층 | RDS CPU, DatabaseConnections, Read/Write latency | DB가 병목인가 |
| 비즈니스 지표 | OrderSuccess, PaymentFailureCount | 기술 증상이 실제 거래 흐름에 영향을 주는가 |
Dashboard는 Alarm을 대체하지 않는다. Dashboard는 사람이 상황을 해석하기 위한 화면이고, Alarm은 상태 전환을 사람이나 자동화에 보내는 장치다.
ECS나 EKS를 쓰면 기본 CloudWatch 지표만으로는 “서비스 전체가 느리다”까지만 보이고, 어떤 태스크나 컨테이너가 병목인지 좁히기 어려울 수 있다. Container Insights는 클러스터, 서비스, 태스크, 컨테이너 단위의 CPU, 메모리, 네트워크, 태스크 수 같은 지표를 더 자세히 제공한다.
| 지표 | 읽는 법 |
|---|---|
CpuUtilized | 실제 사용한 CPU 양이다. CPUUtilization 비율과 함께 보면 할당 대비 사용량을 이해할 수 있다. |
MemoryUtilized | 컨테이너나 태스크가 실제 사용 중인 메모리다. 지속 증가하면 누수나 큰 payload 처리를 의심한다. |
NetworkRxBytes / NetworkTxBytes | 네트워크 수신/송신량이다. 외부 API 호출 증가나 응답 크기 증가를 볼 수 있다. |
RunningTaskCount | 서비스가 기대한 수만큼 태스크를 유지하는지 본다. |
EphemeralStorageUtilized | Fargate 임시 스토리지 사용량이다. 임시 파일, 로그, 압축 작업이 쌓이는지 확인한다. |
Container Insights는 “항상 켜야 하는 정답”이라기보다 컨테이너 단위 진단이 필요한 서비스에 켜는 선택지다. 기본 지표로 충분한 작은 서비스에서는 비용과 필요성을 같이 봐야 한다. 반대로 장애 중에 CPU 평균은 정상인데 일부 태스크만 메모리를 먹고 죽는다면 Container Insights가 없을 때 원인 좁히기가 어려워진다.
도구 선택은 기능 목록 비교보다 운영 환경과 팀의 책임 범위로 결정하는 편이 낫다.
AWS 리소스 기본 지표, CloudWatch Logs, Alarm, SNS/EventBridge 연동을 관리형으로 쓰고 싶을 때 적합하다.
AWS 중심 서비스이고, 관측 스택 운영 부담을 줄이며, 기본 운영 신호부터 빠르게 갖춰야 할 때Kubernetes와 서비스 메트릭을 직접 수집하고 PromQL로 복잡한 비율, rate, histogram 계산을 하고 싶을 때 적합하다.
멀티클라우드, 자체 클러스터, 오픈소스 운영 역량, 세밀한 메트릭 쿼리가 중요할 때메트릭, 로그, APM, 프로파일링, RUM을 SaaS로 통합하고 팀 간 운영 화면을 빠르게 맞추고 싶을 때 적합하다.
멀티클라우드와 애플리케이션 추적을 한 제품에서 묶고 싶고, SaaS 비용을 감당할 수 있을 때실무에서는 하나만 고르는 경우보다 섞어 쓰는 경우도 많다. AWS 기본 리소스 알람은 CloudWatch에 두고, Kubernetes 서비스 메트릭은 Prometheus로 모으고, 사용자 요청 추적과 프로파일링은 Datadog이나 OpenTelemetry 기반 스택으로 보는 식이다.
CloudWatch에서 다른 도구를 검토할 때의 질문은 아래처럼 구체적이어야 한다.
| 신호 | 의미 | 다음 선택지 |
|---|---|---|
| AWS 밖 리소스가 많아진다 | CloudWatch만으로 전체 상태를 통합하기 어렵다. | Datadog, Prometheus, OpenTelemetry Collector |
| PromQL 수준의 rate, ratio, histogram 계산이 자주 필요하다 | CloudWatch 기본 조회 모델이 분석 요구를 따라가기 어렵다. | Prometheus 또는 Managed Prometheus |
| 요청 하나의 서비스 간 경로를 추적해야 한다 | Metric과 Log만으로 병목 위치를 좁히기 어렵다. | X-Ray, OpenTelemetry, Datadog APM, Jaeger |
| 커스텀 지표 Cardinality가 계속 커진다 | Dimension 설계를 다시 보거나 다른 메트릭 백엔드를 검토해야 한다. | Dimension 축소, 로그/트레이스 이동, Prometheus/Datadog 검토 |
| 알림은 많은데 액션이 없다 | 도구 문제가 아니라 알람 설계와 SLO 기준 문제일 수 있다. | SRE Practices에서 SLI/SLO와 alert policy 재설계 |
CloudWatch를 처음 배울 때 자주 생기는 오해는 아래와 같다.
| 오해 | 실제 경계 |
|---|---|
| ”로그만 많으면 관측성이 충분하다” | 로그는 원인 단서에는 좋지만 전체 추세와 자동 알림에는 지표가 필요하다. |
| ”Metric은 자세할수록 좋다” | Dimension이 많을수록 비용과 관리 복잡도가 늘어난다. requestId 같은 값은 로그나 트레이스에 둔다. |
| ”Dashboard가 있으면 운영 준비가 끝났다” | Dashboard는 사람이 보는 화면이다. 중요한 조건은 Alarm과 대응 경로가 있어야 한다. |
| ”INSUFFICIENT_DATA는 정상이다” | 데이터가 없다는 뜻일 뿐이다. 지표 성격에 따라 장애일 수 있다. |
| ”Alarm은 매번 조건을 넘을 때마다 울린다” | Alarm은 상태 전환 중심이다. 이미 ALARM 상태면 같은 조건 지속만으로 새 알림이 반복되지 않을 수 있다. |
| ”CloudWatch 하나로 APM까지 끝난다” | CloudWatch는 AWS 신호의 기본 통합에 강하다. 분산 추적과 프로파일링은 X-Ray, OpenTelemetry, Datadog 같은 추가 도구가 필요할 수 있다. |
긴 트러블슈팅 절차를 외우기보다 증상, 의심할 개념, 첫 확인 지점을 연결하는 것이 학습에 더 도움이 된다.
| 증상 | 먼저 의심할 개념 | 왜 그런가 | 첫 확인 |
|---|---|---|---|
| Alarm이 ALARM인데 Slack이나 이메일이 오지 않는다 | SNS 구독 또는 알림 경로 | Alarm 상태 전환은 되었지만 SNS 구독이 PendingConfirmation이거나 Slack 연동이 끊겼을 수 있다. | Alarm의 Actions, SNS Topic 구독 상태 |
| 지표가 임계값을 넘었는데 ALARM으로 바뀌지 않는다 | Evaluation window, datapoints to alarm, Unit | 3 out of 5이면 세 번 위반해야 하고, Unit 불일치도 평가를 헷갈리게 한다. | Alarm 조건의 Period, Evaluation periods, Datapoints to alarm |
| ECS 태스크가 죽었는데 CPU Alarm은 조용하다 | Missing data | 태스크가 죽으면 CPU가 낮아지는 것이 아니라 지표가 끊길 수 있다. | Missing data 처리, RunningTaskCount, EventBridge ECS Task State Change |
| Log Insights 결과가 비어 있다 | 시간 범위, Log Group, 로그 수집 | 쿼리가 틀린 것이 아니라 시간 범위나 Log Group이 틀렸거나 로그가 수집되지 않았을 수 있다. | 시간 범위 확장, /ecs/<service> Log Group, logConfiguration |
| CloudWatch 비용이 갑자기 늘었다 | Retention, Cardinality, Query scan, high-resolution metric | 로그 보관 기간, 고유 Dimension, 반복 쿼리, 고해상도 지표가 비용을 키운다. | Retention 없는 Log Group, 커스텀 지표 Dimension, Logs Insights 사용량 |
| Metric Filter를 만들었는데 지표가 0이다 | 패턴 불일치 또는 소급 적용 오해 | Metric Filter는 생성 이후 로그에만 적용되고, JSON 로그는 필드 패턴이 필요하다. | Test pattern, 새 테스트 로그, JSON 패턴 |
| 알람이 너무 자주 울린다 | Alert fatigue, threshold, evaluation policy | 단일 지표의 짧은 스파이크를 모두 사람에게 보내면 실제 장애를 놓친다. | 임계값 기준선, 1 out of 1 여부, Composite Alarm 후보 |
| 배포 후 느려졌는데 원인이 안 좁혀진다 | Dashboard 설계와 지표 축 | 사용자 영향, 앱 리소스, 큐, DB, 비즈니스 지표가 따로 떨어져 있으면 상관관계를 보기 어렵다. | 배포 시각 기준으로 핵심 지표를 같은 대시보드에서 비교 |
이 표는 실제 대응 순서를 완전히 대체하지 않는다. 대신 어떤 개념을 먼저 의심해야 하는지 알려준다. 실전 런북은 팀 서비스 이름, 알림 채널, 배포 도구, 롤백 절차에 맞게 따로 있어야 한다.
Alert fatigue는 알람이 너무 자주 울려서 사람이 무시하게 되는 상태다. CloudWatch Alarm을 많이 만드는 것보다 “울리면 행동할 수 있는 알람”을 만드는 것이 중요하다.
알람을 만들 때는 아래 순서로 생각한다.
예를 들어 CPU 85%만으로 결제 서비스 장애 알람을 보내면 배치 작업이나 순간 피크에도 자주 울릴 수 있다. 반대로 5xx 비율 > 1%와 p95 응답시간 > 2초가 동시에 발생할 때 알람을 보내면 사용자 영향에 더 가까운 신호가 된다. CPU는 그 다음 원인 후보로 대시보드에 두는 편이 낫다.
CloudWatch를 공부할 때 “장애 대응”보다 먼저 익히기 좋은 상황은 배포 직후 관찰이다. 배포 시각을 기준으로 전후 지표를 비교하면 CloudWatch의 기본 모델을 손으로 따라갈 수 있다.
| 확인 지표 | 정상 해석 | 위험 신호 |
|---|---|---|
| ALB 5xx, TargetResponseTime p95/p99 | 배포 전 기준선과 비슷하다. | 5xx가 새로 생기거나 p95/p99가 20% 이상 악화된다. |
| ECS RunningTaskCount | desired count와 일치한다. | 새 태스크가 RUNNING으로 안정화되지 않거나 반복 교체된다. |
| ECS CPU/Memory | 기존 범위와 비슷하다. | CPU가 지속적으로 높거나 메모리가 계단식으로 증가한다. |
| RDS DatabaseConnections, latency | 기존 범위와 비슷하다. | 연결 수가 급증하거나 latency가 같이 오른다. |
| Log Insights ERROR count | 새 에러 패턴이 없다. | 배포 이후 특정 ERROR가 반복된다. |
| 비즈니스 커스텀 지표 | 주문, 결제, 가입 흐름이 유지된다. | 기술 지표는 정상인데 성공 건수가 줄어든다. |
이 예시는 긴 온콜 런북이 아니라 학습용 관찰 순서다. 핵심은 “한 지표가 나빠졌다”에서 멈추지 않고 사용자 영향, 앱 리소스, 데이터 계층, 비즈니스 결과를 같은 시간축에서 비교하는 것이다.
CloudWatch를 업무에 연결할 때는 아래 질문으로 시작하면 된다.
userId, orderId, requestId 같은 높은 Cardinality 값이 들어가 있지 않은가?CloudWatch Logs Insights, Metric Filter, Composite Alarm, Anomaly Detection, Container Insights, CloudWatch Agent, Embedded Metric Format, Metrics Insights, CloudWatch Synthetics, AWS X-Ray, OpenTelemetry, Cross-Account Observability.
처음 읽을 때는 모두 깊게 파지 않아도 된다. 이 문서의 핵심은 CloudWatch의 기본 데이터 모델과 Alarm 평가 방식이다. X-Ray, OpenTelemetry, SLI/SLO는 뒤의 Logs / Metrics / Traces, SRE Practices에서 더 자세히 다룬다.
userId, requestId 같은 고카디널리티 값은 지표가 아니라 로그나 트레이스에 둔다.아래 과제는 본문 이해에 필수는 아니다. 실제 AWS 계정이나 팀 서비스가 있을 때 CloudWatch 콘솔에서 개념을 손으로 확인하기 위한 최소 관찰 목록이다.
Logs -> Log groups로 이동한다./ecs/order-api, /aws/lambda/payment-worker.Never expire인지 확인한다.관찰 포인트: “모든 로그를 오래 보관”은 단순하지만 비용과 검색 범위를 키운다. 결제/감사 로그와 debug 로그의 보관 기간은 달라야 한다.
fields @timestamp, @message| filter @message like /ERROR/| stats count(*) as errorCount by bin(5m)| sort @timestamp desc확인할 것:
Alarm 상세 화면에서 아래 값을 찾는다.
관찰 포인트: 같은 임계값이라도 1 out of 1과 3 out of 5는 완전히 다른 알람이다. Treat missing data는 지표 성격에 맞아야 한다.
텍스트 로그라면:
ERRORpayment_failedJSON 로그라면:
{ $.level = "ERROR" }{ $.statusCode >= 500 }{ $.event = "payment_failed" }관찰 포인트: Metric Filter는 생성 이후 들어온 로그에만 적용된다. 과거 장애 로그를 기준으로 필터를 만들었다면 새 테스트 로그를 흘려보내서 지표가 생기는지 확인해야 한다.
기본 ECS 지표와 Container Insights 지표를 비교해 본다.
| 질문 | 기본 지표로 충분한가 | Container Insights가 필요한 신호 |
|---|---|---|
| 서비스 전체 CPU가 높은가 | 대체로 충분하다. | 태스크별 편차가 커서 특정 태스크만 죽는지 봐야 한다. |
| 메모리 누수가 있는가 | 서비스 평균만으로는 부족할 수 있다. | 특정 컨테이너의 MemoryUtilized가 계단식으로 증가한다. |
| 태스크 수가 기대와 맞는가 | RunningTaskCount가 있으면 시작 가능하다. | 태스크/컨테이너 레벨 재시작과 리소스 사용량을 같이 봐야 한다. |