VM과 컨테이너는 무엇이 다를까?
들어가면서
수업에서 가상화 기술을 배우다가, 오래전에 자바의 구동 원리를 정리하며 JVM이라는 가상 머신을 고민했던 기억이 떠올랐다. 예전에 Testcontainers를 활용한 통합 테스트 환경을 구축하면서 Docker를 썼지만, 그때는 VM과 컨테이너의 구조적 차이를 제대로 이해하지 못한 채 적용하느라 애를 먹었다. 이번 학습으로 둘의 근본적인 차이를 분명히 이해하고 그때의 의문을 풀어보려 한다.
“컨테이너는 작은 VM”이라는 말이 과연 맞을까? 왜 Docker는 이렇게 많이 쓰이고, VM은 무겁다고 이야기할까? 이 글에서는 이런 질문에 답하면서 VM과 컨테이너의 근본적인 차이를 깊이 있게 살펴보려 한다.
이 글의 목표
- VM과 컨테이너의 구조적 차이를 이해한다.
- 왜 VM은 무겁고 컨테이너는 가벼운지 설명할 수 있다.
- “격리”가 정확히 무엇인지 이해한다.
1. 컴퓨터 실행 계층: 애플리케이션은 어떻게 동작하는가?
VM과 컨테이너의 차이를 이해하려면 먼저 컴퓨터 시스템이 애플리케이션을 실행하는 방식을 계층별로 살펴봐야 한다.
- Hardware (하드웨어): CPU, 메모리, 디스크, 네트워크 카드 등 물리적인 컴퓨터 자원이다.
- Kernel (커널): 운영체제의 핵심 부분으로, 하드웨어 자원을 관리하고 애플리케이션이 하드웨어에 접근할 수 있도록 중개한다. 시스템 콜(System Call)로 애플리케이션의 요청을 처리한다.
- Userland (유저랜드): 커널 위에서 동작하는 모든 소프트웨어(라이브러리, 유틸리티, 애플리케이션 등)를 포함한다. 사용자 모드에서 실행되며, 커널 모드의 자원에 직접 접근할 수 없다.
- Process (프로세스): 실행 중인 애플리케이션의 인스턴스이다. 각 프로세스는 자신만의 메모리 공간과 실행 컨텍스트를 가지며, 커널의 관리 아래 동작한다.
2. VM 구조: 하드웨어 전체를 가상화하다
가상 머신(VM)은 하이퍼바이저(Hypervisor)로 물리 하드웨어 전체를 소프트웨어로 가상화한다. 마치 물리 컴퓨터 위에 또 다른 물리 컴퓨터를 만드는 것과 같다.
Hypervisor (하이퍼바이저)
하이퍼바이저는 물리 하드웨어와 Guest OS 사이에 자리 잡고, 여러 Guest OS가 하나의 물리 하드웨어 자원을 나눠 쓰도록 중개한다. Type 1(베어메탈)과 Type 2(호스트 OS 위) 방식이 있다.
Guest OS (게스트 운영체제)
각 VM은 자신만의 독립적인 운영체제(Guest OS)를 하나씩 갖는다. 이 Guest OS는 커널, 시스템 라이브러리, 부팅 프로세스 등 실제 OS가 갖춰야 할 모든 요소를 포함한다.
시스템 콜 흐름 (VM)
애플리케이션이 시스템 콜을 호출하면 Guest OS의 커널이 이를 처리한다. Guest OS 커널은 하드웨어에 직접 접근할 수 없어서 하이퍼바이저에게 요청을 넘기고, 하이퍼바이저가 물리 하드웨어 접근을 대신 처리한다. 이렇게 여러 계층을 거치면서 오버헤드가 생긴다.
가상 하드웨어
하이퍼바이저는 각 VM에 가상 CPU, 가상 메모리, 가상 디스크, 가상 네트워크 인터페이스 등 가상 하드웨어를 제공한다. Guest OS는 이 가상 하드웨어를 실제 물리 하드웨어인 것처럼 인식하고 동작한다.
3. 컨테이너 구조: OS 커널을 공유하다
컨테이너는 하드웨어가 아닌 운영체제(OS) 수준에서 가상화한다. 호스트 OS의 커널을 공유하고, 애플리케이션 실행에 필요한 환경만 격리해서 제공한다.
Host Kernel 공유
컨테이너는 호스트 OS의 커널을 직접 쓴다. 각 컨테이너는 별도의 Guest OS를 포함하지 않고, 모든 컨테이너의 시스템 콜을 호스트 OS의 커널이 처리한다. 컨테이너가 VM보다 훨씬 가벼운 결정적인 이유가 여기에 있다.
프로세스 격리
컨테이너는 호스트 OS 위에서 실행되는 격리된 프로세스의 집합이다. 리눅스의 namespace와 cgroup 기술로 격리를 구현한다.
- Namespace: 프로세스들이 파일 시스템, 네트워크, 프로세스 ID(PID) 등을 독립적으로 쓸 수 있도록 격리된 환경을 제공한다. 예를 들어 각 컨테이너는 자신만의 파일 시스템 루트(
chroot와 유사)와 네트워크 인터페이스를 갖는다. - Cgroup (Control Group): CPU, 메모리, 네트워크 대역폭, 디스크 I/O 등 시스템 자원의 사용량을 제한하고 할당하는 기술이다. 덕분에 특정 컨테이너가 시스템 자원을 독점해 다른 컨테이너나 호스트 OS에 영향을 주는 일을 막는다.
시스템 콜 흐름 (컨테이너)
애플리케이션이 시스템 콜을 호출하면 컨테이너는 Guest OS 커널을 거치지 않고 바로 호스트 OS의 커널로 요청을 전달한다. 호스트 OS 커널은 namespace와 cgroup으로 격리된 환경 안에서 요청을 처리한다. 이 흐름은 VM보다 훨씬 효율적이고 오버헤드가 적다.
4. 왜 VM은 무겁고 컨테이너는 가벼운가?
VM과 컨테이너의 무게 차이는 위에서 설명한 구조적 차이에서 비롯된다.
- OS 부팅 여부: VM은 Guest OS를 포함하므로 부팅 과정이 필요하고, 여기에 몇 분이 걸린다. 반면 컨테이너는 호스트 OS의 커널을 공유하니 OS 부팅 없이 애플리케이션 프로세스만 실행하면 되어 수 초 만에 시작된다.
- 커널 개수 차이: VM은 각 인스턴스마다 독립적인 커널을 가지지만, 컨테이너는 모든 인스턴스가 하나의 호스트 커널을 공유한다. 이 차이가 메모리 사용량과 이미지 크기를 크게 좌우한다.
- 자원 사용량 차이: VM은 Guest OS를 위해 상당한 양의 CPU, 메모리, 디스크 공간을 미리 할당해야 한다. 컨테이너는 필요한 바이너리와 라이브러리만 포함하므로 이미지 크기가 작고, 자원도 필요에 따라 유연하게 쓴다.
- 시작 속도 차이: OS 부팅 오버헤드와 자원 할당 방식의 차이 때문에 컨테이너가 VM보다 훨씬 빠르게 시작하고 종료한다.
5. 격리 강도 차이: OS 단위 vs 프로세스 단위
VM과 컨테이너는 모두 격리 기술이지만, 그 격리의 강도와 범위가 다르다.
- VM: OS 단위 격리: VM은 하이퍼바이저로 하드웨어 수준에서 완전히 격리된 환경을 제공한다. 각 VM은 독립적인 OS를 가지므로, 한 VM에서 문제가 생겨도 다른 VM이나 호스트 시스템에 영향을 미치지 않는다. 격리가 매우 강력하고 보안성도 높다.
- 컨테이너: 프로세스 단위 격리: 컨테이너는 호스트 OS의 커널을 공유하고,
namespace와cgroup으로 프로세스 수준에서 격리된다. VM만큼 완전한 격리는 아니지만, 대부분의 애플리케이션에는 충분한 격리 수준이다. 커널을 공유한다는 건, 이론적으로 한 컨테이너의 취약점이 호스트 커널을 타고 다른 컨테이너나 호스트 시스템에 번질 수 있다는 뜻이다.
6. 자주 하는 오해
“컨테이너는 작은 VM이다?”
아니다. 컨테이너는 VM과 근본적으로 다른 가상화 방식이다. VM은 하드웨어를 가상화하여 Guest OS를 포함하지만, 컨테이너는 OS 커널을 공유하고 프로세스 수준에서 격리된다. 컨테이너는 VM보다 훨씬 가볍고 빠르지만, 격리 강도와 유연성 측면에서 차이가 있다.
“Docker 이미지에는 OS가 통째로 들어있다?”
아니다. Docker 이미지에는 애플리케이션 실행에 필요한 최소한의 파일 시스템(바이너리, 라이브러리 등)만 들어 있다. 커널은 호스트 OS의 것을 공유하므로 OS 전체가 들어 있는 게 아니다. 그래서 Docker 이미지는 VM 이미지보다 훨씬 작다.
마무리 정리
VM과 컨테이너는 모두 애플리케이션을 격리된 환경에서 실행하는 도구이지만, 작동 방식과 목적에 따라 뚜렷한 차이를 보인다.
- VM = 가상 컴퓨터: 하드웨어 전체를 가상화하여 완전한 독립성과 강력한 격리를 제공한다. 무겁고 시작이 느리지만, 높은 보안성과 유연성이 필요할 때 적합하다.
- Container = 격리된 프로세스 실행 환경: OS 커널을 공유하고 프로세스 수준에서 격리하여 가볍고 빠르게 동작한다. 효율적인 자원 사용과 빠른 배포가 중요한 마이크로서비스 환경에 적합하다.
두 기술의 장단점을 이해하고 프로젝트 특성에 맞춰 적절히 활용하는 것이 현대 소프트웨어 개발에서 중요한 역량이다.
