본문 바로가기

생활코딩

GoAccess - nginx 접속 로그 분석

웹서버 접속 로그 분석툴인 GoAccess 사용 및 설정에 대한 내용을 정리 차원에서 적어둡니다.

 

1. 설치

웬만한 리눅스 배포본에 대해서는 패키지로 제공하기 때문에 패키지로 설치하는 것이 편합니다. 다음은 debian 패키지 설치 방법이고, 다른 리눅스 배포본에 대해서는 https://goaccess.io/download#distro 에 잘 나와 있습니다.

wget -O - https://deb.goaccess.io/gnugpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/goaccess.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/goaccess.gpg arch=$(dpkg --print-architecture)] https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/goaccess.list
sudo apt-get update
sudo apt-get install goaccess

 

이렇게 설치하면 바로 goaccess라는 명령어로 실행할 수 있는 상태가 됩니다.

 

2. 간단한 실행

제대로 동작하는지 간단히 쉘 상에서 실행해 봅니다.

cd nginx의access.log가있는디렉토리경로
# nginx의 기본 로그 형식은 COMBINED
# 만약 다른 로그 형식일 경우 -c 옵션을 추가해서 실행하면 로그 형식 선택 가능합니다.
goaccess access.log --log-format=COMBINED

 

위와 같이 실행하면 다음과 같은 화면이 나옵니다. 이렇게 나오면 잘 설치된 것입니다.

 

HTML 파일로 저장하고 싶은 경우에는 다음과 같이 합니다.

goaccess access.log -o report.html --log-format=COMBINED

 

저장된 파일을 브라우저로 열어보게 되면 다음과 같은 모습입니다.

 

3. 주기적인 분석 실행 및 편리한 확인

분석 결과 HTML 파일을 주기적으로 웹 서버의 문서 디렉토리에 생성되게 하면 웹 브라우저로 접속해 지속적인 확인을 할 수 있으므로 편리합니다. 주기적인 실행과 관련해서는 goaccess 공식 문서에서 굉장하면서도 종합적인 튜토리얼(정확히는 awesome and conprehensive tutorial 이라고 표현하고 있습니다)이라고 얘기하고 있는 여기에 잘 설명이 돼 있습니다. 저도 여기에서 설명하는 방법 중에 하나를 선택했고 스크립트도 거의 그대로 사용했습니다.

 

3.1 nginx 설정 추가

분석 결과 파일을 웹 서버로 조회할 수 있도록 nginx에 설정을 추가합니다.

# 라즈베리파이 5(혹은 데비안 bookworm)에서 nginx를 패키지로 설치했을 때 기준
cd /etc/nginx/sites-available
sudo vi goaccess

 

다음의 내용을 추가합니다.

server {
    listen 9999;

    location / {
	    root /var/www/goaccess;
    }
}

 

그 후에 활성화 시키고, 재시작합니다(/var/www/goaccess 디렉토리는 있다고 전제합니다).

sudo ln -s /etc/nginx/sites-available/goaccess /etc/nginx/sites-enabled/
sudo systemctl restart nginx

 

3.2 주기적인 분석 실행

logrotate가 실행되는 시점에 실행 전에 분석을 하도록 설정하겠습니다.

logrotate가 시스템에 설정돼 있는지는 다음의 명령어로 확인 가능합니다.

systemctl status logrotate.timer

 

logroate에는 prerotate라는 훅(hook)이 있습니다. 즉, logrotate 실행 전에 원하는 작업을 실행시킬 수 있습니다.

 

먼저 다음과 같은 스크립트 파일을 작성합니다.

#!/bin/bash

set -eu

OUTDIR=
LOGDIR=/var/log/nginx
DBDIR=/var/lib/goaccess-db

fail() { echo >&2 "$@"; exit 1; }

[ $# -eq 1 ] || fail "Usage: $(basename $0) OUTPUT_DIRECTORY"

OUTDIR=$1

[ -d "$OUTDIR" ] || fail "'$OUTDIR' is not a directory"
[ -d "$LOGDIR" ] || fail "'$LOGDIR' is not a directory"
[ -d "$DBDIR"  ] || fail "'$DBDIR' is not a directory"

yesterday=$(date -d "yesterday" +"%Y%m%d")

for d in $(find "$LOGDIR" -mindepth 1 -maxdepth 1 -type d); do
    site=$(basename "$d")
    dbdir=$DBDIR/$site
    logfile=$d/access.log
    echo $logfile
    outfile=$OUTDIR/$site.html

    if [ ! -d "$dbdir" ] || [ ! -e "$logfile" ]; then
        echo "‣ Skipping site '$site'"
        continue
    else
        echo "‣ Processing site '$site'"
    fi

    outfile_per_day=$OUTDIR/$site$yesterday.html
    
    # 실행 사용자와 그룹을 지정합니다.
    # 권한을 최소화하기 위해 OUTPUT 디렉토리와 goaccess DB 디리게토리에 파일을 쓸 수 있는
    # 권한이 있는 일반 사용자/그룹으로 지정합니다.
    setpriv \
        --reuid=원하는사용자 --regid=원하는그룹 \
        --init-groups --inh-caps=-all \
        -- \
    goaccess \
        --agent-list \
        --config-file /etc/goaccess/goaccess.conf \
        --log-format "COMBINED" \
        --output "$outfile_per_day" \
        "$logfile"

    setpriv \
        --reuid=원하는사용자 --regid=원하는그룹 \
        --init-groups --inh-caps=-all \
        -- \
    goaccess \
        --agent-list \
        --persist \
        --restore \
        --config-file /etc/goaccess/goaccess.conf \
        --db-path "$dbdir" \
        --log-format "COMBINED" \
        --output "$outfile" \
        "$logfile"
done

 

이 파일을 /usr/local/bin 파일에 만들고 실행권한을 부여합니다. 

이 파일은 LOGDIR에 설정된 디렉토리 하위의 디렉토리들에서 access.log를 차례로 읽어 들여서 각각 분석을 하고 OUTDIR 위치에 디렉토리이름.html 식의 파일 이름으로 저장합니다. 즉, 기본 설정대로라면 /var/log/nginx/foo 에 access.log 파일이 있는 경우, 이를 OUTPUT 디렉토리에 foo어제날짜.html과 foo.html을 생성합니다.

디렉토리이름YYYYMMDD.html과 디렉토리이름.html을 생성하는 이유

goaceess에서 한 가지 아쉬운 점은 영구 저장 옵션을 사용해도 일별 조회/사용자 수 외에는 날짜나 기간 별로 조회를 할 수 없다는 것입니다. 특정 기간이나 날짜에 대해서만 결과를 보려면 로그에서 해당 기간으로 필터링을 한 목록을 대상으로 goaccess를 실행해야 합니다. 특정 기간에 대해 실행하는 방법은 https://goaccess.io/man#examples 의 WORKING WITH DATES에 잘 설명돼 있습니다.
저 같은 경우는 로그 분석 시작 시점부터 계속해서 누적으로 보는 게 크게 문제는 없지만 일별로의 분석을 봐야 할 필요는 있어서 이전 데이터를 사용하지 않고 (--restore 옵션 사용 안 함) 그날 로그만 분석해서 뒤에 날짜를 붙인 파일이름 저장하는 분석을 한 번 더 하게 했습니다.
어제 날짜를 사용하는 이유는 로그로테이션이 0시 정각에 실행되기 때문에 분석되는 파일은 어제의 로그이기 때문입니다. 

 

영구 저장 데이터를 위한 디렉토리와 생성되는 HTML 파일을 저장할 디렉토리를 생성합니다. 여기서는 저장할 디렉토리는 앞서 nginx에 설정한 /var/www/goaccess로 할 거기 때문에 추가로 생성하지 않겠습니다.

 

sudo mkdir -p /var/lib/goaccess-db/대상디렉토리이름
sudo chown -R 원하는사용자:원하는그룹 /var/lib/goaccess-db/대상디렉토리이름

 

goaccess에 영구 저장 옵션을 사용하는 경우 --db-path 지정 시 디렉토리가 없는 경우 자동으로 만들어 주면 좋겠지만, 그렇지는 않아서 직접 만들어줘야 합니다. 예를 들어 /var/log/nginx/foo, /var/log/nginx/bar 이 있다면 개별적으로 /var/lib/goaccess-db/foo와 /var/lib/goaccess-db/bar를 모두 만들어 줘야 합니다.

 

이렇게 설정을 한 후에 다음과 같이 실행해서 잘 동작하는지 확인합니다.

sudo /usr/local/bin/goaccess-wrapper 결과html저장될경로

 

저장될 디렉토리에 html 파일이 생성됐으면 제대로 된 것입니다.

3.2.1 logrotate 설정

nginx 로그 디렉토리 하위 디렉토리들에 대해서 로그 로테이션이 되게 nginx 관련 설정 파일의 첫 줄을 다음과 같이 변경합니다.

sudo vi /etc/logrotate.d/nginx

/var/log/nginx/**/*.log {
...

 

/etc/logrotate.d/nginx 파일 내용에서 중간 부분을 보면 다음과 같은 내용이 나옵니다.

	prerotate
		if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
			run-parts /etc/logrotate.d/httpd-prerotate; \
		fi \
	endscript

 

내용에서 보이는 것처럼, logrotate는 nginx 로그들을 로테이션 하기 전에  /etc/logrotate.d/httpd-prerorate 디렉토리에 있는 실행 파일들을 실행합니다.

이게 httpd-prerorate 디렉토리를 만들고, 그 곳에 goaccess-wrapper를 실행시키는 스크립트 파일을 하나 만듭니다.

sudo mkdir -p /etc/logrotate.d/httpd-prerotate/
sudo vi /etc/logrotate.d/httpd-prerotate/goaccess

 

goaccess 파일 내용은 다음과 같이 입력합니다.

#!/bin/bash
exec goaccess-wrapper /var/www/goaccess

 

저장을 하고 실행 권한을 부여(sudo chmod +x /etc/logrotate.d/httpd-prerorate/goaccess)합니다.

3.2.2 logrotate 실행 확인

바로 확인해 보려는 경우 다음과 같이 logrotate를 실행해 볼 수 있습니다.

sudo logrotate -f /etc/logrotate.d/nginx

 

이렇게 하는 경우 일단위가 아니라 실행 시점에 로그가 로테이션 되므로 원하지 않는 경우에는 자정이 지난 후에 확인합니다.

확인 방법은 일단 생성될 html 파일이 잘 생성됐는지 확인할 수 있고, 다음과 같은 명령어로 logrotate가 잘 실행됐는지도 확인할 수 있습니다.

journalctl | grep logrotate

 

참고