Docker로 애플리케이션을 배포하여 서비스를 운영 중인 Linux 서버에 디스크 용량이 부족하다는 오류나 경고 메시지가 발생한 경험이 있으신가요? 실제 애플리케이션에 의해 생성되어 저장되는 데이터 및 로그가 차지하는 용량은 그렇게 많지 않은데, 시스템 전체 스토리지 용량이 Full 될 수 있습니다.

왜 그런 것일까요?
Docker를 사용할 때 스토리지 용량이 부족해지는 원인은 여러 가지가 있을 수 있습니다.
그중 가장 많이 발생하는 것으로 Docker 컨테이너 로그가 원인일 수 있습니다.
이번 포스트에서는 잘 못 설정하면 시스템의 디스크 용량 부족으로 이어지는 Docker의 로깅 드라이버에 대해 알아보겠습니다.

Docker 로깅 드라이버 및 로그 위치

Docker에는 실행 중인 컨테이너 및 서비스에서 정보를 얻는 데 도움이 되는 여러 로깅 메커니즘이 포함되어 있습니다. 이러한 메커니즘을 로깅 드라이버라고 합니다.

docker logs 명령은 실행 중인 컨테이너가 기록한 정보를 보여줍니다. docker service logs 명령은 서비스에 참여하는 모든 컨테이너가 기록한 정보를 보여줍니다.

기본적으로 Docker는 모든 컨테이너 로그의 표준 출력(stdout) 또는 표준 에러(stderr)를 캡처하여 JSON 형식으로 파일에 기록하는 json-file 로깅 드라이버를 사용합니다.

docker run 또는 docker-compose up 명령어로 Docker 컨테이너를 실행하면 /var/lib/docker/containers/[Container-ID]/[Container-ID]-json.log 파일이 생성되고 로그가 기록됩니다.

기본적으로 json-file 로깅 드라이버는 로그 로테이션(log-rotation)을 수행하지 않습니다. 결과적으로 이 로깅 드라이버에 의해 저장된 로그 파일은 많은 양의 출력을 생성하는 컨테이너이면 상당한 양의 디스크 공간을 사용할 수 있으며, 이로 인해 디스크 공간이 고갈될 수 있습니다.

Docker는 Docker의 이전 버전과의 호환성을 유지하고 Docker가 Kubernetes의 런타임으로 사용되는 상황을 위해 json-file 로깅 드라이버(로그 로테이션 없이)를 기본값으로 유지합니다.

Docker 컨테이너 로그 크기 줄이기

docker rm 또는 docker-compose down 명령어를 실행하면 Docker 컨테이너가 삭제되는데, 이때 /var/lib/docker/containers 하위에 [Container-ID]에 해당하는 디렉터리도 삭제됩니다. 이렇게 하면 많은 디스크 공간을 차지했던 [Container-ID]-json.log 파일도 같이 삭제되기 때문에 시스템 전체 디스크 공간을 확보할 수 있습니다.

야간에 서비스 중인 Docker 컨테이너를 삭제한 후 재실행하여 디스크 공간을 늘릴 수 있습니다. 좋은 방법일까요?

truncate -s 0 <json-log-file> 명령을 실행하면 로그 파일 크기를 0으로 만들 수 있습니다.

$ cd /var/lib/docker/containers/0707f472977c3e18ea9dbaa2a015d8f4106c5720aa4314fcbf5fda3cc5208cef
$ truncate -s 0 0707f472977c3e18ea9dbaa2a015d8f4106c5720aa4314fcbf5fda3cc5208cef-json.log
$ ls -al
total 44
drwx--x--- 4 root root 4096 Nov 21 12:44 .
drwx--x--- 8 root root 4096 Nov 21 15:21 ..
-rw-r----- 1 root root 0 Nov 21 15:35 0707f472977c3e18ea9dbaa2a015d8f4106c5720aa4314fcbf5fda3cc5208cef-json.log
drwx------ 2 root root 4096 Nov 21 12:44 checkpoints
-rw------- 1 root root 4423 Nov 21 12:44 config.v2.json
-rw-r--r-- 1 root root 1593 Nov 21 12:44 hostconfig.json
-rw-r--r-- 1 root root 13 Nov 21 12:44 hostname
-rw-r--r-- 1 root root 173 Nov 21 12:44 hosts
drwx--x--- 2 root root 4096 Nov 21 12:44 mounts
-rw-r--r-- 1 root root 92 Nov 21 12:44 resolv.conf
-rw-r--r-- 1 root root 71 Nov 21 12:44 resolv.conf.hash

위와 같은 명령어를 실행하는 Crontab을 설정하여 로그 파일 크기를 줄일 수도 있으나, 가장 좋은 방법은 로그 로테이션을 설정하는 것입니다.

로그 로테이션 설정

로그 파일의 최대 크기(max-size)와 최대 파일 개수(max-file)를 지정하여 로그 로테이션을 활성화할 수 있습니다. 파일의 크기가 설정한 최댓값에 도달하면 새로운 파일로 교체되고, 기존 파일은 ...-json.log.1, ...-json.log.2, ...으로 최대 개수까지 로그 파일이 생성되고 초과하면 가장 오래된 파일이 제거되어 Docker 컨테이너의 로그 파일 크기가 무한정 커지는 것을 방지할 수 있습니다.

기본 로깅 드라이버 구성으로 설정

특정 로깅 드라이버 및 옵션으로 Docker 데몬을 구성하면 이 기본값이 생성되는 모든 컨테이너에 적용됩니다.
/etc/docker 디렉터리의 daemon.json 파일을 편집합니다. (파일이 없으면 생성합니다.)

sudo vi /etc/docker/daemon.json

아래와 같이 작성하고 저장합니다.

{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "10"
}
}

Docker를 재시작하면 새로 생성되는 컨테이너에 변경사항이 적용됩니다.

sudo systemctl restart docker

기존 컨테이너에는 변경사항이 적용되지 않습니다.

컨테이너에 대한 로깅 드라이버 구성으로 설정

컨테이너를 시작할 때 --log-driver 및 하나 이상의 --log-opt <NAME>=<VALUE> 플래그를 사용하면 Docker 데몬의 기본값 대신 설정한 값으로 컨테이너를 구성할 수 있습니다.

docker run 명령에서는 아래와 같이 로깅 드라이버와 옵션을 지정할 수 있습니다.

docker run -d \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=10 \
--name gitlab-runner --restart always \
-v /data/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:v15.6.0

Docker Compose를 사용하는 경우, docker-compose.yml 파일에 아래와 같이 설정할 수 있습니다.

version: '3.9'

services:
gitlab:
image: 'gitlab/gitlab-ee:15.5.4-ee.0'
container_name: gitlab
restart: always
hostname: 'gitlab'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.example.com'

...

ports:
- '80:80'
- '443:443'
- '8022:22'
volumes:
- './config:/etc/gitlab'
- './logs:/var/log/gitlab'
- './data:/var/opt/gitlab'
logging:
driver: 'json-file'
options:
max-size: '10m'
max-file: '10'

GitLab 컨테이너는 각각 10MB 이하의 로그 파일을 최대 10개까지 생성합니다.

맺음말

지금까지 시스템 전체의 디스크 공간 고갈의 원인이 되는 Docker 컨테이너 로그와 이를 해결하기 위한 로깅 드라이버 옵션에 대해 알아보았습니다.

GitLab에는 GitLab 내의 모든 서비스와 구성 요소가 시스템 로그를 출력하는 고급 로그 시스템이 포함되어 있습니다. 이에 따라 GitLab Rails, GitLab Shell, NGINX, PostgreSQL, Puma 등의 로그가 production_json.log, api_json.log, application_json.log, integrations_json.log, sidekiq.log, gitlab-shell.log, puma_stdout.log, gitlab_access.log, gitlab_error.log 등 여러 로그 파일에 기록됩니다. 또한, 내장된 runit (svlogd) 및 logrotate 서비스를 통해 로그가 관리되고 로테이션됩니다.

GitLab은 많은 양의 로그가 생성되므로, 로깅 드라이버 옵션으로 로그 로테이션을 설정하지 않으면 GitLab 컨테이너 로그에 많은 양의 로그가 기록되어 디스크 Full의 원인이 될 수 있습니다. GitLab은 자체적으로 로그 로테이션이 활성화된 고급 로그 시스템이 있으므로, Docker Volume 또는 Bind mount로 별도의 디스크 공간에 저장하고 GitLab 컨테이너에는 많은 양의 로그를 기록하지 않는 것이 좋습니다. 필요하다면 EFK 스택으로 로깅 시스템을 구축하고 GitLab 로그를 수집하여 보관할 수도 있습니다.

인포그랩은 GitLab 및 DevOps에 대한 맞춤 기술 지원을 제공합니다.
GitLab(Omnibus/Cloud Native Hybrid) 구축 관련한 지원이 필요하시면 문의하기 로 연락 주십시오.