[BASIC #3] 밀키트 비유로 완벽 이해하는 도커 이미지와 컨테이너

2025. 5. 31. 22:33·Docker

0. 들어가며

[BASIC #2]에서는 도커를 설치하고 기본 명령어들을 실습해보았다. docker run nginx 같은 명령어를 실행하면 컨테이너가 실행된다는 것은 알겠는데, 도대체 이미지(Image) 와 컨테이너(Container) 는 정확히 무엇이고, 둘은 어떤 관계일까? 이번 포스팅에서는 도커의 가장 핵심 개념인 이미지와 컨테이너를 밀키트(Meal Kit) 비유를 통해 쉽고 완벽하게 이해해보겠다.


1. 밀키트 비유: 이미지와 컨테이너

1.1. 도커 이미지 = 밀키트

밀키트(Meal Kit) 란? 된장찌개 밀키트를 생각해보자.

  • 밀키트 안에는 된장, 두부, 호박, 양파, 육수, 고춧가루 등 요리에 필요한 모든 재료가 정해진 양만큼 들어있다.
  • 또한 조리법(레시피)도 함께 제공된다. "물 500ml를 넣고 끓이다가 두부를 넣으세요" 같은 설명이 적혀 있다.
  • 하지만 밀키트 자체는 아직 요리된 상태가 아니다. 먹을 수 없고, 단지 요리를 만들기 위한 준비물일 뿐이다.

도커 이미지도 마찬가지다.

  • 이미지 안에는 애플리케이션을 실행하는 데 필요한 모든 요소가 들어있다.
    • 소스 코드
    • 자바, 파이썬 같은 런타임
    • 각종 라이브러리와 의존성
    • 환경 변수, 설정 파일
    • 실행 명령어
  • 하지만 이미지 자체는 읽기 전용(read-only) 템플릿이다. 실행할 수 없고, 단지 컨테이너를 만들기 위한 설계도 역할을 한다.

1.2. 도커 컨테이너 = 완성된 요리

이제 밀키트를 실제로 냄비에 넣고 끓여보자.

  • 밀키트의 재료들을 냄비에 넣고 레시피대로 조리하면, 비로소 먹을 수 있는 된장찌개가 완성된다.
  • 이 완성된 된장찌개는 실행 중인 인스턴스다. 실제로 냄비에 담겨 있고, 국자를 떠서 먹을 수 있다.
  • 냄비 안에서는 재료들이 섞이고, 국물이 끓는 등 동적인 상태가 유지된다.

도커 컨테이너도 마찬가지다.

  • 이미지를 기반으로 생성되어, 실제로 실행 중인 인스턴스다.
  • 컨테이너는 파일을 읽고 쓸 수 있고, 네트워크를 사용할 수 있으며, 프로세스가 동작하는 살아있는 환경이다.
  • 하나의 이미지로 여러 개의 컨테이너를 만들 수 있다. (된장찌개 밀키트로 여러 냄비의 된장찌개를 끓일 수 있는 것과 같다)

1.3. 비유의 확장: 중요한 포인트들

① 하나의 이미지로 여러 컨테이너 생성 가능

된장찌개 밀키트 한 박스로 3개의 냄비에 된장찌개를 끓일 수 있다. 각 냄비의 된장찌개는 서로 독립적이다. 한 냄비가 눌어붙어도 다른 냄비는 영향을 받지 않는다. 도커도 마찬가지다. 하나의 nginx 이미지로 10개의 nginx 컨테이너를 실행할 수 있다. 각 컨테이너는 완전히 독립적이며, 포트만 다르게 설정하면 동시에 실행해도 문제없다.

② 컨테이너는 읽기/쓰기 레이어를 가진다

밀키트로 요리할 때, 재료들은 그대로지만 냄비 안에서 물이 끓고 재료가 익는 변화가 일어난다. 도커 컨테이너는 이미지의 읽기 전용 레이어 위에 읽기/쓰기 레이어를 추가한다. 컨테이너 내부에서 파일을 생성하거나 수정하는 모든 변경 사항은 이 읽기/쓰기 레이어에 저장된다.

③ 컨테이너 삭제 = 요리를 먹어치움

된장찌개를 다 먹고 냄비를 비우면, 그 냄비의 된장찌개는 사라진다. 새로운 된장찌개를 먹고 싶다면 밀키트로 다시 끓여야 한다. 도커 컨테이너를 삭제하면, 그 컨테이너의 읽기/쓰기 레이어에 저장된 모든 데이터도 함께 사라진다. 하지만 원본 이미지는 그대로 남아 있으므로, 언제든지 새 컨테이너를 다시 생성할 수 있다.


2. 이미지와 컨테이너의 관계

2.1. 이미지와 컨테이너의 생명주기

[이미지] --(docker run)--> [컨테이너(실행중)] --(docker stop)--> [컨테이너(중지)] --(docker start)--> [컨테이너(실행중)]
   ^                                                                                            |
   |                                                                                            |
   +--------------------------------(docker commit)---------------------------------------------+
  • 이미지: 정적인 템플릿. Docker Hub에서 pull 받거나, Dockerfile로 빌드하여 생성한다.
  • 컨테이너: 이미지를 실행한 상태. docker run으로 생성하고, docker stop으로 중지하고, docker start로 다시 시작할 수 있다.
  • 새 이미지 생성: 실행 중인 컨테이너를 변경한 후, docker commit으로 그 상태를 새로운 이미지로 만들 수 있다.

2.2. 레이어(Layer) 개념 이해하기

도커 이미지는 여러 개의 레이어(Layer) 로 구성된다.

+-------------------+
|   Container Layer (읽기/쓰기) |  <- 컨테이너 생성 시 추가됨
+-------------------+
|   Image Layer 3   |  <- 설정 파일
+-------------------+
|   Image Layer 2   |  <- 애플리케이션 코드
+-------------------+
|   Image Layer 1   |  <- 베이스 이미지 (Ubuntu)
+-------------------+
  • 각 레이어는 읽기 전용이다.
  • Dockerfile의 명령어 한 줄이 하나의 레이어가 된다.
  • 레이어는 캐시되므로, 이미지 빌드 속도가 빠르고 디스크 공간을 절약할 수 있다.

레이어 확인하기:

# nginx 이미지의 레이어 확인
docker history nginx

실행하면 각 레이어의 크기와 생성 명령어를 볼 수 있다.


3. 이미지 관련 상세 명령어

3.1. 이미지 검색 및 다운로드

# Docker Hub에서 이미지 검색
docker search nginx

# 이미지 다운로드
docker pull nginx:latest
docker pull ubuntu:20.04
docker pull mysql:8.0

3.2. 이미지 목록 및 상세 정보

# 로컬 이미지 목록
docker images

# 특정 이미지의 상세 정보
docker inspect nginx

# 이미지의 레이어 히스토리
docker history nginx

3.3. 이미지 태그 관리

# 이미지에 태그 추가
docker tag nginx:latest my-nginx:v1

# 태그 확인
docker images | grep nginx

3.4. 이미지 삭제

# 특정 이미지 삭제
docker rmi nginx

# 사용하지 않는 이미지 모두 삭제
docker image prune

# 모든 이미지 강제 삭제
docker rmi -f $(docker images -q)

 

4. 컨테이너 관련 상세 명령어

4.1. 컨테이너 실행 심화

# 기본 실행
docker run nginx

# 백그라운드 실행 + 이름 지정
docker run -d --name web-server nginx

# 포트 매핑
docker run -d -p 8080:80 --name web nginx

# 환경 변수 전달
docker run -d -e MYSQL_ROOT_PASSWORD=root --name mysql mysql:8.0

# 볼륨 마운트
docker run -d -v /host/data:/container/data --name app myapp

# 컨테이너 종료 시 자동 삭제
docker run --rm -it ubuntu bash

4.2. 컨테이너 생명주기 관리

# 실행 중인 컨테이너 목록
docker ps

# 모든 컨테이너 목록 (종료 포함)
docker ps -a

# 컨테이너 중지
docker stop web-server

# 컨테이너 시작
docker start web-server

# 컨테이너 재시작
docker restart web-server

# 컨테이너 일시 중지/재개
docker pause web-server
docker unpause web-server

# 컨테이너 삭제
docker rm web-server

# 실행 중인 컨테이너 강제 삭제
docker rm -f web-server

4.3. 컨테이너 내부 접속 및 명령 실행

# 실행 중인 컨테이너에 접속
docker exec -it web-server bash

# 컨테이너 내부에서 단일 명령 실행
docker exec web-server ls -la

# 컨테이너 로그 확인
docker logs web-server
docker logs -f web-server  # 실시간 로그 확인

4.4. 컨테이너와 파일 주고받기

# 호스트 → 컨테이너 파일 복사
docker cp ./index.html web-server:/usr/share/nginx/html/

# 컨테이너 → 호스트 파일 복사
docker cp web-server:/etc/nginx/nginx.conf ./

5. 실습: nginx 이미지와 컨테이너 다루기

5.1. nginx 이미지로 여러 컨테이너 실행하기

# nginx 이미지 pull
docker pull nginx

# 첫 번째 nginx 컨테이너 (포트 8081)
docker run -d -p 8081:80 --name nginx1 nginx

# 두 번째 nginx 컨테이너 (포트 8082)
docker run -d -p 8082:80 --name nginx2 nginx

# 실행 중인 컨테이너 확인
docker ps

두 개의 nginx 컨테이너가 각각 8081, 8082 포트로 실행된다. 브라우저에서 localhost:8081과 localhost:8082에 접속해보면 둘 다 nginx 환영 페이지가 보인다. 완전히 독립적인 인스턴스임을 확인할 수 있다.

5.2. 컨테이너 내부 파일 수정하기

# nginx1 컨테이너에 접속
docker exec -it nginx1 bash

# 웹 루트 디렉토리로 이동
cd /usr/share/nginx/html

# index.html 내용 확인
cat index.html

# index.html 수정
echo "<h1>Hello from nginx1</h1>" > index.html

# 컨테이너 종료
exit

이제 localhost:8081에 접속하면 "Hello from nginx1"이 보이고, localhost:8082는 여전히 기본 페이지가 보인다. 각 컨테이너의 파일 시스템이 독립적임을 확인할 수 있다.

5.3. 컨테이너 삭제와 이미지의 영속성

# nginx1 컨테이너 삭제
docker rm -f nginx1

# 이미지 확인 (여전히 존재)
docker images | grep nginx

# nginx1과 동일한 이미지로 새 컨테이너 실행
docker run -d -p 8083:80 --name nginx3 nginx

컨테이너는 삭제되었지만, 이미지는 그대로 남아 있다. 이미지로 새로운 컨테이너를 언제든지 다시 생성할 수 있다.


6. 이미지와 컨테이너의 핵심 차이점 정리

구분 도커 이미지 도커 컨테이너
비유 밀키트 (재료 + 레시피) 완성된 요리
상태 정적 (읽기 전용) 동적 (실행 중)
생성 docker build 또는 docker pull docker run (이미지 필요)
목록 확인 docker images docker ps
삭제 docker rmi docker rm
데이터 레이어로 저장 (변경 불가) 읽기/쓰기 레이어에 저장 (변경 가능)
수명 영구적 (삭제할 때까지) 일시적 (삭제하면 데이터 소멸)

7. 마치며

이번 포스팅에서는 도커의 가장 핵심 개념인 이미지와 컨테이너를 밀키트 비유를 통해 이해해보았다.

  • 이미지는 실행하기 위한 설계도(템플릿) 다.
  • 컨테이너는 그 설계도로 실제로 실행한 인스턴스다.
  • 하나의 이미지로 여러 컨테이너를 만들 수 있으며, 각 컨테이너는 독립적이다.
  • 컨테이너는 삭제되면 내부 데이터도 사라지지만, 이미지는 남아 있어 다시 컨테이너를 생성할 수 있다.

이 개념만 확실히 이해하면 도커의 절반은 이해한 것이다. 다음 포스팅에서는 이 이미지를 직접 만드는 방법, 즉 Dockerfile 작성법에 대해 알아보겠다.

'Docker' 카테고리의 다른 글

[BASIC #6] 컨테이너 간 통신: Docker Network 이해하기  (0) 2025.06.01
[BASIC #5] 데이터를 영구적으로 저장하는 법: Docker Volume  (0) 2025.05.31
[BASIC #4] 나만의 이미지 만들기: Dockerfile 완전 정복  (0) 2025.05.31
[BASIC #2] 도커 설치부터 첫 컨테이너 실행까지 (Hello World 실습)  (0) 2025.05.31
[BASIC #1] VM과 비교하며 이해하는 도커(Docker)의 개념과 등장 배경  (0) 2025.05.31
'Docker' 카테고리의 다른 글
  • [BASIC #5] 데이터를 영구적으로 저장하는 법: Docker Volume
  • [BASIC #4] 나만의 이미지 만들기: Dockerfile 완전 정복
  • [BASIC #2] 도커 설치부터 첫 컨테이너 실행까지 (Hello World 실습)
  • [BASIC #1] VM과 비교하며 이해하는 도커(Docker)의 개념과 등장 배경
h6bro
h6bro
백엔드 개발자의 기술 블로그
  • h6bro
    Jun's Tech Blog
    h6bro
  • 전체
    오늘
    어제
    • 분류 전체보기 (241) N
      • Java (18)
        • Core (9)
        • Design Pattern (9)
      • Spring (80)
        • Core (24)
        • MVC (6)
        • DB (10)
        • JPA (26)
        • Monitoring (3)
        • Security (11)
        • WebSocket (0)
      • Database (33)
        • Redis (15)
        • MySQL (18)
      • MSA (16)
        • MSA 기본 (11)
        • MSA 아키텍처 (5)
      • Kafka (30) N
        • Core (18) N
        • Connect (12)
      • ElasticSearch (11)
        • Search (11)
        • Logging (0)
      • Test (4)
        • k6 (4)
      • Docker (9)
      • CI&CD (10)
        • GitHub Actions (6)
        • ArgoCD (4)
      • Kubernetes (18)
        • Core (12)
        • Ops (6)
      • Cloud Engineering (4)
        • AWS Infrastructure (3)
        • AWS EKS (1)
        • Terraform (0)
      • Project (8)
        • LinkFolio (1)
        • Secondhand Market (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • Cloud Engineering 포스팅 정리
  • 인기 글

  • 태그

    ㅈ
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
h6bro
[BASIC #3] 밀키트 비유로 완벽 이해하는 도커 이미지와 컨테이너
상단으로

티스토리툴바