이기훈 (Kenneth)
Amazon RDS for MySQL을 운영하다 보면 관측 데이터가 여러 계층에 흩어집니다. CPU·IOPS·스토리지 같은 서비스 지표는 CloudWatch에, 연결·스레드·InnoDB 상태는 MySQL 내부에, error·slow query 로그는 다시 CloudWatch Logs에 존재합니다. 장애가 발생했을 때 이 신호를 따로 살펴보면 원인 파악이 늦어지고, 지표와 로그 사이의 상관관계도 놓치기 쉽습니다.
이 글에서는 OpenTelemetry Collector Contrib를 단일 수집 계층으로 두고 Amazon RDS for MySQL의 주요 관측 데이터를 ClickStack으로 전달하는 구성을 단계별로 구현합니다.
- RDS 기본 CloudWatch 메트릭
- MySQL 내부 상태 메트릭
- RDS가 CloudWatch Logs로 내보낸 error·slow query 로그
- 선택 사항: MySQL receiver의 query sample 로그 이벤트
검증 기준: 2026년 9월 6일, OpenTelemetry Collector Contrib main 및 최신 릴리스 계열. 운영 환경에서는 latest나 main 대신 검증한 버전(예: 0.159.0)을 고정하세요.
이 구성은 실무에서 활용하기 좋은 공통 모니터링 패턴이지만 RDS의 모든 관측 데이터를 포괄하지는 않습니다. 프로세스·파일시스템 수준의 OS 정보가 필요하다면 RDS Enhanced Monitoring을, DB load·wait event·SQL별 통계가 필요하다면 CloudWatch Database Insights 또는 Performance Insights API를 별도로 검토해야 합니다.
1. 범위와 아키텍처
Collector 배치 위치는 EC2, EKS, ECS 등으로 자유롭습니다. 다만 다음 연결이 모두 가능해야 합니다.
- Collector → RDS 엔드포인트와 포트: 라우팅, 보안 그룹, NACL, DNS
- Collector → AWS CloudWatch·CloudWatch Logs·STS API: 인터넷 또는 VPC endpoint
- Collector → ClickStack OTLP endpoint
동일 VPC 또는 peering된 VPC가 일반적이지만 필수 조건은 아닙니다. 적절한 Transit Gateway, VPN 또는 기타 안전한 라우팅도 가능합니다.
데이터별 역할
경로 | 주로 얻는 데이터 | 한계 |
awscloudwatch metrics | CPU, IOPS, 지연, 스토리지, 연결 수 등 RDS 서비스 인스턴스 지표 | 상세 OS 프로세스 및 MySQL 내부 상태는 없음 |
mysql metrics | global status, InnoDB, buffer pool, 연결·스레드 상태 | RDS 하이퍼바이저·서비스 지표는 없음 |
awscloudwatch logs | error, slow query, 선택적 audit/general log | DB 내부 카운터는 없음 |
mysql query sample | 현재 실행 중인 SQL과 세션 정보 | 기본 비활성, 로그 신호는 development 등급, 민감정보·고카디널리티 위험 |
CloudWatch 기본 CPU 측정과 Enhanced Monitoring 값은 수집 계층이 다릅니다. 기본 CPUUtilization은 하이퍼바이저 측정값이고, Enhanced Monitoring은 DB 인스턴스 내부 에이전트가 수집합니다.
2. RDS 준비
2-1. 로그 파라미터
Custom DB parameter group에서 필요에 맞게 설정합니다.
slow_query_log = 1
log_output = FILE
long_query_time = 1
log_queries_not_using_indexes = 0- CloudWatch Logs에 slow/general log를 내보내려면
log_output=FILE이어야 합니다. long_query_time=1은 예시입니다. 실제 트래픽과 비용을 관찰해 결정합니다.log_queries_not_using_indexes=1은 로그량을 크게 늘릴 수 있습니다.- General log는 연결과 거의 모든 statement를 기록하므로 짧은 진단 외에는 권장하지 않습니다.
2-2. Performance Schema
MySQL receiver의 performance schema 기반 메트릭과 query sample을 사용하려면 활성 상태를 확인합니다.
SHOW GLOBAL VARIABLES LIKE 'performance_schema';필요한 경우 parameter group에서 다음을 설정합니다.
performance_schema = 1Performance Schema를 켜거나 끄는 변경은 RDS 재부팅이 필요합니다. Database Insights가 Performance Schema를 자동 관리하는 환경도 있으므로 parameter group 표시만 보지 말고 실제 변수 값을 확인합니다. 소형 인스턴스에서는 메모리 영향을 먼저 평가합니다.
2-3. CloudWatch Logs export
RDS 콘솔에서 DB 인스턴스를 선택하고 Modify → Log exports에서 필요한 로그를 활성화합니다.
로그 | 일반적인 로그 그룹 | 사전 조건 |
Error | /aws/rds/instance/<id>/error | Error log 자체는 기본 활성 |
Slow query | /aws/rds/instance/<id>/slowquery | slow_query_log=1, log_output=FILE |
Audit | /aws/rds/instance/<id>/audit | MARIADB_AUDIT_PLUGIN을 포함한 custom option group |
General | /aws/rds/instance/<id>/general | general_log=1, log_output=FILE; 상시 사용 비권장 |
로그 그룹 retention은 별도로 설정합니다. 생성된 로그 그룹의 기본 retention이 무기한일 수 있으므로 비용과 보존 정책을 확인합니다.
2-4. 모니터링 전용 MySQL 사용자
기본 메트릭 수집용 예시입니다.
CREATE USER 'otel_monitor'@'%' IDENTIFIED BY '<strong-password>' REQUIRE SSL;
GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'otel_monitor'@'%';
GRANT SELECT ON performance_schema.* TO 'otel_monitor'@'%';- 가능하면
'%'대신 Collector가 사용하는 제한된 source 범위를 적용합니다. - 계정 생성과
GRANT뒤에FLUSH PRIVILEGES는 필요하지 않습니다. - query sample의
events_waits_current값을 채우려면 다음 권한이 추가로 필요할 수 있습니다.
GRANT UPDATE ON performance_schema.setup_consumers TO 'otel_monitor'@'%';RDS에서는 events_waits_current consumer 설정이 재시작·장애조치 때 초기화됩니다. 위 UPDATE 권한이 있으면 receiver가 재연결 시 활성화를 시도할 수 있습니다. 이 권한을 주지 않으면 query sample의 해당 wait duration이 0일 수 있습니다.
Query plan 수집은 대상 statement에 대한 EXPLAIN 권한이 필요할 수 있습니다. 모니터링 편의를 위해 애플리케이션 전체 테이블에 광범위한 권한을 부여하지 말고, query plan 필요성과 데이터 접근 위험을 별도로 검토합니다.
2-5. TLS 인증서
운영에서는 insecure_skip_verify: true를 사용하지 않습니다. AWS RDS CA bundle을 Collector에 읽기 전용으로 배치하고 경로를 환경변수로 제공합니다.
export RDS_CA_FILE=/etc/otelcol/certs/global-bundle.pem인증서 교체 일정과 현재 bundle은 AWS의 RDS SSL/TLS 문서를 기준으로 관리합니다.
3. IAM
아래 예시는 명시적 queries와 명시적 named 로그 그룹을 사용하는 최소 권한의 출발점입니다.
sts:GetCallerIdentity가 거부되어도 수집은 가능하지만cloud.account.id속성이 생략됩니다.- metrics
discovery를 사용할 때만cloudwatch:ListMetrics를 추가합니다. - logs
autodiscover를 사용할 때는 아래 권한을 별도 statement로 추가합니다.DescribeLogGroups는 특정 로그 그룹 ARN으로 제한할 수 없습니다.
{
"Sid": "DiscoverLogGroups",
"Effect": "Allow",
"Action": "logs:DescribeLogGroups",
"Resource": "*"
}조직의 SCP, permission boundary, KMS 설정과 cross-account 관측 구성도 함께 확인합니다.
4. 기본 config.yaml
아래 기본 구성은 CloudWatch 메트릭, MySQL 메트릭, CloudWatch의 error/slow query 로그를 수집합니다. MySQL query sample은 민감도 때문에 기본 구성에서 제외하고 다음 절에서 별도로 활성화합니다.
필요한 환경변수 예시:
export MYSQL_PASSWORD='<secret>'
export RDS_CA_FILE='/etc/otelcol/certs/global-bundle.pem'
export CLICKSTACK_OTLP_ENDPOINT='http://clickstack-otel-collector:4318'
export CLICKSTACK_INGESTION_KEY='<ingestion-key>'ClickStack 배포 방식에 따라 인증이 꺼져 있을 수 있지만, 운영에서는 OTLP endpoint 인증과 TLS를 구성합니다. ClickStack이 발급한 ingestion key는 authorization 헤더의 값으로 전송합니다.
5. Query sample 선택 활성화
Query sample에는 SQL 원문, 사용자·클라이언트 정보, query plan이 포함될 수 있습니다. SQL literal에 개인정보나 인증정보가 들어갈 수 있으므로 보안·개인정보·보존 정책 검토 없이 활성화하지 않습니다.
활성화하려면 mysql receiver에 다음을 추가합니다.
receivers:
mysql:
events:
db.server.query_sample:
enabled: true
query_sample_collection:
max_rows_per_query: 100그리고 동일한 mysql receiver를 logs 파이프라인에도 연결합니다.
service:
pipelines:
logs:
receivers: [awscloudwatch/rds_logs, mysql]확인 사항:
- MySQL receiver의 metrics는 beta지만 logs는 development 등급입니다.
- MySQL·MariaDB 버전에 따라 query plan, client port, replica status 수집 동작이 다릅니다.
performance_schema_max_sql_text_length등 관련 설정을 늘리면 메모리와 민감정보 노출 범위도 커집니다.- 원문 SQL을 저장하지 않아야 한다면 query sample 대신 low-cardinality 메트릭이나 Database Insights를 검토합니다.
6. CloudWatch receiver 동작에서 주의할 점
6-1. delay
metrics의 기본값은 다음과 같습니다.
필드 | 기본값 |
collection_interval | 5분 |
period | 5분 |
delay | 10분 |
각 scrape는 now - delay에서 끝나는 정확히 한 collection_interval 구간을 조회하며 구간이 겹치지 않습니다. 따라서 delay를 과도하게 줄이면 늦게 게시된 데이터포인트가 이후에도 다시 조회되지 않을 수 있습니다.
- 안전한 시작점은 기본 10분입니다.
- 더 낮은 지연이 필요하면 실제 데이터 누락률을 먼저 측정합니다.
- 수 분의 지연 자체가 허용되지 않으면 CloudWatch Metric Streams 같은 push 경로를 검토합니다.
6-2. stats
설정 | OTel 출력 | CloudWatch sub-query |
생략 | Sum·SampleCount·Minimum·Maximum을 합친 Summary | 메트릭당 4개 |
[Average] | stat=Average인 Gauge | 메트릭당 1개 |
[Average, Maximum] | 통계별 Gauge | 메트릭당 2개 |
따라서 “생략하면 언제나 비용이 4배”가 아니라, 명시한 통계가 한 개일 때 4:1입니다. 확장 통계 p99 등은 대상 CloudWatch 메트릭이 지원하는지 확인한 후 추가합니다.
6-3. 출력 이름과 Dimensions
CloudWatch metric 출력은 다음 형태입니다.
- 메트릭 이름:
amazonaws.com/{Namespace}/{MetricName} - resource attributes:
cloud.provider=aws,cloud.region=<region>등 - datapoint attributes:
Namespace,MetricName, 중첩 map인Dimensions
위 기본 구성은 ClickStack 쿼리를 단순화하기 위해 DBInstanceIdentifier를 평탄화하고 원래 Dimensions map을 삭제합니다. 원본 Dimensions가 필요하면 삭제문을 제거합니다.
6-4. 로그 체크포인트
file_storage를 service.extensions에 등록하는 것만으로 receiver 체크포인트가 저장되지는 않습니다. awscloudwatch/rds_logs의 최상위 storage: file_storage 연결이 필요합니다.
저장소가 없으면 재시작 때 lookback 구간이 중복되거나 다운타임보다 lookback이 짧을 때 로그가 누락될 수 있습니다. 저장소가 있어도 디스크 손상, 가득 찬 볼륨, 잘못된 volume mount까지 막아주지는 않습니다.
6-5. 로그 파싱
현재 receiver는 cloudwatch.log.group.name과 cloudwatch.log.stream resource attribute를 생성합니다. 다만 slow query 한 건이 CloudWatch event 하나에 항상 완전하게 들어간다고 가정하지 말고 실제 샘플을 확인합니다.
OTTL parser는 다음 변화에 영향을 받을 수 있습니다.
- MySQL 엔진 버전별 error log 형식
log_slow_extra사용 여부- CloudWatch event 분할과 multiline 형태
- Collector 버전의 attribute 또는 OTTL 동작 변화
7. 검증 절차
7-1. 시작 전
otelcol-contrib validate --config config.yaml검증한 Collector 이미지나 바이너리 버전을 기록합니다. main 문서만 보고 다른 릴리스 바이너리에 적용하지 않습니다.
7-2. 첫 실행
일시적으로 debug exporter를 각 파이프라인에 추가합니다.
exporters: [otlphttp/clickstack, debug]다음을 확인합니다.
- CloudWatch metric 이름과
Dimensions실제 구조 - error/slowquery 로그의
cloudwatch.log.group.name - slow query event가 한 쿼리 단위로 들어오는지
- ClickStack exporter의 인증·TLS 오류
- MySQL TLS와 권한 오류
debug exporter는 SQL이나 민감한 로그를 stdout에 노출할 수 있으므로 검증 후 제거합니다.
7-3. Collector 자체 상태
curl -fsS http://localhost:8888/metrics \
| grep -E 'receiver_accepted|receiver_refused|scraper_errored|exporter_sent|exporter_send_failed|exporter_enqueue_failed|exporter_queue'특히 다음을 함께 봅니다.
- accepted만 증가하고 sent가 증가하지 않는지
- send_failed 또는 enqueue_failed가 증가하는지
- queue size가 계속 증가하는지
- scraper_errored가 반복되는지
7-4. 장애 복구 시험
운영 투입 전에 비운영 환경에서 다음을 확인합니다.
- ClickStack endpoint를 잠시 차단했을 때 queue가 증가하는지
- Collector를 정상 재시작한 뒤 queue와 CloudWatch checkpoint가 복구되는지
- 장시간 장애와 queue full 때 어떤 데이터가 유실되는지
- 중복 로그를 ClickStack 쪽에서 어떻게 식별할지
Persistent queue는 전송 성공을 보장하는 메시지 브로커가 아닙니다. 용량 제한, permanent error, 잘못된 인증, 디스크 장애 때는 데이터가 유실될 수 있습니다.
8. 비용 계산
CloudWatch metrics
명시적 queries 사용 시 scrape당 대략적인 metric data sub-query 수는 다음과 같습니다.
각 query에서 요청한 stats 수의 합stats 생략 query는 4개로 계산합니다. 시간당 scrape 횟수는 60분 / collection_interval입니다.
CloudWatch Logs
다음 계산은 최소 호출 수의 근사치일 뿐입니다.
로그 그룹 수 × 시간당 poll 횟수FilterLogEvents는 paginated API이므로 로그량, 1MB/10,000-event 응답 제한, 빈 페이지와 next token에 따라 실제 호출 수가 더 많아질 수 있습니다.
ClickStack/ClickHouse
다음 데이터는 저장량과 cardinality를 크게 늘릴 수 있습니다.
- General log
- Audit log
- Query sample의 SQL 원문과 query plan
- digest 또는 SQL text를 metric attribute로 갖는 선택 메트릭
ClickStack의 지원 방식에 맞춰 retention/TTL을 설정하고, 기본 테이블의 partitioning을 임의 변경하기 전에 공식 운영 지침을 확인합니다.
9. 대규모 환경의 push 경로
인스턴스가 많거나 pull 지연·API 요청량이 문제가 될 때 다음 구조를 검토할 수 있습니다.
대상 | 구조 |
메트릭 | CloudWatch Metric Streams → Amazon Data Firehose → awsfirehose receiver |
로그 | CloudWatch Logs subscription filter → Amazon Data Firehose → awsfirehose receiver |
이는 무조건적인 “프로덕션 정답”은 아닙니다. 다음 추가 조건이 있습니다.
- Data Firehose의 HTTP endpoint는 HTTPS와 일반적으로 443 포트를 요구
- Collector receiver의 TLS 인증서와 접근키 구성
- Metric Streams OTel 포맷용
awscloudwatchmetricstreams_encoding - CloudWatch Logs subscription 포맷용
aws_logs_encoding - Firehose와 CloudWatch data delivery 자체 비용
awsfirehosereceiver도 metrics/logs가 alpha 등급
Metric Streams의 OTel 1.0 포맷을 사용하면 metric 이름과 Dimensions 모델을 pull 경로와 가깝게 유지할 수 있지만, 실제 전환 전에는 동일 시간대 값을 병행 비교합니다.
10. 기존 Prometheus/YACE에서 전환
기존 exporter를 즉시 제거하지 않고 Collector의 Prometheus receiver로 먼저 수집할 수 있습니다.
receivers:
prometheus/yace:
config:
scrape_configs:
- job_name: yace-rds
scrape_interval: 60s
static_configs:
- targets: [yace-exporter:5000]전환 순서:
- 기존 Prometheus/YACE 경로를 유지한 채 ClickStack으로 복제
- 동일 시간대의 값, 단위, statistic, dimension을 비교
- 대시보드와 경보 쿼리를 새 metric 모델에 맞춰 확인
- 검증 후 기존 exporter 제거
CloudWatch receiver의 Gauge와 기존 exporter의 metric type·이름·label이 같다고 가정하지 않습니다.
11. Aurora MySQL 차이
Aurora에서는 metric마다 지원하는 dimension 조합을 확인해야 합니다.
구분 | Aurora MySQL |
인스턴스 dimension | DBInstanceIdentifier |
클러스터 dimension | DBClusterIdentifier |
역할별 집계 | DBClusterIdentifier • Role (WRITER/READER) |
일부 볼륨 metric | DBClusterIdentifier • EngineName |
클러스터 스토리지 사용량 | VolumeBytesUsed |
인스턴스 로컬 스토리지 | FreeLocalStorage |
복제 지연 | AuroraReplicaLag 등, metric별 적용 범위 확인 |
로그 그룹 | /aws/rds/cluster/<cluster-id>/<log-type> |
FreeStorageSpace를 VolumeBytesUsed로 단순 치환하면 안 됩니다. 하나는 남은 용량이고 다른 하나는 사용량이며, Aurora의 공유 클러스터 볼륨과 인스턴스 로컬 스토리지도 구분해야 합니다.
Aurora writer/reader endpoint 가운데 어느 endpoint를 MySQL receiver가 조회할지도 명시합니다. Writer 하나만 조회하면 reader별 내부 상태는 수집되지 않습니다.
12. 운영 체크리스트
insecure_skip_verify를 사용하지 않는다./var/lib/otelcol/storage를 영속 volume으로 mount하고 디스크 사용량을 감시한다.delay를 낮췄다면 데이터 누락 여부를 측정했다.awscloudwatch, mysql, awsfirehose 변경 내역을 확인한다.마치며
RDS 모니터링의 핵심은 메트릭과 로그를 많이 모으는 데 있지 않습니다. RDS 서비스 계층, MySQL 내부 상태, 쿼리 로그를 동일한 속성 체계와 전송 경로로 연결하는 것이 더 중요합니다. 이 글의 구성은 awscloudwatch와 mysql receiver를 함께 사용해 세 계층을 하나의 ClickStack 관측 화면으로 모으는 실용적인 출발점입니다.
운영 적용 전에는 Collector 버전 고정, 최소 권한, TLS, 영속 checkpoint와 queue, 실제 로그 framing을 반드시 검증하세요. 특히 query sample과 general/audit log는 문제 해결에 강력하지만 민감정보와 저장 비용을 크게 늘릴 수 있으므로 필요한 범위에서만 단계적으로 활성화하는 것이 안전합니다.
공식 참고자료
- OpenTelemetry AWS CloudWatch receiver
- OpenTelemetry MySQL receiver
- MySQL receiver version compatibility
- OpenTelemetry file storage extension
- OpenTelemetry exporter queue and retry
- OpenTelemetry Collector internal telemetry
- ClickStack Collector ingestion
- AWS RDS MySQL logs to CloudWatch Logs
- AWS RDS Enhanced Monitoring
- AWS CloudWatch Logs IAM actions
- AWS CloudWatch Metric Streams OTel 1.0
- OpenTelemetry AWS Firehose receiver
- AWS Aurora CloudWatch dimensions
- AWS Aurora MySQL logs to CloudWatch Logs
- OpenTelemetry database attribute migration
- OpenTelemetry deployment attributes