AWS 기초 #7 CloudWatch 입문: 로그 / 메트릭
#1 ~ #6으로 AWS 셋업의 기반이 모였습니다. 이제 운영의 또 다른 축이 남았습니다. 무엇이 어디서 무슨 일을 하고 있는가를 보는 도구입니다.
CloudWatch는 AWS의 관측 (Observability) 표준입니다. AWS 안의 거의 모든 서비스가 기본으로 메트릭을 CloudWatch로 보내고, 로그도 CloudWatch Logs가 받습니다. 운영의 첫 시야는 여기서 시작합니다.
이 글은 CloudWatch의 4가지 구성인 Logs / Metrics / Alarms / Dashboards를 한 번에 정리하겠습니다.
큰 그림: CloudWatch의 4가지 구성 #
| 구성 요소 | 무엇 | 흔한 사용처 |
|---|---|---|
| Logs | 텍스트 로그 저장 / 검색 | EC2 / Lambda / ECS / API Gateway의 로그 |
| Metrics | 시계열 숫자 (CPU%, 요청 수 등) | 모든 AWS 서비스가 자동 송신 |
| Alarms | 메트릭이 임계 넘으면 알림 / 액션 | 운영 알림, 자동 스케일 |
| Dashboards | 그래프 / 위젯 페이지 | 팀 / 서비스별 한눈 보기 |
이 넷이 한 흐름으로 엮입니다. 로그 → 메트릭 → 알람 → 대시보드.
CloudWatch Logs #
로그 그룹과 로그 스트림 #
Log Group : 보통 한 애플리케이션 / 서비스 단위
└── Log Stream : 보통 한 프로세스 / 컨테이너 단위
└── Log Event : 한 줄| 항목 | 예 |
|---|---|
| Log Group | /aws/lambda/my-function, /ecs/my-service, /var/log/myapp |
| Log Stream | Lambda 실행 환경 ID, ECS Task ID, EC2 인스턴스 ID |
| Log Event | 한 줄 텍스트 + timestamp |
Lambda / ECS Fargate는 자동으로 로그를 CloudWatch Logs로 보냅니다. EC2는 CloudWatch Agent 또는 보조 도구 (fluent-bit 등)가 필요합니다.
Retention: 가장 중요한 설정 #
기본 retention은 영구. 그대로 두면 로그가 영원히 쌓여 비용 폭주. 가입 직후 / 새 로그 그룹마다 retention을 정해야 합니다.
| 항목 | 권장 retention |
|---|---|
| 일반 애플리케이션 로그 | 30~90일 |
| 디버그 / 개발 로그 | 7일 |
| 보안 / 감사 로그 (CloudTrail) | 1~7년 (S3로 보내는 게 더 쌈) |
| Lambda 로그 | 14~30일 |
aws logs put-retention-policy \
--log-group-name /aws/lambda/my-function \
--retention-in-days 30aws logs describe-log-groups --query 'logGroups[].logGroupName' --output text \
| tr '\t' '\n' \
| while read name; do
aws logs put-retention-policy --log-group-name "$name" --retention-in-days 30
done이 한 줄이 CloudWatch 비용 사고를 절반 이상 막아 줍니다.
새 로그 그룹의 자동 retention #
새로 만들어지는 로그 그룹에 자동으로 retention을 적용하는 방법은 두 가지.
방법 1: EventBridge + Lambda. CreateLogGroup 이벤트를 받아 자동 적용합니다 (실전에서 자주 씁니다).
방법 2: 정책으로 강제. 만들 때 retention을 명시하지 않으면 거부합니다 (살짝 과합니다).
Lambda 로그 보내기 #
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def handler(event, context):
logger.info("Received event: %s", event)
return {"ok": True}stdout / stderr가 자동으로 CloudWatch Logs로 흘러갑니다. 로그 그룹 이름은 /aws/lambda/<함수명>.
EC2 / ECS: CloudWatch Agent #
EC2는 자동이 아닙니다. CloudWatch Agent 설치 필요.
sudo yum install -y amazon-cloudwatch-agent # AL
# 또는
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i amazon-cloudwatch-agent.deb설정 파일 /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json:
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/myapp/*.log",
"log_group_name": "/myapp/server",
"log_stream_name": "{instance_id}",
"retention_in_days": 30
}
]
}
}
},
"metrics": {
"metrics_collected": {
"mem": { "measurement": ["mem_used_percent"] },
"disk": { "measurement": ["used_percent"], "resources": ["*"] }
}
}
}ECS의 Fargate는 컨테이너 정의에 awslogs 드라이버를 적으면 자동입니다. 이건 고급 #1에서 자세히 다룹니다.
Logs Insights: 쿼리로 검색 #
CloudWatch Logs의 검색 / 분석 도구. SQL 비슷한 자체 문법.
fields @timestamp, @message
| sort @timestamp desc
| limit 100fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 50fields @timestamp, @duration
| filter @type = "REPORT"
| stats avg(@duration), max(@duration), count(*) by bin(5m)fields @timestamp, status, path
| filter status >= 500
| stats count(*) as errors by path
| sort errors desc자주 쓰는 명령어:
| 명령 | 무엇 |
|---|---|
fields | 보일 필드 |
filter | 조건 필터 |
parse | 문자열에서 필드 추출 |
stats | 집계 (count, avg, max, percentile) |
sort | 정렬 |
limit | 최대 결과 수 |
bin(5m) | 시간 버킷 |
Logs Insights 비용 주의 #
쿼리 시 스캔한 데이터 GB당 과금 (~$0.005/GB). 큰 로그 그룹을 무제한 시간으로 쿼리하면 비용 사고. 항상 시간 범위 좁게.
CloudWatch Metrics #
메트릭은 시계열 숫자. AWS의 거의 모든 서비스가 자동 송신합니다.
자주 보는 메트릭 #
| 서비스 | 자주 보는 메트릭 |
|---|---|
| EC2 | CPUUtilization, NetworkIn/Out, DiskReadOps |
| RDS | CPUUtilization, DatabaseConnections, FreeStorageSpace, ReadLatency |
| Lambda | Invocations, Errors, Duration, Throttles, ConcurrentExecutions |
| ECS | CPUUtilization, MemoryUtilization (Service / Task 별) |
| ALB | RequestCount, TargetResponseTime, HTTPCode_Target_5XX_Count |
| API Gateway | Count, Latency, 4XXError, 5XXError |
| S3 | BucketSizeBytes, NumberOfObjects (1일 1회) |
| DynamoDB | ConsumedReadCapacity/WriteCapacity, ThrottledRequests |
메트릭의 차원 (Dimensions) #
같은 메트릭이라도 차원으로 쪼개집니다.
Service: AWS/EC2
Metric: CPUUtilization
Dimensions:
- InstanceId: i-1234567890
- InstanceId: i-2345678901
- InstanceId: i-3456789012차원이 다르면 별도 메트릭. 메트릭 수 = 비용이라 차원이 폭발하면 비용 사고.
통계 (Statistic) #
| 통계 | 무엇 |
|---|---|
Sum | 합계. Invocations, RequestCount |
Average | 평균. CPU, Latency |
Maximum | 최대. Spike 감지 |
Minimum | 최소 |
p95 / p99 | 백분위수. Latency |
SampleCount | 데이터 포인트 수 |
대부분의 경우 Average + p95가 의미 있습니다. p99 / p99.9는 SLA / 사용자 경험에 직접 영향.
표준 vs 고해상도 #
| 종류 | 해상도 | 비용 |
|---|---|---|
| 표준 | 1분 | 표준 |
| 고해상도 | 1초 | 고가. 스파이크 짧은 경우만 |
대부분 표준 1분으로 충분합니다.
커스텀 메트릭 보내기 #
애플리케이션이 직접 메트릭을 송신합니다.
import boto3
cloudwatch = boto3.client("cloudwatch")
cloudwatch.put_metric_data(
Namespace="MyApp",
MetricData=[{
"MetricName": "OrderCreated",
"Value": 1,
"Unit": "Count",
"Dimensions": [
{"Name": "Environment", "Value": "prod"},
{"Name": "Region", "Value": "ap-northeast-2"},
],
}],
)비용: 메트릭당 $0.30 / 월. 차원의 조합마다 별도. 사용자 ID 같은 고카디널리티 차원은 절대 금지.
EMF (Embedded Metric Format): Lambda 용 패턴 #
Lambda에선 put_metric_data 호출 자체가 비용 / 지연 부담. 로그에 특정 JSON 형식으로 적으면 CloudWatch가 자동으로 메트릭으로 변환.
import json
print(json.dumps({
"_aws": {
"Timestamp": int(time.time() * 1000),
"CloudWatchMetrics": [{
"Namespace": "MyApp",
"Dimensions": [["Environment"]],
"Metrics": [{"Name": "OrderCreated", "Unit": "Count"}],
}],
},
"Environment": "prod",
"OrderCreated": 1,
}))aws-embedded-metrics-python 같은 SDK가 이걸 더 깔끔하게 도와줍니다.
Metric Filter: 로그에서 메트릭 만들기 #
이미 로그에 있는 정보 (ERROR 발생, 응답 시간 등)를 메트릭으로 변환.
CloudWatch → Log groups → 그룹 선택 → Metric filters → Create
- Filter pattern: ERROR
- Metric namespace: MyApp
- Metric name: ErrorCount
- Metric value: 1이제 ERROR가 한 번 발생할 때마다 메트릭이 +1씩 올라갑니다. 알람 / 대시보드에 활용할 수 있습니다.
ERROR # ERROR 단어 포함
[..., level="ERROR", ...] # 구조화된 로그의 필드
{ $.level = "ERROR" } # JSON 로그의 키CloudWatch Alarms #
메트릭이 임계를 넘으면 액션을 실행합니다. 알림의 본진입니다.
첫 알람: Lambda Errors #
CloudWatch → Alarms → Create alarm
- Metric: AWS/Lambda → Errors → Function: my-function
- Statistic: Sum
- Period: 1 minute
- Threshold: > 0 for 1 datapoint within 5 minutes
- Action: SNS → 알림 토픽
- Name: lambda-my-function-errorsAlarm의 상태 #
| 상태 | 의미 |
|---|---|
OK | 임계 안 |
ALARM | 임계 초과. 액션 발생 |
INSUFFICIENT_DATA | 데이터 부족. 새로 만든 경우 / 메트릭 안 옴 |
INSUFFICIENT_DATA를 알람 처리 (== 알림) 할지는 옵션입니다. 새 알람 평가 중에 자주 뜨는 상태라 일반적으론 무시합니다.
Composite Alarms #
여러 알람의 AND / OR로 조합합니다. “ALB 5xx ≥ 1% AND CPU > 80%” 같은 패턴.
ALARM("alb-5xx") AND ALARM("ec2-high-cpu")오탐을 줄이는 데 효과적입니다.
Alarm 액션 #
| 액션 | 무엇 |
|---|---|
| SNS Topic | 이메일 / Slack / SMS / Lambda 등으로 fanout |
| EC2 Action | 인스턴스 stop / terminate / reboot / recover |
| Auto Scaling | ASG 스케일 in / out |
| Systems Manager | OpsItem 만들기 |
운영의 90%는 SNS → Slack / 이메일입니다.
Anomaly Detection #
기준선 (band)을 자동으로 학습해 그 밖이면 알람을 울립니다. 트래픽 / CPU처럼 패턴이 있는 메트릭에 효과적입니다. 정적 임계값보다 오탐이 적습니다.
SNS 통합: 알림을 보내는 방법 #
대부분의 알람이 SNS Topic으로 가고, 거기서 다음 단계로 fanout.
| 구독 | 어디로 |
|---|---|
| 이메일 | |
| HTTPS | Slack incoming webhook |
| Lambda | 가공 후 다른 단계 |
| SMS | 휴대폰 (드물게) |
| SQS | 큐로 |
Slack 연동: Lambda 패턴 #
직접 webhook으로 보내거나, AWS Chatbot으로.
import json, os, urllib.request
WEBHOOK = os.environ["SLACK_WEBHOOK"]
def handler(event, context):
msg = json.loads(event["Records"][0]["Sns"]["Message"])
payload = json.dumps({
"text": f"🚨 *{msg['AlarmName']}*: {msg['NewStateValue']}",
"blocks": [...]
}).encode()
req = urllib.request.Request(WEBHOOK, data=payload,
headers={"Content-Type": "application/json"})
urllib.request.urlopen(req)고급 #4 API Gateway + Lambda, 고급 #5 EventBridge / SQS / SNS에서 더 깊게.
CloudWatch Dashboards #
팀 / 서비스별 핵심 지표를 한눈에 보는 위젯 페이지입니다. JSON으로 정의해 코드로 관리할 수 있습니다.
자주 만드는 대시보드 #
| 종류 | 무엇 |
|---|---|
| 서비스 대시보드 | 한 서비스의 핵심 메트릭 (요청, 지연, 에러, 인프라) |
| 인프라 대시보드 | EC2/RDS의 CPU / 메모리 / 네트워크 |
| 비즈니스 대시보드 | 가입 / 결제 / 주문 같은 비즈니스 메트릭 |
| 온콜 대시보드 | 활성 알람 / 최근 사고 / 핵심 지표 |
위젯 종류 #
- 메트릭 그래프 (line, stacked, number)
- 로그 (Logs Insights 쿼리 결과)
- 텍스트 (Markdown. 대시보드 안내)
- Alarm status
좋은 대시보드 = “이 한 페이지를 30초 보면 시스템 상태를 안다.”
가입 직후 켜야 할 설정 #
이 시리즈가 끝나는 시점, 다음 설정들이 갖춰져 있어야 합니다.
| 항목 | 어디서 |
|---|---|
| 새 로그 그룹에 retention 자동 적용 | 콘솔 / EventBridge + Lambda |
| Lambda Errors 알람 | 함수마다 |
| RDS FreeStorageSpace 알람 | DB마다 |
| ALB 5xx 알람 | LB마다 |
| 빌링 알람 (#3) | 계정 단위 |
| GuardDuty findings 알람 (#6) | 계정 단위 |
이 6가지가 작은 운영의 알림 기반입니다.
자주 만나는 함정 #
1) 로그 retention 영구 #
가장 흔한 비용 사고입니다. 로그 그룹마다 retention을 명시하고 새 그룹은 자동화합니다. 이 한 줄이 비용을 절반 이상 절감합니다.
2) 고카디널리티 커스텀 메트릭 #
Dimensions: [{Name: 'UserId', Value: user_id}]. 사용자 1만 명이면 메트릭 1만 개 × 차원 조합. 한 달 $3000+. 사용자별은 로그 (Logs Insights)가 답.
3) Logs Insights 무제한 시간 쿼리 #
큰 로그 그룹에 시간 범위 안 정하면 GB 단위 스캔 비용. 항상 시간 범위 좁게.
4) Alarm의 Period 너무 짧음 #
1분 1 데이터포인트로 알람을 걸면 일시적 spike마다 알람이 폭주합니다. 보통 5분 / 3 데이터포인트 정도가 노이즈가 적당합니다.
5) Alarm 액션 안 붙임 #
알람을 만들고 SNS / 액션을 안 붙이면 콘솔에서만 빨갛게 됩니다. 아무도 모릅니다. 만들 때 액션을 같이 붙입니다.
6) 대시보드만 만들고 안 봄 #
만든 직후엔 모두 보지만 시간 지나면 잊힙니다. 온콜 / 일일 standup의 첫 체크 항목으로 정착시킵니다.
7) Metric Math 미사용 #
여러 메트릭의 비율 / 합계 / 변환을 계산할 수 있는 Metric Math. “5xx / 총 요청 = 에러율” 같은 계산입니다. 잘 쓰면 대시보드 / 알람이 훨씬 깔끔합니다.
정리 #
이번 글에서 잡은 것:
- CloudWatch의 4가지 구성. Logs / Metrics / Alarms / Dashboards입니다.
- Logs. 그룹 → 스트림 → 이벤트 구조입니다. retention을 가입 직후 설정하고, CloudWatch Agent로 EC2 로그를 수집합니다.
- Logs Insights.
fields / filter / stats / sort / parse+bin(5m)을 쓰고, 시간 범위를 좁게 잡습니다. - Metrics. AWS 서비스가 자동 송신합니다. 차원 / 통계 (Avg, p95, p99)를 쓰고, 고카디널리티 차원은 금지합니다.
- Metric Filter. 로그에서 메트릭을 추출합니다 (ERROR 등).
- EMF. Lambda에서 로그로 메트릭을 보냅니다 (put_metric_data 대안).
- Alarms. 임계 + Period + Datapoints로 구성합니다. SNS / EC2 액션 / ASG, Composite / Anomaly를 씁니다.
- SNS. 알람을 fanout합니다. Slack / 이메일 / Lambda로 보냅니다.
- Dashboards. 서비스 / 인프라 / 비즈니스 / 온콜용으로 만듭니다.
- 함정. retention 영구, 고카디널리티 차원, Insights 무제한 시간 쿼리, Period 너무 짧음, 액션 미연결, 대시보드 안 봄입니다.
다음 시리즈: AWS 중급 #
이로써 AWS 기초 7편이 끝났습니다. 콘솔 / 계정 / IAM / 비용 / CLI / SSO / 보안 / CloudWatch. AWS 위에서 무언가를 시작하는 데 필요한 도구상자가 한 곳에 모였습니다.
이제 진짜 자원을 만들 차례입니다. AWS 중급 7편은 백엔드 운영의 핵심 요소들을 정리하겠습니다.
AWS 중급 #1 EC2와 VPC 기초에서는 가상 머신 EC2, 그리고 그 EC2가 사는 가상 네트워크 VPC의 구조를 Subnet / Internet Gateway / Route Table / Security Group / NACL까지 한 줄에 정리하겠습니다.