
알림 전송을 위한 FCM 서버를 배포하는 도중 아래 이유로 배포에 실패했다.

no space left on device
docker 이미지를 레포지토리에서 pull 해야하는데 서버에 디스크 공간이 부족하다는 이유이다.
뭐가 이렇게 디스크를 많이 잡아먹나 싶어서 서버를 확인하러 갔다.
[ec2-user@ bin]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 768M 924K 767M 1% /run
/dev/nvme0n1p1 30G 30G 0M 100% /
tmpfs 1.9G 0 1.9G 0% /tmp
/dev/nvme0n1p128 10M 1.4M 8.7M 14% /boot/efi
tmpfs 384M 0 384M 0% /run/user/0
EBS 볼륨을 30G 할당했는데 다썼다..
당시에는 다른 작업 우선순위가 높아 이에 대한 근본적인 원인을 파악할 시간이 없었다.
그래서 일단 급한 불 끄자는 생각에 임시방편으로 50G까지 볼륨을 올렸다.
저 사태가 있고 며칠 지나지 않아서, 다른 작업들 다 끝내고 지금은 괜찮나 싶어 서버를 확인했는데 이번에도 거의 disk full 상태에 육박했었다.
[ec2-user@ bin]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 768M 73M 695M 10% /run
/dev/nvme0n1p1 50G 47G 3G 97% /
tmpfs 1.9G 0 1.9G 0% /tmp
/dev/nvme0n1p128 10M 1.4M 8.7M 14% /boot/efi
tmpfs 384M 0 384M 0% /run/user/0
EBS를 무작정 늘릴 순 없기에 이젠 근본적인 원인을 파악해서 해결해야했다.
현재 디렉토리들 중 어디가 잡아먹는지를 확인하려고 했다.
/var, /home-user, /opt, /usr 디렉토리들을 확인하려고 했다.
/var 디렉토리 공간을 확인해보았다.
sudo du -xhd1 /var | sort -h
[ec2-user@ ~]$ sudo du -xhd1 /var | sort -h
0 /var/account
0 /var/adm
0 /var/db
0 /var/empty
0 /var/ftp
0 /var/games
0 /var/kerberos
0 /var/local
0 /var/nis
0 /var/opt
0 /var/preserve
0 /var/spool
0 /var/yp
16K /var/tmp
64M /var/cache
358M /var/log
36G /var
36G /var/lib
/var/lib가 무려 36G나 잡아먹고 있었다. 더 세분화해서 정확히 무엇이 원인인지 파악하고자 파고들었다.
sudo du -xhd1 /var/lib | sort -h
[ec2-user@ ~]$ sudo du -xhd1 /var/lib | sort -h
0 /var/lib/games
0 /var/lib/gssproxy
(생략) ...
18M /var/lib/selinux
36G /var/lib
36G /var/lib/docker
docker가 /var/lib의 대부분 용량을 차지하고 있었다.
/var/lib/docker 용량을 파고들어보자.
[ec2-user@ip-10-0-5-182 ~]$ sudo du -xhd1 /var/lib/docker | sort -h
0 /var/lib/docker/plugins
(생략) ...
5.8M /var/lib/docker/image
30M /var/lib/docker/volumes
3.1G /var/lib/docker/overlay2
32.7G /var/lib/docker/containers
36G /var/lib/docker
36GB 중 32.7GB를 먹는 /containers 디렉토리..
처음엔 /docker/containers에 이미지나 볼륨이 저장되어서
이렇게까지 큰 용량을 차지하는 것이라고 예상했다.
그런데 오히려 /image, /volumes 디렉토리는 바깥에 따로 있었고,
찾아보니 /containers의 대부분은 컨테이너 로그가 차지한다는 사실을 알게됐다.
❓
/var/lib/docker/containers에는 어떤 데이터가 저장되어있을까?
- stdout/stderr로 출력되는 컨테이너 로그 (
*-json.log)- 컨테이너 메타데이터 (ex: 환경변수, 네트워크 등 상태정보)
[ec2-user@ ~]$ sudo du -h /var/lib/docker/containers | sort -h | tail
0 /var/lib/docker/containers/8e10d3f0ca6e74807a4e66bfde.../mounts
0 /var/lib/docker/containers/931f313c2b036a41cb39a5ce6b.../checkpoints
0 /var/lib/docker/containers/555e26892d87ae65ce7e4b4a7a2...
(생략) ...
241M /var/lib/docker/containers/8e10d3f0ca6e74807a4e66bfde2...
31G /var/lib/docker/containers/11ff45adf6149a269721ec345c1...
32G /var/lib/docker/containers
특정 컨테이너에 대한 로그가 31G 쌓인 것이었다.
어떤 컨테이너에서 이렇게 로그를 많이 쌓나 확인했더니 컨테이너 ID가 FCM 애플리케이션 컨테이너였다.
왜이렇게까지 docker 컨테이너의 로그가 폭증했을까?
Docker처럼 잘 만들어진 플랫폼은 log rotation이나 retention 정책에 대해
당연히 default 값이 있을 것이라고 생각했다.
근데 알고보니 Docker는 내가 직접 설정해주지 않는 이상, 로그 보관과 관련된 default 설정이 없다고 한다.
Docker는 logging driver가 탑재되어있다.
흔히 우리가 docker logs로 로그를 가져올 수 있도록 해주는 로그 메커니즘이다.
각 Docker에는 이 logging driver가 데몬 프로세스로 탑재되어 있다.
"As a default, Docker uses the
json-filelogging driver, which caches container logs as JSON internally.""기본적으로 Docker는 컨테이너 로그를 JSON 형태로 내부적으로 캐싱하는
json-file로깅 드라이버를 사용합니다."
공식문서를 참고해보면 Docker의 default driver는 json-file 로깅 드라이버라고 한다.
그 아래로 공식문서를 읽다보면 logging driver 설정에 대한 Tip 부분이 있다.
"Use the
locallogging driver to prevent disk-exhaustion.
By default, no log-rotation is performed.
As a result, log-files stored by the defaultjson-filelogging driver logging driver can cause a significant amount of disk space to be used for containers that generate much output, which can lead to disk space exhaustion.""디스크 공간 부족을 방지하려면
local로깅 드라이버를 사용하세요.
기본적으로 로그 rotation은 수행되지 않습니다.
따라서 기본json-file로깅 드라이버가 저장하는 로그 파일은 출력을 많이 생성하는 컨테이너에서 상당한 디스크 공간을 차지하여 디스크 공간 부족으로 이어질 수 있습니다."
Docker에서도 공식적으로 디스크 공간 차지 문제 때문에 logging driver를
json-file보다 local로 사용하도록 권장하고 있다.
json-file과 local은 무슨 차이일까?
❓ Docker는 왜 default logging driver를
json-file로 했을까?두 개의 드라이버 설명을 보면
json-file보다local의 이점이 훨씬 커보였다.
그럼 Docker는 왜 default로local을 안두고json-file로 두었을까?아래는 Docker 공식문서 내용이다.
"Docker는 이전 버전의 Docker와의 하위 호환성을 유지하고
Docker가 Kubernetes의 런타임으로 사용되는 상황을 위해 (로그 순환 기능이 없는)
json-file로깅 드라이버를 기본값으로 유지합니다."Docker 이전 버전의 하위호환성 문제와 쿠버네티스간 호환성 문제였다.
(옛날에는 쿠버네티스에서 Docker의 컨테이너 로그가 json-file로 쌓인다는 전제가 있었다고 한다.)
이 문제를 위한 근본적인 해결책은 로깅의 rotation 지정이다.
따라서 options를 통해 rotation 설정만 켜주면 괜찮을 수 있다.
하지만 공식 문서가 권장하듯, json-file보다 local이 더 좋은 옵션이라고 판단해서
'local이 지금 우리 서버에 적절한가?'를 고려하게 되었다.
그래서 우리 서버가 json-file 드라이버를 고집해야 하는 필수적인 이유가 있지 않다면 local로 변경해도 무방할 것이라고 생각했다.
우리 서버가 json-file을 고집해야 하는 이유가 있을까?
서버는 Promtail을 통해 애플리케이션 로그를 수집하고, 이를 loki에게 push 해주는 방식이다.
이 때 Promtail의 Docker service discovery(docker_sd_configs)를 쓰는 구성일 경우,
즉, Promtail이 docker log를 스크랩 하는 경우 공식 문서에 컨테이너는 json-file 또는 journald 로깅 드라이버여야 한다고 명시돼 있다.

이 경우 local로 바꾸면 Promtail이 기대하는 방식으로 못 읽을 수 있는 것이다.
하지만 지금 알림 서버는 logback 기반으로 생성된 로그를 스크래핑 하고 있었으므로,
docker log는 json format을 유지할 필요가 없었다.
따라서 local 드라이버를 적용해도 무방하겠다는 결론을 지었다.
그래서 Docker 공식 문서에서도 권장함에 따라, 우리도 json-file에서 local로 logging driver를 변경했다.
docker-compose.yml 파일에서 설정할 수 있다.
services:
fcm-app:
...
logging:
driver: "local"
options:
max-size: "50m"
max-file: "5"
max-size: 로그 파일 1개가 커질 수 있는 최대 크기이다. (default: 100m)max-file: rotation 된 로그 파일을 유지할 최대 개수이다. (default: 5)max-file개를 넘으면 가장 오래된 파일부터 삭제한다.위 설정들은
json-file드라이버를 사용해도 위처럼 설정할 수 있다.
default 값이 없을 뿐이다.
따라서 실제 컨테이너당 로그 상한은 max-size * max-file 이 될 것이다.
두 설정 뿐 만 아니라 logging driver도 local로 지정했으므로 압축까지 발생해서
사실상 이보다 더 작은 용량을 차지할 것이다.
무난한 운영 기본 값으로 max-size: 50m, max-file: 5로 지정해,
FCM 서버 컨테이너 당 약 최대 250MB 수준으로 유지되도록 지정했다.
적용 후 docker의 디스크 용량을 확인해보았다.
확실하게 /container 용량이 완전히 줄어들었다.
[ec2-user@ ~]$ sudo du -xhd1 /var/lib/docker | sort -h
0 /var/lib/docker/plugins
0 /var/lib/docker/runtimes
0 /var/lib/docker/swarm
0 /var/lib/docker/tmp
100K /var/lib/docker/buildkit
108K /var/lib/docker/network
5.8M /var/lib/docker/image
26M /var/lib/docker/volumes
987M /var/lib/docker/containers
3.1G /var/lib/docker/overlay2
4.1G /var/lib/docker
전체 디스크 사용량도 확인해보았다.
97% 사용하던 디스크가 35%까지 내려간 모습이다.
[ec2-user@ bin]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4.0M 0 4.0M 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 768M 1.3M 766M 1% /run
/dev/nvme0n1p1 50G 18G 33G 35% /
tmpfs 1.9G 0 1.9G 0% /tmp
/dev/nvme0n1p128 10M 1.4M 8.7M 14% /boot/efi
tmpfs 384M 0 384M 0% /run/user/0
(2월 5일 기준, 여전히 35~36%의 사용률을 유지하고 있다.)
위에서 설정한 이 값들이 정답이라는건 아니다.
로그 보관 정책은 서비스 특성과 내부 정책에 따라 다양할 수 있으며,
디스크 사용량도 마냥 적다고 좋아할 것은 아니다.
로그를 운영상 얼마나 보관하고 있는 것이 적절할지, 안전한 목표 디스크 사용량도 고민해봐야한다.
꾸준히 모니터링하고 고민해봐야 할 사항인 것 같다.