반응형
컨테이너·이미지·볼륨·빌드캐시가 디스크를 어떻게 차지하는지, 어떻게 확인하고 줄이는지 전부 정리
1. 도커 전체 디스크 사용량 한눈에 보기
# 요약 보기
docker system df
# 상세 보기 (항목별 전부)
docker system df -v
출력 예시
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 12 3 18.5GB 14.2GB (76%)
Containers 5 2 1.2GB 800MB (66%)
Local Volumes 8 3 45GB 30GB (66%)
Build Cache - - 3.1GB 3.1GB| 항목 | 설명 |
|---|---|
Images |
로컬에 있는 이미지 총 용량 |
Containers |
실행/중지된 컨테이너의 쓰기 레이어 용량 |
Local Volumes |
도커 볼륨 총 용량 |
Build Cache |
docker build 빌드 캐시 |
RECLAIMABLE |
prune으로 회수 가능한 용량 |
2. 이미지 용량
이미지 목록 + 용량
# 이미지 목록 (SIZE 컬럼 포함)
docker images
# 용량 기준 정렬 (큰 것부터)
docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -rh
# 특정 이미지 상세 용량 정보
docker inspect <이미지> --format='{{.Size}}'
이미지 레이어 구조 확인
# 레이어별 용량 히스토리
docker history <이미지>
# 더 상세하게 (잘리지 않게)
docker history --no-trunc <이미지>
# 용량만 보기
docker history --format "{{.Size}}\t{{.CreatedBy}}" <이미지>
출력 예시
IMAGE CREATED BY SIZE
a1b2c3d4e5f6 CMD ["python", "app.py"] 0B
<missing> COPY . /app 512MB
<missing> RUN pip install -r requirements.txt 1.2GB
<missing> FROM python:3.11 1.0GB이미지 용량이 커지는 이유
# ❌ 레이어가 쌓여서 용량 낭비
RUN apt-get update
RUN apt-get install -y curl wget
RUN rm -rf /var/lib/apt/lists/* # 이미 별도 레이어에 파일이 올라가서 의미 없음
# ✅ 한 레이어에 묶어야 효과 있음
RUN apt-get update && \
apt-get install -y curl wget && \
rm -rf /var/lib/apt/lists/*
3. 컨테이너 용량
컨테이너가 차지하는 용량 확인
# SIZE 컬럼으로 확인 (쓰기 레이어 / 가상 전체)
docker ps -s
# 특정 컨테이너
docker ps -s -f name=<컨테이너명>
docker ps -s SIZE 컬럼 해석
NAMES SIZE
my_container 150MB (virtual 2.3GB)| 항목 | 의미 |
|---|---|
150MB |
컨테이너가 실제로 추가로 쓴 쓰기 레이어 크기 |
virtual 2.3GB |
이미지 레이어 + 쓰기 레이어 합산 (실제 디스크 점유와 다름) |
핵심: 이미지 레이어는 여러 컨테이너가 공유하므로
virtual용량을 단순 합산하면 실제보다 훨씬 크게 보임.
컨테이너 상세 용량 확인
# 컨테이너 전체 파일시스템 크기
docker inspect <컨테이너명> --format='{{.SizeRootFs}}'
# 쓰기 레이어 크기만
docker inspect <컨테이너명> --format='.SizeRw}}'
# 모든 컨테이너 쓰기 레이어 용량 정렬
docker ps -s --format "{{.Size}}\t{{.Names}}" | sort -rh
컨테이너 내부 용량 확인
# 컨테이너 내부에서 df 실행
docker exec <컨테이너명> df -h
# 컨테이너 내부 특정 디렉토리 용량
docker exec <컨테이너명> du -sh /app
# 큰 파일 찾기
docker exec <컨테이너명> find / -size +100M -type f 2>/dev/null
컨테이너 로그 파일 용량
# 로그 파일 위치 확인
docker inspect --format='{{.LogPath}}' <컨테이너명>
# 로그 파일 크기 확인
ls -lh $(docker inspect --format='{{.LogPath}}' <컨테이너명>)
# 전체 컨테이너 로그 용량 합산
du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null
4. 볼륨 용량
# 볼륨 목록
docker volume ls
# 특정 볼륨 상세 (마운트 경로 확인)
docker volume inspect <볼륨명>
# 볼륨 실제 경로 용량 확인
du -sh $(docker volume inspect --format='{{.Mountpoint}}' <볼륨명>)
# 전체 볼륨 용량 한 번에
docker volume ls -q | xargs -I{} sh -c \
'echo -n "{}: "; du -sh $(docker volume inspect --format="{{.Mountpoint}}" {}) 2>/dev/null | cut -f1'
5. 빌드 캐시 용량
# 빌드 캐시 확인
docker system df
# 빌드 캐시만 상세히
docker builder du
# 빌드 캐시 삭제
docker builder prune -f
# 특정 기간 이전 캐시만 삭제
docker builder prune --filter until=24h -f
6. 도커 데이터 저장 위치 (호스트 기준)
# 기본 경로 (Linux)
/var/lib/docker/
# 세부 구조
/var/lib/docker/
├── containers/ # 컨테이너 메타데이터 + 로그
│ └── <ID>/
│ └── <ID>-json.log # 로그 파일
├── image/ # 이미지 메타데이터
├── overlay2/ # 이미지·컨테이너 레이어 실제 데이터 (핵심!)
├── volumes/ # 도커 볼륨 데이터
└── network/ # 네트워크 설정
# 전체 도커 데이터 디렉토리 용량
du -sh /var/lib/docker/
# 레이어별 용량 (overlay2가 대부분을 차지함)
du -sh /var/lib/docker/overlay2/
7. 용량 아끼는 실전 전략
로그 용량 제한 (컨테이너 실행 시)
# 로그 드라이버 json-file, 최대 100MB, 3개 파일 순환
docker run \
--log-driver json-file \
--log-opt max-size=100m \
--log-opt max-file=3 \
<이미지>
로그 용량 제한 (daemon.json 전체 적용)
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
# 적용
sudo systemctl restart docker
도커 데이터 루트 경로 변경
// /etc/docker/daemon.json
{
"data-root": "/mnt/data/docker"
}
기본 경로(
/var/lib/docker)가 있는 파티션이 꽉 찰 때 용량 큰 디스크로 옮기는 방법.
Dockerfile 용량 최적화
# ✅ 멀티스테이지 빌드로 최종 이미지 용량 최소화
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
FROM python:3.11-slim # 슬림 이미지 사용
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
CMD ["python", "app.py"]
# ✅ .dockerignore 활용
# .dockerignore 파일에 아래 추가
node_modules/
__pycache__/
*.log
.git/
.env
8. 이미지 빌드 시 용량 줄이는 방법
8-1. 베이스 이미지 선택
# ❌ 풀 이미지 — 불필요한 패키지 전부 포함
FROM python:3.11 # ~1.0GB
FROM node:20 # ~1.1GB
FROM ubuntu:22.04 # ~77MB (OS만 있어도 패키지 깔면 금방 불어남)
# ✅ slim — 최소 패키지만 포함
FROM python:3.11-slim # ~130MB
FROM node:20-slim # ~240MB
# ✅ alpine — 가장 작음 (musl libc 기반, 일부 패키지 호환 주의)
FROM python:3.11-alpine # ~50MB
FROM node:20-alpine # ~130MB
# ✅ distroless — OS 셸도 없음, 보안 최강 (Google 제공)
FROM gcr.io/distroless/python3
FROM gcr.io/distroless/nodejs20
alpine은
musl libc를 써서 일부 C 확장 패키지(numpy, pandas 등)가 빌드 오래 걸리거나 깨질 수 있음. ML 환경엔 slim 권장.
8-2. 멀티스테이지 빌드 (가장 효과 큼)
빌드 도구·컴파일러·캐시를 최종 이미지에서 제거하는 핵심 기법.
# Python 예시
FROM python:3.11 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.11-slim AS runtime
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "app.py"]
# 결과: 1.2GB → 280MB
# Node.js 예시
FROM node:20 AS builder
WORKDIR /app
COPY package*.json .
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-slim AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
# 결과: 1.1GB → 300MB
# Go 예시 (바이너리 하나만 복사 — 극단적으로 작음)
FROM golang:1.21 AS builder
WORKDIR /app
COPY go.mod go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o server .
FROM scratch # 완전 빈 이미지
COPY --from=builder /app/server /server
CMD ["/server"]
# 결과: 1.2GB → 10~20MB
8-3. RUN 레이어 최소화
도커는 RUN 명령어 하나당 레이어 하나를 생성함. 레이어가 많을수록 용량 낭비.
# ❌ 레이어 3개 생성 — 중간 삭제가 의미 없음
RUN apt-get update
RUN apt-get install -y curl wget git
RUN rm -rf /var/lib/apt/lists/*
# ✅ 레이어 1개 — 삭제가 같은 레이어에서 이루어짐
RUN apt-get update && \
apt-get install -y --no-install-recommends curl wget git && \
rm -rf /var/lib/apt/lists/*
# ❌ pip 캐시가 레이어에 남음
RUN pip install -r requirements.txt
# ✅ 캐시 없이 설치
RUN pip install --no-cache-dir -r requirements.txt
# ❌ npm 캐시 잔존
RUN npm install
# ✅ 프로덕션 의존성만 + 캐시 정리
RUN npm ci --only=production && npm cache clean --force
8-4. .dockerignore 철저히 활용
COPY . . 할 때 불필요한 파일이 이미지에 들어가는 것을 막음.
# .dockerignore
# 버전 관리
.git/
.gitignore
# 의존성 (컨테이너 안에서 다시 설치)
node_modules/
vendor/
# 파이썬 캐시
__pycache__/
*.pyc
*.pyo
.pytest_cache/
*.egg-info/
# 환경 설정 (보안!)
.env
.env.*
*.key
*.pem
secrets/
# 로그
*.log
logs/
# 빌드 결과물
dist/
build/
*.o
*.a
# 개발 도구
.vscode/
.idea/
*.swp
# 테스트
tests/
test/
coverage/
# 도커 관련 (불필요)
Dockerfile*
docker-compose*.yml
8-5. COPY는 필요한 것만, 순서도 중요
# ❌ 전체 복사 후 설치 — 코드 변경마다 캐시 무효화
COPY . .
RUN pip install -r requirements.txt
# ✅ 의존성 파일 먼저 복사 → 설치 → 코드 복사
# requirements.txt 안 바뀌면 pip install 레이어 캐시 재사용
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
레이어 캐시 원칙: 자주 바뀌는 것(소스코드)은 아래에, 거의 안 바뀌는 것(의존성)은 위에.
8-6. 불필요한 패키지 설치 안 하기
# apt — 추천 패키지 제외
RUN apt-get install -y --no-install-recommends <패키지>
# apt — 문서·맨페이지 제외 (dpkg 설정)
RUN echo 'path-exclude=/usr/share/doc/*' > /etc/dpkg/dpkg.cfg.d/nodoc && \
echo 'path-exclude=/usr/share/man/*' >> /etc/dpkg/dpkg.cfg.d/nodoc && \
apt-get install -y --no-install-recommends <패키지>
# pip — 불필요한 extras 제외
RUN pip install --no-cache-dir torch --index-url https://download.pytorch.org/whl/cu118
# conda — 캐시 정리
RUN conda install <패키지> && conda clean -afy
8-7. 빌드 후 이미지 용량 분석 도구
# dive — 레이어별 파일 변경 내역 시각적으로 분석
# https://github.com/wagoodman/dive
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
wagoodman/dive:latest <이미지:태그>
# docker history로 빠르게 확인
docker history --format "{{.Size}}\t{{.CreatedBy}}" <이미지> | sort -rh
# 이미지 전체 크기
docker inspect <이미지> --format='{{.Size}}' | \
awk '{printf "%.1f MB\n", $1/1024/1024}'
8-8. 용량 감소 효과 비교표
| 기법 | 감소 효과 | 난이도 |
|---|---|---|
| slim/alpine 베이스 이미지 사용 | ★★★★★ | 낮음 |
| 멀티스테이지 빌드 | ★★★★★ | 중간 |
| .dockerignore 작성 | ★★★☆☆ | 낮음 |
| RUN 레이어 합치기 | ★★★☆☆ | 낮음 |
--no-cache-dir / --no-install-recommends |
★★☆☆☆ | 낮음 |
| COPY 순서 최적화 (빌드 속도 개선) | ★☆☆☆☆ | 낮음 |
| distroless / scratch 이미지 | ★★★★★ | 높음 |
9. 용량 정리 명령어 총정리
# exited 컨테이너 삭제
docker container prune -f
# dangling 이미지(태그 없는) 삭제
docker image prune -f
# 미사용 이미지 전부 삭제
docker image prune -af
# 미사용 볼륨 삭제
docker volume prune -f
# 미사용 네트워크 삭제
docker network prune -f
# 빌드 캐시 삭제
docker builder prune -f
# 위 전부 한 번에 (컨테이너+이미지+네트워크+캐시)
docker system prune -f
# 볼륨까지 포함 (주의! 데이터 날아감)
docker system prune -af --volumes
10. 용량 모니터링 스크립트
#!/bin/bash
# docker_disk_report.sh
echo "===== Docker 디스크 사용량 리포트 ====="
echo ""
echo "[전체 요약]"
docker system df
echo ""
echo "[컨테이너별 쓰기 레이어 용량 TOP 10]"
docker ps -s --format "{{.Size}}\t{{.Names}}" | sort -rh | head -10
echo ""
echo "[이미지 용량 TOP 10]"
docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -rh | head -10
echo ""
echo "[로그 파일 총 용량]"
du -sh /var/lib/docker/containers/*/*-json.log 2>/dev/null | \
awk '{sum+=$1} END {print sum "MB total"}'
echo ""
echo "[회수 가능한 용량]"
docker system df | awk 'NR>1 {print $1": "$NF}'
11. 자주 묻는 것들
Q. 이미지 삭제했는데 용량이 안 줄어요
# dangling 레이어가 남아있을 수 있음
docker image prune -f
# 그래도 안 줄면 실행 중 컨테이너가 이미지 참조 중
docker ps -a # 컨테이너 확인 후 삭제
Q. overlay2 디렉토리가 너무 큰데 어떻게 해요
# overlay2는 이미지+컨테이너 레이어 저장소
# 안전하게 줄이는 방법
docker system prune -af # 미사용 전부 정리
docker builder prune -f # 빌드 캐시 정리
Q. 로그가 디스크를 다 먹어요
# 즉시 로그 비우기 (컨테이너 재시작 없이)
truncate -s 0 $(docker inspect --format='{{.LogPath}}' <컨테이너명>)
Q. 볼륨이 어느 컨테이너가 쓰는지 모르겠어요
# 볼륨 사용 중인 컨테이너 확인
docker ps -a --filter volume=<볼륨명>
# 또는 inspect로
docker inspect <볼륨명>
반응형