도커를 쓰다 보면 빌드 로그에 Using cache가 뜨거나, 한 줄만 고쳤는데 빌드가 한참 걸리는 경험을 합니다. 이걸 이해하려면 도커 이미지가 통짜 파일이 아니라 여러 겹의 레이어(layer)가 쌓인 구조라는 걸 알아야 합니다. 레이어 구조를 알면 빌드가 왜 빠르거나 느린지, 이미지가 왜 무거운지가 한눈에 보입니다.
이미지는 레이어의 적층이다
Dockerfile의 명령어 하나하나(FROM, RUN, COPY 등)가 대체로 레이어 하나를 만듭니다. 각 레이어는 "직전 상태에서 바뀐 부분"만 담은 변경분입니다. 최종 이미지는 이 변경분을 아래에서 위로 차곡차곡 겹친 결과입니다.
Dockerfile
FROM node:20-slim # 레이어 1: 베이스
WORKDIR /app # 레이어 2
COPY package*.json ./ # 레이어 3: 의존성 목록
RUN npm ci # 레이어 4: 설치 결과
COPY . . # 레이어 5: 소스 전체
각 레이어는 읽기 전용(read-only)이고, 컨테이너를 실행하면 그 위에 쓰기 가능한 얇은 층 하나가 더 얹힙니다.
레이어 캐시 — 빌드 속도의 핵심
도커는 레이어마다 "입력이 그대로면 결과도 그대로"라는 원리로 캐시를 재사용합니다. 어떤 레이어가 바뀌면 그 위의 모든 레이어 캐시가 무효화됩니다.
| 상황 | 결과 |
|---|---|
package.json 안 바뀜 | npm ci 레이어 캐시 재사용 → 빠름 |
소스만 바뀜(COPY . .) | 그 위 레이어만 재빌드 |
package.json 바뀜 | npm ci부터 전부 재실행 → 느림 |
그래서 자주 안 바뀌는 것(의존성 목록)을 위에, 자주 바뀌는 것(소스)을 아래에 두는 순서가 빌드 속도를 좌우합니다.
레이어를 직접 확인하기
docker history로 어떤 레이어가 얼마나 무거운지 볼 수 있습니다.
로컬 터미널
docker history node:20-slim
docker image inspect node:20-slim --format '{{.RootFS.Layers}}'
크기가 큰 레이어가 보이면, 그 명령어가 무엇을 이미지에 남기는지 점검할 신호입니다.
레이어를 알면 보이는 것들
- 같은 베이스 이미지를 쓰는 여러 이미지는 그 베이스 레이어를 공유해서 디스크를 아낍니다.
RUN apt-get install후 다른 레이어에서 캐시를 지워도 용량이 안 줄어듭니다 — 삭제는 새 레이어일 뿐, 아래 레이어의 파일은 그대로 남기 때문입니다. 그래서 설치와 삭제를 한RUN에 묶습니다.
요점 정리
- 이미지는 통짜가 아니라 변경분 레이어를 쌓은 구조다.
- Dockerfile 명령어가 대체로 레이어 하나를 만든다.
- 한 레이어가 바뀌면 그 위 캐시가 전부 무효화된다 — 변경 빈도 낮은 순서로 위에 둔다.
docker history로 무거운 레이어를 추적할 수 있다.
레이어 캐시가 살고 깨지는 과정을 직접 빌드하며 확인하는 실습은 도커 트랙에서 할 수 있습니다 — 회원가입 없이 무료로.