빠른 비교
# 이미지 하나로 컨테이너 여러 개 실행
docker pull nginx:alpine
docker run -d --name web1 nginx:alpine
docker run -d --name web2 nginx:alpine
docker run -d --name web3 nginx:alpine갈리는 기준
이미지를 볼지 컨테이너를 볼지 먼저 어떻게 고르나
| 상황 | 먼저 떠올릴 선택 |
|---|---|
| 실행 환경 템플릿을 만들고 배포 | 이미지 |
| 이미지를 실제 프로세스로 실행 | 컨테이너 |
| 여러 인스턴스를 같은 기준으로 띄우기 | 이미지 하나 + 여러 컨테이너 |
| 데이터 유지가 필요하다 | 볼륨 추가 |
이미지가 읽기 전용 레이어 스택인 이유
이미지는 파일 변경 사항을 레이어 단위로 쌓는다. FROM ubuntu는 베이스 레이어, RUN apt-get install은 그 위에 쌓이는 새 레이어다. 모든 레이어는 읽기 전용으로 고정되어 있고, 레이어 내용을 해시로 식별해 여러 이미지가 공통 레이어를 공유한다. 덕분에 디스크 공간과 pull 시간이 절약된다.
# 이미지 레이어 확인
docker image history nginx:alpine
# 레이어 공유 예시: python:3.12-slim 기반 두 이미지는
# python 레이어를 한 번만 저장
docker pull my-api:v1
docker pull my-api:v2 # 공통 레이어는 이미 있으므로 생략컨테이너가 이미지 위에 쓰기 가능한 레이어를 추가하는 방식 (Copy-on-Write)
컨테이너를 시작하면 Docker는 이미지 레이어 스택 위에 얇은 쓰기 가능 레이어를 하나 추가한다. 컨테이너 안에서 파일을 수정하면 그 파일만 이미지에서 복사해와 쓰기 레이어에 기록한다. 이 방식을 Copy-on-Write(CoW)라고 한다. 이미지 원본은 절대 변경되지 않는다.
# 컨테이너 안에서 파일 수정 — 이미지는 변하지 않음
docker run -it nginx:alpine sh
# 컨테이너 내부에서:
# echo "hello" > /tmp/test.txt (쓰기 레이어에만 기록됨)
# 컨테이너를 삭제하면 쓰기 레이어도 함께 사라짐
docker rm -f my-container하나의 이미지로 여러 컨테이너를 실행할 수 있는 원리
이미지 레이어는 공유되고 각 컨테이너는 독립된 쓰기 레이어를 갖는다. web1, web2, web3가 동일한 nginx 이미지를 사용해도 각자의 로그, 임시 파일, 설정 변경은 서로 격리된다. 이미지를 여러 번 복사하지 않으므로 디스크 사용량이 크게 늘지 않는다.
# nginx 이미지 하나로 포트만 다르게 세 인스턴스 실행
docker run -d --name web1 -p 8081:80 nginx:alpine
docker run -d --name web2 -p 8082:80 nginx:alpine
docker run -d --name web3 -p 8083:80 nginx:alpine
# 이미지는 한 번만 저장됨
docker image ls nginx:alpine컨테이너 삭제 시 쓰기 레이어만 사라지고 이미지가 유지되는 이유
docker rm은 컨테이너의 쓰기 레이어만 제거한다. 이미지 레이어는 건드리지 않는다. 컨테이너 안에서 만든 파일, 수정한 설정은 삭제되지만 이미지는 그대로 남아 다시 컨테이너를 만들 수 있다. 데이터를 유지하려면 볼륨을 사용해야 한다.
# 컨테이너 삭제 후에도 이미지는 남아있음
docker rm web1
docker image ls nginx:alpine # 이미지는 그대로
# 영속 데이터는 볼륨으로 분리
docker run -d --name db \
-v pgdata:/var/lib/postgresql/data \
postgres:17commit과 Dockerfile 재빌드는 목적이 다르다
docker commit은 지금 떠 있는 컨테이너 상태를 임시 이미지로 굳히는 데는 쓸 수 있지만, 재현 가능한 배포 흐름으로는 적합하지 않습니다. 반복 가능한 변경은 Dockerfile에 남겨 다시 빌드해야 합니다. 디버깅 중 잠깐 상태를 저장하는 용도와, 팀이 공유할 배포 이미지를 만드는 용도를 구분해야 합니다.
# 임시 상태 저장
docker commit debug-box my-app:debug-snapshot
# 실제 배포용 변경은 Dockerfile 수정 후 재빌드
docker build -t my-app:latest .선택 기준
| 상황 | 적합한 선택 |
|---|---|
| 앱 실행 환경을 정의할 때 | Dockerfile로 이미지 빌드 |
| 이미지에서 인스턴스를 만들 때 | docker run |
| 컨테이너 안 변경을 이미지에 반영할 때 | 새 이미지 빌드 (컨테이너 수정 금지) |
| 컨테이너 재시작 후에도 데이터 유지 | 볼륨(-v) 사용 |
주의할 점
컨테이너 안을 직접 수정해 문제를 해결하면 컨테이너 재시작 시 변경이 사라진다. 재현 가능한 변경은 Dockerfile과 이미지 빌드 단계에서 관리해야 하며, 데이터 유지가 필요한 파일은 반드시 볼륨으로 분리해야 한다.
docker exec -it web1 sh
# 컨테이너 안에서 파일 수정
echo "patched" > /usr/share/nginx/html/index.html
docker rm -f web1
docker run -d --name web1 nginx:alpine
# 수정 내용은 새 컨테이너에 없음참고 링크
2 sources