1. "사이트가 좀 느린데요?"
서비스 런칭 일주일 차, 사용자로부터 문의 메일이 왔습니다. "저녁 9시만 되면 사이트가 너무 느려요. 어쩔 땐 접속도 안 되고요."
"그럴 리가 없는데?"
저는 제 로컬 환경과 배포된 서버에 직접 접속해 봤습니다. 아주 빠르고 쾌적했습니다.
서버에 SSH로 접속해서 top 명령어를 쳐봤지만 CPU 사용률도 5% 미만으로 아주 평온했습니다.
로그 파일(access.log)을 tail -f로 지켜봤지만 에러 로그도 없었습니다.
"사용자 인터넷 문제인가?" 하고 넘어갔죠. 하지만 다음 날 저녁 9시, 또다시 "느리다", "터졌다"는 불만이 커뮤니티에 올라왔습니다. 저는 망망대해에 떠 있는 기분이었습니다. 도대체 어디가, 얼마나, 왜 느린지 알 수가 없었으니까요.
마치 눈을 감고 고속도로를 운전하는 기분이었습니다. 어디가 막히는지 모르고, 엔진 과열인지 타이어 펑크인지 모른 채 달리고 있었죠. 사고(서버 다운)가 나야만 "아, 뭔가 잘못됐구나" 하고 알 수 있는 상태였습니다.
2. 눈을 뜨다: Prometheus & Grafana
모니터링 문서를 읽으면서 Prometheus(데이터 수집)와 Grafana(시각화)를 부랴부랴 설치했습니다. AWS CloudWatch도 있었지만, 더 세밀한 애플리케이션 지표를 보고 싶어서 오픈소스를 선택했습니다.
그리고 대시보드를 띄운 순간, 저는 소름이 돋았습니다. 저녁 9시가 되자 그래프 하나가 수직 상승하고 있었거든요.
바로 DB Connection Latency(데이터베이스 연결 지연)였습니다.
알고 보니 특정 배치 작업(Batch Job)이 매일 밤 9시에 돌면서 DB 커넥션 풀(Connection Pool) 50개를 다 써버리고 있었습니다.
일반 사용자들은 남은 커넥션이 없어서 무한 대기(Pending) 상태에 빠져 있었던 거죠.
에러 로그가 안 남았던 이유도, 아직 에러가 난 게 아니라 '대기 중'이었기 때문입니다.
제가 접속했을 땐 운 좋게 잠깐 커넥션을 얻어서 빨랐던 거고요.
그래프를 보자마자 원인을 파악했고, 배치 작업 시간을 새벽 4시로 옮겨서 문제를 10분 만에 해결했습니다. "측정할 수 없으면, 개선할 수 없다(You can't improve what you don't measure)"는 피터 드러커의 말을 뼈저리게 느꼈습니다.
3. 무엇을 봐야 하는가? (4 Golden Signals)
"그럼 뭘 모니터링해야 하나요?" 초보자는 보통 CPU 사용량, 메모리 같은 기본적인 것만 봅니다. 하지만 구글 SRE(Site Reliability Engineering) 팀은 4가지 황금 신호(The 4 Golden Signals)를 반드시 봐야 한다고 말합니다.
1. Latency (지연 시간)
"사용자가 기다리는 시간은 얼마인가?" 평균(Average) 시간만 보면 안 됩니다. 평균이 200ms라도, p95(상위 5%), p99(상위 1%) 사용자는 10초를 기다리고 있을 수 있습니다. 꼬리(Tail Latency)를 잡아야 시스템이 안정됩니다.
2. Traffic (트래픽)
"얼마나 많은 요청이 들어오는가?" 보통 RPS(Requests Per Second)로 측정합니다. 평소 100 RPS던 트래픽이 갑자기 1000 RPS로 튄다면? → 디도스(DDoS) 공격이거나 이벤트 트래픽 폭증. 갑자기 0이 된다면? → 네트워크 단절이나 로드밸런서 장애.
3. Errors (에러율)
"얼마나 자주 실패하는가?" HTTP 500(서버 에러)뿐만 아니라, 400(잘못된 요청)이나 200 OK지만 내용이 비어있는 논리적 에러도 감시해야 합니다. 전체 요청 중 실패한 요청의 비율(%)을 봅니다.
4. Saturation (포화도)
"자원을 얼마나 다 썼는가?" CPU, 메모리, 디스크 용량, DB 커넥션 등이 한계에 얼마나 근접했는지 봅니다. 이 지표는 현재의 문제보다 "미래의 장애"를 예고해 줍니다. "이대로면 3일 뒤에 디스크 꽉 참" 같은 거죠.
4. 잠 좀 자자: 알림(Alerting) 전략
모니터링 대시보드를 24시간 쳐다보고 있을 순 없습니다. 저는 Slack과 연동해서 알림(Alert)을 설정했습니다. 처음엔 의욕이 넘쳐서 "CPU 50% 넘으면 알림", "에러 1개라도 나면 알림"으로 설정했다가... 그날 밤 잠을 한숨도 못 잤습니다. 알림이 1분마다 울렸거든요.
그래서 알림 정책(Alert Policy)을 세웠습니다.
- P1 (Critical - 긴급):
- 조건: 에러율 > 5% 지속, Latency p95 > 3초, 디스크 95% 참.
- 행동: PagerDuty를 통해 전화 걸어 깨움. (새벽 3시라도 일어나야 함).
- P2 (Warning - 주의):
- 조건: CPU > 80%, 메모리 누수 조짐.
- 행동: 슬랙 채널에 멘션(@channel) 보냄. 출근해서 고쳐도 됨.
- P3 (Info - 정보):
- 조건: 배포 성공, 서버 재시작.
- 행동: 로그 채널에 기록. 궁금할 때만 봄.
알림은 "진짜로 내가 지금 자다가 일어나야 하는 일인가?"를 기준으로 설정해야 합니다. 안 그러면 '양치기 소년'이 돼서 진짜 장애 알림을 무시하게 됩니다.
5. 심화: Push vs Pull 방식의 차이
모니터링 도구는 크게 두 가지 방식으로 나뉩니다.
Pull 방식 (Prometheus)
- 작동 원리: 서버가 "자, 나 여기 있어" 하고 메트릭을 노출(
http://localhost:3000/metrics)하면, Prometheus 서버가 주기적으로 긁어갑니다(Scrape). - 장점: 애플리케이션이 모니터링 서버의 존재를 몰라도 됩니다. (결합도 낮음). 수집 서버 부하 조절이 쉽습니다.
- 단점: 중앙 서버가 모든 타겟의 IP를 알아야 합니다. (Service Discovery 필요).
Push 방식 (DataDog, New Relic)
- 작동 원리: 애플리케이션 안에 Agent를 심어서, Agent가 중앙 서버로 데이터를 쏩니다.
- 장점: 설정이 쉽습니다. 중앙 서버가 타겟 IP를 몰라도 됩니다 (오토스케일링 환경에 유리).
- 단점: Agent가 애플리케이션 성능에 영향을 줄 수 있습니다. (Overhead).
현대의 대세는 Kubernetes 환경과 찰떡궁합인 Prometheus (Pull) 방식입니다.
6. 마무리: "비행기 조종석"을 만드세요
서버를 운영하는 건 비행기를 조종하는 것과 같습니다. 창밖(User Logs)만 보고 감으로 운전할 순 없습니다. 고도계(CPU), 속도계(RPS), 연료 게이지(Memory)가 필요합니다.
- Prometheus + Grafana (오픈소스, 무료): 가장 대중적인 조합.
- Datadog, New Relic (SaaS, 유료): 돈 있으면 최고. 설정할 게 거의 없음.
- AWS CloudWatch: AWS 쓴다면 기본.
최소한 RPS(트래픽), Latency(속도), Error Rate(에러) 이 3개는 지금 당장 대시보드에 띄우세요. 여러분의 서버가 지금 비명을 지르고 있을지도 모릅니다. 단지 여러분이 듣지 못하고 있을 뿐이죠. Observability(관측 가능성)를 확보하세요. 그래야 밤에 푹 잘 수 있습니다.
Would You Drive with Your Eyes Closed? (Why You Need Monitoring)
1. "The Site Feels Slow..."
It was just one week after our grand launch. An email arrived from a frustrated user. "The site gets super slow around 9 PM every night. Sometimes it times out completely."
"That can't be right."
I accessed the site from my laptop and phone. It loaded instantly.
I SSH'd into the server and ran top. The CPU usage was calm at 5%.
I watched the logs with tail -f access.log. No error stacks.
"Must be the user's internet," I dismissed it. But the next day at 9 PM, complaints poured into the community forum. "Is the server down?", "Why is it spinning?" I felt like I was drifting in the open ocean without a compass. I had absolutely no idea WHERE, HOW MUCH, or WHY it was slow.
It felt like driving on a highway with my eyes closed. I couldn't tell if the engine was overheating or if I had a flat tire. I was in a state where I only knew something was wrong after crashing (server down).
2. Opening My Eyes: Prometheus & Grafana
Reading through monitoring documentation, I frantically installed Prometheus (Metric Collector) and Grafana (Visualization Tool). While AWS CloudWatch sends basic metrics, I needed application-level insights.
The moment I loaded the dashboard, I got goosebumps. At 9 PM sharp, one graph spiked vertically like a skyscraper.
It was DB Connection Latency.
The root cause? A scheduled Batch Job ran every night at 9 PM, hogging all 50 connections in the Connection Pool.
Regular users couldn't grab a connection and were stuck in a Pending state.
That's why there were no error logs—the requests didn't fail, they were just waiting endlessly.
I experienced "fast" speeds because I got lucky and grabbed a connection in between batch queries.
I identified the cause instantly and moved the batch job to 4 AM. Problem solved in 10 minutes. I painfully learned Peter Drucker's quote: "You can't improve what you don't measure."
3. What to Watch? (The 4 Golden Signals)
"So what should I monitor?" Beginners often stare at CPU and Memory. But the Google SRE (Site Reliability Engineering) team recommends watching The 4 Golden Signals.
1. Latency
"How long does it take to serve a request?" Don't just watch the Average. Averages hide problems. Even if the average is 200ms, your p95 (95th percentile) or p99 users might be waiting 10 seconds. You must optimize the Tail Latency to ensure stability.
2. Traffic
"How much demand is being placed on your system?" Usually measured in RPS (Requests Per Second). If traffic jumps from 100 to 1000 RPS? → Potentially a DDoS attack or a viral event. If it drops to 0? → Network outage or Load Balancer failure.
3. Errors
"Run rate of requests that fail." Watch not just explicit failures (HTTP 500s), but also implicit ones (HTTP 200 with "error: true" in body). Monitor the percentage of failed requests vs. total requests.
4. Saturation
"How 'full' is your service?" This measures your most constrained resources (CPU, Memory, Disk I/O, DB Connections). Saturation is a leading indicator. It predicts "Future Failures". "At this rate, the disk will be full in 3 days."
4. Let Me Sleep: Alerting Strategy
You can't stare at Grafana screens 24/7. I integrated Slack for alerts. At first, I was over-eager. "Alert if CPU > 50%", "Alert on any Error". I didn't sleep that night. My phone buzzed every minute.
So I established an Alert Policy:
- P1 (Critical):
- Condition: Error Rate > 5% for 5m, Latency p95 > 3s, Disk 95% full.
- Action: Wake me up (PagerDuty phone call). Even at 3 AM.
- P2 (Warning):
- Condition: CPU > 80%, Slow memory leak trend.
- Action: Slack Mention (@channel). Fix it during business hours.
- P3 (Info):
- Condition: Deployment success/fail, autoscaling events.
- Action: Log to channel. Check only if curious.
Set alerts based on: "Do I REALLY need to wake up for this?" Otherwise, you become the 'Boy Who Cried Wolf', and you'll ignore the alert when the server actually catches fire.
Bonus: How to set up in 5 minutes (Docker)
If you want to try this locally right now, copy this docker-compose.yml.
It sets up Prometheus (Collector) and Grafana (Dashboard) instantly.
version: '3'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
grafana:
image: grafana/grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
And a simple prometheus.yml configuration:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'my-app'
static_configs:
- targets: ['host.docker.internal:3000'] # Your App URL
Run docker-compose up -d, and visit localhost:3000. You are now an SRE.
6. The Golden Signals vs RED Method
You might have heard of the RED Method as well. What's the difference?
- Golden Signals (Google): Latency, Traffic, Errors, Saturation. (Focus on System Health & Capacity). Great for infrastructure.
- RED Method (Tom Wilkie): Rate, Errors, Duration. (Focus on Request Processing). Great for Microservices APIs.
- Rate = Traffic (RPS)
- Errors = Error Rate
- Duration = Latency
Basically, RED is a subset of Golden Signals, removing "Saturation". For Application Developers, RED is often enough. For DevOps/SREs, Saturation involves disk usage and memory leaks, so it's critical.
What about Logs?
Monitoring tells you when and where something is wrong. Logging tells you why it is wrong. You need both. Prometheus gives you the high-level alert "Error Rate is 5%". Then you go to ELK Stack (Elasticsearch, Logstash, Kibana) or Loki to find the specific stack trace. Don't mix them up. Prometheus is not for logs.
7. Deep Dive: Push vs Pull Architecture
Why did I choose Prometheus? It uses a Pull model, unlike Datadog or New Relic.
Pull Model (Prometheus)
- How it works: Your app exposes a endpoint (
/metrics). Prometheus "visits" this page every 15 seconds to scrape data. - Pros: Your app doesn't need to know where the monitoring server is. If monitoring dies, your app keeps running 100% unaffected. Low overhead.
- Cons: Prometheus needs to know the IP address of every container. (Solved by Kubernetes Service Discovery).
Push Model (Datadog, Graphite, Legacy)
- How it works: An Agent inside your app sends data to a central server.
- Pros: Easy to set up in dynamic environments where IPs change constantly.
- Cons: If the central server is slow, the Agent might block your app (if not async). High network traffic if you have thousands of metrics.
For Kubernetes environments, Pull (Prometheus) is the industry standard. It scales better and is more resilient.