0Pricing
DevOps Bootcamp · 강의

대규모 웹 및 애플리케이션 로그 분석

grep, cut 및 awk를 사용하여 액세스 로그에서 상태 코드, 지연 시간 및 클라이언트 필드를 추출합니다.

대규모 웹 및 애플리케이션 로그 분석은(는) CoddyKit의 무료 DevOps Bootcamp 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 DevOps Bootcamp 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.

웹 접근 로그란 무엇인가요

Apache, Nginx, Caddy와 같은 모든 HTTP 서버는 각 요청마다 접근 로그에 한 줄을 기록합니다. 이 줄의 구조를 이해하는 것은 모든 로그 분석 작업의 기초입니다.

일반적인 Combined Log Format(CLF) 줄은 다음과 같습니다.

  • 클라이언트 IP — 요청을 보낸 주체
  • 타임스탬프 — 요청이 발생한 시각
  • 요청 줄 — 메서드, 경로, 프로토콜
  • 상태 코드 — HTTP 응답(200, 404, 500…)
  • 전송된 바이트 수 — 응답 본문의 크기
  • 리퍼러 — 요청이 시작된 페이지
  • 사용자 에이전트 — 브라우저 또는 봇을 나타내는 문자열

/var/log/nginx/access.log에서 가져온 예시 줄입니다.

192.168.1.10 - alice [11/Jun/2026:14:32:01 +0000] "GET /api/orders HTTP/1.1" 200 1482 "-" "curl/7.88.1"

대규모 환경에서는 이러한 파일이 하루에 수백만 줄씩 늘어납니다. 이 학습의 목표는 표준 BASH 도구를 사용하여 이러한 파일의 필드를 효율적으로 추출하고, 필터링하고, 집계하는 것입니다.

tail과 grep으로 실시간 로그 표본 확인하기

파이프라인을 작성하기 전에 로그를 살펴보고 구조를 파악해야 합니다. tail은 실시간 스트림을 확인하게 해 주고, grep은 관련 줄만 즉시 추려냅니다.

일반적인 패턴은 다음과 같습니다.

  • tail -n 1000 access.log — 마지막 1000줄
  • tail -f access.log — 실시간으로 따라가기
  • tail -f access.log | grep '" 5' — 발생하는 5xx 오류만 확인하기

핵심은 grep이 줄 전체를 대상으로 일치 여부를 확인한다는 점입니다. 따라서 패턴을 정확히 지정하는 것이 중요합니다. ' 500 '처럼 공백을 포함해 일치시키면 URL 경로에 500이라는 문자열이 들어 있는 경우를 잘못 찾는 일을 피할 수 있습니다.

#!/usr/bin/env bash
# Watch only HTTP 5xx errors arriving in real time
tail -f /var/log/nginx/access.log \
  | grep --line-buffered '" 5[0-9][0-9] '

cut으로 상태 코드 추출하기

cut은 구분 기호를 기준으로 각 줄을 나누고 선택한 필드를 출력합니다. Combined Log Format에서는 공백으로 나눴을 때 상태 코드가 9번째 필드에 위치하지만, 요청 줄을 감싸는 따옴표 때문에 알려진 기준점부터 세는 편이 더 안전합니다.

신뢰할 수 있는 방법은 다음과 같습니다. 요청 줄은 항상 따옴표로 묶이므로 상태 코드는 요청 필드를 닫는 따옴표 뒤에 오는 첫 번째 토큰입니다. cut -d'"' -f3로 요청을 감싼 따옴표 뒤의 모든 내용을 분리한 다음, 두 번째 cut -d' ' -f2로 상태 코드를 선택합니다.

이 두 단계의 cut 방식은 CLF 로그에서 사용하는 전형적인 관용구입니다. 빠르고 외부 의존성이 없습니다.

#!/usr/bin/env bash
# Print only the HTTP status code from each log line
# Input format: ... "GET /path HTTP/1.1" 200 1482 ...
cut -d'"' -f3 /var/log/nginx/access.log \
  | cut -d' ' -f2 \
  | sort \
  | uniq -c \
  | sort -rn

awk로 상태 코드 집계하기

awk는 줄 사이에서 상태를 누적할 수 있으므로 cut보다 강력합니다. 발생 횟수를 세는 관용적인 패턴은 관심 있는 값을 키로 사용하는 연관 배열입니다.

CLF에서 필드 $9(1부터 시작하며 공백으로 구분됨)는 상태 코드입니다. awk는 각 줄을 처리하고 카운터를 증가시킨 다음 END 블록에서 정렬된 요약을 출력합니다.

cut | sort | uniq -c 대신 awk를 사용하는 이유는 무엇일까요? awk는 전체 파일을 먼저 정렬하지 않고 한 번의 순회로 작업을 수행하기 때문입니다. 로그가 수백 기가바이트에 이를 때 특히 중요합니다.

#!/usr/bin/env bash
# Count HTTP status codes in a single awk pass
awk '{ count[$9]++ }
END {
  for (status in count)
    printf "%6d  %s\n", count[status], status
}' /var/log/nginx/access.log \
  | sort -rn

오류 필터링 및 클라이언트 IP 추출

가장 일반적인 운영 작업 중 하나는 어떤 클라이언트 IP가 가장 많은 오류를 발생시키는지 찾는 것입니다. 이 작업은 필터링(오류 줄만 선택)과 필드 추출(1번째 필드의 IP)을 결합합니다.

파이프라인 전략은 다음과 같습니다.

  • awk를 사용하여 상태 코드 범위로 필터링하고 IP를 한 단계에서 추출합니다. 별도의 grep 순회를 수행하지 않습니다
  • sort | uniq -c | sort -rn | head로 연결하여 상위 N개를 빠르게 확인합니다

이 패턴은 파일 전체를 메모리에 올리지 않고도 단일 서버에서 10GB 로그 파일을 처리할 만큼 빠릅니다.

#!/usr/bin/env bash
# Top 10 IPs generating HTTP 4xx or 5xx errors
awk '$9 ~ /^[45][0-9][0-9]$/ { print $1 }' \
    /var/log/nginx/access.log \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -10

애플리케이션 로그에서 응답 지연 시간 분석하기

애플리케이션 서버(Rails, Gunicorn, morgan을 사용하는 Express 등)는 요청 소요 시간을 기록하는 경우가 많습니다. Nginx는 각 줄 끝에 추가 필드로 $request_time을 출력하도록 설정할 수 있습니다.

nginx.conf에 설정하는 사용자 지정 Nginx 로그 형식의 예입니다.

log_format timed '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" rt=$request_time';

로그에 지연 시간이 기록되면 데이터를 데이터베이스에 적재하지 않고도 awk를 사용하여 수백만 요청에 대한 평균, 최댓값, 백분위수 근사값을 계산할 수 있습니다.

#!/usr/bin/env bash
# Compute average and max request_time from Nginx timed log
# Assumes last field is rt=<seconds> e.g. rt=0.042
awk '{
  # Extract numeric value after rt=
  n = split($NF, a, "=")
  if (n == 2 && a[1] == "rt") {
    t = a[2] + 0
    sum += t
    count++
    if (t > max) max = t
  }
}
END {
  if (count > 0)
    printf "Requests: %d  Avg: %.4fs  Max: %.4fs\n", count, sum/count, max
}' /var/log/nginx/timed_access.log

awk로 지연 시간 히스토그램 만들기

단일 평균값만으로는 꼬리 지연 시간을 알 수 없습니다. 히스토그램은 분포를 보여 줍니다. 대부분의 요청이 빠르고 일부만 매우 느린지(긴 꼬리), 아니면 분포가 균일한지 확인할 수 있습니다.

핵심은 awk 내부에서 정수 연산을 사용하여 각 값을 반올림한 구간에 넣는 것입니다. 초를 밀리초로 변환하기 위해 1000을 곱한 다음 정수 나눗셈을 사용하면 깔끔한 구간 경계를 얻을 수 있습니다.

이렇게 하면 터미널에서 바로 읽을 수 있는 텍스트 히스토그램이 생성됩니다. 간단한 조사를 위해 데이터를 Grafana로 전송하는 것보다 빠른 경우가 많습니다.

#!/usr/bin/env bash
# Latency histogram (50ms buckets) from Nginx timed log
awk '{
  n = split($NF, a, "=")
  if (n == 2 && a[1] == "rt") {
    ms = int(a[2] * 1000)      # convert to ms
    bucket = int(ms / 50) * 50 # round down to 50ms boundary
    hist[bucket]++
  }
}
END {
  for (b in hist)
    printf "%6dms  %d\n", b, hist[b]
}' /var/log/nginx/timed_access.log \
  | sort -n

사용자 에이전트 추출 및 봇 감지

사용자 에이전트 필드("를 기준으로 나눴을 때 6번째 필드)는 클라이언트를 식별합니다. 크롤러, 스크레이퍼, 악성 봇은 지표를 오염시키고 오류 수를 부풀리는 경우가 많습니다. 이러한 항목을 필터링하면 실제 사용자 트래픽을 더 정확하게 파악할 수 있습니다.

일반적인 봇 식별 문자열은 다음과 같습니다. bot, crawler, spider, curl, python-requests, Googlebot, Bingbot.

grep -iv(대소문자를 구분하지 않고 일치 항목을 제외하는 옵션)를 사용하여 알려진 봇을 제외하거나, awk로 "를 기준으로 나눈 다음 UA 필드를 직접 대조할 수 있습니다.

#!/usr/bin/env bash
# Count top 15 User-Agent strings, excluding known bots
awk -F'"' '{ print $6 }' /var/log/nginx/access.log \
  | grep -iv -e 'bot' -e 'crawler' -e 'spider' -e 'curl' \
              -e 'python' -e 'wget' -e 'Go-http-client' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -15

엔드포인트별 트래픽 집계

어떤 엔드포인트가 가장 많은 트래픽을 받고 가장 많은 오류를 발생시키는지 알면 최적화와 용량 계획의 우선순위를 정하는 데 도움이 됩니다. 요청 경로는 따옴표로 묶인 요청 필드 안에 있습니다.

"를 기준으로 나눈 뒤 2번째 필드(요청 줄)를 가져오고, 메서드와 프로토콜을 잘라내 경로만 분리합니다. /users/12345처럼 경로 매개변수가 있는 API에서는 sed나 더 복잡한 awk 패턴을 사용하여 ID를 /users/:id로 정규화할 수도 있습니다.

#!/usr/bin/env bash
# Top 20 requested endpoints (method + path, no query string)
awk -F'"' '{ print $2 }' /var/log/nginx/access.log \
  | awk '{ print $1, $2 }' \
  | sed 's|/[0-9][0-9]*\b|/:id|g' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -20

awk로 엔드포인트별 오류 연관 분석하기

가장 강력한 단일 순회 분석은 엔드포인트, 상태 코드, 그리고 선택적으로 지연 시간 등 여러 필드를 한 번에 결합합니다. 복합 값을 키로 사용하는 awk 연관 배열을 이용하면 이 작업을 깔끔하고 빠르게 처리할 수 있습니다.

다음 패턴은 한 번의 순회로 엔드포인트별 5xx 오류를 집계합니다. 임시 파일이 필요 없고, 마지막까지 중간 정렬도 수행하지 않습니다. 대규모 로그에서 1분 이내에 결과가 필요할 때 운영 환경의 관측성 스크립트에서 사용하는 방식입니다.

#!/usr/bin/env bash
# Count 5xx errors per endpoint path in a single pass
awk -F'"' '{
  # $2 = request line e.g. "GET /api/orders HTTP/1.1"
  # $0 in original space-split: $9 = status
  split($0, fields, " ")
  status = fields[9]
  if (status ~ /^5/) {
    split($2, req, " ")
    path = req[2]
    # Normalise numeric IDs
    gsub(/\/[0-9]+/, "/:id", path)
    errors[path]++
  }
}
END {
  for (p in errors)
    printf "%6d  %s\n", errors[p], p
}' /var/log/nginx/access.log \
  | sort -rn \
  | head -20

순환 및 압축된 로그 처리하기

대부분의 서버에서는 로그가 매일 순환됩니다. 오래된 파일은 access.log.1.gz, access.log.2.gz와 같이 gzip으로 압축됩니다. 표준 도구는 이러한 파일을 직접 읽을 수 없지만, 다음 두 가지 방법을 사용할 수 있습니다.

  • zcat — 표준 출력으로 압축을 해제한 다음 파이프라인으로 연결합니다
  • zgrep — 압축을 해제하지 않고 gzip 파일 내부에서 직접 grep을 수행합니다

압축되지 않은 파일과 압축된 파일에 걸쳐 있는 일주일치 로그를 분석하려면 프로세스 치환을 사용하거나 zcat으로 연결합니다. 다음 코드 조각은 임시 파일 없이 단일 awk 호출로 최근 순환된 로그 7개와 현재 실시간 로그를 처리합니다.

#!/usr/bin/env bash
# Aggregate status codes across a week of rotated logs
# Handles both plain and gzip-compressed rotation files

LOG_DIR="/var/log/nginx"

{
  cat  "${LOG_DIR}/access.log" 2>/dev/null
  zcat "${LOG_DIR}/access.log".*.gz 2>/dev/null
} | awk '
{ count[$9]++ }
END {
  for (s in count)
    printf "%6d  %s\n", count[s], s
}' | sort -rn

Combined Log Format에서 HTTP 상태 코드를 담고 있는 awk 필드는 무엇인가요?

awk 한 줄 명령을 작성하여 Combined Log Format(공백으로 구분되고 요청 줄이 따옴표로 묶인 형식)의 표준 Nginx 액세스 로그에서 HTTP 4xx 응답만 필터링하려고 합니다. HTTP 상태 코드를 올바르게 식별하는 필드 번호는 무엇입니까?

강의 요약: 로그 분석 파이프라인

이 강의에서는 표준 BASH 유틸리티만 사용하여 웹 및 애플리케이션 로그를 대규모로 분석하기 위한 완전한 도구 모음을 구축했습니다.

다룬 주요 기법:

  • 먼저 구조 파악하기 — Combined Log Format에는 예측 가능한 필드 배치가 있으므로, 이를 알면 cut -d'"' 또는 awk 필드 참조를 사용하여 안정적으로 분할할 수 있습니다.
  • 상태 코드 추출 — awk '{ count[$9]++ }'는 한 번의 순회로 모든 코드를 셉니다. 서버 오류를 찾으려면 $9 ~ /^5/로 필터링합니다.
  • 지연 시간 분석 — awk로 사용자 정의 rt= 필드를 분석하면 외부 도구 없이 평균, 최댓값, 히스토그램 구간을 계산할 수 있습니다.
  • 클라이언트 및 봇 분석 — -F'"'와 함께 "을 기준으로 분할하여 User-Agent 필드에 접근하고, 집계하기 전에 grep -iv를 통해 봇을 제외합니다.
  • 엔드포인트 정규화 — awk 안에서 gsub(/\/[0-9]+/, "/:id")를 사용하여 개수 세기 전에 매개변수가 포함된 경로를 하나로 정리합니다.
  • 순환된 로그 — 서브셸에서 cat과 zcat을 결합하여 모든 순환 로그 파일을 하나의 파이프라인 처리에 공급합니다.

이러한 패턴은 서로 조합할 수 있습니다. 필터링, 추출, 정규화, 집계를 하나의 파이프라인으로 연결하여 일반 하드웨어에서도 수억 개의 행을 처리할 수 있습니다. 이러한 기본 기능을 익히면 임시 장애 조사를 위해 전용 로그 집계 서비스를 사용할 일이 거의 없습니다.

자주 묻는 질문

“대규모 웹 및 애플리케이션 로그 분석” 강의는 무료인가요?

네 — “대규모 웹 및 애플리케이션 로그 분석” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 DevOps Bootcamp 강의 전체를 잠금 해제할 수 있습니다. DevOps Bootcamp 강의에는 총 4개의 강의가 포함되어 있습니다.

“대규모 웹 및 애플리케이션 로그 분석”에서 뭘 배우나요?

grep, cut 및 awk를 사용하여 액세스 로그에서 상태 코드, 지연 시간 및 클라이언트 필드를 추출합니다. 브라우저에서 직접 실행하는 실습 코드로 DevOps Bootcamp을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

DevOps Bootcamp을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 DevOps Bootcamp은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.

“대규모 웹 및 애플리케이션 로그 분석” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 DevOps Bootcamp 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 DevOps Bootcamp 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 대규모 웹 및 애플리케이션 로그 분석
  2. 실시간 로그 추적 및 스트리밍 알림
  3. 스크립트에서 journalctl로 journald 조회
  4. 로그 스트림에서 지표 및 히스토그램 계산
← DevOps Bootcamp(으)로 돌아가기