오늘은 logrotate에 대해 이야기해볼까 합니다. logrotate 중에서도 copytruncate라는 옵션에 대해서 이야기하려고 합니다. 아주 매력적인 copytruncate 옵션이 가지고 있는 장점과 단점에 대해 살펴보겠습니다.
logrotate는 리눅스 상에서 발생하는 애플리케이션의 로그를 관리하는데 아주 유용한 필수 유틸리티 중 하나입니다. 비교적 간단한 설정을 통해서 로그를 날짜 별로 순환시킬 수 있습니다. logrotate는 기본적으로 rename을 기반으로 동작합니다.
로테이트의 대상이 되는 로그 파일을 찾은 다음 설정에 맞게 날짜를 붙이거나 숫자를 붙이는 방식으로 rename 합니다.
하지만 여기서 아주 중요한 부분이 있는데요, 기존 파일의 이름만 바꾸기 때문에 그 외의 i-node 번호와 같은 정보는 그대로라는 것입니다. 그래서 로테이트 후 아무 작업도 하지 않고 그대로 둔다면 이름이 바뀐 로그 파일에 계속해서 로그가 쌓이는 모습을 볼 수 있습니다.
그래서 logrotate에는 create , postscript 등의 옵션을 제공하며 기본적인 동작 이후에 필요한 작업들을 할 수 있게 해 줍니다. 그리고 그중에서 copytruncate는 단연 최고의 옵션 중에 하나입니다.
제가 copytruncate는 단연 최고의 옵션 중에 하나라는 표현을 썼는데요, 그 이유는 별다른 옵션을 사용하지 않아도 아주 훌륭하게 로그 파일의 순환 작업을 진행하기 때문입니다. copytruncate는 기본 동작 방식인 rename을 기반으로 동작하면서 copy와 truncate 작업을 병행합니다. logrotate는 임시 파일을 만든 후 파일의 이름을 룰에 맞는 이름으로 바꿔 줍니다. 그리고 기존 로그 파일의 내용을 복사한 후 기존 로그 파일을 truncate, 즉 비워 줍니다. 그래서 rename 된 로그 파일들이 차곡차곡 쌓이고 새로운 로그는 기존 로그 파일에 새롭게 들어옵니다. 복잡하고 힘든 다른 설정들을 다 빼버릴 수 있도록 아주 편한 기능을 제공해 줍니다.
이를 통해 기존 파일의 내용을 유지하면서 로테이트 완료 후에는 새로운 로그만 수집할 수 있게 해 줍니다. 아주 편하게 말이죠. 하지만.. 정말 좋기만 할까요?
문제가 있습니다. 경우에 따라선 문제가 아닐 수 있지만 서비스에 치명적일 수도 있습니다. 바로 copy 때문입니다. 로그 파일의 크기가 작을 때에는 큰 문제가 되지는 않겠지만 크기가 GB 단위로 클 경우, copy 하는 작업 때문에 시스템에 I/O 로드가 올라갈 수 있습니다. 실제로 SATA 기반의 RPM이 느린 서버의 경우 GB 단위의 copy 작업이 수초에서 수분까지도 걸릴 수 있습니다. 그렇게 되면 애플리케이션의 응답 속도가 느려질 수 있고, 수초에서 수분 사이의 로그가 유실될 수도 있습니다. logrotate의 매뉴얼 페이지를 보면 copytruncate에 대해 아래와 같이 나와 있습니다.
Truncate the original log file in place after creating a copy, instead of moving the old log file and optionally creating a new one. It can be used when some program cannot be told to close its logfile and thus might continue writing (appending) to the previous log file forever. Note that there is a very small time slice between copying the file and truncating it, so some logging data might be lost. When this option is used, the create option will have no effect, as the old log file stays in place.
그렇기 때문에 copytruncate를 사용한다면 현재 서버의 I/O 성능과 쌓이게 될 예상 로그의 크기를 반드시 고려해서 사용해야 합니다.
그렇다면 해결책은 무엇일까요?
가장 이상적인 해결책은 애플리케이션마다 제공하는 signal을 이용하는 것입니다.
대부분의 오픈소스 애플리케이션들을 로그 파일을 다시 쓸 수 있도록 reopen 하는 signal을 제공해 줍니다. nginx를 예로 들면 USR1 signal 이 있습니다. nginx.org에 가서 살펴보면 아래와 같은 항목을 볼 수 있습니다.
USR1 re-opening log files
그래서 logrotate로 기존 파일은 rename 하고 USR1 signal을 보내서 새롭게 파일을 생성해서 쓰도록 해 줍니다.
이렇게 하면 copy로 인한 시스템 부하도 없고, rename 완료와 동시에 USR1 signal을 통해 로그 파일을 새로 만들기 때문에 로그 유실도 없습니다.
지금까지 간단하게 logrotate의 copytruncate 옵션에 대해 살펴봤습니다. copytruncate는 분명 매력적인 옵션임에 틀림없지만, 경우에 따라서는 서비스에 악영향을 미치는 계륵과 같은 존재가 될 수 있습니다. 그 동작 원리를 충분히 이해하고 자신이 운영하는 서비스에 알맞은 옵션으로 잘 사용할 수 있었으면 좋겠습니다.
ps. 새벽 시간에 원인 불명의 타임아웃을 자주 접한다면 logrotate의 copytruncate 영향을 받았을 가능성이 큽니다!