• yundream
  • 2016-07-26 16:11:54
  • 2016-07-08 07:49:56
  • 158508

Contents

로그 파일 관리

백앤드 애플리케이션의 대부분이 REST 방식의 웹 애플리케이션이다. 애플리케이션들은 모두 아파치 웹 서버 방식의 엑세스로그를 남긴다. 일반유저들이 사용하는 API들이다 보니 상당한 크기의 로그파일들이 만들어진다. 일주일만 그냥 둬도 파일의 크기가 수 기가바이트를 넘어갈 판이다. 모든 로그 파일들은 ELK로 어차피 색인이 되기 때문에, 굳이 시스템에 남겨두거나 백업할 필요도 없다.

하여 logrotate 파일을 이용해서 로그로테이션을 적용하기로 했다. 로그로테이션이란 일정시간 주기로 원본 로그 파일을 다른 이름으로 복사하고, 로그 파일을 truncate 해서 새로 로그를 쌓게 하는 일련의 과정을 말한다. 일정 시간이 지난 로그파일은 알아서 삭제를 해주기 때문에 로그파일이 계속 커지는 것을 막을 수 있다.

logrotate

logrotate는 로그파일을 관리해주는 소프트웨어로 아래의 기능들을 가지고 있다.
  • 여러 개의 로그 파일을 동시에 관리.
  • 로테이트 갯수(몇 개의 복사본을 남길지)설정.
  • 압축.
  • 로테이트 전/후로 스크립트를 실행 할 수 있다.
우분투 리눅스의 경우 기본설치된 경우가 많다. 없다면 apt-get 으로 설치한다.
# apt-get install logrotate

logroate 설정 파일

logroate 설정 파일은 아래와 같이 작성한다.
/opt/log/test.log {
    daily
    compress
    rotate 90
    missingok
    notifempty
    size +4096k
    create 644 root root
    sharedscripts
    postrotate
    /etc/init.d/syslog reload
    endscript
}

  • rotate 옵션으로 몇 개의 백업 파일을 유지 할지 설정 할 수 있다.
  • 로테이트주기로 daily, weekly, monthly, yearly를 선택할 수 있다.
  • postrotate로 로테이트를 수행 한 후 실행할 스크립트를 설정 할 수 있다. 로테이트를 실행하면 원본파일을 백업 파일로 복사한 후에 삭제한다. 이렇게 되면, 로그를 만드는 애플리케이션은 파일포인터를 잃어버려서 로그를 쌓을 수 없게 된다. 많은 애플리케이션들이 특정 시그널을 받으면 로그파일을 새로 만들도록 해서 이 문제를 해결 하고 있다. 예를 들어 nginx는 USR1 시그널을 받으면 로그파일을 새로 만든다.
  • compress : 백업파일을 gzip 압축한다. 압축하고 싶지 않다면 nocompress를 설정하면 된다.
  • copytruncate : 대부분의 잘 만든 애플리케이션들은 로그파일을 새로 만들기 위한 시그널 처리 코드를 가지고 있다. 하지만 그렇지 않은 애플리케이션들도 있다. copytruncate를 이용하면 원본파일을 지우지 않고 truncate(파일 크기를 0으로 만든다)한다. 애플리케이션은 파일을 잃어버리지 않으므로 계속 해서 로그를 쓸 수 있다. 다만 파일을 복사하고 truncate 하는 순간 로그를 잃어버릴 수도 있다.
  • size : size 보다 로그파일이 클 경우 로테이션을 수행한다. M(메가), K(키로)단위로 설정 할 수 있다.

테스트

간단한 웹 애플리케이션을 만들어서 테스트를 진행하기로 했다. 아래는 테스트에 사용한 애플리케이션이다. go 언어로 만들었다.
package main

import (
    "fmt"
    "github.com/gorilla/handlers"
    "log"
    "net/http"
    "os"
)

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello world")
}
func main() {
    logfile, err := os.OpenFile("/opt/joinc/test.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    if err != nil {
        log.Fatal(err)
    }
    http.HandleFunc("/hello", hello)
    http.ListenAndServe("0.0.0.0:3000", handlers.CombinedLoggingHandler(logfile, http.DefaultServeMux))
}
유저의 모든 요청은 아래와 같은 combind형태의 로그로 남긴다. 아파치 웹서버등에서 널리 사용하고 있는 로그형식이다.
127.0.0.1 - - [26/Jul/2016:23:57:17 +0900] "GET /hello HTTP/1.1" 200 11 "" "curl/7.43.0"
127.0.0.1 - - [26/Jul/2016:23:57:21 +0900] "GET /hello2 HTTP/1.1" 404 19 "" "curl/7.43.0"
그리고 루프를 돌면서 HTTP 요청을 전송하는 간단한 스크립트도 만들었다.
#!/bin/bash

while [ 1 ]
do
	curl http://localhost:3000/hello
	sleep 1
done
로그파일의 변화도 살펴보기 위해서 tail 도 걸었다.
# tail -f /opt/joinc/test.log
127.0.0.1 - - [27/Jul/2016:00:24:36 +0900] "GET /hello HTTP/1.1" 200 11 "" "curl/7.43.0"
127.0.0.1 - - [27/Jul/2016:00:24:37 +0900] "GET /hello HTTP/1.1" 200 11 "" "curl/7.43.0"

테스트에 사용한 logrotate 설정파일이다.
# cat hello 
/opt/joinc/test.log {
    daily
    compress
    rotate 5 
    missingok
    notifempty
    create 644 root root
    copytruncate
}

-f 옵션을 이용해서 강제로 logrotate를 실행했다.
# logrotate -f hello
tail에 파일이 truncate 됐다는 메시지가 뜨는 걸 확인 할 수 있다.
127.0.0.1 - - [27/Jul/2016:00:24:40 +0900] "GET /hello HTTP/1.1" 200 11 "" "curl/7.43.0"
tail: joinc/test.log: 파일이 잘렸음
127.0.0.1 - - [27/Jul/2016:00:24:41 +0900] "GET /hello HTTP/1.1" 200 11 "" "curl/7.43.0"

로그 파일 디렉토리에서 백업파일을 확인 할 수 있다.
# ls
test.log  test.log.1.gz

백업파일을 5개 까지 남기도록 설정을 했다. 어떻게 적용되는지 확인하기 위해서 logrotate -f 를 7번 실행해봤다.
# ls -al
합계 40
drwxrwxr-x 4 root     root     4096  7월 27 00:32 .
drwxr-xr-x 8 root     root     4096  6월  4 14:11 ..
-rw-r--r-- 1 yundream yundream  356  7월 27 00:32 test.log
-rw-r--r-- 1 yundream yundream  157  7월 27 00:32 test.log.1.gz
-rw-r--r-- 1 yundream yundream  128  7월 27 00:32 test.log.2.gz
-rw-r--r-- 1 yundream yundream  266  7월 27 00:32 test.log.3.gz
-rw-r--r-- 1 yundream yundream  173  7월 27 00:31 test.log.4.gz
-rw-r--r-- 1 yundream yundream  320  7월 27 00:30 test.log.5.gz
5개의 파일만 유지되는 걸 확인 할 수 있다. 파일의 시간을 보면 가장 먼저 생성됐을 test.log1.gz의 시간이 test.log.5.gz 보다 나중에 생성된 걸 확인 할 수 있다. rotate 설정 값을 초과하면 다시 1부터 시작함을 알 수 있다.

확장자를 날짜로 설정

파일의 확장자가 맘에 들지 않는다. 일련의 숫자 만으로는 이 백업파일이 언제 만들어 진 것인지 확인 할 수가 없다. 백업파일이 만들어진 시간으로 확인 할 수 있겠으나 이런 백업 파일은 NASS3같은 외부 저장매체에 복사하기 마련이다. 이 경우 파일이 만들어진 시간은 무용지물이 된다. 그래서 백업 파일의 이름에 날짜가 들어가도록 바꾸기로 했다. 설정파일에 dateext만 추가하면 된다.
/opt/joinc/test.log {
    daily
    compress
    dateext
    rotate 5
    missingok
    notifempty
    create 644 root root
    copytruncate
}
파일 이름을 확인해 보자.
# ls -al
합계 44
drwxrwxr-x 4 root     root     4096  7월 27 00:37 .
drwxr-xr-x 8 root     root     4096  6월  4 14:11 ..
-rw-r--r-- 1 yundream yundream  623  7월 27 00:37 test.log
-rw-r--r-- 1 yundream yundream  926  7월 27 00:37 test.log-20160727.gz
-rw-r--r-- 1 yundream yundream  157  7월 27 00:32 test.log.1.gz
-rw-r--r-- 1 yundream yundream  128  7월 27 00:32 test.log.2.gz
-rw-r--r-- 1 yundream yundream  266  7월 27 00:32 test.log.3.gz
-rw-r--r-- 1 yundream yundream  173  7월 27 00:31 test.log.4.gz
-rw-r--r-- 1 yundream yundream  320  7월 27 00:30 test.log.5.gz
dateformat 옵션을 이용해서 날짜 형식을 설정 할 수도 있다.
# ls -al
-rw-r--r-- 1 yundream yundream  164  7월 27 00:40 test.log-20160727-1469547643.gz
훨씬 보기 좋아졌다.

cron을 이용한 관리

logrotate는 cron을 이용해서 실행 주기를 관리한다. 그냥 logrotate 설정파일을 /etc/logrotate.d에 복사하면 된다. logrotate 패키지를 설치하면, /etc/cron.daily, /etc/cron.weekly 등에 자동으로 cron 설정이 저장되므로 관리자가 신경 쓸 건 없다.