
VM(가상머신) vs 컨테이너: 가상화 기술의 모든 것 (완벽 분석)
하이퍼바이저 Type 1, 2의 차이부터 컨테이너 격리 기술(Namespace), AWS Firecracker 마이크로 VM, 그리고 비용 최적화 전략까지.

하이퍼바이저 Type 1, 2의 차이부터 컨테이너 격리 기술(Namespace), AWS Firecracker 마이크로 VM, 그리고 비용 최적화 전략까지.
내 서버가 왜 이렇게 작고 강력한지 이해하려면, 집채만 했던 1세대 컴퓨터를 봐야 합니다. 하드웨어의 다이어트 역사와 클라우드 비용 절약의 비밀을 파헤칩니다.

서버를 끄지 않고 배포하는 법. 롤링, 카나리, 블루-그린의 차이점과 실제 구축 전략. DB 마이그레이션의 난제(팽창-수축 패턴)와 AWS CodeDeploy 활용법까지 심층 분석합니다.

가상 머신들의 조율자. 사장님이 직접 운영하느냐(Type 1), 매니저를 두느냐(Type 2)의 차이.

새벽엔 낭비하고 점심엔 터지는 서버 문제 해결기. '택시 배차'와 '피자 배달' 비유로 알아보는 오토 스케일링과 서버리스의 차이, 그리고 Spot Instance를 활용한 비용 절감 꿀팁.

클라우드에서 서버를 여러 대 운영하다 보면, 매달 청구서가 나올 때마다 가슴이 철렁하는 순간이 온다. "CPU 사용률 5%인데 왜 이렇게 많이 나가지?" 관련 문서를 찾다 보면 공통된 조언을 만나게 된다. "컨테이너로 전환해봐. 서버 비용이 1/5로 줄어들 거야."
그래서 도커를 공부하기 시작했는데, 처음엔 완전히 헷갈렸다. "도커는 가벼운 VM이다"라고 이해했는데, 나중에 알고 보니 이건 완전히 틀린 개념이었다. VM과 컨테이너는 근본적으로 다른 기술이다. 마치 아파트와 쉐어하우스처럼.
아파트(VM)는 각 세대마다 화장실, 부엌, 보일러가 다 따로 있다. 완전히 독립적이다. 옆집이 불나도 나랑은 상관없다. 반면 쉐어하우스(Container)는 부엌(커널)을 공유하지만, 방(namespace)은 분리되어 있다. 가벼운 대신 옆집에서 부엌에 불을 내면 나도 위험하다.
이 차이를 제대로 이해하고 나니, 왜 AWS Fargate가 고객마다 커널을 분리하는지, 왜 Firecracker가 탄생했는지, 왜 Docker Desktop이 Mac에서 느린지 모든 게 와닿았다. 그 과정에서 정리한 내용을 공유해본다.
첫 번째 착각은 이거였다. "Docker = 가벼운 VirtualBox"라고 생각했다. 틀렸다.
VirtualBox (VM 방식)uname -r 치면 Ubuntu 커널이 나온다docker run ubuntu bash 해서 들어가도, 커널은 호스트 Mac/Linux 커널이다이걸 실감한 순간이 있었다. Mac에서 docker stats 명령을 쳤는데, CPU 사용률이 이상하게 높았다. 알고 보니 Docker Desktop은 HyperKit이라는 경량 VM 위에서 Linux 커널을 돌리고, 그 위에서 컨테이너를 실행하고 있었던 거다. 그래서 Mac에서 Docker가 느린 거였다. 리눅스 서버에서 직접 돌리는 것과는 체감이 완전히 달랐다.
# Mac에서 Docker 실행하면 실제로는 이런 구조
HyperKit (VM) → Linux Kernel → Docker Container
결국 이거였다: 컨테이너는 "OS를 공유하는" 기술이다. 그래서 Linux 컨테이너는 Linux 호스트에서만 네이티브로 돌아간다. Windows나 Mac에서는 어차피 VM을 거쳐야 한다.
기술의 흐름을 이해하면, 미래가 보인다고 받아들였다.
VM을 생성하고 구동하는 소프트웨어를 하이퍼바이저(Hypervisor) 또는 VMM(Virtual Machine Monitor)이라고 한다.
하드웨어 바로 위에 설치된다. Host OS가 없다.
Windows나 macOS 같은 Host OS 위에 응용 프로그램으로 설치된다.
내가 처음 리눅스를 공부할 때 VirtualBox를 썼는데, 가끔 디스크 I/O가 답답할 정도로 느렸던 이유가 바로 이거였다. 디스크 읽기/쓰기가 게스트 OS → VirtualBox → Host OS 드라이버 → 하드웨어 이렇게 세 단계를 거치니 당연히 병목이 생긴다.
컨테이너는 별도의 OS가 아니다. Host OS 커널 입장에서는 그저 PID, UID, Network가 분리된 프로세스일 뿐이다.
Namespace는 프로세스에게 "너는 혼자야"라고 거짓말을 한다.
ps aux 치면 PID 1번(init)부터 시작하는 것처럼 보인다. 하지만 호스트에서 보면 평범한 PID 12345번 프로세스다./)를 가진 것처럼 보인다. 실제로는 호스트의 /var/lib/docker/overlay2/xyz/ 경로다.172.17.0.2)를 가진 것처럼 보인다. 호스트는 veth 가상 네트워크 인터페이스로 이를 연결한다.# 실제로 Docker 컨테이너 실행하면 이렇게 된다
docker run -d nginx
# 호스트에서 보면
ps aux | grep nginx
# root 12345 0.0 0.1 nginx: master process
# PID는 12345번인데, 컨테이너 안에서는 PID 1번으로 보인다
Cgroups는 "너는 이만큼만 써"라고 제한을 건다.
--cpus=1.5 옵션을 주면, CPU 코어 1.5개만큼만 쓸 수 있다.--memory=512m 옵션을 주면, 512MB 넘어가는 순간 OOM Killer가 프로세스를 죽인다.--device-write-bps 옵션으로 디스크 쓰기 속도를 제한할 수 있다.# Docker 컨테이너에 리소스 제한 걸기
docker run -d \
--cpus=0.5 \
--memory=256m \
--name limited-nginx \
nginx
실제로 한번은 컨테이너가 메모리를 무한정 잡아먹어서 호스트가 다운됐던 적이 있었다. 그 이후로는 무조건 --memory 제한을 건다. 특히 프로덕션에서는 필수다.
리눅스의 root 권한은 너무 막강하다. 시스템 시간 변경, 커널 모듈 로드, 네트워크 설정 변경 등 뭐든 할 수 있다. Capability는 이 권한을 30개 정도로 잘게 쪼갠다.
CAP_NET_ADMIN: 네트워크 설정 변경 가능CAP_SYS_TIME: 시스템 시간 변경 가능CAP_SYS_MODULE: 커널 모듈 로드 가능Docker는 기본적으로 위험한 Capability를 다 제거한다. 그래서 컨테이너 안에서 sudo reboot를 쳐도 안 먹힌다.
"컨테이너는 뚫릴 수 있다." 이것이 클라우드 제공자들의 악몽이었다.
VM은 하드웨어 가상화 기술을 통해 메모리 영역을 철저히 분리한다. VM 하나가 악성코드에 감염되어도, 하이퍼바이저를 뚫고 호스트 머신을 장악하거나 옆의 VM을 공격하는 것(VM Escape)은 매우 어렵다. CPU의 MMU(Memory Management Unit)가 하드웨어 레벨에서 메모리를 분리하기 때문이다.
모든 컨테이너가 하나의 커널을 공유한다. 만약 커널에 버그가 있다면?
그래서 AWS Fargate 같은 퍼블릭 컨테이너 서비스는, 서로 모르는 고객의 컨테이너를 같은 서버(커널) 위에서 절대 돌리지 않는다. 고객마다 EC2 인스턴스를 분리하거나, Firecracker MicroVM으로 격리한다.
실제로 --privileged 옵션 관련 보안 사고 사례가 문서에 많이 나온다. 개발자가 실수로 이 옵션을 켜고 컨테이너를 띄웠다가, 그 컨테이너가 해킹당하면서 호스트 전체가 장악당할 뻔한 케이스다. 그래서 프로덕션에서는 --privileged 옵션을 절대 쓰지 않는 게 원칙이다.
VM의 보안성과 컨테이너의 속도를 합치려는 시도가 "Sandbox Container"다.
불필요한 장치(USB, 키보드, 모니터)를 다 뺀 초경량 KVM 기반 VM이다.
AWS는 왜 이걸 만들었을까? 멀티테넌시 보안 때문이다. 고객 A의 Lambda 함수와 고객 B의 Lambda 함수를 같은 커널 위에서 돌리면 위험하다. 그래서 각각 MicroVM에 격리한다. 컨테이너만큼 빠르면서 VM만큼 안전하다.
애플리케이션과 호스트 커널 사이에 "가짜 커널(Sentry)"을 끼워 넣는다. Sentry는 Go 언어로 작성됐다.
open, read, write)을 하면, gVisor가 중간에서 가로채 검사하고 안전하게 처리한다.Google Cloud Run이 여러 고객의 컨테이너를 같은 노드에서 돌릴 수 있는 이유가 바로 gVisor 덕분이다.
기술적 차이보다 중요한 게 비용(TCO)이다. 청구서를 보면 답이 나온다.
| 항목 | EC2 (VM) | Fargate (Container) | Lambda (Function) |
|---|---|---|---|
| 과금 기준 | 인스턴스 실행 시간 (초) | CPU/Memory 사용량 (초) | 요청 수 + 실행 시간 (ms) |
| 유휴 비용 | 트래픽 없어도 100% 비용 발생 | 트래픽 없어도 100% 비용 발생 | 0원 (Zero Scale) |
| 관리 비용 | OS 패치, 보안 업데이트 직접 함 | AWS가 OS 관리 | AWS가 런타임 관리 |
| 적합한 상황 | 24시간 돌아가는 베이스 로드 | 가변적인 워크로드, 배치 작업 | 간헐적인 이벤트, API |
컨테이너로 전환하면 서버 비용을 60% 이상 절감할 수 있다는 사례가 많다. 다만 Fargate도 유휴 시간엔 비용이 나간다. 그래서 간헐적인 API는 Lambda로 전환하는 방식도 많이 쓴다. 트래픽이 없으면 비용이 0원이니까.
요즘 트렌드는 Containers in VMs다. AWS EKS는 EC2 인스턴스(VM) 위에서 쿠버네티스 포드(Container)를 돌린다. 두 겹의 격리를 얻는다.
Bare Metal → Type 1 Hypervisor → EC2 VM → Kubernetes → Container
이게 현대 클라우드의 표준 아키텍처다.
처음엔 VM과 컨테이너가 완전히 다른 세계였다. 하지만 지금은 Kata Containers (컨테이너를 VM으로 실행), Firecracker (MicroVM), gVisor (샌드박스 컨테이너) 같은 기술로 경계가 흐려지고 있다.
결국 이거였다: 성능, 보안, 비용 중 2개만 선택할 수 있다. 그래서 상황에 맞는 기술을 고르는 게 중요하다.
나는 이제 청구서를 보면서 떨지 않는다. 적재적소에 기술을 쓰는 법을 배웠으니까.
Running multiple servers in the cloud, you eventually hit that moment when the monthly AWS bill makes you wince. "Why am I paying this much when my CPU usage is 5%?" Search through the documentation and communities and you keep finding the same advice: "Switch to containers. You'll cut your server costs by 80%."
So I dove into Docker. At first, I was completely confused. I thought "Docker is just a lightweight VM." Turns out, that's completely wrong. VMs and containers are fundamentally different technologies. Think of it like apartments vs co-living spaces.
An apartment (VM) has its own bathroom, kitchen, and boiler. Completely independent. If your neighbor's place catches fire, you're safe. But a co-living space (Container) shares the kitchen (kernel) while keeping bedrooms (namespaces) separate. It's lighter, but if someone sets the kitchen on fire, everyone's in danger.
Once I understood this difference, everything clicked. Why AWS Fargate isolates kernels per customer. Why Firecracker was invented. Why Docker Desktop is slow on Mac. Let me share what I learned the hard way.
My first misconception was thinking "Docker = lightweight VirtualBox." Wrong.
VirtualBox (VM approach)uname -r inside shows the Ubuntu kerneldocker run ubuntu bash, the kernel is your host Mac/Linux kernelI realized this when I ran docker stats on my Mac and CPU usage was suspiciously high. Turns out Docker Desktop runs HyperKit (a lightweight VM), boots a Linux kernel inside it, and then runs containers on that. That's why Docker feels slower on Mac. Running it natively on a Linux server is a completely different experience.
# On Mac, Docker actually works like this:
HyperKit (VM) → Linux Kernel → Docker Container
The key insight: Containers "share the OS." Linux containers run natively only on Linux hosts. On Windows or Mac, you're always going through a VM layer anyway.
Understanding the technology evolution helps predict the future.
The software that creates and runs VMs is called a Hypervisor (or VMM - Virtual Machine Monitor).
Installed directly on hardware. No host OS.
Installed as an application on a host OS (Windows/macOS).
When I first studied Linux using VirtualBox, disk I/O was painfully slow. Now I know why. Every disk read/write went through Guest OS → VirtualBox → Host OS drivers → Hardware. Three layers of indirection.
A container isn't a separate OS. From the host kernel's perspective, it's just a process with isolated PID, UID, and network.
Namespaces lie to the process, saying "You're alone."
ps aux inside a container shows PID 1 (init). But from the host, it's just process 12345./). But it's actually /var/lib/docker/overlay2/xyz/ on the host.172.17.0.2). The host connects it via a veth virtual network interface.# When you run a Docker container
docker run -d nginx
# On the host, you see
ps aux | grep nginx
# root 12345 0.0 0.1 nginx: master process
# It's PID 12345 on the host, but PID 1 inside the container
Cgroups say "You can only use this much."
--cpus=1.5 limits the container to 1.5 CPU cores.--memory=512m kills the process (OOM Killer) if it exceeds 512MB.--device-write-bps throttles disk write speed.# Limiting container resources
docker run -d \
--cpus=0.5 \
--memory=256m \
--name limited-nginx \
nginx
I once had a container eat infinite memory and crash the host. Ever since, I always set --memory limits. Especially in production. Non-negotiable.
Linux root is too powerful. It can change system time, load kernel modules, modify network settings, etc. Capabilities split this into ~30 fine-grained permissions.
CAP_NET_ADMIN: Modify network settingsCAP_SYS_TIME: Change system timeCAP_SYS_MODULE: Load kernel modulesDocker removes dangerous capabilities by default. That's why sudo reboot inside a container doesn't work.
"Containers can be breached." This was the cloud provider's nightmare.
VMs use hardware virtualization to strictly separate memory regions. If one VM gets infected with malware, escaping the hypervisor to compromise the host or attack neighboring VMs (VM Escape) is extremely difficult. The CPU's MMU (Memory Management Unit) separates memory at the hardware level.
All containers share one kernel. What if the kernel has a bug?
That's why AWS Fargate never runs different customers' containers on the same kernel. They either isolate EC2 instances per customer or use Firecracker MicroVMs.
Security incident reports around the --privileged flag are common in the wild. A developer accidentally uses this flag, the container gets compromised, and the entire host is nearly taken over. That's why --privileged should be banned in production environments.
To secure containers, we layer defenses:
reboot(), swapon().root (UID 0) to host nobody (UID 65534).The industry is trying to combine VM security with container speed. Enter "Sandbox Containers."
A stripped-down KVM-based VM with unnecessary devices removed (USB, keyboard, monitor).
Why did AWS build this? Multi-tenancy security. Running Customer A's Lambda and Customer B's Lambda on the same kernel is risky. So each gets its own MicroVM. As fast as containers, as secure as VMs.
# Firecracker architecture
Firecracker MicroVM → Minimal Linux Kernel → Lambda Function Container
Inserts a "fake kernel (Sentry)" between the app and host kernel. Sentry is written in Go.
open, read, write), gVisor intercepts, validates, and safely processes it.Google Cloud Run can run multiple customers' containers on the same node thanks to gVisor.
Runs standard Docker images inside lightweight VMs (QEMU/Firecracker). You get the Docker workflow (images, tags, portability) with VM isolation. Kubernetes can use Kata as a runtime, giving you VM-level security per pod.
# Kubernetes pod with Kata Containers
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
runtimeClassName: kata
containers:
- name: app
image: myapp:latest
Technical differences matter, but TCO (Total Cost of Ownership) matters more. The billing statement reveals the truth.
| Item | EC2 (VM) | Fargate (Container) | Lambda (Function) |
|---|---|---|---|
| Billing Basis | Instance runtime (seconds) | CPU/Memory usage (seconds) | Requests + Runtime (ms) |
| Idle Cost | 100% even with zero traffic | 100% even with zero traffic | $0 (Zero Scale) |
| Management Cost | You handle OS patching/security | AWS manages OS | AWS manages runtime |
| Best For | 24/7 baseline workloads | Variable workloads, batch jobs | Sporadic events, APIs |
Teams that have made this switch report 60%+ cost reductions. Note that Fargate still charges during idle time. A common follow-up is moving sporadic APIs to Lambda. Zero traffic = $0 cost.
Containers achieve higher density. On a 64GB RAM server:
Result: You need 5x fewer servers with containers. Massive hardware savings.
# Simple Dockerfile example
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
The modern trend is Containers in VMs. AWS EKS runs Kubernetes pods (containers) on EC2 instances (VMs). You get two layers of isolation.
Bare Metal → Type 1 Hypervisor → EC2 VM → Kubernetes → Container
This is the standard modern cloud architecture.
Q: Which is faster, VM or Container? A: Containers are faster because they run natively on the host OS kernel. VMs have overhead from hardware emulation and the guest OS.
Q: Can I replace all VMs with Containers? A: Not necessarily. If you need legacy apps, different operating systems, or strict security isolation, VMs are still better.
Q: Is Docker a Type 2 Hypervisor? A: No. Docker isn't a hypervisor at all. It uses kernel features to isolate processes.
Q: Do containers have their own Kernel? A: No. Containers share the host OS kernel. This is the main architectural difference from VMs.
Q: What is "Cloud Native"? A: It usually means using containers, microservices, and orchestration to build scalable applications.
Q: Why are VMs considered "Heavy"? A: Because they include a full OS image (kernel, drivers, libraries, system tools), taking up GBs of space and RAM.
Q: What is the main security risk of Containers? A: Kernel sharing. A vulnerability in the kernel affects all containers on that host.
Q: Can I run Docker inside a VM? A: Yes. This is the standard way to run Docker in the cloud (e.g., EC2 instances running Docker).
Q: Can I run a VM inside a Container? A: Yes (KVM inside Docker), but it requires special privileges and is complex to set up.
Q: What is the future of virtualization? A: Convergence. Lightweight VMs (MicroVMs) running containers, managed by Kubernetes.