[도커 30강] 03강. 이미지와 컨테이너 차이, 생명주기 완전 이해
도커를 배우는 초반에 가장 많이 헷갈리는 질문이 있습니다. “이미지랑 컨테이너가 뭐가 달라요?” 이 질문에 정확히 답하지 못하면, docker run, docker stop, docker rm 같은 기본 명령도 단순 암기가 됩니다. 반대로 이 차이를 확실히 이해하면 이후 Dockerfile, Compose, 배포 자동화까지 훨씬 수월해집니다.
핵심은 아주 간단합니다. **이미지는 실행을 위한 설계도(읽기 중심, 불변에 가까운 단위)**이고, **컨테이너는 그 설계도를 실제로 실행한 인스턴스(쓰기 가능한 프로세스 단위)**입니다. 즉, 이미지는 “파일 시스템 + 메타데이터 묶음”, 컨테이너는 “실행 중(혹은 실행됐던) 애플리케이션의 생명주기 상태”입니다.
이번 강의에서는 이미지/컨테이너 관계를 단순 비유가 아니라, 명령어와 상태 전이 관점으로 끝까지 정리하겠습니다. 3강을 끝내면 run 한 번으로 생기는 내부 단계(이미지 확인 → pull → create → start)를 설명할 수 있고, 언제 stop, start, rm, prune를 써야 하는지도 기준이 생깁니다.
핵심 개념
첫째, 이미지는 템플릿입니다. 애플리케이션 실행에 필요한 파일과 설정이 레이어 형태로 저장됩니다. 같은 이미지에서 컨테이너를 10개 띄워도, 기본 레이어는 공유되고 각 컨테이너는 얇은 writable layer를 가집니다. 그래서 이미지 재사용이 빠르고, 배포 일관성이 좋아집니다.
둘째, 컨테이너는 생명주기 상태를 가집니다. 대표적으로 created, running, exited, paused, dead 같은 상태가 있고, 상태에 따라 가능한 명령이 달라집니다. 예를 들어 실행 중인 컨테이너는 stop 대상이고, 종료된 컨테이너는 start로 재시작할 수 있지만, 삭제된 컨테이너는 start가 불가능합니다.
셋째, docker run은 단일 동작처럼 보이지만 사실상 복합 명령입니다. 내부적으로는 보통 다음 흐름입니다.
- 로컬에 이미지 존재 확인
- 없으면 레지스트리에서 pull
- 컨테이너 create
- 컨테이너 start
이 구조를 이해하면 에러 해석이 쉬워집니다. “이미지를 못 찾는 오류”와 “컨테이너 시작 실패 오류”는 원인 층위가 다릅니다.
넷째, 데이터 영속성은 컨테이너 생명주기와 별개로 관리해야 합니다. 컨테이너 writable layer에만 데이터를 두면 컨테이너 삭제 시 함께 사라질 수 있습니다. 그래서 실무에서는 볼륨/바인드 마운트를 설계 단계에서 반드시 분리합니다.
기본 사용
예제 1) 이미지와 컨테이너를 분리해서 관찰하기
docker pull nginx:alpine
docker images | grep nginx
docker ps -a --filter "ancestor=nginx:alpine"
설명:
docker pull은 이미지 단위 작업입니다.docker images는 로컬에 저장된 이미지 목록을 확인합니다.docker ps -a --filter ancestor=...는 특정 이미지 기반 컨테이너를 조회합니다.- 여기서 이미지가 있어도 컨테이너가 0개일 수 있습니다. 이게 둘의 가장 기본적인 차이입니다.
예제 2) run 이후 상태 전이 확인하기
docker run -d --name web01 -p 8080:80 nginx:alpine
docker ps --filter "name=web01"
docker inspect -f '{{.State.Status}}' web01
설명:
-d로 백그라운드 실행하면 컨테이너 상태는 보통running입니다.docker inspect로 상태를 정확히 문자열로 확인할 수 있습니다.- 이때 웹 브라우저에서
http://localhost:8080접속이 되면 네트워크/포트 매핑/프로세스가 모두 정상입니다.
예제 3) stop/start/rm으로 생명주기 흐름 체험
docker stop web01
docker inspect -f '{{.State.Status}}' web01
docker start web01
docker rm -f web01
설명:
stop후 상태는exited가 됩니다.start는 기존 컨테이너를 다시 올리는 동작입니다(새 컨테이너 생성 아님).rm은 컨테이너 객체 자체를 삭제합니다. 삭제 후에는 해당 이름으로 다시 만들 수 있습니다.
예제 4) --rm 옵션의 의미 정확히 이해하기
docker run --name once-test --rm alpine:3.20 sh -c 'echo hello && sleep 1'
docker ps -a --filter "name=once-test"
설명:
--rm은 컨테이너가 종료되면 자동 삭제하라는 뜻입니다.- 실습/일회성 배치 작업에서 흔적을 남기지 않아 깔끔합니다.
- 단, 종료 후 로그/상태를 다시 확인하기 어렵기 때문에 디버깅 단계에서는
--rm을 잠시 빼고 관찰하는 편이 좋습니다.
자주 하는 실수
실수 1) 이미지를 삭제하면 실행 중 컨테이너도 즉시 사라진다고 오해
- 원인: 이미지와 컨테이너가 같은 객체라고 생각함.
- 해결: 실행 중인 컨테이너는 별도 객체입니다. 다만 이미지 정리 시 의존 관계 때문에 삭제가 거부될 수 있으니, 컨테이너 정리 순서를 먼저 잡습니다.
실수 2) docker run을 재시작 명령처럼 사용해서 컨테이너가 계속 늘어남
- 원인: 기존 컨테이너 재기동은
start인데 매번run을 실행함. - 해결: 기존 컨테이너를 다시 올릴 때는
docker start <name>을 사용하고, 새 인스턴스가 필요할 때만run을 사용합니다.
실수 3) 종료된(exited) 컨테이너를 방치해 디스크/가독성 악화
- 원인:
docker ps만 확인하고docker ps -a는 거의 보지 않음. - 해결: 주기적으로 종료 컨테이너를 정리합니다.
docker ps -a --filter "status=exited"
docker container prune -f
- 주의:
prune는 기준에 맞는 객체를 일괄 삭제하므로, 필요한 컨테이너가 없는지 먼저 확인해야 합니다.
실수 4) 데이터 파일을 컨테이너 writable layer에만 저장
- 원인: “컨테이너 살아있으니 데이터도 안전하겠지”라고 가정함.
- 해결: DB/업로드 파일/캐시처럼 유지가 필요한 데이터는 볼륨이나 외부 스토리지를 사용합니다.
실무 패턴
실무에서는 이미지와 컨테이너를 역할 기준으로 분리해서 운영합니다.
-
이미지 관리 규칙
- 태그 전략 고정:
app:1.4.2,app:staging,app:prod등 - 빌드 재현성 확보: Dockerfile/lockfile/빌드 인자 문서화
- 취약점 점검: 빌드 후 스캔 자동화
- 태그 전략 고정:
-
컨테이너 운영 규칙
- 이름 규칙: 서비스-환경-번호 (
api-prod-01) - 상태 관찰:
ps,logs,inspect를 대시보드/알람과 연결 - 정리 규칙: 종료 컨테이너/미사용 이미지 정리 주기 설정
- 이름 규칙: 서비스-환경-번호 (
특히 팀 협업에서 중요한 건 “명령어 자체”보다 “언제 어떤 명령을 쓰는지”입니다. 예를 들어 장애 상황에서 누군가는 run으로 새 컨테이너를 만들고, 누군가는 start로 기존 컨테이너를 되살리면 진단 기준이 깨집니다. 그래서 운영 런북에는 아래처럼 절차를 고정해두는 게 좋습니다.
- 현재 상태 확인 (
docker ps -a,docker inspect) - 재시작 가능 여부 판단 (
startvs 새run) - 로그 보존 필요 시
--rm금지 - 장애 복구 후 불필요 컨테이너 정리
이렇게 하면 “살아난 것처럼 보이는데 왜 또 죽지?” 같은 반복 장애를 크게 줄일 수 있습니다.
오늘의 결론
한 줄 요약: 이미지는 실행 템플릿, 컨테이너는 실행 인스턴스이며, 문제 해결의 속도는 이 둘을 분리해서 사고하는 순간부터 빨라집니다.
도커를 잘 쓰는 사람은 명령어를 많이 아는 사람이 아니라, 상태 전이를 정확히 읽는 사람입니다. 다음 강의에서 docker run 옵션을 깊게 다룰 때도 오늘 배운 생명주기 관점을 기준으로 보면 훨씬 덜 헷갈립니다.
연습문제
nginx:alpine이미지 하나로 컨테이너 2개를 서로 다른 이름/포트로 실행해보세요. 그리고 이미지 개수와 컨테이너 개수가 왜 다를 수 있는지 설명해보세요.- 컨테이너 하나를
stop→start→rm순서로 처리하면서 각 단계의 상태를docker inspect로 기록해보세요. --rm을 사용한 실행과 사용하지 않은 실행을 각각 1회씩 수행한 뒤,docker ps -a결과 차이를 비교해보세요.
이전 강의 정답
2강 연습문제 해설:
docker version은 Client/Server 버전과 API 호환을 보여주고,docker compose version은 Compose 플러그인 버전을 확인합니다.docker info는 데몬 런타임/리소스/스토리지 드라이버 등 운영 정보를 제공합니다. 즉 version 계열은 “무엇이 설치됐는가”, info는 “엔진이 어떤 상태로 동작 중인가”에 가깝습니다.docker run --rm hello-world는 실행 후 자동 삭제되므로 보통docker ps -a에 해당 컨테이너가 남지 않습니다. 이것이 일회성 검증에서--rm을 권장하는 이유입니다.- 점검 순서 예시는 다음이 안정적입니다: (a) 데몬 기동 여부 확인 (
docker info/서비스 상태) → (b) 권한/소켓 접근 확인 (Linux 그룹 권한 등) → (c) 환경변수/컨텍스트 확인 (DOCKER_HOST,docker context ls).
실습 환경/재현 정보
- OS: macOS 15+ 또는 Ubuntu 22.04+
- Docker 버전: Engine/CLI 최신 안정 버전 권장
- 실습 이미지:
nginx:alpine,alpine:3.20 - 실행 순서:
- 이미지 pull 및 목록 확인
- 컨테이너 실행 후 상태 확인
- stop/start/rm으로 생명주기 전이 확인
- exited 컨테이너 정리(prune) 체험
- 재현 체크:
- 이미지 1개로 컨테이너 여러 개 생성 가능 여부 확인
- 상태가
running/exited로 기대대로 바뀌는지 확인 --rm유무에 따른 흔적 차이를 확인- 정리 명령이 실제로 리소스를 비우는지 확인