[Docker] Docker 이미지와 컨테이너 개념 정리

Docker를 처음 공부할 때 가장 먼저 헷갈리는 개념이 이미지(Image)와 컨테이너(Container)다.
둘은 서로 밀접하게 연결되어 있지만 같은 개념은 아니다. 가장 간단하게 정리하면 다음과 같다.
Docker 이미지 = 프로그램 실행 환경을 담은 설계도
Docker 컨테이너 = 이미지를 기반으로 실제 실행된 환경
이 차이를 이해하면 Docker의 전체 구조도 훨씬 쉽게 이해할 수 있다.
1. Docker 컨테이너란?

컨테이너는 프로그램을 실행하기 위한 독립적인 실행 공간이다.
예를 들어 Python으로 만든 웹 애플리케이션을 실행한다고 생각해보자. 프로그램을 실행하려면 소스 코드만 필요한 게 아니다.
보통 다음과 같은 것들이 함께 필요하다.
- Python 3.12
- FastAPI
- 각종 Python 라이브러리
- 환경 변수
- 애플리케이션 코드
일반적인 방식이라면 이런 프로그램과 라이브러리를 서버나 PC에 직접 설치해야 한다.
하지만 Docker를 사용하면 필요한 실행 환경을 하나의 독립된 공간 안에 구성할 수 있다.
PC 또는 서버
│
└─ Docker
│
├─ Container A
│ ├─ Python 3.12
│ ├─ FastAPI
│ └─ Python Application
│
├─ Container B
│ └─ MySQL
│
└─ Container C
└─ Redis
각 컨테이너는 서로 분리되어 있기 때문에 사용하는 프로그램이나 라이브러리 버전이 달라도 충돌을 줄일 수 있다.
예를 들어 Container A에서는 Python 3.12를 사용하고, 다른 컨테이너에서는 Python 3.10 기반 프로그램을 실행하는 것도 가능하다.
2. Docker 이미지란?

그렇다면 컨테이너는 무엇을 기준으로 만들어질까?
바로 Docker 이미지(Image)다.
Docker 이미지는 프로그램 실행에 필요한 파일과 설정을 미리 구성해놓은 템플릿 또는 설계도라고 생각하면 된다.
예를 들어 다음과 같은 이미지가 있다고 해보자.
Ubuntu
+ Python 3.12
+ FastAPI
+ 필요한 라이브러리
+ 애플리케이션 코드
이 상태에서는 아직 프로그램이 실제로 실행되고 있는 건 아니다.
이 이미지를 Docker가 실행하면 실제 동작하는 컨테이너가 만들어진다.
Docker Image
│
│ docker run
↓
Docker Container
즉, 이미지는 실행하기 전의 상태, 컨테이너는 이미지를 실제로 실행한 상태라고 보면 된다.
3. 이미지와 컨테이너의 관계

이미지와 컨테이너의 관계는 객체지향 프로그래밍의 클래스와 인스턴스에 비유할 수도 있다.
Class
↓ 생성
Object
Docker에서는 다음과 같다.
Image
↓ 실행
Container
집을 짓는 과정으로 생각해도 이해하기 쉽다.
설계도 → 실제 집
Image → Container
여기서 중요한 점은 하나의 이미지로 여러 개의 컨테이너를 만들 수 있다는 것이다.
Docker Image
│
┌─────────┼─────────┐
↓ ↓ ↓
Container1 Container2 Container3
세 컨테이너는 같은 이미지를 기반으로 만들어졌기 때문에 기본적인 실행 환경은 동일하다.
하지만 각각은 별도의 프로세스로 동작한다.
이 특징은 서버를 여러 대로 확장하거나 같은 애플리케이션을 여러 개 실행할 때 특히 유용하다.
4. 왜 이미지를 사용할까?
Docker를 사용하는 가장 큰 이유 중 하나는 실행 환경을 동일하게 유지할 수 있기 때문이다.
개발하다 보면 이런 상황을 자주 만나게 된다.
개발자 PC
Python 3.12
FastAPI 0.116
→ 정상 실행
그런데 서버 환경은 이렇게 되어 있을 수 있다.
운영 서버
Python 3.9
FastAPI 0.95
→ 오류 발생
이럴 때 흔히 나오는 말이 있다.
"내 컴퓨터에서는 잘 되는데?"
Docker는 이런 문제를 줄이기 위해 실행 환경 자체를 이미지로 묶어둔다.
Docker Image
Python 3.12
FastAPI 0.116
필요한 라이브러리
애플리케이션
이 이미지를 개발자 PC에서 실행하든 운영 서버에서 실행하든 거의 동일한 환경의 컨테이너를 만들 수 있다.
그래서 Docker의 핵심 장점 중 하나가 바로 환경의 일관성이다.
5. 이미지와 컨테이너는 어떻게 만들까?
Docker에서 이미지는 보통 Dockerfile을 이용해서 만든다.
예를 들어 간단한 Python 애플리케이션이라면 다음과 같은 Dockerfile을 작성할 수 있다.
FROM python:3.12
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
이 Dockerfile을 기반으로 이미지를 생성한다.
docker build -t my-app .
전체 흐름은 다음과 같다.
Dockerfile
│
│ docker build
↓
Docker Image
│
│ docker run
↓
Docker Container
이 흐름은 Docker를 사용할 때 계속 등장하기 때문에 기억해두는 게 좋다.
6. 이미지와 컨테이너의 대표 명령어
현재 저장되어 있는 Docker 이미지를 확인하려면 다음 명령어를 사용한다.
docker images
또는
docker image ls
이미지를 실행해서 컨테이너를 만들려면 다음과 같이 사용한다.
docker run my-app
현재 실행 중인 컨테이너를 확인하려면 다음 명령어를 사용한다.
docker ps
실행이 종료된 컨테이너까지 모두 확인하려면 -a 옵션을 붙이면 된다.
docker ps -a
결국 기본적인 Docker 명령어의 흐름은 다음처럼 정리할 수 있다.
docker build
↓
Image 생성
docker run
↓
Container 생성 및 실행
docker ps
↓
Container 확인
7. 컨테이너와 가상머신은 뭐가 다를까?

둘 다 프로그램을 독립된 환경에서 실행한다는 점에서는 비슷하지만 구조에는 차이가 있다.
가상머신은 각각 별도의 운영체제를 포함한다.
Physical Server
│
└─ Hypervisor
│
├─ Virtual Machine A
│ ├─ Ubuntu
│ └─ Application
│
└─ Virtual Machine B
├─ Ubuntu
└─ Application
반면 Docker 컨테이너는 호스트 운영체제의 커널을 공유한다.
Physical Server
│
├─ Operating System
│
└─ Docker
│
├─ Container A
│ └─ Application
│
├─ Container B
│ └─ Application
│
└─ Container C
└─ Application
그래서 일반적으로 컨테이너는 VM보다 가볍고 빠르게 생성할 수 있다.
하나의 서버에서 여러 개의 컨테이너를 실행하는 것도 흔한 형태다.
8. 실제 서버에서는 어떻게 사용할까?
예를 들어 하나의 웹 서비스를 만든다고 해보자.
서비스 구성은 다음과 같을 수 있다.
Web Server
Application Server
Database
Cache Server
Docker를 이용하면 각각을 별도의 컨테이너로 구성할 수 있다.
Server
│
└─ Docker
│
├─ Nginx Container
│
├─ Spring Boot Container
│
├─ MySQL Container
│
└─ Redis Container
이렇게 서비스별로 컨테이너를 분리하면 각각의 프로그램을 독립적으로 관리하거나 교체하기 편하다.
예를 들어 Spring Boot 애플리케이션을 업데이트하더라도 MySQL이나 Redis 컨테이너는 그대로 유지할 수 있다.
또한 동일한 애플리케이션 이미지를 여러 번 실행해서 서버를 확장하는 것도 가능하다.
Spring Boot Image
│
┌─────────┼─────────┐
↓ ↓ ↓
App #1 App #2 App #3
트래픽이 증가하면 같은 이미지를 기반으로 컨테이너를 추가해서 처리량을 늘릴 수 있는 식이다.
9. 핵심 정리
Docker의 이미지와 컨테이너는 다음과 같이 정리할 수 있다.
| 구분 | Docker Image | Docker Container |
| 의미 | 실행 환경의 설계도 | 실제 실행된 환경 |
| 상태 | 정적인 파일 | 실행 중인 프로세스 |
| 생성 | docker build | docker run |
| 개수 | 하나의 이미지 존재 | 하나의 이미지에서 여러 개 생성 가능 |
| 비유 | 클래스 / 설계도 | 객체 / 실제 집 |
Docker의 전체 흐름까지 포함하면 다음 한 줄로 정리할 수 있다.
Dockerfile → Image → Container
Dockerfile에는 어떤 환경을 만들지 작성하고,
Image에는 그 환경이 저장되어 있고,
Container는 그 이미지를 실제로 실행한 상태다.
결국 Docker를 공부할 때는 이 세 가지 관계만 제대로 잡아도 절반은 이해한 셈이다.
Dockerfile은 만드는 방법, Image는 만들어진 설계도, Container는 실제로 실행되고 있는 프로그램이다.
다음에는 직접 Dockerfile을 작성하고 docker build, docker run을 사용해서 컨테이너를 띄워보면 개념이 훨씬 확실하게 잡힐 것 같다.