Docker를 사용하면 하나의 서버나 내 컴퓨터 위에서 여러 개의 독립된 실행 환경을 만들 수 있다.
각 컨테이너는 자기만의 파일 시스템, 네트워크, 프로세스 공간을 가진 것처럼 보인다.
그래서 처음 Docker를 접하면 VMware, VirtualBox처럼 가상 머신을 띄우는 기술과 비슷하다고 느끼기 쉽다.
- 하지만 Docker Container는 VM처럼 컴퓨터 전체를 가상화하지 않는다.
- Docker는 Linux 커널 기능을 이용해 프로세스 실행 환경을 격리하는 방식에 가깝다.
따라서 Docker를 이해하려면 먼저 "가상화"라는 말을 어떤 의미로 사용하는지 구분해야 한다.
Docker는 VM처럼 하드웨어를 가상화하는 기술은 아니다.
하지만 OS 레벨에서 실행 환경을 격리하는 컨테이너 기반 가상화 기술이라고 볼 수 있다.
이 글에서는 Docker가 왜 VM처럼 보이는지, VM과 Container의 실행 방식이 어떻게 다른지, 그리고 Docker Image에 OS가 들어간다는 표현이 왜 정확하지 않은지를 순서대로 정리한다.
1. Docker를 VM처럼 생각하기 쉬운 이유
Docker와 VM은 겉으로 보면 비슷한 목적을 가진다.
하나의 물리 머신 위에서 여러 개의 독립된 실행 환경을 만들 수 있기 때문이다.
Server
├── App A
├── App B
└── App C이때 모든 애플리케이션을 하나의 OS 환경에 그대로 설치하면 문제가 생길 수 있다.
- App A는
Node 18이 필요하다. - App B는
Node 20이 필요하다. - App C는
Python 3.12가 필요하다. - 각 앱마다 사용하는 라이브러리 버전이 다를 수 있다.
- 포트, 환경 변수, 파일 경로가 충돌할 수 있다.
- 한 애플리케이션의 장애가 다른 애플리케이션에 영향을 줄 수 있다.
이런 문제를 해결하려면 애플리케이션마다 독립된 실행 환경을 제공해야 한다.
VM과 Docker Container는 모두 이 문제를 해결하기 위해 사용된다.
둘 다 하나의 물리 머신 위에서 여러 실행 환경을 만들 수 있다.
그래서 비슷해 보이지만, 실제 실행 방식은 다르다.
이 차이를 이해하려면 먼저 VM이 어떤 방식으로 실행되는지 알아야 한다.
2. 가상화 기술이 등장한 이유
가상화 기술이 본격적으로 사용되기 전에는 하나의 물리 서버를 하나의 용도로 사용하는 경우가 많았다.
Physical Server
└── OS
└── App이 방식은 단순하고 안정적이다.
하지만 자원 활용 측면에서는 비효율적이다.
- 서버의 CPU와 메모리가 많이 남아 있어도 다른 애플리케이션을 쉽게 함께 운영하기 어렵다.
- 하나의 OS 안에 여러 프로그램을 함께 설치하면 의존성 충돌이 생길 수 있다.
- 애플리케이션마다 다른 운영 환경이 필요할 때 관리가 복잡해진다.
- 하나의 애플리케이션 문제가 전체 서버 환경에 영향을 줄 수 있다.
그래서 하나의 물리 서버를 논리적으로 나누어, 여러 개의 독립된 서버처럼 사용하고 싶어졌다.
이 문제를 해결하기 위해 등장한 대표적인 방식이 하이퍼바이저 기반 가상화다.
하나의 물리 서버 위에서 여러 개의 독립된 가상 컴퓨터, 즉 VM을 실행하게 해주는 방식이다.
여기서 핵심 역할을 하는 소프트웨어가 하이퍼바이저이다.
하이퍼바이저는 물리 서버의 CPU, 메모리, 디스크, 네트워크 같은 하드웨어 자원을 추상화해서 각각의 VM에게 나누어 제공한다.
각 VM은 자신이 실제 컴퓨터 한 대를 독점해서 사용하는 것처럼 동작하지만, 실제로는 하나의 물리 서버 자원을 여러 VM이 나누어 쓰고 있다.
더 자세히 보기: 하이퍼바이저가 하는 일
VM 안에는 애플리케이션만 있는 것이 아니다.
각 VM에는 운영체제 전체가 함께 들어간다.
하이퍼바이저는 물리 하드웨어와 VM 사이에 위치해서 여러 VM이 같은 물리 자원을 안전하게 나누어 쓰도록 중재한다.
VM이 CPU를 사용하거나 메모리에 접근하거나 디스크 I/O를 수행하려고 하면, 하이퍼바이저가 이를 관리하고 조율한다.
하이퍼바이저 기반 가상화의 특징은 다음과 같다.
- 운영체제 단위로 격리된 실행 환경을 제공한다.
- 각 VM은 별도의 OS와 커널을 가진다.
- 서로 다른 운영체제를 동시에 실행할 수 있다.
- 격리 수준이 높다.
- 다만 VM마다 OS 전체를 포함해야 하므로 상대적으로 무겁다.
- Guest OS 부팅이 필요하기 때문에 컨테이너보다 시작 시간이 오래 걸릴 수 있다.
AWS EC2 같은 클라우드 VM도 큰 흐름에서는 하이퍼바이저 기반 가상화 개념을 서비스 형태로 제공하는 사례로 볼 수 있다.
3. VM은 컴퓨터 전체를 가상화한다
VM은 물리 컴퓨터 위에 가상의 컴퓨터를 만드는 방식이다.
Physical Server
└── Hypervisor
└── VM
├── Guest OS
├── Kernel
├── Libraries
└── App하나의 물리 서버 위에 여러 VM을 실행하면 다음과 같은 구조가 된다.
Physical Server
└── Hypervisor
├── Ubuntu VM
│ ├── Guest OS
│ ├── Kernel
│ └── App
├── Windows VM
│ ├── Guest OS
│ ├── Kernel
│ └── App
└── CentOS VM
├── Guest OS
├── Kernel
└── AppVM의 핵심은 각 VM이 자기만의 OS를 가진다는 점이다.
그래서 VM은 독립성이 강하다.
각 VM이 별도의 Guest OS와 커널을 가지고 있기 때문이다.
하지만 그만큼 무겁다.
VM마다 OS 전체를 부팅해야 하고, OS가 사용하는 메모리와 디스크 공간도 필요하기 때문이다.
VM은 애플리케이션 하나를 실행하기 위해서도, 그 애플리케이션이 올라갈 운영체제 전체를 함께 실행한다.
Guest OS란?
Guest OS는 가상 머신 안에 설치된 운영체제를 말한다.
예를 들어 내 노트북의 실제 OS가 macOS인데, VirtualBox나 VMware로 Ubuntu VM을 만들었다고 해보자.
[내 실제 노트북]
macOS
↓
[VirtualBox / VMware]
↓
[Ubuntu VM]
Ubuntu OS| 구분 | 의미 |
|---|---|
| Host OS | 실제 컴퓨터에 설치된 운영체제 |
| Guest OS | VM 안에 설치된 운영체제 |
즉, Host OS는 실제 머신의 운영체제이고, Guest OS는 가상 머신 안에서 실행되는 운영체제이다.
커널이란?
커널은 운영체제 안에서 하드웨어 자원을 직접 관리하는 핵심 제어층이다.
애플리케이션은 CPU, 메모리, 디스크, 네트워크 같은 하드웨어 자원을 직접 제어하지 않는다.
대신 운영체제의 커널을 통해 필요한 자원에 접근한다.
| 역할 | 설명 |
|---|---|
| CPU 관리 | 어떤 프로세스가 CPU를 얼마나 사용할지 관리 |
| 메모리 관리 | 프로그램마다 메모리를 할당하고 보호 |
| 파일 시스템 관리 | 파일 읽기, 쓰기, 저장 처리 |
| 네트워크 관리 | 네트워크 송수신 처리 |
| 장치 관리 | 디스크, 키보드, 마우스, GPU 같은 장치 제어 |
| 프로세스 관리 | 실행 중인 프로그램 생성, 종료, 스케줄링 |
[Application]
Chrome, Node.js, Java App, Python App
↓
[OS User Space]
Shell, Library, System Tools
↓
[Kernel]
CPU / Memory / File / Network / Device 관리
↓
[Hardware]
CPU, RAM, Disk, Network Card하이퍼바이저의 종류
하이퍼바이저는 크게 두 가지 방식으로 나눌 수 있다.
Type 1. 네이티브 하이퍼바이저
Hardware
└── Hypervisor
├── Guest OS
└── Guest OS- 호스트 OS 없이 하이퍼바이저가 하드웨어를 직접 제어한다.
- 서버 가상화 환경에서 많이 사용된다.
- 상대적으로 자원 효율이 좋다.
- 다만 하드웨어 드라이버나 운영 환경 구성이 복잡할 수 있다.
Type 2. 호스트형 하이퍼바이저
Hardware
└── Host OS
└── Hypervisor
├── Guest OS
└── Guest OS- 개인 PC에서
VirtualBox,VMware Workstation,Parallels Desktop같은 도구로 VM을 띄울 때 자주 접하는 방식이다. - 사용하기 쉽다.
- 다만 Host OS 위에 다시 하이퍼바이저와 Guest OS가 올라가기 때문에 오버헤드가 생길 수 있다.
4. Docker 컨테이너는 별도의 Guest OS 없이 Linux 커널 위에서 실행된다
Docker Container는 VM처럼 운영체제 전체를 새로 부팅하지 않는다.
- Docker Container 안의 애플리케이션은 결국 Linux 커널 위에서 실행되는 하나의 프로세스다.
- Docker는 이 프로세스가 자기만의 파일 시스템, 네트워크, 프로세스 공간을 가진 것처럼 보이게 만든다.
- 하드웨어 접근, 파일 읽기, 네트워크 통신 같은 작업은 모두 Linux 커널을 통해 처리된다.
여기서 중요한 것은 운영체제를 두 영역으로 나누어 보는 것이다.
[User Space]
애플리케이션
Shell
라이브러리
패키지 매니저
파일 시스템 구조
명령어 도구들
--------------------------
[Kernel Space]
커널
CPU 관리
메모리 관리
프로세스 관리
파일 시스템 관리
네트워크 관리
장치 제어예를 들어 Ubuntu 운영체제를 단순화하면 다음과 같이 볼 수 있다.
Ubuntu OS
├── Linux Kernel
├── bash
├── apt
├── glibc
├── /bin
├── /usr
├── /etc
├── system libraries
└── system tools여기서 Docker Image에 주로 들어가는 것은 User Space 영역이다.
커널은 이미지 안에 들어가지 않는다.
즉, Docker에서 Ubuntu 이미지를 실행한다고 해서 Ubuntu 운영체제 전체가 부팅되는 것이 아니다.
정확히는 Ubuntu 커널을 새로 띄우는 것이 아니라, Ubuntu 이미지에 들어 있는 사용자 공간 파일 시스템을 사용해 프로세스를 실행하는 것이다.
docker run -it ubuntu:22.04 bash이 명령어를 실행하면 Ubuntu 환경에 접속한 것처럼 보인다.
bash도 있고, /bin, /usr, /etc 같은 디렉터리도 있다.
하지만 이때 Ubuntu OS 전체가 부팅된 것은 아니다.
Ubuntu 이미지에 들어 있는 파일 시스템을 기반으로 bash 프로세스가 실행된 것이다.
Docker에서
ubuntu:22.04이미지를 실행한다는 것은
Ubuntu 커널을 부팅한다는 뜻이 아니라, Ubuntu의 사용자 공간 파일 시스템을 사용해 프로세스를 실행한다는 뜻에 가깝다.
Linux Host
└── Linux Kernel
└── Docker Engine
├── Container A
│ └── App Process
├── Container B
│ └── App Process
└── Container C
└── App Process- 이 경우 Docker Container는 호스트 Linux 커널을 직접 공유한다.
- 각 컨테이너는 별도의 커널을 가지지 않는다.
- VM은 애플리케이션을 실행하기 위해 Guest OS와 커널을 함께 실행한다.
- 반면 Docker Container는 별도의 Guest OS를 부팅하지 않고, Linux 커널 위에서 격리된 프로세스를 실행한다.
VM
└── Guest OS
└── Kernel
└── App
Container
└── Linux Kernel 위에서 실행되는 격리된 App ProcessmacOS와 Windows에서 Docker를 실행하면 어떻게 될까?
여기서 한 가지 헷갈리는 지점이 있다.
“Linux 커널 위에서 실행된다”는 말은 Linux 서버에서는 직관적으로 이해된다.
그런데 macOS나 Windows에서 Docker Desktop을 사용할 때는 어떨까?
- macOS는 Linux 커널을 사용하지 않는다.
- Windows도 기본적으로 Linux 커널을 사용하지 않는다.
- 하지만 Linux 컨테이너는 Linux 커널 기능을 필요로 한다.
그래서 macOS나 Windows에서 Linux 컨테이너를 실행할 때는 Docker Desktop 또는 WSL2가 내부적으로 Linux 커널을 제공한다.
macOS / Windows
└── Docker Desktop / WSL2
└── Linux VM
└── Linux Kernel
└── Docker Engine
└── Linux Containers따라서 macOS에서 Docker를 실행한다고 해서 컨테이너가 macOS 커널 위에서 직접 실행되는 것은 아니다.
Windows에서 Linux 컨테이너를 실행한다고 해서 컨테이너가 Windows 커널 위에서 직접 실행되는 것도 아니다.
정확히는 Docker Desktop이나 WSL2가 제공하는 Linux 환경 위에서 Linux 컨테이너가 실행된다.
Docker Container 자체가 VM인 것은 아니다.
다만 Linux가 아닌 운영체제에서 Linux 컨테이너를 실행하기 위해, 내부적으로 Linux 커널을 제공하는 VM 계층이 사용될 수 있다.
즉, 실행 환경에 따라 Docker를 받쳐주는 구조는 달라질 수 있다.
하지만 컨테이너의 본질은 여전히 VM이 아니라 Linux 커널 기능을 활용한 프로세스 실행 환경 격리다.
5. 컨테이너는 어떻게 격리될까?
Docker Container가 단순히 호스트에서 실행되는 프로세스라면, 어떻게 서로 독립된 환경처럼 보일 수 있을까?
핵심은 Linux 커널 기능이다.
Docker는 주로 Linux 커널의
namespace와cgroups를 활용해 컨테이너 실행 환경을 격리하고 자원 사용량을 제한한다.
namespace는 컨테이너가 자기만의 독립된 환경을 가진 것처럼 보이게 한다.cgroups는 컨테이너가 사용할 수 있는 CPU, 메모리 같은 자원을 제한하고 관리한다.
docker run을 실행하면 Docker는 컨테이너를 위해 필요한 namespace와 cgroups를 설정하고, 그 안에서 프로세스를 실행한다.
namespace
namespace는 컨테이너끼리 서로의 프로세스, 네트워크, 파일 시스템 마운트 등을 보지 못하게 분리하는 역할을 한다.
| namespace 종류 | 분리되는 것 | 쉬운 설명 |
|---|---|---|
| PID namespace | 프로세스 ID | 컨테이너 안에서는 자기 컨테이너의 프로세스만 보이는 것처럼 보임 |
| Network namespace | 네트워크 | 컨테이너마다 IP, 포트, 네트워크 인터페이스가 따로 있는 것처럼 보임 |
| Mount namespace | 파일 시스템 마운트 | 컨테이너마다 루트 디렉터리가 따로 있는 것처럼 보임 |
| UTS namespace | hostname | 컨테이너마다 hostname을 다르게 가질 수 있음 |
| User namespace | 사용자와 권한 | 컨테이너 안의 root와 호스트의 root를 다르게 매핑 가능 |
| IPC namespace | 프로세스 간 통신 자원 | 공유 메모리, 메시지 큐 등을 분리 |
예를 들어 컨테이너 안에서 ps 명령어를 실행하면 호스트의 모든 프로세스가 보이는 것이 아니라, 해당 컨테이너 안에서 실행 중인 프로세스만 보이는 것처럼 보인다.
이것은 PID namespace가 프로세스 공간을 분리해주기 때문이다.
cgroups
cgroups는 컨테이너가 사용할 수 있는 CPU, 메모리, 디스크 I/O 같은 자원을 제한하고 관리한다.
예를 들어 특정 컨테이너가 메모리를 과도하게 사용하지 못하도록 제한하거나, CPU 사용량을 조절할 수 있다.
docker run --memory="512m" nginx이 경우 해당 컨테이너는 설정된 메모리 제한 안에서 실행된다.
정리하면 namespace는 “무엇을 볼 수 있는가”를 분리하고, cgroups는 “얼마나 사용할 수 있는가”를 제한한다.
이처럼 Docker는 Linux 커널의 기능을 활용해서 프로세스를 격리하고 자원을 제한한다.
그래서 컨테이너는 단순한 프로세스 하나가 아니라, 파일 시스템, 네트워크, PID, hostname, 자원 사용량 등이 분리된 실행 환경으로 동작한다.
6. 그래서 Docker는 가상화 기술일까?
이제 처음 질문으로 돌아가보자.
Docker는 가상화 기술일까?
→ 답은 가상화라는 말을 어떤 의미로 사용하느냐에 따라 달라진다.
좁은 의미에서 가상화를 하드웨어를 가상화해서 VM을 실행하는 기술로 본다면, Docker는 VM 방식의 가상화 기술이 아니다.
VM은 하이퍼바이저를 통해 물리 하드웨어를 추상화하고, 그 위에 독립된 가상 컴퓨터를 만든다.
각 VM은 자기만의 Guest OS와 커널을 가지고 실제 컴퓨터 한 대처럼 동작한다.
반면 Docker Container는 컴퓨터 전체를 가상화하지 않는다.
Docker는 Linux 커널의 기능을 사용해 프로세스 실행 환경을 격리한다.
즉, VM과 Docker Container의 차이는 다음과 같다.
| 구분 | VM | Docker Container |
|---|---|---|
| 핵심 방식 | 하이퍼바이저 기반 가상화 | OS 레벨 격리 |
| 가상화 대상 | 하드웨어 또는 컴퓨터 전체 | 프로세스 실행 환경 |
| 실행 단위 | Guest OS 위의 애플리케이션 | 격리된 프로세스 |
| 커널 | VM마다 별도 커널 보유 | 실행 환경의 Linux 커널 공유 |
| OS 포함 여부 | VM마다 Guest OS 포함 | OS 전체가 아니라 사용자 공간 파일 시스템 포함 |
| 부팅 방식 | Guest OS 부팅 필요 | 프로세스 실행에 가까움 |
| 무게 | 상대적으로 무거움 | 상대적으로 가벼움 |
| 격리 수준 | 강함 | 프로세스 수준 격리 |
| 대표 기술 | VMware, VirtualBox, Hyper-V, KVM | Docker, containerd |
핵심만 다시 정리하면 다음과 같다.
VM은 컴퓨터 전체를 가상화한다. Docker Container는 프로세스 실행 환경을 격리한다.
하지만 넓은 의미에서 가상화를 “실제 환경과 분리된 독립적인 실행 환경을 제공하는 기술”로 본다면, Docker도 가상화의 한 종류로 볼 수 있다.
다만 이때의 가상화는 VM과 같은 하드웨어 가상화가 아니다.
OS 레벨에서 실행 환경을 분리하는 컨테이너 기반 가상화에 가깝다.
따라서 Docker는 전통적인 VM 기반 가상화 기술이라기보다,
Linux 커널 기능을 활용한 컨테이너 기반 실행 환경 격리 기술로 이해하는 것이 더 정확하다.
7. Docker Image와 Container는 어떻게 다를까?
- 앞에서 Docker Container는 VM처럼 Guest OS를 실행하지 않는다고 정리했다.
- 또한
ubuntu:22.04같은 Docker Image에도 Ubuntu 운영체제 전체가 들어 있는 것이 아니라, 주로 사용자 공간 파일 시스템이 들어 있다고 했다.
그렇다면 이제 Image와 Container의 관계를 조금 더 명확하게 볼 수 있다.
Docker Image는 실행에 필요한 파일 시스템과 설정을 담은 정적인 실행 단위이고,
Docker Container는 그 Image를 기반으로 실제 실행된 프로세스다.
즉, Image는 아직 실행되지 않은 상태다.
애플리케이션 코드, 런타임, 라이브러리, 설정 파일, 실행 명령 같은 것들이 하나의 패키지처럼 묶여 있을 뿐이다.
반면 Container는 그 Image를 기반으로 실제 프로세스가 실행된 상태다.
Docker Image
└── 실행에 필요한 파일 시스템과 설정
├── application code
├── runtime
├── libraries
├── config files
└── command
Docker Container
└── Image를 기반으로 실행된 격리 프로세스
├── process
├── isolated filesystem
├── isolated network
└── resource limits예를 들어 다음 명령어를 실행한다고 해보자.
docker run -it ubuntu:22.04 bash이때 ubuntu:22.04는 Image이고, 그 Image를 기반으로 실행된 bash 프로세스가 Container 안에서 동작한다.
이를 이해하면 Docker를 “가벼운 VM”으로 보는 관점에서 벗어날 수 있다.
- VM에서는 운영체제 전체를 부팅한 뒤 그 위에서 애플리케이션을 실행한다.
- 하지만 Docker에서는 Image에 담긴 파일 시스템을 바탕으로, Linux 커널 위에서 격리된 프로세스를 실행한다.
Docker를 이해할 때 Image와 Container를 단순히 “설계도와 인스턴스” 정도로만 비유하면 한계가 있다.
더 정확하게는 다음과 같이 이해하는 것이 좋다.
- Image는 실행 가능한 파일 시스템과 설정을 담은 정적 스냅샷이다.
- Container는 그 Image를 기반으로 Linux 커널 위에서 실행된 격리 프로세스다.
- Image 자체에는 실행 중인 프로세스, 메모리 상태, 요청 처리 상태가 들어 있지 않다.
- Container가 실행되면서 비로소 프로세스, 네트워크, 파일 시스템 변경 사항, 런타임 상태가 생긴다.
따라서 Docker의 실행 흐름은 다음처럼 정리할 수 있다.
Docker Image
↓ docker run
Docker Container
↓
Linux Kernel 위에서 격리된 프로세스로 실행- 결국 Docker는 Image를 통해 애플리케이션 실행에 필요한 환경을 준비하고,
- Container를 통해 그 환경을 실제 프로세스로 실행한다.
- Docker는 VM처럼 컴퓨터 전체를 가상화하지 않는다.
- Docker Container는 별도의 Guest OS와 커널을 가지지 않는다.
- Docker Container는 Linux 커널 기능을 이용해 격리된 프로세스로 실행된다.
- Docker Image에는 OS 전체가 아니라 사용자 공간 파일 시스템과 실행에 필요한 파일들이 들어간다.
- Image는 실행 가능한 정적 파일 시스템 스냅샷이고, Container는 그 Image를 기반으로 실행된 동적 프로세스다.
- 따라서 Docker는 전통적인 VM 기반 가상화가 아니라, OS 레벨의 컨테이너 기반 실행 환경 격리 기술로 이해하는 것이 정확하다.
