티스토리 뷰
jenkins로 CI를 하던 와중 jenkins 서버 내 용량이 이상하리 만큼 많이 쌓여서 원인을 분석하면서 storage driver에 대해 알게 됐습니다. storage driver를 잘 알고 있지 못하면 생기는 문제 그리고 storage driver 종류에 따라 생기는 문제를 알아보겠습니다.
Storage Driver란?
docker storage driver는 docker image로 부터 container를 생성하는데 이 때 docker image는 application code, system, library등을 포함합니다. 그리고 이를 저장하고 관리하기 위해선 store system이 필요한데 이것을 storage driver 이라고 합니다.
또한, storage driver는 image가 여러 레이어로 구성된 것 처럼 레이어를 효율적으로 저장하기 위해 병합 및 추출하는 역할을 합니다. Copy-on-Write 전략을 사용하여 새 컨테이너를 시작할 때 마다 전체 이미지를 복사하는 것이 아닌 변경된 부분만 디스크에 기록하여 디스크를 아껴사용합니다.
하지만, 이러한 좋은 사용성도 결국 개발자가 어떻게 활용하냐에 따라 천차 만별입니다.
잠시 예시를 보고 문제가 있는 Dockerfile입니다. 한 번 문제를 찾아봅시다.
## Dockerfile
FROM openjdk:17-alpine
USER root
ARG SCOUTER_GIT_HUB_URL
RUN apk add curl && mkdir "scouter";
RUN curl -o /scouter/scouter-agent.java.tar.gz $SCOUTER_GIT_HUB_URL
RUN rm -rf /scouter/scouter-agent.java.tar.gz
ADD build/libs/blog.jar build/libs/blog.jar
EXPOSE 8099
잠시 생각해봅시다 ....
뭐가 문제일까?
아까 우리는 Storage driver가 여러 레이어를 효율적으로 저장하기 위해 병합 및 추출을 하고 변경된 레이어만 디스크에 기록한다고 했습니다. 그럼 다시 살펴보죠 현재 RUN 명령어 레이어만 3개가 있습니다. 이 경우 스토리지 엔진은 다음과 같이 쌓을 것입니다.
Alpine-Linux OS 디렉토리 생성
openJDK가 추가된 레이어에 대한 디렉토리 생성
apk curl 패키지가 추가된 레이어에 대한 디렉토리 생성
scouter를 다운로드 받아 생겨난 레이어 디렉토리 생성
scouter를 삭제한 후 생겨난 레이어 디렉토리 생성
blog.jar 파일이 추가되며 생겨난 레이어 디렉토리 생성
맞습니다. 너무 많은 레이어가 생겨나면서 폴더가 너무 많이 생겨났고 나중에 보겠지만 overlay2 storage라면 그나마 디렉토리간 파일을 공유하기 때문에 중복된 파일이 덜 발생하겠지만 vfs storage driver는 그렇지 않기 때문에 대단히 많은 용량이 storage에 쌓일 것입니다.
그리고 overlay2를 사용하더라도 용량을 효율적으로 쓰는 것은 아니기 때문에 당연히 주의하여야합니다.
그렇다면 어떻게 개선해볼 수 있을까요?
RUN 레이어를 하나로 만드는 것입니다. RUN 간에 발생하는 명령어를 \ 를 이용해서 합칠 수 가 있습니다.
## Dockerfile
FROM openjdk:17-alpine
USER root
ARG SCOUTER_GIT_HUB_URL
RUN apk add curl && mkdir "scouter"; \
curl -o /scouter/scouter-agent.java.tar.gz $SCOUTER_GIT_HUB_URL && \
rm -rf /scouter/scouter-agent.java.tar.gz && \
ADD build/libs/blog.jar build/libs/blog.jar
EXPOSE 8099
개선된 Dockerfile은 위와 같습니다. RUN이 하나의 레이어에서 모든 명령어를 처리하기 때문에 명령어가 끝난 직후의 스냅샷이 디스크에 기록될 것입니다. 그리고 디렉토리는 총 3개만 생기기 때문에 적은 용량이 쌓이게 됩니다.
그럼 모든 용량이 개선된걸 까요? 사실 overlay2 storage driver를 사용했다면 여기서 추가적인 개선이 필요 없습니다. 하지만 vfs storage driver는 다릅니다.
그럼 이 둘의 차이를 알아봅시다.
Overlay2 vs VFS
먼저 각각의 특징을 살펴보겠습니다.
overlay2
overlay2는 overlayFS 파일 시스템을 이용하여 여러 레이어를 하나의 뷰로 관리합니다. Copy-on-write 전략을 사용하기 때문에 공간을 효율적으로 관리하며 하나의 뷰로 관리하기 때문에 storage에 저장된 디렉토리는 하나의 이미지 크기랑 유사합니다.
docker에서 권장하는 storage driver이며 성능 또한 storage driver 중 꽤 높은 성능을 자랑합니다.
다만, linux 커널 4.0 이하에선 사용할 수 없으며 CentOS 7.6 버전 이상에서만 사용이 가능하여 비교적 호환이 안되는 경우가 발생할 수 있습니다.
vfs
vfs는 여러 레이어를 하나의 뷰로 관리하지 않씁니다. 각 레이어는 별도의 디렉토리로 저장됩니다. 성능이 낮은 편이며 공간 효율성 또한 Image의 각 레이어를 독립된 디렉토리로 관리하기 때문에 중복 파일이 발생하여 공간을 낭비할 수 있습니다.
다만, CentOS 낮은 버전이나 linux 낮은 버전에서도 호환이 되며 호환성으로 인한 문제는 발생하지 않습니다.
결론적으로 말씀드리면 vfs를 사용하는 것은 한계가 있습니다. 위 같은 이미지도 host에는 결국 무거운 용량이 쌓이게 됩니다. 아무리 레이어를 최대한 줄여 쌓이는 양을 줄여도 위 같은 경우 OS만 총 4번 무거워 지며 중복되어 저장되기 때문에 비효율적입니다.
물론 이 또한 최적화가 가능합니다. 실제로 필자는 호환성 문제 때문에 vfs로 갈 수 밖에 없었고 최대한 Dockerfile의 레이어를 가볍게 쌓기 위하여 다음과 같이 개선했습니다.
## Dockerfile
FROM alpine:3.18.3
USER root
ARG SCOUTER_GIT_HUB_URL
ADD build/libs/blog.jar build/libs/blog.jar
RUN apk add curl openJdk17 && mkdir "scouter"; \
curl -o /scouter/scouter-agent.java.tar.gz $SCOUTER_GIT_HUB_URL && \
rm -rf /scouter/scouter-agent.java.tar.gz
EXPOSE 8099
Alpine-Linux는 5MB에 적은 용량을 자랑하는 경량 Linux입니다. 그렇기 때문에 먼저 OS만 추가한 후 여기에 가장 무거울 것이라 예상되는 JDK와 curl을 한 번에 RUN 레이어에 넣어줍니다. 또한 jar 파일 복사 시점을 RUN 전으로 하여 최대한 무거운 디렉토리가 마지막에 쌓이게 유도합니다.
다음과 같은 최적화로 약 279MB의 curl + jdk가 들어간 디렉토리를 마지막에 쌓아 빌드 시 발생하는 이미지 storage 총 용량을
750MB -> 400MB 정도로 줄였습니다.
하지만 이 경우도 Alpine-Linux를 사용했을 때 상황입니다. OS 안정성을 위해 alpine-linux보다 debian을 선호한다면 vfs 스토리지는 아마 한 번 빌드 할 때 마다 1GB 정도의 디렉토리가 쌓일 것입니다.
결국 가장 좋은 방법은 호환 되는 OS로 업그레이드 하여 overlay2로 넘어가는 것입니다.
마무리
항상 문제 해결에 정답은 없는 것 같습니다. 저 같은 경우엔 당장 서버 centOS 버전을 올리는 것은 무리라고 판단 Dockerfile을 최소화하여 빌드하고 추후 너무 많은 용량이 쌓이면 docker system prune 같은 dangling image와 storage를 비워주는 명령어로 용량을 주기적으로 비워주기로 하였습니다.
이는 CI 만을 하기 위한 Jenkins 서버여서 가능했습니다. 빌드만 일어나기 때문에 중요한 docker volume이나 network 정보가 없기 때문입니다.
하지만 예를들어 운영중인 서버라면 CentOS 버전업이 가장 좋은 솔루션일 수 있습니다. docker system prune은 운영 중인 서비스 서버에선 다소 위험한 명령어이기 때문입니다.
적절한 환경 고려를 꼭 해봅시다!
'docker' 카테고리의 다른 글
[Docker Swarm으로 고가용성 서버 구축하기 - 5] 도입 후기 (0) | 2023.08.20 |
---|---|
[Docker Swarm으로 고가용성 서버 구축하기 - 4] 편리한 운영을 위한 다양한 Tool 도입하기 (0) | 2023.08.15 |
[Docker Swarm으로 고가용성 서버 구축하기 - 3] 무중단으로 운영 가능한 Reverse Proxy 도입기 (5) | 2023.08.12 |
[Docker swarm으로 고가용성 서버 구축하기 - 2] Docker swarm 배포하기 (0) | 2023.08.05 |
[Docker swarm으로 고가용성 서버 구축하기 - 1] 클러스터와 부하분산 (0) | 2023.07.30 |
- Total
- Today
- Yesterday
- 코드
- 취준
- Kotlin
- Spring
- 코딩
- Redis
- IT
- 프로그래밍
- CS
- java
- DevOps
- 게시판
- 자바
- 개발
- swarm
- DB
- 프로젝트
- 면접
- 백엔드
- 취업
- JPA
- 취업준비
- 개발자
- 면접 준비
- 면접준비
- 인터뷰
- docker
- MySQL
- thread
- 동시성
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |