사건의 발단 :
1) airflow가 설치된 서버의 스토리지가 모든 용량을 꽉 채우고 죽는 사태가 벌어진 적이 있다.
2) 문제는 airflow의 로그가 계속 쌓여 있어서, 해당 로그들을 삭제함으로써 몇달은 버틸 수 있었다.
3) 이제 스토리지 용량이 다 되어 로그 삭제를 개발하려 했다.
먼저 해당 서버의 스토리지 용량을 다음의 명령어들을 사용해서 확인하려고 했다.
df (disk free) - 디스크 여유 공간 확인
df 명령을 사용하면 리눅스 시스템 전체의 (마운트 된) 디스크 사용량을 확인할 수 있다.
파일시스템, 디스크 크기, 사용량, 여유공간, 사용률, 마운트 지점 순으로 나타낸다.
du (disk usage) - 디렉토리 디스크 사용량 확인
df 명령어가 시스템 전체의 디스크 공간을 확인하는 명령어라면, du 명령은 특정 디렉터리를 기준으로 디스크 사용량을 확인할 수 있다
df -h
du -h 의 경우 마운트 된 스토리지에 대해서 사용량을 잘 보여주고 있다.
cd /
du -sh *
실제 할당된 스토리지 용량은 다음과 같다.
도커가 문제인가? ( 도커가 용량을 대부분을 차지하고 있다 )
도커에서 사용중인 용량 확인하기
docker system df
docker system df는 정상적으로 잘 나옴
du 명령의 결과가 실제 디스크 용량을 반영하지 못하는 경우
시스템을 오랫동안 리부팅 없이 사용하는 경우에 흔히 발견되곤 합니다.
- 누가 : 특정 프로세스가
- 언제 : "file descriptor" 를 열고
- 무엇을 : file descriptor가 가리키는 파일이 지우면
- 어떻게 : 그 프로세스가 여전히 file descriptor 를 열어놓고
- 왜 : 계속해서 write 를 하고 있기 때문입니다.
그러면 사용중인 docker 컨테이너를 stop 해 보았다.
> docker-compose stop
> df -h
> du -sh *
80G → 42G로 변경됨
끝!!
일리가 없죠?
일단 하나는 해결 및 원인은 알았음.
그런데
- 도커는 왜 저런 파일 시스템으로 돌고 있는 거지? (mount + overlay??)
- 도커는 얼마나 디스크를 사용하는건가?
도커의 파일 시스템
0. UFS(Union File System)
여러 개의 파일 시스템을 하나의 파일 시스템에 마운트 하는 기능
여러 파일 시스템을 하나로 합치다 보면 중복되는 파일이 있기 마련인데, UFS에서는 이 경우 나중에 마운트 된 파일로 덮어쓴다
도커 이미지에서 레이어는 개념적으로 각각의 파일 시스템을 겹쳐 놓은 형태와 유사하다.
도커 이미지의 레이어 확인하기
> docker image inspect apache/airflow
해당 이미지가 많은 layers들로 이루어져 있는 것을 볼 수 있다.
1. overlay 실습
UFS의 하나인 overlay 실습
$ mkdir overlayfs
$ cd overlayfs
$ mkdir container image1 image2 merge work
$ touch image1/a image1/b image2/c
$ sudo mount -t overlay overlay -o lowerdir=image2:image1,upperdir=container,workdir=work merge
mount overlay로 실행해보면 image1, image2의 파일들이 merge 폴더로 들어가 있는 것을 볼 수 있다.
즉 docker에서 보듯이 각각의 레이어(image폴더)들이 합쳐져서 merge(docker image)에서 사용 가능하다.
이번엔 파일을 변경해 보자
> rm ./merge/a
> touch ./merge/d
> tree . -I work
merge 디렉터리에 파일을 변경하면, 모든 변경사항은 upperdir인 container 디렉터리에 저장된다.
우선 container 디렉터리에 a파일과 d 파일이 추가된 것을 확인할 수 있다. 이는 앞에서 Character device라는 특수한 형식의 파일로 변경된 파일을 의미한다. 그리고 image1과 image2에는 아무런 변화도 없습니다.
위의 예지를 도커로 생각해 보면 container가 수정된 파일들의 폴더로 해당 폴더만 layer로 만들어서 배포하면 되는 것이다.
2. 도커도 이렇게 작동하는 건가요?
도커에서 OverlayFS를 사용한다. 도커 이미지의 각 레이어가 lower 레이어에 해당하며, 새롭게 생성된 컨테이너의 컨테이너 레이어가 upper 레이어에 해당한다. 도커 이미지는 읽기 전용으로 불변의 상태를 유지해야 하므로, 도커 이미지에서 변경된 사항들은 R/W 전용의 컨테이너 레이어에 디렉터리에 저장된다. 그러나 사용자가 실제로 컨테이너 내부에서 파일 시스템을 사용할 때는 컨테이너 레이어 (upper)와 이미지 레이어 (lower)를 통합함으로써 완전한 디렉터리 (merge)를 보여주게 된다.
lowerdir는 읽기 전용의 이미지 레이어에 대응한다. 컨테이너는 레이어는 이미지 레이어로부터 만들어지며, 변경되는 정보들이 여기에 저장된다. 최종적으로 컨테이너 마운트를 통해서 이들 정보가 merged 된다.
AUFS와 같은 구성이지만 여러 개의 레이어가 스택을 이루는 AUFS와는 달리 단지 두 개의 레이어만 가지고 있다. 따라서 AUFS와 같은 멀티레이어 이미지를 구성할 수 없다. 대신 각 이미지 레이어마다 /var/lib/docker/overlay 디렉터리 밑에 자신만의 디렉터리를 가지는 것으로 이 문제를 해결한다.
3. 그래서 이제 무엇을 해야 하죠?
뭐긴요. Union File System에도 여러 가지가 있습니다. 그것을 한번 봅시다.
AUFS
- AUFS는 커널에 포함된 파일 시스템이 아니기 때문에, 지원하지 않는 배포판들도 있다. 레드햇 계열의 리눅스의 경우 AUFS를 지원하지 않는다.
- AUFS 스택은 여러 개의 파일 시스템을 단일 뷰로 만들고 이를 마운트 포인트로 제공한다.
- 디렉터리들은 하나의 마운트 포인터로 묶일 수 있어야 하므로 반드시 같은 리눅스 호스트에 존재해야 한다
OverlayFS
AUFS와 비슷한 유니온 파일 시스템이다. AUFS와 비교해서 아래와 같은 차이점을 가지고 있다.
- AUFS 보다 단순한 디자인을 가지고 있다.
- 리눅스 커널 3.18부터 정식으로 지원한다.
- 좀 더 빠르다.
Backing File System
backing file system은 docker 호스트의 로컬 저장 영역인 '/var/lib/docker'을 생성하는 데 사용되는 파일 시스템을 참조한다. docker 호스트의 로컬 스토리지 영역에 사용할 backing file system에 따라서 스토리지 드라이버를 결정한다. 예를 들어, 로컬에서 ext4 file system을 사용하려면, 가능한 storage driver는 overlay, overlay2, aufs가 된다.
그러나, 나머지 스토리지 드라이버는 스토리지 드라이버와 동일한 backing filesystem을 요구한다.
예를 들어, overlay는 ext4, xfs에서 작동할 수 있지만, btrfs는 btrfs에서만 작동한다.
4. 이걸 왜 알아야 하는데?
사용하는 쓰임새에 따라서 파일시스템을 변경하여 도커의 성능을 높일 수 있다
- aufs
- ubuntu, debian에서만 지원
- 나쁜 use case : 높은 write 활동
- devicemapper
- centos, redhat 포함 범용 스토리지 드라이버
- 나쁜 use case : production 배포, 성능
- overlay
- aufs, devicemapper 보다는 덜 안정적이지만, 더 빠름
- 효율적인 메모리 사용
- 나쁜 use case : container 변경이 잦을때
docker 설치 시 디폴트로 overlay2로 설정됩니다. ( docker info로 확인 가능)
docker 스토리지 선택에 대한 자세한 사항은 공식 문서를 참고하자.
https://docs.docker.com/storage/storagedriver/select-storage-driver/
5. 처음으로 돌아가서
컨테이너를 만들고 호스트에서 df -h를 쳤을 때 overlay라고 마운트 되어있는 것은 각 컨테이너의 루트 디렉터리이다. 즉 /var/lib/docker/overlay2 에는 각 컨테이너의 루트 디렉터리, /var/lib/docker/volumes 에는 생성한 볼륨이 들어간다.
df -h를 쳤을 때. 아래 overlay라고 쓰여있는 것이 도커 컨테이너들의 루트이다.
또한 해당 데이터는 마운트 포인트인 /var/lib/docker/overlay2/이름/merged 디렉터리에 저장된다. 들어가 보면 루트 파일 시스템 같은 모양을 확인할 수 있을 것이다. 컨테이너가 삭제되면, 이 오버레이 마운트 경로의 데이터는 모두 삭제된다.
overlay2의 용량이 50g인 것은 해당 서버에 할당된 스토리지의 최대치를 뽑아낸다. 그렇게 도커는 제한 없이 모든 스토리지를 사용하다가 서서히 용량을 차지하면서 죽는다!!!
참고
https://docs.docker.com/storage/storagedriver/
https://www.44bits.io/ko/post/how-docker-image-work
https://blog.naver.com/alice_k106/221530340759
https://www.joinc.co.kr/w/man/12/docker/storage
https://velog.io/@koo8624/Docker-유니온-파일-시스템-Union-File-System
번외 1 )) 용량을 더 확보해 보자
도커 build, pull, 기존에 쓰지 않는 도커의 레이어는 이미지를 삭제해도 깨끗하게 삭제되지 않는다.
그래서 prune 명령어를 날리는데, 방법이 두 가지가 있다.
> docker image prune -a
image prune는 이름 없는 모든 이미지를 삭제한다.
> docker system prune -a
system prune는 중지된 컨테이너 , 사용하지 않는 네트워크, 사용하지 않는 레이어를 모두 삭제한다. (현재 실행 중인 컨테이너 빼고 전부 삭제)
빌드 서버가 아닌 실행 서버라면 두 번째인 system prune를 실행해서 용량을 확보한다. (실행 중지된 모든 컨테이너 레이어가 삭제되므로 조심히 사용하자)
하지만 나의 경우 빌듯이 착실하게 지워주고 있어서 변동 사항이 없다. 0B 줄어듦
번외 2 )) airflow 로그 문제 - 사실 오늘 글을 쓰게 된 계기
airflow의 로그가 막대하게 쌓여서 용량 문제로 서버가 내려간 적이 있다.
/var/lib/docker/overlay2/397 e3 cf7896 eefb1 a 51 de5277 b6 b1888 be2942 f27 c2 b40 e85 e65 ca65533 fdd85/diff/usr/local/airflow/logs/scheduler
여기에 scheduler log가 쌓인다.
결국 arbiter_scheduler_1의 도커에 로그가 쌓이고 있다.
worker 컨테이너의 로그 용량은 4k로 별다른 영향을 주지 않는다.
scheduler 컨테이너의 로그 용량은 하루 230M씩 쌓이고 있다. 이놈이 범인이다.
로그들만 삭제하기 위해서 출동!!
문제점 1.
airflow에서 로그 로테이션을 지원하지 않는다.
대부분의 해결책으로 https://github.com/teamclairvoyant/airflow-maintenance-dags/tree/master/log-cleanup의 소스를 이용하라 하지만 도커에서는 실행되지 정상적으로 동작하지 않는다 ( schodule 서버의 로그는 삭제되지 않았다)
문제점 2.
dag으로 일정 주기마다 로그를 삭제하고 싶었다.
우리는 airflow를 docker-compose로 실행하는데, 실제 소스가 실행되는 worker와 schodule의 서버가 나뉘어 있다. 서로 접근을 못하는 상태.
- 도커 명령어를 통해 컨테이너 to 컨테이너 접속을 하려면 airflow 안에 docker를 설치해야 하는 상황이다.
- ssh로 접근하려면 airflow의 소스를 고쳐야 한다!!
어느 방법이든 배보다 배꼽이 더 커지는 상태!!!!!
간단하게 해결
앞써 도커의 마운트 경로를 알게 되었다. 그렇다면 해당 log가 마운트 되어 쌓이고 있는 경로도 알 수가 있다.
airflow docker-compose 가 돌아가는 서버에 crontab으로 docker overlay안에 airflow log를 삭제하도록 했다. 끝!
find /var/lib/docker/overlay2/*/diff/usr/local/airflow/logs/scheduler -type d -mtime +10 -exec rm -rf {} +
80G → 42G → 22G
번외 3 )) 도커 로그
docker 자체 로그는 해당 폴더에 쌓인다. (위에는 /var/lib/docker/containers/)
설정은 간단하다. /etc/docker/daemon.json (없으면 파일을 만든다) 파일에 아래와 같은 형태로 로그 로테이션을 걸어줄 수 있었다.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
참조
'server > docker' 카테고리의 다른 글
docker 빌드 실패한 이미지 지우기 (많이!) (0) | 2024.06.11 |
---|---|
airflow ModuleNotFoundError: No module named 'slacker’ (0) | 2022.11.09 |