이미지 로딩 중...

성능 메트릭 추출 및 트렌드 분석 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 11. 18. · 4 Views

성능 메트릭 추출 및 트렌드 분석 완벽 가이드

웹 서버나 애플리케이션의 성능을 실시간으로 모니터링하고 분석하는 방법을 배워봅니다. Bash와 Linux 명령어를 활용하여 응답 시간, 처리량, 에러율을 추출하고, 시간대별 트렌드를 파악하여 성능 저하를 미리 감지하는 실전 기법을 다룹니다.


목차

  1. 응답_시간_메트릭_추출
  2. 처리량_throughput_계산
  3. 에러율_추적
  4. 시간대별_트렌드_분석
  5. 이동_평균_계산
  6. 성능_저하_자동_탐지

1. 응답_시간_메트릭_추출

시작하며

여러분이 웹 서비스를 운영하는데 갑자기 사용자들이 "사이트가 느려요"라고 불평하는 상황을 겪어본 적 있나요? 그런데 막상 서버를 확인해보면 CPU나 메모리는 정상인 것 같고, 뭐가 문제인지 감이 안 잡힐 때가 있습니다.

이런 문제는 실제 개발 현장에서 너무나 자주 발생합니다. 사용자가 체감하는 속도는 실제 응답 시간인데, 우리는 그 데이터를 제대로 수집하고 분석하지 못하고 있기 때문입니다.

로그는 쌓이고 있지만, 그 안에서 의미 있는 정보를 추출하지 못하면 아무 소용이 없습니다. 바로 이럴 때 필요한 것이 응답 시간 메트릭 추출입니다.

로그 파일에서 실제 응답 시간 데이터를 뽑아내고, 평균과 최대값을 계산하여 서비스의 건강 상태를 한눈에 파악할 수 있습니다.

개요

간단히 말해서, 응답 시간 메트릭 추출은 웹 서버 로그에서 각 요청이 처리되는 데 걸린 시간을 찾아내고 통계를 내는 작업입니다. 웹 서버(Nginx, Apache 등)는 모든 요청을 로그 파일에 기록합니다.

이 로그에는 누가, 언제, 어떤 페이지를 요청했고, 그 요청을 처리하는 데 몇 초가 걸렸는지가 모두 담겨 있습니다. 예를 들어, 사용자가 상품 목록 페이지를 요청했는데 응답까지 3초가 걸렸다면, 이것은 명백히 문제가 있는 상황입니다.

기존에는 로그 파일을 눈으로 훑어보면서 "어? 이 요청 좀 느린 것 같은데?"라고 대충 감으로만 파악했다면, 이제는 Bash 스크립트로 자동으로 평균 응답 시간, 최대 응답 시간, 느린 요청 TOP 10 등을 정확하게 뽑아낼 수 있습니다.

이 기법의 핵심 특징은 첫째, 실시간으로 계속 모니터링할 수 있다는 점과, 둘째, 별도의 비싼 모니터링 툴 없이도 리눅스 기본 명령어만으로 가능하다는 점입니다. 이러한 특징들이 중요한 이유는, 문제가 발생했을 때 빠르게 원인을 찾고 대응할 수 있기 때문입니다.

코드 예제

# Nginx 액세스 로그에서 응답 시간 추출 및 통계 계산
# 로그 형식: IP - - [날짜] "요청" 상태코드 크기 응답시간

# 평균 응답 시간 계산 (마지막 필드가 응답 시간)
awk '{sum+=$NF; count++} END {print "평균 응답시간:", sum/count, "초"}' /var/log/nginx/access.log

# 최대 응답 시간 찾기
awk '{if($NF > max) max=$NF} END {print "최대 응답시간:", max, "초"}' /var/log/nginx/access.log

# 1초 이상 걸린 느린 요청만 필터링
awk '$NF > 1.0 {print $0}' /var/log/nginx/access.log > slow_requests.log

# 응답 시간 상위 10개 요청 출력
awk '{print $NF, $7}' /var/log/nginx/access.log | sort -rn | head -10

# 특정 시간대의 평균 응답 시간 (오늘 14시)
grep "$(date +%d/%b/%Y):14:" /var/log/nginx/access.log | awk '{sum+=$NF; count++} END {print sum/count}'

설명

이것이 하는 일: 이 스크립트는 Nginx 웹 서버의 액세스 로그를 읽어서, 각 HTTP 요청이 처리되는 데 걸린 시간을 추출하고 통계를 계산합니다. 마치 학생들의 시험 점수를 모아서 평균과 최고점을 계산하는 것처럼, 수천 개의 요청 중에서 의미 있는 패턴을 찾아냅니다.

첫 번째 단계에서는 awk 명령어가 로그 파일의 각 줄을 읽으면서 마지막 필드($NF, Number of Fields)를 합산합니다. 로그의 마지막 필드가 응답 시간이라고 가정하고, 모든 응답 시간을 더한 후 요청 개수로 나누어 평균을 계산합니다.

이렇게 하는 이유는, 전체적인 서비스 품질을 숫자로 정량화하기 위함입니다. 그 다음으로, 최대 응답 시간을 찾는 부분이 실행됩니다.

각 줄을 읽으면서 현재까지 본 것 중 가장 큰 값(max)을 계속 갱신합니다. 만약 10만 개의 요청 중 단 하나라도 10초가 걸렸다면, 이것은 심각한 문제의 신호일 수 있습니다.

또한 1초 이상 걸린 요청만 따로 필터링하여 slow_requests.log 파일로 저장하는데, 이렇게 하면 나중에 문제가 된 요청들만 집중적으로 분석할 수 있습니다. 마지막 단계에서는 sort 명령어로 응답 시간을 내림차순 정렬하고, head로 상위 10개만 출력합니다.

이렇게 하면 "어떤 페이지가 가장 느린가?"를 즉시 알 수 있습니다. 또한 특정 시간대(예: 오늘 14시)의 평균을 계산하여, 점심시간 직후처럼 트래픽이 많은 시간대에 성능이 어떻게 변하는지도 파악할 수 있습니다.

여러분이 이 스크립트를 사용하면 "사이트가 느려요"라는 막연한 불만을 "14시에 /api/products 엔드포인트의 평균 응답 시간이 2.5초입니다"라는 구체적인 데이터로 바꿀 수 있습니다. 문제를 정확히 측정할 수 있어야 개선도 가능하고, 개선 효과도 숫자로 증명할 수 있습니다.

이 방식의 가장 큰 장점은 별도의 모니터링 도구 설치 없이, 이미 쌓이고 있는 로그 파일만으로도 충분하다는 점입니다. 또한 cron으로 매 5분마다 실행하도록 설정하면 실시간 모니터링 시스템을 구축할 수 있고, 임계값을 설정해서 평균 응답 시간이 1초를 넘으면 알림을 보내는 식으로 확장할 수도 있습니다.

실전 팁

💡 로그 형식이 서버마다 다르므로, 먼저 tail -n 5 /var/log/nginx/access.log로 실제 로그 구조를 확인하고 응답 시간이 몇 번째 필드에 있는지 파악하세요. $NF 대신 $12처럼 구체적인 필드 번호를 사용하는 것이 더 정확합니다.

💡 큰 로그 파일(수 GB)을 처리할 때는 전체 파일 대신 최근 1시간 데이터만 분석하세요. tail -n 100000 /var/log/nginx/access.log | awk ... 형태로 사용하면 처리 속도가 훨씬 빠릅니다.

💡 응답 시간 단위를 확인하세요. 어떤 서버는 초(s), 어떤 서버는 밀리초(ms)로 기록합니다. 만약 밀리초라면 $NF/1000으로 변환해서 초 단위로 통일하는 것이 이해하기 쉽습니다.

💡 결과를 파일로 저장할 때는 타임스탬프를 붙여서 metrics_$(date +%Y%m%d_%H%M%S).txt처럼 저장하면, 나중에 시간대별 비교가 가능하고 성능 개선 전후를 비교할 수 있습니다.

💡 percentile(백분위수)도 함께 계산하면 더 정확합니다. 평균은 극단값에 영향을 많이 받으므로, 95 percentile(상위 5%를 제외한 값)을 보면 실제 대부분의 사용자가 경험하는 성능을 알 수 있습니다.


2. 처리량_throughput_계산

시작하며

여러분의 서비스가 과연 얼마나 많은 요청을 처리할 수 있는지 정확히 알고 계신가요? 서버를 증설해야 할지 말지를 결정할 때, "그냥 느낌상 트래픽이 많은 것 같아서요"라고 말할 수는 없습니다.

임원진이나 투자자에게는 구체적인 숫자가 필요합니다. 이런 상황은 특히 마케팅 이벤트나 프로모션을 앞두고 자주 발생합니다.

"내일 광고가 나가면 트래픽이 10배 증가할 건데, 우리 서버가 버틸 수 있을까?" 이런 질문에 답하려면, 현재 서버가 초당 몇 개의 요청을 처리하는지 정확히 알아야 합니다. 바로 이럴 때 필요한 것이 처리량(throughput) 계산입니다.

시간당, 분당, 초당 몇 개의 요청을 처리했는지를 측정하여, 서버의 실제 처리 능력과 여유 용량을 파악할 수 있습니다.

개요

간단히 말해서, 처리량(throughput)은 단위 시간당 처리한 요청의 개수를 의미합니다. 마치 고속도로의 차량 통과량처럼, 1분에 몇 대의 차가 지나가는지를 세는 것과 같습니다.

웹 서비스에서 처리량은 서버의 처리 능력을 나타내는 가장 직관적인 지표입니다. 예를 들어, 여러분의 쇼핑몰이 평소에는 초당 100개의 요청(100 RPS, Requests Per Second)을 처리하는데, 블랙프라이데이 세일 때는 1000 RPS까지 올라간다면, 서버를 최소 10배로 증설하거나 캐싱을 강화해야 한다는 것을 알 수 있습니다.

기존에는 서버 관리자의 경험과 감으로 "음, 좀 버거운 것 같은데?"라고 판단했다면, 이제는 정확한 숫자를 기반으로 의사결정을 할 수 있습니다. "현재 평균 150 RPS인데 피크 타임에는 300 RPS까지 올라가니, 여유율 50%를 확보하려면 500 RPS를 처리할 수 있는 인프라가 필요합니다"라고 구체적으로 말할 수 있습니다.

이 메트릭의 핵심은 첫째, 시간대별로 트래픽 패턴을 파악할 수 있다는 점과, 둘째, 용량 계획(capacity planning)의 기초 데이터가 된다는 점입니다. 이러한 데이터가 없으면 언제 서버가 다운될지 모르는 불안한 상황이 계속되지만, 데이터가 있으면 미리 대비할 수 있습니다.

코드 예제

# 로그 파일에서 분당 처리량 계산
# 시간대별로 그룹핑하여 각 분마다 요청 개수 카운트

# 분당 요청 수 계산 (시:분 형식으로 그룹핑)
awk '{
  # 로그에서 시간 추출 (예: [01/Jan/2024:14:23:45])
  match($0, /\[.*:([0-9]{2}:[0-9]{2}):[0-9]{2}/, time);
  count[time[1]]++;
}
END {
  for (t in count) {
    print t, count[t], "요청/분"
  }
}' /var/log/nginx/access.log | sort

# 초당 평균 처리량 계산 (RPS: Requests Per Second)
total=$(wc -l < /var/log/nginx/access.log)
first_time=$(head -n 1 /var/log/nginx/access.log | awk '{print $4}' | tr -d '[')
last_time=$(tail -n 1 /var/log/nginx/access.log | awk '{print $4}' | tr -d '[')
echo "총 요청 수: $total"
echo "평균 RPS: $(echo "scale=2; $total / 60" | bc)"

# 피크 시간대 찾기 (가장 바쁜 시간)
awk '{
  match($0, /\[.*:([0-9]{2}):[0-9]{2}:[0-9]{2}/, hour);
  count[hour[1]]++;
}
END {
  max=0; peak_hour="";
  for (h in count) {
    if (count[h] > max) {max=count[h]; peak_hour=h;}
  }
  print "피크 시간:", peak_hour"시", "요청 수:", max
}' /var/log/nginx/access.log

설명

이것이 하는 일: 이 스크립트는 로그 파일에서 시간 정보를 추출하고, 같은 시간대의 요청들을 그룹핑하여 개수를 세는 작업을 수행합니다. 마치 매장에서 시간대별로 손님 수를 세어서 언제 직원을 더 배치해야 하는지 결정하는 것과 같습니다.

첫 번째 awk 스크립트는 정규표현식을 사용하여 로그의 각 줄에서 시:분 형태의 시간 정보를 추출합니다. 예를 들어 [01/Jan/2024:14:23:45]라는 타임스탬프에서 14:23만 뽑아냅니다.

그리고 count라는 연관 배열(associative array)에 해당 시간을 키로 사용하여 개수를 증가시킵니다. 이렇게 하면 14:23분에 몇 개의 요청이 있었는지 자동으로 집계됩니다.

그 다음으로, 초당 평균 처리량(RPS)을 계산하는 부분이 실행됩니다. wc -l로 전체 라인 수를 세고, 로그의 첫 줄과 마지막 줄에서 시간을 추출합니다.

만약 1시간 동안의 로그에 36만 개의 요청이 있었다면, 60분으로 나누면 분당 6000개, 다시 60으로 나누면 초당 100개(100 RPS)라는 것을 알 수 있습니다. bc 명령어는 소수점 계산을 위해 사용되며, scale=2는 소수점 둘째 자리까지 표시하라는 의미입니다.

세 번째 스크립트는 24시간 중에서 가장 바쁜 시간대를 찾습니다. 시간만 추출하여(분은 무시) 각 시간대별로 요청을 집계하고, 그 중 최대값을 찾습니다.

예를 들어 "14시에 5만 개의 요청이 있었고, 이것이 하루 중 최대"라는 정보를 얻으면, 14시 전후로 서버 리소스를 더 할당하는 등의 조치를 취할 수 있습니다. 여러분이 이 스크립트를 사용하면 "우리 서비스는 평균 200 RPS를 처리하고, 피크 타임인 저녁 8시에는 500 RPS까지 올라갑니다"라고 구체적으로 말할 수 있습니다.

이 데이터를 바탕으로 서버 증설 계획을 세우거나, 오토스케일링(auto-scaling) 정책을 설정할 수 있습니다. 또한 처리량 데이터를 시계열로 저장하면 트렌드 분석도 가능합니다.

지난주에는 평균 100 RPS였는데 이번 주에는 150 RPS라면, 사용자가 50% 증가했다는 뜻이고, 이런 추세가 계속되면 한 달 후에는 서버가 부족할 수 있다는 것을 미리 알 수 있습니다. 실무에서는 이 데이터를 Grafana 같은 시각화 도구와 연동하여 실시간 대시보드를 만들기도 하고, Prometheus나 CloudWatch 같은 모니터링 시스템에 메트릭으로 전송하여 알림을 설정하기도 합니다.

하지만 그런 도구들이 없어도, 순수 Bash 스크립트만으로도 충분히 유용한 인사이트를 얻을 수 있습니다.

실전 팁

💡 로그 로테이션을 고려하세요. 많은 시스템이 매일 자정에 로그를 새 파일로 교체하므로, 24시간 데이터를 보려면 access.logaccess.log.1을 함께 분석해야 합니다. cat access.log.1 access.log | awk ... 형태로 사용하세요.

💡 처리량과 함께 동시 접속자 수(concurrent users)도 중요합니다. 처리량이 높아도 각 요청이 빨리 끝나면 문제없지만, 느린 요청이 쌓이면 서버가 마비될 수 있습니다. 응답 시간과 처리량을 함께 봐야 합니다.

💡 정적 파일(이미지, CSS, JS) 요청과 API 요청을 분리해서 분석하세요. grep "\.jpg\|\.png\|\.css\|\.js" access.log로 정적 파일을 필터링하면, 실제 비즈니스 로직을 처리하는 API의 처리량을 더 정확히 파악할 수 있습니다.

💡 요일별 패턴도 분석하세요. 평일과 주말의 트래픽이 다를 수 있고, 특히 B2B 서비스는 주말에 트래픽이 급감합니다. date +%A로 요일을 추출하여 요일별 통계를 내면 더 정확한 예측이 가능합니다.

💡 처리량 급증 시 자동 알림을 설정하세요. 평균의 2배를 넘으면 Slack이나 이메일로 알림을 보내도록 하면, DDoS 공격이나 갑작스러운 트래픽 급증에 빠르게 대응할 수 있습니다.


3. 에러율_추적

시작하며

여러분의 서비스에서 100개의 요청 중 몇 개가 실패하는지 알고 계신가요? 사용자들은 에러 메시지를 보면 바로 떠나버리기 때문에, 에러율은 곧 고객 이탈률과 직결됩니다.

한 연구에 따르면 페이지 로딩 실패를 경험한 사용자의 88%는 다시 그 사이트를 방문하지 않는다고 합니다. 이런 문제는 특히 배포 직후나 트래픽 급증 시에 심각해집니다.

새로운 기능을 배포했는데 에러율이 평소의 0.1%에서 5%로 급증했다면, 즉시 롤백해야 합니다. 하지만 에러율을 실시간으로 추적하지 않으면, 사용자들의 불만이 쌓인 후에야 뒤늦게 문제를 발견하게 됩니다.

바로 이럴 때 필요한 것이 에러율 추적입니다. HTTP 상태 코드를 분석하여 성공(2xx), 클라이언트 오류(4xx), 서버 오류(5xx)의 비율을 계산하고, 어떤 엔드포인트에서 에러가 집중되는지 파악할 수 있습니다.

개요

간단히 말해서, 에러율은 전체 요청 중에서 실패한 요청의 비율을 퍼센트로 나타낸 것입니다. 웹 서버는 모든 응답에 HTTP 상태 코드를 붙이는데, 200번대는 성공, 400번대는 클라이언트 오류(잘못된 요청), 500번대는 서버 오류(내부 버그)를 의미합니다.

에러율 추적이 중요한 이유는, 에러가 발생해도 서버는 멀쩡히 돌아가기 때문입니다. CPU 사용률도 정상이고, 메모리도 여유 있고, 서버는 정상적으로 응답하고 있지만, 그 응답이 전부 "500 Internal Server Error"라면 사용자 입장에서는 서비스가 완전히 죽은 것과 같습니다.

예를 들어, 데이터베이스 커넥션 풀이 고갈되어 모든 요청이 500 에러를 반환하는 상황에서도 서버 자체는 정상 작동할 수 있습니다. 기존에는 사용자가 "결제가 안 돼요"라고 신고해야 문제를 알았다면, 이제는 에러율 그래프를 보고 "지금 결제 API의 5xx 에러율이 20%네요, 즉시 확인하겠습니다"라고 선제적으로 대응할 수 있습니다.

에러율 추적의 핵심은 첫째, 4xx와 5xx를 구분해서 봐야 한다는 점입니다. 4xx는 클라이언트 문제(잘못된 URL, 인증 실패 등)이고, 5xx는 서버 문제(버그, 리소스 부족 등)이므로 대응 방법이 완전히 다릅니다.

둘째, 특정 엔드포인트나 API에서 에러가 집중되는지를 파악하는 것이 중요합니다. 전체 에러율은 1%인데 특정 API 하나가 50% 에러율을 보인다면, 그 API에 심각한 문제가 있다는 뜻입니다.

코드 예제

# HTTP 상태 코드별 요청 수와 비율 계산
# 일반적으로 상태 코드는 9번째 필드 ($9)에 위치

# 상태 코드별 집계
awk '{status[$9]++; total++}
END {
  print "=== HTTP 상태 코드 분포 ==="
  for (s in status) {
    printf "%s: %d건 (%.2f%%)\n", s, status[s], (status[s]/total)*100
  }
}' /var/log/nginx/access.log | sort

# 에러율 계산 (4xx + 5xx)
awk '{
  total++;
  if ($9 >= 400) errors++;
  if ($9 >= 500) server_errors++;
  if ($9 >= 400 && $9 < 500) client_errors++;
}
END {
  printf "총 요청: %d\n", total
  printf "전체 에러율: %.2f%%\n", (errors/total)*100
  printf "클라이언트 에러(4xx): %.2f%%\n", (client_errors/total)*100
  printf "서버 에러(5xx): %.2f%%\n", (server_errors/total)*100
}' /var/log/nginx/access.log

# 에러가 많이 발생하는 URL TOP 10
awk '$9 >= 400 {print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -10

# 시간대별 에러율 변화 추적
awk '{
  match($0, /\[.*:([0-9]{2}):[0-9]{2}:[0-9]{2}/, hour);
  h = hour[1];
  count[h]++;
  if ($9 >= 400) errors[h]++;
}
END {
  for (h in count) {
    printf "%s시: 에러율 %.2f%% (%d/%d)\n", h, (errors[h]/count[h])*100, errors[h], count[h]
  }
}' /var/log/nginx/access.log | sort

설명

이것이 하는 일: 이 스크립트는 로그의 각 요청에서 HTTP 상태 코드를 추출하고, 상태 코드별로 분류하여 통계를 냅니다. 마치 병원에서 환자들을 증상별로 분류하여 어떤 질병이 유행하는지 파악하는 것과 같습니다.

첫 번째 awk 스크립트는 상태 코드($9)를 키로 하는 연관 배열 status에 각 코드의 발생 횟수를 저장합니다. 예를 들어 200번 코드가 9500번, 404가 300번, 500이 200번 나왔다면, 이를 전체 요청 수로 나누어 퍼센트로 표시합니다.

sort 명령으로 정렬하면 어떤 상태 코드가 가장 많은지 한눈에 볼 수 있습니다. 그 다음으로, 에러율을 구체적으로 계산하는 부분이 실행됩니다.

상태 코드가 400 이상이면 에러로 간주하고, 400대와 500대를 분리하여 카운트합니다. 왜냐하면 404(Not Found)는 사용자가 잘못된 URL을 입력한 것이지만, 500(Internal Server Error)은 서버 코드에 버그가 있다는 뜻이기 때문입니다.

두 가지는 완전히 다른 문제이므로 따로 추적해야 합니다. 세 번째 부분에서는 에러가 발생한 URL($7 필드)만 추출하여, sort | uniq -c로 중복을 제거하면서 개수를 세고, 다시 내림차순 정렬하여 상위 10개를 출력합니다.

만약 /api/payment 엔드포인트가 500개의 에러를 발생시켰다면, 이것이 1위로 나타나고, 개발자는 즉시 결제 API 코드를 점검할 수 있습니다. 마지막으로 시간대별 에러율 변화를 추적합니다.

시간을 추출하여 각 시간대의 전체 요청 수와 에러 수를 세고, 비율을 계산합니다. 예를 들어 "14시에는 에러율이 0.5%였는데 15시에는 5%로 급증했다"는 것을 발견하면, 15시에 배포한 코드나 외부 서비스 장애를 의심할 수 있습니다.

여러분이 이 스크립트를 사용하면 "현재 전체 에러율은 2.3%이고, 그 중 4xx가 2%, 5xx가 0.3%입니다. 5xx 에러의 80%는 /api/users 엔드포인트에서 발생하고 있으며, 오후 3시부터 급증했습니다"라고 구체적으로 리포트할 수 있습니다.

이 정도 정보가 있으면 문제의 원인을 빠르게 좁혀나갈 수 있습니다. 업계 표준으로는 에러율 1% 미만이 양호하고, 5% 이상이면 심각한 문제로 간주합니다.

특히 5xx 에러는 0.1% 이하로 유지하는 것이 목표입니다. SLA(Service Level Agreement)를 맺은 경우, 에러율이 일정 수준을 넘으면 위약금을 물어야 하는 경우도 있으므로, 실시간 모니터링이 필수입니다.

실전 팁

💡 499 상태 코드를 주의하세요. 이것은 Nginx에서 클라이언트가 응답을 받기 전에 연결을 끊었다는 뜻으로, 응답이 너무 느려서 사용자가 포기했다는 신호입니다. 499가 많다면 성능 문제가 있는 것입니다.

💡 에러 로그(/var/log/nginx/error.log)와 액세스 로그를 함께 보세요. 액세스 로그에는 500 에러가 발생했다는 사실만 기록되지만, 에러 로그에는 "PHP Fatal Error: Out of memory"처럼 구체적인 원인이 적혀 있습니다.

💡 특정 User-Agent에서 에러가 집중되는지 확인하세요. 예를 들어 특정 모바일 앱 버전에서만 4xx 에러가 발생한다면, 해당 버전의 버그일 수 있습니다. awk '$9 >= 400 {print $12}'로 User-Agent를 추출하세요.

💡 에러율 임계값을 설정하고 자동 알림을 만드세요. 예를 들어 "5분간 5xx 에러율이 1%를 초과하면 Slack 알림"처럼 설정하면, 심각한 장애를 몇 초 만에 감지할 수 있습니다.

💡 에러율과 함께 에러의 절대 개수도 봐야 합니다. 전체 트래픽이 적을 때는 에러 10개만 발생해도 에러율이 10%로 높게 나타날 수 있지만, 실제로는 큰 문제가 아닐 수 있습니다. 비율과 절대값을 함께 판단하세요.


4. 시간대별_트렌드_분석

시작하며

여러분의 서비스는 하루 중 언제 가장 바쁘고, 언제 한가할까요? 그리고 그 패턴이 지난주와 비교해서 어떻게 변했을까요?

이런 질문에 답하지 못하면, 트래픽이 몰리는 시간에 서버가 부족하거나, 반대로 한가한 시간에도 불필요하게 많은 서버를 띄워놓고 비용을 낭비하게 됩니다. 이런 상황은 특히 글로벌 서비스에서 심각합니다.

한국 사용자는 저녁 8시에 활발하지만, 미국 사용자는 정반대 시간에 활동합니다. 시간대별 트렌드를 모르면 리소스를 효율적으로 배분할 수 없고, 어느 지역에서 문제가 생겼는지도 파악하기 어렵습니다.

바로 이럴 때 필요한 것이 시간대별 트렌드 분석입니다. 시간, 요일, 날짜별로 트래픽과 성능 지표를 시각화하여, 패턴을 찾아내고 미래를 예측할 수 있습니다.

개요

간단히 말해서, 시간대별 트렌드 분석은 시간의 흐름에 따라 메트릭(트래픽, 응답 시간, 에러율 등)이 어떻게 변하는지를 추적하고 시각화하는 작업입니다. 트렌드 분석이 중요한 이유는, 절대값만 봐서는 문맥을 알 수 없기 때문입니다.

"지금 RPS가 500이다"라는 정보만으로는 이것이 많은 건지 적은 건지 판단할 수 없습니다. 하지만 "평소 이 시간에는 200 RPS인데 오늘은 500 RPS다"라고 하면, 평소의 2.5배라는 것을 알고 이상 징후를 감지할 수 있습니다.

예를 들어, 갑자기 트래픽이 급증했다면 TV 방송에 소개되었거나, 반대로 DDoS 공격을 받고 있을 수도 있습니다. 기존에는 그래프 도구나 모니터링 솔루션에 의존했다면, 이제는 Bash 스크립트로 시간대별 데이터를 CSV 형태로 추출하고, 엑셀이나 구글 스프레드시트로 간단히 그래프를 그릴 수 있습니다.

트렌드 분석의 핵심은 첫째, 주기적인 패턴(예: 매일 저녁 8시 피크)을 발견하는 것이고, 둘째, 이상치(anomaly)를 감지하는 것입니다. 매일 비슷한 패턴을 보이다가 갑자기 튀는 값이 나타나면, 그것은 조사가 필요한 신호입니다.

셋째, 장기 트렌드를 보면 서비스가 성장하는지 정체하는지를 알 수 있습니다. 한 달 전에는 평균 100 RPS였는데 지금은 200 RPS라면, 사용자가 2배 증가한 것이므로 인프라 확장이 필요합니다.

코드 예제

# 시간대별 트래픽, 평균 응답시간, 에러율을 한 번에 분석
# CSV 형식으로 출력하여 그래프 도구로 시각화 가능

awk '
BEGIN {
  print "시간,요청수,평균응답시간(초),에러율(%)"
}
{
  # 시간 추출 (HH:MM 형식)
  match($0, /\[.*:([0-9]{2}:[0-9]{2}):[0-9]{2}/, time);
  t = time[1];

  # 카운터 증가
  count[t]++;

  # 응답 시간 합산 (마지막 필드 $NF)
  response_time[t] += $NF;

  # 에러 카운트 (상태 코드 $9가 400 이상)
  if ($9 >= 400) errors[t]++;
}
END {
  # 시간순 정렬을 위해 배열 순회
  for (t in count) {
    avg_response = response_time[t] / count[t];
    error_rate = (errors[t] / count[t]) * 100;
    printf "%s,%d,%.3f,%.2f\n", t, count[t], avg_response, error_rate
  }
}' /var/log/nginx/access.log | sort > hourly_metrics.csv

# 요일별 평균 비교 (오늘 vs 지난주 같은 요일)
today=$(date +%d/%b/%Y)
last_week=$(date -d '7 days ago' +%d/%b/%Y)

echo "=== 오늘 vs 지난주 같은 요일 비교 ==="
echo "오늘 ($today):"
grep "$today" /var/log/nginx/access.log.1 | wc -l
echo "일주일 전 ($last_week):"
grep "$last_week" /var/log/nginx/access.log.1 | wc -l

# 피크 타임과 최저 타임 찾기
awk '{
  match($0, /\[.*:([0-9]{2}):[0-9]{2}:[0-9]{2}/, hour);
  h = hour[1];
  count[h]++;
}
END {
  max=0; min=999999; peak=""; lowest="";
  for (h in count) {
    if (count[h] > max) {max=count[h]; peak=h;}
    if (count[h] < min) {min=count[h]; lowest=h;}
  }
  printf "피크 타임: %s시 (%d 요청)\n", peak, max
  printf "최저 타임: %s시 (%d 요청)\n", lowest, min
  printf "피크/최저 비율: %.1fx\n", max/min
}' /var/log/nginx/access.log

설명

이것이 하는 일: 이 스크립트는 로그 데이터를 시간축을 기준으로 그룹핑하고, 각 시간대의 여러 메트릭(트래픽, 응답 시간, 에러율)을 동시에 계산하여 CSV 파일로 출력합니다. 마치 주식 차트처럼 시간에 따른 변화를 한눈에 볼 수 있게 만듭니다.

첫 번째 awk 스크립트는 BEGIN 블록에서 CSV 헤더를 먼저 출력합니다. 그런 다음 각 로그 라인을 읽으면서 시간을 추출하고, 그 시간을 키로 하여 세 가지 데이터를 누적합니다: 요청 개수(count), 응답 시간 합계(response_time), 에러 개수(errors).

마지막 END 블록에서 누적된 데이터를 기반으로 평균 응답 시간과 에러율을 계산하여 CSV 형식으로 출력합니다. 이 파일을 엑셀이나 구글 스프레드시트로 열면 자동으로 그래프를 그릴 수 있습니다.

그 다음으로, 요일별 비교를 수행합니다. date 명령어를 사용하여 오늘 날짜와 7일 전 날짜를 구하고, grep으로 해당 날짜의 로그만 추출하여 개수를 셉니다.

예를 들어 오늘이 금요일이라면, 지난주 금요일과 비교하여 트래픽이 얼마나 증가하거나 감소했는지 파악할 수 있습니다. 만약 지난주 금요일에는 5만 개 요청이었는데 오늘은 7만 개라면, 40% 증가했다는 뜻이고, 이런 추세가 계속되면 인프라를 확장해야 한다는 것을 알 수 있습니다.

세 번째 부분에서는 하루 중 가장 바쁜 시간과 가장 한가한 시간을 찾습니다. 각 시간대의 요청 수를 세고, 최대값과 최소값을 추적합니다.

그리고 피크와 최저의 비율도 계산합니다. 만약 피크가 5만 요청이고 최저가 500 요청이라면, 비율은 100배입니다.

이 정보는 오토스케일링 정책을 설정할 때 매우 중요합니다. 최저 타임에 맞춰 서버를 띄우되, 피크 타임에는 자동으로 100배까지 확장할 수 있도록 설정해야 합니다.

여러분이 이 스크립트를 매일 실행하고 결과를 시계열로 저장하면, 주 단위, 월 단위 트렌드를 볼 수 있습니다. 예를 들어 "우리 서비스는 매주 월요일 오전 9시에 피크를 기록하고, 주말 새벽에는 거의 트래픽이 없습니다.

최근 3개월간 평균 트래픽이 월 20%씩 증가하고 있으므로, 6개월 후에는 현재 인프라 용량의 3배가 필요할 것입니다"라는 식으로 데이터 기반 의사결정을 할 수 있습니다. 또한 이상치 탐지에도 활용할 수 있습니다.

평소 저녁 8시에 1만 요청이 오는데 어느 날 갑자기 10만 요청이 왔다면, 이것은 정상적인 사용자 증가가 아니라 봇 공격일 가능성이 높습니다. 트렌드를 알아야 이상한 점을 감지할 수 있습니다.

실무에서는 이 데이터를 Grafana, Kibana, CloudWatch 같은 도구로 시각화하여 실시간 대시보드를 만들기도 하지만, 예산이나 환경이 여의치 않을 때는 이렇게 Bash 스크립트와 CSV만으로도 충분히 유용한 인사이트를 얻을 수 있습니다.

실전 팁

💡 타임존을 명확히 하세요. 로그의 시간이 UTC인지 로컬 시간인지 확인하고, 필요하면 date -u 옵션으로 UTC로 통일하세요. 특히 글로벌 서비스라면 모든 시간을 UTC로 관리하는 것이 혼란을 줄입니다.

💡 CSV 파일을 구글 스프레드시트에 업로드하면 자동으로 차트를 생성할 수 있습니다. 시간을 X축, 트래픽을 Y축으로 설정하고 꺾은선 그래프를 그리면 트렌드가 한눈에 보입니다. 에러율은 보조 Y축으로 추가하세요.

💡 데이터를 데이터베이스에 저장하면 더 강력합니다. mysql -e "INSERT INTO metrics VALUES (...)"처럼 스크립트 결과를 바로 DB에 넣으면, SQL로 복잡한 분석(월별 평균, 동일 요일 비교 등)이 가능합니다.

💡 계절성을 고려하세요. 쇼핑몰은 블랙프라이데이나 연말에 트래픽이 급증하고, 학습 서비스는 방학에 감소합니다. 작년 같은 시기 데이터와 비교하면 더 정확한 예측이 가능합니다.

💡 여러 메트릭을 한 그래프에 그릴 때는 스케일을 조정하세요. 트래픽은 만 단위, 응답 시간은 초 단위라서 스케일이 완전히 다릅니다. 듀얼 Y축 차트를 사용하거나, 각 메트릭을 정규화(normalize)하여 0-100 범위로 맞추면 비교하기 쉽습니다.


5. 이동_평균_계산

시작하며

여러분이 실시간 트래픽 그래프를 보는데 선이 너무 울퉁불퉁해서 전체적인 추세를 파악하기 어려운 경험을 해본 적 있나요? 초 단위로 데이터를 보면 한 순간에 1000 RPS였다가 다음 순간에 100 RPS로 떨어지는 식으로 너무 변동이 심해서, 정말로 트래픽이 증가하는 건지 감소하는 건지 알기 어렵습니다.

이런 문제는 특히 알림 시스템을 구축할 때 심각합니다. "트래픽이 평균의 2배를 넘으면 알림"이라는 규칙을 만들었는데, 순간적인 스파이크 때문에 매 분마다 알림이 울려서 알림 피로(alert fatigue)가 발생합니다.

정작 진짜 문제가 생겨도 "또 오작동이겠지"하고 무시하게 됩니다. 바로 이럴 때 필요한 것이 이동 평균(Moving Average) 계산입니다.

최근 N개 데이터의 평균을 구하여 노이즈를 제거하고, 실제 트렌드만 부각시킬 수 있습니다.

개요

간단히 말해서, 이동 평균은 일정한 기간(예: 최근 5분, 최근 1시간)의 데이터 평균을 계속 업데이트하면서 계산하는 방법입니다. 마치 이동하는 창문(sliding window)을 통해 데이터를 보는 것과 같습니다.

이동 평균이 필요한 이유는, 실제 데이터는 항상 노이즈가 섞여 있기 때문입니다. 어떤 순간에 갑자기 요청이 몰렸다가 다음 순간에는 정상으로 돌아오는 식의 순간적인 변동은, 장기적인 트렌드를 보는 데 방해가 됩니다.

예를 들어, 주식 차트를 볼 때도 일봉(일일 가격)만 보면 너무 변동이 심하지만, 20일 이동평균선을 그으면 전체적인 추세를 쉽게 파악할 수 있는 것과 같은 원리입니다. 기존에는 "지금 RPS가 200이네, 500이네"하고 순간값만 봤다면, 이제는 "최근 10분간 평균 RPS는 300이고, 지금은 500이니 평균보다 1.6배 높구나"라고 문맥을 가지고 판단할 수 있습니다.

이동 평균의 핵심은 첫째, 창(window) 크기를 적절히 선택하는 것입니다. 5분 이동평균은 짧은 변화에 민감하고, 1시간 이동평균은 장기 트렌드를 보여줍니다.

둘째, 이동 평균과 실제 값의 차이를 보면 이상치를 감지할 수 있습니다. 실제 값이 이동 평균보다 2배 이상 크면 스파이크(급증), 절반 이하면 급감으로 판단할 수 있습니다.

코드 예제

# 10분 이동 평균을 사용한 RPS 스무딩
# 각 분의 RPS를 계산하고, 최근 10분 평균과 비교

awk '
BEGIN {
  window_size = 10;  # 10분 이동평균
  print "시간,실제RPS,10분이동평균,차이(%)"
}
{
  # 시:분 추출
  match($0, /\[.*:([0-9]{2}:[0-9]{2}):[0-9]{2}/, time);
  t = time[1];
  count[t]++;
}
END {
  # 배열을 시간순으로 정렬하기 위해 인덱스 배열 생성
  n = asorti(count, times);

  for (i = 1; i <= n; i++) {
    t = times[i];
    rps = count[t] / 60;  # 분당 요청을 초당으로 변환

    # 최근 window_size 개의 RPS 평균 계산
    sum = 0;
    cnt = 0;
    for (j = i - window_size + 1; j <= i; j++) {
      if (j > 0) {
        sum += count[times[j]] / 60;
        cnt++;
      }
    }
    moving_avg = (cnt > 0) ? sum / cnt : 0;

    # 차이 계산
    diff = (moving_avg > 0) ? ((rps - moving_avg) / moving_avg) * 100 : 0;

    printf "%s,%.2f,%.2f,%+.1f%%\n", t, rps, moving_avg, diff
  }
}' /var/log/nginx/access.log > moving_average.csv

# 이동 평균 기반 이상치 탐지
# 실제값이 이동평균의 2배를 넘으면 경고
awk '
NR > 1 {  # 헤더 건너뛰기
  split($0, fields, ",");
  time = fields[1];
  actual = fields[2];
  ma = fields[3];

  if (ma > 0 && actual > ma * 2) {
    printf "경고: %s - RPS %.2f (평균 %.2f의 %.1f배)\n", time, actual, ma, actual/ma
  }
}' moving_average.csv

# 지수 이동 평균 (EMA) 계산 (최근 데이터에 더 큰 가중치)
awk '
BEGIN {
  alpha = 0.3;  # 스무딩 계수 (0~1, 클수록 최근 데이터 가중)
  ema = 0;
  first = 1;
}
{
  match($0, /\[.*:([0-9]{2}:[0-9]{2}):[0-9]{2}/, time);
  count[time]++;
}
END {
  n = asorti(count, times);
  for (i = 1; i <= n; i++) {
    rps = count[times[i]] / 60;
    if (first) {
      ema = rps;
      first = 0;
    } else {
      ema = alpha * rps + (1 - alpha) * ema;
    }
    printf "%s,%.2f,%.2f\n", times[i], rps, ema
  }
}' /var/log/nginx/access.log

설명

이것이 하는 일: 이 스크립트는 시간대별 RPS를 계산한 후, 각 시점의 값과 함께 최근 10개 시점의 평균값을 함께 계산하여 비교합니다. 마치 물결치는 바다에서 파도(노이즈)를 걸러내고 조류(트렌드)만 보는 것과 같습니다.

첫 번째 awk 스크립트는 먼저 모든 로그를 읽어 시:분별 요청 개수를 count 배열에 저장합니다. 그런 다음 asorti 함수로 시간순으로 정렬된 인덱스 배열 times를 만듭니다.

이것이 중요한 이유는, 연관 배열은 기본적으로 순서가 없기 때문에 시간 순서대로 처리하려면 정렬이 필요하기 때문입니다. 그 다음으로, 각 시점 i에 대해 현재 RPS를 계산하고, 이전 9개 시점(i-9부터 i-1까지)과 함께 10개의 평균을 구합니다.

예를 들어 14:30분의 이동 평균은 14:21부터 14:30까지 10분간의 평균입니다. 그리고 실제 RPS와 이동 평균의 차이를 퍼센트로 계산합니다.

만약 실제가 300인데 평균이 200이라면, +50%로 표시됩니다. 두 번째 스크립트는 생성된 CSV 파일을 읽어서, 실제 값이 이동 평균의 2배를 넘는 경우만 필터링하여 경고를 출력합니다.

이렇게 하면 일시적인 작은 변동은 무시하고, 정말로 의미 있는 급증만 감지할 수 있습니다. 예를 들어 평소 평균 100 RPS인데 갑자기 250 RPS로 2.5배 증가했다면, 이것은 조사가 필요한 이상 신호입니다.

세 번째 부분은 지수 이동 평균(EMA, Exponential Moving Average)을 계산합니다. 단순 이동 평균은 모든 데이터에 동일한 가중치를 주지만, EMA는 최근 데이터에 더 큰 가중치를 줍니다.

alpha 값이 0.3이면 최신 데이터가 30%, 이전 EMA가 70%의 비중을 갖습니다. 이렇게 하면 트렌드 변화에 더 빠르게 반응하면서도, 노이즈는 여전히 걸러낼 수 있습니다.

여러분이 이 스크립트를 사용하면 "지금 RPS가 500인데, 10분 이동평균은 300이니 평소보다 66% 높다. 하지만 1시간 이동평균은 400이니 큰 문제는 아닌 것 같다"라는 식으로 다층적으로 판단할 수 있습니다.

짧은 이동평균과 긴 이동평균을 함께 보면, 단기 변동과 장기 트렌드를 동시에 파악할 수 있습니다. 금융에서는 단기 이동평균이 장기 이동평균을 상향 돌파하면 "골든 크로스"라고 부르며 상승 신호로 봅니다.

마찬가지로 트래픽에서도 5분 이동평균이 1시간 이동평균을 넘어서면 "트래픽 급증 추세"로 판단하고, 미리 서버를 증설하는 식의 대응이 가능합니다. 이동 평균의 또 다른 장점은 예측에도 활용할 수 있다는 점입니다.

과거 데이터의 이동 평균 패턴을 학습하면, "매주 월요일 오전 9시에 이동평균이 급상승한다"는 패턴을 발견하고, 다음 주 월요일 9시 전에 미리 서버를 준비할 수 있습니다.

실전 팁

💡 창 크기를 여러 개 사용하세요. 5분, 30분, 1시간 이동평균을 동시에 계산하면 단기/중기/장기 트렌드를 모두 파악할 수 있습니다. 엑셀에서 여러 선을 한 그래프에 그리면 교차점에서 트렌드 전환을 감지할 수 있습니다.

💡 표준편차도 함께 계산하면 변동성을 측정할 수 있습니다. 이동 평균은 300인데 표준편차가 10이면 안정적이지만, 표준편차가 100이면 매우 불안정한 상태입니다. awk에서 sqrt(sum_sq/n - (sum/n)^2)로 표준편차를 계산할 수 있습니다.

💡 계절성 조정을 추가하세요. 매주 월요일이 바쁘다는 것을 알고 있다면, 월요일의 이동평균과 화요일의 이동평균을 직접 비교하는 것은 의미가 없습니다. 같은 요일끼리 비교하거나, 요일별 가중치를 적용하세요.

💡 이동 평균의 변화율(기울기)을 보세요. 이동평균 자체보다, 이동평균이 상승 중인지 하락 중인지가 더 중요할 때가 많습니다. 현재 이동평균과 10분 전 이동평균을 비교하면 추세의 방향을 알 수 있습니다.

💡 EMA의 alpha 값을 조정하면 민감도를 제어할 수 있습니다. alpha=0.1은 매우 스무스하지만 반응이 느리고, alpha=0.9는 원본 데이터와 거의 비슷하게 민감합니다. 보통 0.2~0.3이 적절합니다.


6. 성능_저하_자동_탐지

시작하며

여러분의 서비스가 서서히 느려지고 있는데, 너무 점진적이어서 아무도 눈치채지 못하는 상황을 상상해보세요. 한 달 전에는 평균 응답 시간이 0.5초였는데 지금은 2초인데, 매일 조금씩 느려져서 사용자도 개발자도 "원래 이 정도였나?"하고 넘어갑니다.

그러다가 어느 날 갑자기 터져서 큰 장애로 이어집니다. 이런 문제는 특히 "끓는 물 속의 개구리" 신드롬이라고 불립니다.

서서히 악화되는 상황은 감지하기 어렵고, 눈치챘을 때는 이미 사용자가 떠난 후입니다. 갑작스러운 장애보다 서서히 진행되는 성능 저하가 더 위험할 수 있습니다.

바로 이럴 때 필요한 것이 성능 저하 자동 탐지입니다. 기준선(baseline)을 설정하고, 현재 성능이 기준선에서 얼마나 벗어났는지 자동으로 감지하여 알림을 보냅니다.

개요

간단히 말해서, 성능 저하 자동 탐지는 "정상" 상태의 기준을 정의하고, 현재 상태가 그 기준에서 유의미하게 벗어났는지를 통계적으로 판단하는 시스템입니다. 성능 저하 탐지가 중요한 이유는, 사람은 절대값을 기억하기 어렵기 때문입니다.

"응답 시간이 1.5초인데 이게 정상인가요?"라는 질문에 명확히 답하기 어렵습니다. 하지만 "평소 평균이 0.8초인데 지금은 1.5초니까 87% 느려졌습니다"라고 하면 명확합니다.

예를 들어, 데이터베이스 쿼리가 서서히 느려지는 것(인덱스 없이 데이터가 계속 쌓이는 경우)은 단순 임계값 알림으로는 잡기 어렵지만, 기준선 대비 변화를 추적하면 조기에 발견할 수 있습니다. 기존에는 "응답 시간이 3초를 넘으면 알림"같은 고정 임계값을 사용했다면, 이제는 동적 임계값(평균의 2배, 또는 평균 + 2 표준편차)을 사용하여 더 똑똑하게 감지할 수 있습니다.

자동 탐지의 핵심은 첫째, 기준선을 어떻게 정의하느냐입니다. 최근 7일 평균을 쓸지, 같은 요일 같은 시간대의 과거 평균을 쓸지 결정해야 합니다.

둘째, 얼마나 벗어나야 "이상"으로 볼지 임계값을 정하는 것입니다. 평균보다 50% 느리면 경고, 100% 느리면 심각한 장애로 분류할 수 있습니다.

셋째, 거짓 양성(false positive)을 줄이는 것입니다. 순간적인 스파이크는 무시하고, 일정 시간(예: 5분) 이상 지속될 때만 알림을 보내야 합니다.

코드 예제

# 성능 저하 자동 탐지 스크립트
# 최근 7일 평균과 비교하여 현재 성능 평가

# 1단계: 기준선 계산 (최근 7일 평균 응답시간)
baseline=$(find /var/log/nginx -name "access.log.*" -mtime -7 | \
  xargs awk '{sum+=$NF; count++} END {print sum/count}')

echo "=== 성능 저하 탐지 시스템 ==="
echo "기준선 (7일 평균 응답시간): ${baseline}초"

# 2단계: 현재 성능 측정 (최근 10분)
current=$(tail -n 10000 /var/log/nginx/access.log | \
  awk '{sum+=$NF; count++} END {print sum/count}')

echo "현재 (최근 10분 평균): ${current}초"

# 3단계: 차이 계산 및 판단
diff=$(echo "scale=2; ($current - $baseline) / $baseline * 100" | bc)
echo "변화율: ${diff}%"

# 4단계: 임계값 기반 알림
if (( $(echo "$diff > 100" | bc -l) )); then
  echo "🚨 심각: 성능이 기준선의 2배 이상 저하됨!"
  # 실제로는 여기서 Slack/이메일 알림 전송
elif (( $(echo "$diff > 50" | bc -l) )); then
  echo "⚠️  경고: 성능이 기준선보다 50% 이상 저하됨"
elif (( $(echo "$diff > 20" | bc -l) )); then
  echo "ℹ️  주의: 성능이 기준선보다 20% 저하됨"
else
  echo "✅ 정상: 성능이 기준선 범위 내"
fi

# 5단계: 표준편차 기반 이상치 탐지 (더 정교한 방법)
awk -v baseline="$baseline" '
BEGIN {
  sum = 0; sum_sq = 0; count = 0;
}
{
  sum += $NF;
  sum_sq += ($NF * $NF);
  count++;
}
END {
  mean = sum / count;
  variance = (sum_sq / count) - (mean * mean);
  stddev = sqrt(variance);

  # 2 시그마 범위를 벗어나면 이상 (정규분포 가정시 95% 신뢰구간)
  upper_limit = mean + 2 * stddev;
  lower_limit = mean - 2 * stddev;

  printf "\n=== 통계적 분석 ===\n"
  printf "평균: %.3f초, 표준편차: %.3f초\n", mean, stddev
  printf "정상 범위: %.3f ~ %.3f초\n", lower_limit, upper_limit

  if (mean > upper_limit) {
    print "통계적 이상: 현재 평균이 정상 범위를 초과함"
  }
}' /var/log/nginx/access.log

# 6단계: 연속 5분 저하 시에만 알림 (false positive 방지)
awk '{
  match($0, /\[.*:([0-9]{2}:[0-9]{2}):[0-9]{2}/, time);
  t = time[1];
  sum[t] += $NF;
  count[t]++;
}
END {
  consecutive_degraded = 0;
  for (t in sum) {
    avg = sum[t] / count[t];
    if (avg > BASELINE * 1.5) {  # 기준선의 1.5배 초과
      consecutive_degraded++;
    } else {
      consecutive_degraded = 0;
    }

    if (consecutive_degraded >= 5) {
      printf "🚨 지속적 성능 저하: %d분 연속으로 기준선 초과\n", consecutive_degraded
    }
  }
}' BASELINE="$baseline" /var/log/nginx/access.log

설명

이것이 하는 일: 이 스크립트는 과거 데이터로부터 "정상" 상태의 기준선을 계산하고, 현재 상태를 측정하여 둘을 비교한 후, 차이가 임계값을 넘으면 알림을 발생시킵니다. 마치 의사가 환자의 정상 체온을 알고 있다가, 현재 체온이 정상 범위를 벗어나면 진료를 시작하는 것과 같습니다.

첫 번째 단계에서는 find 명령으로 최근 7일간의 로그 파일을 모두 찾고, xargs로 awk에 전달하여 전체 평균 응답 시간을 계산합니다. 이것이 "기준선(baseline)"이 됩니다.

7일이라는 기간은 주간 패턴(평일/주말 차이)을 포함하면서도 너무 오래되지 않아 최근 트렌드를 반영하기 좋은 기간입니다. 예를 들어 기준선이 0.8초라면, "정상적인 상태에서 우리 서비스는 평균 0.8초에 응답한다"는 것을 의미합니다.

그 다음으로, 현재 성능을 측정합니다. tail -n 10000으로 최근 1만 개 요청(보통 최근 10분~1시간 정도)의 평균 응답 시간을 계산합니다.

만약 현재 평균이 1.5초라면, 기준선 0.8초보다 0.7초 느린 것이고, 이는 87% 저하입니다. bc 명령어로 퍼센트 변화율을 계산합니다.

세 번째 단계에서는 변화율을 기반으로 심각도를 분류합니다. 100% 이상(2배) 저하면 심각한 장애로 분류하고 즉각 알림을 보냅니다.

50% 저하면 경고, 20% 저하면 주의 수준으로 분류합니다. 실무에서는 여기서 Slack Webhook이나 이메일 API를 호출하여 실제 알림을 전송합니다.

예를 들어 curl -X POST https://hooks.slack.com/... -d '{"text":"성능 저하 감지!"}'처럼 사용할 수 있습니다.

네 번째 부분은 더 정교한 통계적 방법을 사용합니다. 단순히 평균만 보는 것이 아니라, 표준편차를 계산하여 데이터의 변동성을 고려합니다.

정규분포를 가정할 때, 평균 ± 2 표준편차 범위에 95%의 데이터가 포함됩니다. 따라서 현재 값이 이 범위를 벗어나면 통계적으로 "이상치"로 판단할 수 있습니다.

이 방법은 단순 퍼센트 비교보다 더 과학적이고, 거짓 양성을 줄여줍니다. 다섯 번째 단계는 거짓 양성(false positive) 방지를 위한 장치입니다.

순간적으로 1분만 느린 것은 일시적인 문제일 수 있지만, 5분 연속으로 기준선을 초과한다면 이것은 진짜 문제입니다. consecutive_degraded 변수로 연속된 저하 시간을 추적하고, 5분 이상 지속될 때만 알림을 발생시킵니다.

이렇게 하면 "울지 않는 늑대" 문제를 방지하고, 정말 중요한 알림만 받을 수 있습니다. 여러분이 이 스크립트를 cron으로 매 5분마다 실행하면, 24/7 자동 모니터링 시스템이 됩니다.

예를 들어 */5 * * * * /path/to/performance_check.sh처럼 crontab에 등록하면, 자는 동안에도 스크립트가 계속 감시하다가 문제가 생기면 즉시 알려줍니다. 실무에서는 여러 메트릭을 동시에 모니터링합니다.

응답 시간뿐만 아니라 처리량, 에러율, CPU 사용률 등을 함께 보고, 여러 지표가 동시에 악화되면 더 높은 우선순위로 알림을 보냅니다. 또한 기계학습 기법(예: Prophet, ARIMA)을 적용하면 계절성과 트렌드를 자동으로 학습하여 더 정확한 예측과 탐지가 가능하지만, 간단한 통계 기반 방법만으로도 충분히 실용적입니다.

실전 팁

💡 기준선을 정기적으로 업데이트하세요. 서비스가 성장하면 트래픽 패턴이 변하므로, 한 달 전 기준선은 더 이상 유효하지 않을 수 있습니다. 매주 기준선을 재계산하거나, 슬라이딩 윈도우(항상 최근 7일)를 사용하세요.

💡 요일과 시간대를 고려한 기준선을 만드세요. 월요일 오전과 일요일 새벽의 "정상"은 완전히 다릅니다. baseline_mon_09, baseline_sun_03 처럼 요일+시간대별 기준선을 저장하면 더 정확합니다.

💡 여러 백분위수를 추적하세요. 평균만 보면 극단값에 영향을 많이 받으므로, p50(중앙값), p95, p99를 함께 보면 더 정확합니다. p99가 급증하면 일부 사용자가 심각하게 느린 경험을 하고 있다는 뜻입니다.

💡 근본 원인 분석(RCA)을 자동화하세요. 성능 저하가 감지되면, 동시에 "지금 에러율은? CPU는? 데이터베이스 커넥션 수는?"을 자동으로 체크하는 스크립트를 실행하면, 문제의 원인을 빠르게 좁힐 수 있습니다.

💡 알림 피로를 방지하기 위해 알림 빈도를 제한하세요. 같은 문제로 5분마다 알림을 보내는 대신, "1시간에 한 번만" 또는 "문제가 해결되었다가 다시 발생할 때만" 알림을 보내도록 설정하세요. 파일에 마지막 알림 시간을 저장하고 비교하면 됩니다.


#Bash#성능모니터링#로그분석#메트릭추출#트렌드분석#Bash,Linux,모니터링

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.