Skip to content
Published on

하이퍼바이저 & 가상화 Deep Dive — KVM, QEMU, Xen, Firecracker, VT-x, 중첩 페이지 테이블 완전 정복 (2025)

Authors

TL;DR

  • 하이퍼바이저는 여러 OS(게스트)를 같은 하드웨어 위에서 격리 실행. 1967년 IBM CP-40에서 시작, x86은 2005년 VT-x 이후 하드웨어 지원.
  • Type 1 (bare metal): VMware ESXi, Xen, Hyper-V. Type 2 (hosted): VMware Workstation, VirtualBox, QEMU+KVM.
  • x86 virtualization의 문제: x86 ISA의 17개 특권 명령이 ring 3에서도 조용히 실패(trap 없이) → 소프트웨어로 "잡을 수 없음". VMware가 1999년 binary translation으로 극복.
  • Intel VT-x / AMD-V (2005-2006): 하드웨어 가상화 지원. 새 모드(VMX root/non-root), VMEntry/VMExit, VMCS 구조.
  • Nested Page Tables (EPT/NPT): 게스트 가상주소 → 게스트 물리주소 → 호스트 물리주소 두 단계 변환을 하드웨어로. 이전 shadow page table 기법을 대체.
  • KVM: Linux 커널 모듈. "Linux 자체가 하이퍼바이저". VM은 일반 프로세스, QEMU가 device emulation. 2007년 커널 병합.
  • QEMU: 원래 user-mode 에뮬레이터(2003). KVM과 결합하면 완전한 Type 2 하이퍼바이저. Device model 제공.
  • VirtIO: Paravirtualized 장치 표준. 게스트가 "가상 장치임을 알고" 협력 → 전체 에뮬레이션보다 10배 빠름.
  • Xen: Type 1, Dom0(관리)와 DomU(게스트) 분리. AWS EC2 초기 기반.
  • Firecracker (AWS 2018): Rust로 쓴 경량 microVM. 125ms boot, ~5 MB 메모리. Lambda/Fargate의 기반.

1. 가상화의 역사

1.1 IBM의 1960년대

1967년 IBM CP-40: 세계 최초의 하이퍼바이저. System/360 메인프레임에서 여러 OS 동시 실행. 당시 이유: "비싼 메인프레임을 여러 사용자에게 공유".

CP/CMS: Control Program + Cambridge Monitor System. 각 사용자가 "자기만의 가상 머신"을 가짐.

1972년 VM/370으로 상용화. 이후 수십 년 동안 IBM 메인프레임의 핵심 기능.

1.2 Popek-Goldberg 정리 (1974)

Gerald Popek와 Robert Goldberg가 "Formal Requirements for Virtualizable Third Generation Architectures" 발표. 하이퍼바이저가 존재하려면 ISA가 만족해야 할 조건:

정리: 모든 sensitive instruction (시스템 상태를 읽거나 변경)이 privileged (특권 모드에서만 실행 가능)여야 한다.

즉: 게스트가 민감한 명령을 실행하려 하면 trap이 발생해 하이퍼바이저로 제어가 넘어가야 한다.

문제: x86 ISA는 이 조건을 위반. 17개의 명령이 특권이 아닌데도 sensitive. 예: POPF (flags 복원). Ring 3에서 실행 시 trap 없이 조용히 실패.

이 때문에 x86은 가상화 불가능하다는 것이 1974년부터의 통념이었다.

1.3 VMware의 1999년 돌파구

Diane Greene, Mendel Rosenblum 등 Stanford 출신이 VMware를 설립. 목표: "x86을 가상화하라".

해결책: Binary Translation. 실행 전에 게스트 코드를 스캔해서 문제가 되는 명령을 찾아 동적으로 수정. 모든 sensitive instruction을 trap-inducing 명령으로 교체.

놀라운 기술적 성취. 1999년 VMware Workstation 출시 → 2001년 VMware ESX (Type 1) → 상용 성공.

1.4 Xen의 등장 (2003)

Cambridge 대학의 Ian Pratt 등이 Xen 발표. 다른 접근: Paravirtualization.

"게스트 OS를 수정해서 하이퍼바이저와 협력하게 만들자".

게스트가 문제 명령을 직접 실행 대신 hypercall (하이퍼바이저에 대한 직접 호출)을 사용. 성능은 좋지만 게스트 OS 수정 필요 → Windows 같은 proprietary OS는 지원 어려움.

1.5 Intel VT-x와 AMD-V (2005-2006)

Intel이 VT-x (Virtualization Technology for x86), AMD가 AMD-V를 발표. 하드웨어 수준에서 가상화 지원.

핵심: 새로운 CPU 모드 도입. "VMX root mode" (하이퍼바이저용)와 "VMX non-root mode" (게스트용). sensitive 명령이 non-root에서 실행되면 자동으로 VMExit → root로 제어 이동.

이후 VMware, Xen, 새로 등장한 KVM이 모두 하드웨어 지원 활용. Binary translation은 점차 사라졌다.

1.6 KVM의 등장 (2007)

Qumranet의 Avi Kivity가 KVM (Kernel-based Virtual Machine) 개발. 아이디어: "Linux 커널을 하이퍼바이저로". 별도의 거대한 하이퍼바이저 코드 대신 커널 모듈 하나.

2007년 Linux 2.6.20에 병합. Red Hat이 2008년 Qumranet 인수 후 KVM이 Red Hat/오픈소스 주류.

1.7 클라우드 시대

2006 AWS EC2 출시: Xen 기반 가상화. 클라우드 컴퓨팅의 시작.

2013 Google GCP: KVM 기반.

2017 Firecracker: AWS가 Rust로 작성한 경량 microVM. Lambda의 기반.

2025년 현재, 전 세계 클라우드는 대부분 KVM 또는 KVM 파생 기술 위에서 돈다. 가상화는 인프라의 기본 가정.


2. Type 1 vs Type 2

2.1 Type 1 — Bare Metal

하이퍼바이저가 하드웨어 위에 직접 실행. 호스트 OS 없음.

Hardware
Hypervisor (Type 1)
Guest OS 1 | Guest OS 2 | ...

예: VMware ESXi, Xen, Hyper-V, KVM (기술적으로는 Linux 커널이 하이퍼바이저 역할).

장점:

  • 성능: 호스트 OS 오버헤드 없음.
  • 보안: 작은 공격 표면.
  • 관리: 엔터프라이즈 기능 (live migration, DRS).

단점:

  • 하드웨어 호환성: 드라이버 부족할 수 있음.
  • 개발 환경 불편: 로컬 개발자용이 아님.

2.2 Type 2 — Hosted

하이퍼바이저가 호스트 OS 위에서 프로세스로 실행.

Hardware
Host OS (Linux, macOS, Windows)
Hypervisor (Type 2)
Guest OS

예: VMware Workstation, VirtualBox, Parallels, QEMU (단독).

장점:

  • 쉬운 설치: 일반 애플리케이션처럼.
  • 호스트 OS 활용: 드라이버, 파일시스템, 네트워크.
  • 개발 편의.

단점:

  • 오버헤드: 이중 레이어.
  • 성능 제한.

2.3 경계가 모호

KVM은 Type 1인가 2인가? 논쟁.

  • Type 1 관점: 커널 모듈이 하드웨어를 직접 제어. Linux 커널이 곧 하이퍼바이저.
  • Type 2 관점: Linux는 일반 OS로도 동작하고, KVM은 그 위의 모듈.

실용적으로: "Linux 커널 자체가 하이퍼바이저". Type 1.5라고 부르기도.

2.4 AWS의 Xen → Nitro 전환

Xen (Type 1): AWS EC2 2006-2018. Nitro (2017+): KVM 기반 + SmartNIC 오프로드. 더 낮은 오버헤드.

현재 대부분 EC2는 Nitro. Xen은 legacy.


3. x86 가상화 난제

3.1 Ring 구조

x86 보호 모드는 4개 ring:

  • Ring 0: 커널, 최고 특권.
  • Ring 1, 2: 거의 사용 안 됨.
  • Ring 3: 유저 공간.

전통적으로 OS가 ring 0에 있고, 애플리케이션이 ring 3.

3.2 문제

가상화 시 게스트 OS를 어디에 둘까?

  • Ring 0: 하이퍼바이저와 충돌. 게스트가 전체 시스템 제어 가능.
  • Ring 1 또는 2: "deprivileging" 시도. 하지만 sensitive instruction 문제.
  • Ring 3: 유저 공간으로 강등. 성능 손해 크고 여전히 sensitive instruction.

3.3 17개의 "Sensitive Non-Privileged" Instructions

x86에서 trap 발생 없이 sensitive한 명령어들:

  • POPF: EFLAGS 레지스터 pop. 게스트가 실행하면 IF(interrupt flag) 조용히 실패.
  • PUSHF: EFLAGS push. 실제 호스트 상태 노출.
  • LAR, LSL, VERR, VERW: segment descriptor 읽기.
  • STR, SGDT, SIDT, SLDT: system table 읽기.
  • ... 등 17개.

Ring 3에서 실행 시 silently fail. 트랩 안 걸려서 하이퍼바이저가 개입 불가.

이것이 1974년 Popek-Goldberg 조건 위반. 결론: "x86은 trap-and-emulate 방식으로 가상화 불가능".

3.4 VMware의 Binary Translation

VMware는 코드를 실행 전에 검사:

  1. 게스트 binary를 읽음.
  2. 문제 있는 명령을 찾음.
  3. Trap을 유발하는 다른 명령으로 교체.
  4. 변환된 코드를 실행.

예: POPFint 3 (breakpoint)로 교체. 실행 시 trap → 하이퍼바이저가 실제 의미대로 에뮬레이션 → 게스트 재개.

동적 바이너리 번역 (Dynamic Binary Translation, DBT)이라 함. 매우 복잡하지만 동작한다.

VMware는 cache로 변환된 코드를 재사용 → 성능 최적화. 초기 VMware는 native의 ~70-90% 성능.

3.5 Paravirtualization (Xen)

대안: 게스트 OS를 수정해서 문제 명령을 직접 쓰지 않게.

게스트가 하이퍼바이저의 **API (hypercall)**를 호출:

// 수정된 Linux 커널 (Xen 게스트)
HYPERVISOR_set_gdt(...)    // hypercall
HYPERVISOR_update_va(...)  // hypercall

장점:

  • 빠름: trap 없이 직접 호출.
  • 간단: 하이퍼바이저가 에뮬레이션 복잡도 감소.

단점:

  • 게스트 수정 필요: Linux는 OK, Windows는 어려움.
  • 배포 어려움: 커널 패치 관리.

Xen은 초기에 paravirtualization 전용이었지만, 이후 하드웨어 지원 + HVM (Hardware Virtual Machine) 모드 추가.


4. Intel VT-x / AMD-V

4.1 등장

Intel VT-x (2005, Vanderpool): x86의 하드웨어 가상화. AMD-V (2006, Pacifica): AMD의 같은 개념.

ISA 수준에서 가상화를 지원. 소프트웨어 트릭 불필요.

4.2 새로운 모드

CPU에 두 가지 실행 모드 추가:

  • VMX root mode: 하이퍼바이저용. 기존 ring 0-3 + 하이퍼바이저 명령어.
  • VMX non-root mode: 게스트용. 기존 ring 0-3 그대로. 하지만 sensitive 명령은 VMExit 발생.

게스트는 **"자기가 ring 0에서 돌고 있다"**고 생각. 실제로는 non-root mode의 ring 0.

4.3 VMCS — Virtual Machine Control Structure

VM의 상태를 저장하는 메모리 구조:

  • 게스트 state (레지스터, 컨트롤 레지스터).
  • 호스트 state.
  • 실행 제어 (어떤 명령에 trap을 걸지).
  • Exit 정보 (왜 VMExit 했는지).

각 VCPU마다 하나. 하이퍼바이저가 메모리 할당 후 VMPTRLD로 로드.

4.4 VMEntry / VMExit

Hypervisor (VMX root)
VMLAUNCH / VMRESUME
Guest (VMX non-root)
    │ sensitive instruction or interrupt
VMExit
Hypervisor (VMX root)
    │ handle exit reason
VMRESUME
Guest
  • VMLAUNCH: 첫 번째 VMEntry.
  • VMRESUME: 이후 재진입.
  • VMExit: 게스트 → 하이퍼바이저 (자동).

4.5 Exit Reasons

게스트가 하이퍼바이저로 돌아오는 이유:

  • EXIT_REASON_CPUID: 게스트가 CPUID 실행. 하이퍼바이저가 가상 CPUID 반환.
  • EXIT_REASON_EPT_VIOLATION: 페이지 폴트.
  • EXIT_REASON_IO_INSTRUCTION: I/O 포트 접근.
  • EXIT_REASON_MSR_READ/WRITE: 모델 특정 레지스터 접근.
  • EXIT_REASON_EXTERNAL_INTERRUPT: 외부 인터럽트.
  • ... 수십 가지.

각 reason에 대해 하이퍼바이저가 처리 후 게스트 재개.

4.6 오버헤드

VMExit는 수백 cycle (200-800 ns). 자주 발생하면 성능 저하.

Goal: VMExit 최소화. Paravirtualized device, in-hardware acceleration 등으로.

4.7 확장

시간이 지나며 Intel/AMD가 가상화 기능 확장:

  • VT-d (IOMMU): 디바이스 직접 할당 (PCIe passthrough).
  • VT-c: 네트워크 I/O 가속.
  • VMFUNC: 게스트 간 빠른 전환.
  • APICv: interrupt virtualization.
  • VMCS shadowing: 중첩 가상화.

5. Nested Page Tables

5.1 메모리 가상화 문제

게스트는 자기 "물리 메모리"가 있다고 생각. 하지만 그것은 실제로 호스트의 가상 메모리.

세 가지 주소 공간:

  • GVA (Guest Virtual Address): 게스트 앱의 주소.
  • GPA (Guest Physical Address): 게스트 OS가 생각하는 물리 주소.
  • HPA (Host Physical Address): 실제 하드웨어 주소.

변환:

  • GVA → GPA: 게스트의 page table.
  • GPA → HPA: 하이퍼바이저가 관리.

문제: CPU의 MMU는 한 단계만 처리. 두 단계를 어떻게?

5.2 Shadow Page Tables (초기 방법)

하이퍼바이저가 "shadow" page table을 만듦:

  • GVA → HPA 직접 매핑 (두 단계 합성).
  • CPU의 CR3는 shadow table을 가리킴.

게스트가 자기 page table을 수정하면 하이퍼바이저가 trap하고 shadow를 업데이트.

단점: 게스트 매 페이지 테이블 수정이 VMExit → 큰 오버헤드. 페이지 폴트도 느림.

5.3 EPT (Extended Page Tables) / NPT (Nested Page Tables)

Intel의 EPT (2008 Nehalem), AMD의 NPT (2007 Barcelona).

하드웨어 수준에서 두 단계 변환:

GVA
  (게스트 page table)
GPA
  (EPT)
HPA

MMU가 두 단계를 한 번에 처리. 하드웨어 지원으로 빠름.

5.4 성능 영향

TLB가 있으면 캐시 히트 시 빠름. TLB 미스 시 두 단계 모두 메모리 접근 → 더 느림.

Huge pages (2 MB, 1 GB)로 TLB 압박 감소. 가상화 환경에서 huge page는 거의 필수.

EPT로 전환 후 가상화 오버헤드가 10-30%에서 5% 이하로 감소.

5.5 Dirty Logging

Live migration을 위해 "어느 페이지가 수정됐는지" 추적해야 한다. EPT가 dirty bit를 제공.

하이퍼바이저가 주기적으로 scan → 수정된 페이지만 migrate. 이것이 live migration의 핵심.


6. KVM — Linux 내부의 하이퍼바이저

6.1 설계 철학

"Linux 커널을 하이퍼바이저로 만들자". 별도 하이퍼바이저 OS가 아니라 Linux 자체.

이점:

  • 기존 커널 기능 재사용: 스케줄러, 메모리 관리, I/O, 네트워크.
  • 유지보수: 개별 하이퍼바이저보다 훨씬 작은 코드.
  • Ecosystem: 모든 Linux 도구 그대로.

6.2 KVM 모듈

KVM은 Linux 커널 모듈 (kvm.ko + kvm-intel.ko 또는 kvm-amd.ko).

Linux Kernel
├── Scheduler
├── Memory Management
├── Networking
├── ...
└── KVM module
    ├── VCPU management
    ├── VMCS / guest state
    └── VT-x / AMD-V interface

KVM이 하는 일:

  • VCPU를 일반 thread처럼 관리. Linux 스케줄러가 스케줄.
  • 게스트 메모리를 일반 프로세스 메모리처럼.
  • VMExit 처리, 인터럽트 주입, I/O 리다이렉트.

6.3 VM이 프로세스?

KVM에서 VM = Linux 프로세스. 게스트의 VCPU는 그 프로세스의 thread.

ps aux | grep qemu
# qemu-system-x86_64 -enable-kvm -m 4G ...

이 qemu 프로세스가 VM. top에서 일반 프로세스처럼 보임.

6.4 KVM API

User space가 KVM을 사용하려면:

#include <linux/kvm.h>

int kvm = open("/dev/kvm", O_RDWR);
int vm = ioctl(kvm, KVM_CREATE_VM, 0);
int vcpu = ioctl(vm, KVM_CREATE_VCPU, 0);

// 게스트 메모리 설정
struct kvm_userspace_memory_region region = {
    .slot = 0,
    .guest_phys_addr = 0,
    .memory_size = 0x100000,
    .userspace_addr = (uintptr_t) guest_memory,
};
ioctl(vm, KVM_SET_USER_MEMORY_REGION, &region);

// 게스트 실행
struct kvm_run *run = mmap(...);
ioctl(vcpu, KVM_RUN, 0);

// VMExit 처리
switch (run->exit_reason) {
    case KVM_EXIT_IO: ... break;
    case KVM_EXIT_MMIO: ... break;
    // ...
}

간단한 VMM을 수백 줄의 C로 작성 가능. "kvmtool"이 대표적 예 (~5,000 줄).

6.5 보안

KVM은 커널 내부이므로 bug가 호스트 탈취로 이어질 수 있음. 엄격한 코드 리뷰.

Sandboxing 추가:

  • seccomp: 허용된 syscall만.
  • User namespace: 권한 제한.
  • SELinux / AppArmor: MAC.

AWS Firecracker는 KVM 자체는 그대로 쓰지만 user-space 부분(QEMU)을 Rust로 재작성해 공격 표면 감소.


7. QEMU

7.1 정체성

QEMU (Quick EMUlator): Fabrice Bellard의 2003년 작품. 원래 user-mode CPU emulator.

예: ARM 바이너리를 x86 Linux에서 실행 (dynamic translation).

7.2 Full System Emulation

이후 system emulation으로 확장: 전체 컴퓨터를 에뮬레이션 (CPU, 메모리, 디바이스).

qemu-system-x86_64 -hda disk.img -m 4G -smp 4

이것은 KVM 없이 순수 소프트웨어 에뮬레이션. 느림 (native의 1-10%).

7.3 QEMU + KVM

QEMU를 KVM 가속으로:

qemu-system-x86_64 -enable-kvm -hda disk.img -m 4G -smp 4

이제 CPU 명령 실행은 하드웨어 가상화, QEMU는 디바이스 에뮬레이션만.

7.4 역할 분담

QEMU (user space)
├── Device model (VirtIO, NIC, disk)
├── BIOS, bootloader
├── UI (VNC, Spice)
└── Management (snapshots, migration)

KVM (kernel)
├── CPU virtualization
├── Memory management
└── Interrupt controller

QEMU가 "VM 껍데기", KVM이 "CPU 엔진".

7.5 Device Emulation

QEMU는 수많은 가상 디바이스 지원:

  • 네트워크: rtl8139, e1000, vmxnet3, virtio-net.
  • 디스크: IDE, SATA, virtio-blk.
  • USB, PCI, VGA.
  • 사운드.

대부분 실제 하드웨어를 에뮬레이션 → 게스트 OS가 표준 드라이버 사용.

7.6 문제

Full emulation은 느림. 각 MMIO 접근이 VMExit + user space로 이동 + QEMU 처리 + 커널 복귀 → 수 μs.

고성능 I/O는 VirtIO 사용.


8. VirtIO — Paravirtualized Devices

8.1 아이디어

"게스트 OS가 가상 환경임을 알고 하이퍼바이저와 협력".

전체 에뮬레이션: 게스트는 실제 하드웨어인 것처럼 레지스터 읽기/쓰기. 하이퍼바이저가 trap해서 해석.

Paravirtualization: 게스트가 공유 메모리 ring buffer를 통해 하이퍼바이저에게 직접 요청. 훨씬 빠름.

8.2 VirtIO 표준

2008년 Rusty Russell이 IBM에서 설계. Linux 커널과 qemu에 구현. 이후 OASIS 표준 → 업계 전반 사용.

VirtIO device 종류:

  • virtio-net: 네트워크.
  • virtio-blk: 블록 디바이스 (디스크).
  • virtio-scsi: SCSI.
  • virtio-console: 시리얼.
  • virtio-rng: 난수 생성.
  • virtio-balloon: 메모리 ballooning.
  • virtio-gpu: 그래픽.
  • virtio-fs: 파일시스템 공유.
  • virtio-vsock: host ↔ guest 소켓.

8.3 VirtIO Ring Buffer

게스트와 하이퍼바이저가 공유 메모리로 통신:

Guest                         Hypervisor (QEMU)
[Ring Buffer in shared memory]
    ↑                             ↑
request 작성                    처리 후 response

게스트가 request를 ring에 쓰고 kick (MMIO write → VMExit). QEMU가 처리 후 response 작성. 게스트가 polling 또는 interrupt로 수신.

배치 처리 가능. 한 번의 VMExit로 여러 request 처리.

8.4 성능

VirtIO 대비 full emulation:

  • 네트워크: 10배 이상.
  • 디스크: 5-10배.
  • CPU 오버헤드: 크게 감소.

거의 모든 현대 VM이 VirtIO 사용. Windows 게스트도 드라이버 설치로 가능.

8.5 Vhost

VirtIO의 user-space part를 커널로 이동한 버전. 더 빠름.

[Guest][Vhost (kernel)][실제 네트워크/디스크]

QEMU를 거치지 않음. I/O가 커널 내부에서 바로 처리.

vhost-net (네트워크), vhost-scsi (디스크), vhost-user (DPDK 같은 user-space 포워딩) 등.


9. Xen

9.1 역사

Xen (2003): Cambridge 대학 Ian Pratt의 프로젝트. Linux 커널에 별도 패치로 시작. 이후 XenSource 회사 → Citrix 인수 → 오픈소스 유지.

AWS EC2 (2006): 초기 Xen 기반.

2010s: KVM이 주류가 되면서 Xen 시장 점유율 감소. 하지만 여전히 사용 (특히 임베디드, 엣지).

9.2 Dom0 / DomU

Xen의 독특한 구조:

Xen Hypervisor (작은 마이크로커널)
Dom0 (Privileged VM) | DomU (Guest VMs)
  ├── 드라이버            ├── Linux
  ├── 관리                ├── Windows
  └── I/O backend         └── ...

Dom0: "Domain 0" — 특권 VM. 실제 하드웨어 드라이버를 포함. 관리 도구 실행. DomU: "Domain Unprivileged" — 일반 게스트 VM.

9.3 왜 이 구조?

Xen 하이퍼바이저 자체를 작게 유지. 드라이버 같은 복잡한 코드는 Dom0의 Linux에 맡김.

  • 보안: 하이퍼바이저가 작으면 공격 표면 작음.
  • 드라이버 재사용: Linux의 모든 드라이버 사용 가능.
  • 격리: DomU가 Dom0 통해 하드웨어에 접근 → 각 단계가 독립.

9.4 Split Driver Model

게스트(DomU)의 네트워크 패킷:

Guest app → Guest kernel (netfront)
               (shared memory)
          Dom0 (netback)
          Real network driver
          Physical NIC

Frontend(게스트)와 Backend(Dom0)가 shared ring buffer로 통신. VirtIO의 원조.

9.5 KVM과의 차이

항목KVMXen
구조Linux 커널 내 모듈독립 하이퍼바이저
드라이버Linux 커널 (host)Dom0의 Linux
특권 VM없음 (host가 특권)Dom0
TypeType 2 (거의 1)Type 1
라이선스GPL (Linux의 일부)GPL (별도 프로젝트)
관리 도구libvirt + qemuxm, xl

두 접근 모두 유효. KVM이 "Linux 통합"으로 이기는 중.

9.6 AWS의 전환

AWS가 Xen → Nitro (KVM 기반)로 이동 (2017+). 이유:

  • 더 낮은 오버헤드: Nitro card가 가상화 작업 오프로드.
  • 빠른 provisioning: 새 인스턴스 시작 빠름.
  • Bare metal instances: 직접 VM 없이 EC2 제공 가능.

현재 대부분 EC2 인스턴스 타입이 Nitro. Xen은 레거시.


10. Firecracker — AWS의 microVM

10.1 문제

AWS Lambda: 서버리스 함수 서비스. 요구:

  • 빠른 시작 (< 125 ms cold start).
  • 강한 격리 (multi-tenant).
  • 작은 footprint (수 MB 오버헤드).

전통 VM은 너무 느림 (수 초 boot). 컨테이너는 빠르지만 격리가 약함.

10.2 해결책

Firecracker (2018). Rust로 작성한 microVM 관리자.

  • Boot time: ~125 ms.
  • Memory overhead: ~5 MB per VM.
  • Minimal device emulation: 단지 몇 개.
  • Stripped down: QEMU의 ~1%.

10.3 설계 원칙

  1. Minimalism: QEMU의 수백만 줄 → Firecracker의 ~50,000 줄 Rust.
  2. Rust: 메모리 안전. 공격 표면 감소.
  3. API 기반: REST API로 VM 관리. CLI 없음.
  4. KVM 기반: CPU 가상화는 리눅스 KVM 재사용.
  5. Seccomp-BPF: 하이퍼바이저 프로세스 자체도 sandbox.

10.4 디바이스

제거한 것:

  • PCI: 없음. 대신 MMIO-only devices.
  • USB: 없음.
  • Sound, VGA, floppy: 없음.
  • BIOS: 아주 단순한 bootloader.

지원하는 것:

  • virtio-net, virtio-block, virtio-vsock, virtio-balloon 등 핵심 VirtIO.
  • Serial console.

10.5 Boot 최적화

  • Custom kernel: 커널 configure를 최소화.
  • No bootloader: KVM이 커널을 직접 로드.
  • Pre-initialized rootfs: 준비된 이미지.
  • Parallel init: 여러 스레드 동시 시작.

결과: 125 ms에 쓸 만한 Linux VM.

10.6 사용처

  • AWS Lambda: 함수 실행 환경.
  • AWS Fargate: 컨테이너 실행 (초기 일부).
  • OpenFaaS, Kata Containers: 일부 가속.

Firecracker는 오픈소스. AWS 외에도 사용 가능.

10.7 Cloud Hypervisor

Intel이 시작한 Firecracker 대안. 유사 목표, 더 많은 디바이스 지원. Rust로 작성.

Cloud Hypervisor + Kata Containers: OCI 컨테이너 스펙을 하이퍼바이저로 실행. 컨테이너의 강한 격리 버전.


11. 가상화 대안 — 다른 격리 기법

11.1 Container

Linux namespaces + cgroups. 커널 공유, 같은 OS 위에서 프로세스 격리.

항목ContainerVM
격리중간 (커널 공유)강함 (전체 OS)
Bootms수 초
MemoryMB100+ MB
Density매우 높음낮음
보안커널 버그 노출하이퍼바이저 격리

11.2 gVisor

Google의 user-space 커널. 컨테이너가 시스템 호출을 내리면 gVisor가 가로채서 자체 구현으로 처리.

  • 실제 Linux 커널 호출을 줄임 → 공격 표면 감소.
  • Pythonic하게 쓰면 Python 기반 보안 샌드박스 유사.
  • Performance 페널티 있음.

App Engine, Cloud Run에서 사용.

11.3 Kata Containers

"컨테이너 API를 VM 격리로". OCI 런타임이지만 내부적으로 VM 생성.

  • runc 대신 kata-runtime.
  • 각 컨테이너가 자체 VM.
  • 강한 격리 + 컨테이너 UX.

Cloud Hypervisor, Firecracker를 백엔드로 사용 가능.

11.4 Unikernel

게스트 OS를 거의 없애고 애플리케이션 + 미니멀 커널만.

예:

  • MirageOS (OCaml).
  • IncludeOS (C++).
  • Nanos.

이점: 극도로 빠른 boot (수 ms), 매우 작은 공격 표면. 단점: 디버깅 어려움, 생태계 작음, 일부 유스케이스만.


12. 성능 최적화

12.1 CPU

CPU Pinning: VCPU를 특정 pCPU에 고정. 캐시 히트 향상.

virsh vcpupin <vm> 0 2  # VCPU 0을 pCPU 2에

NUMA: NUMA 시스템에서 VM의 메모리와 CPU를 같은 노드에.

12.2 메모리

Huge Pages: 2 MB, 1 GB pages. TLB 압박 감소.

KSM (Kernel Samepage Merging): 같은 페이지를 공유해 메모리 절약. 밀도 높은 환경에.

Memory Ballooning: virtio-balloon으로 게스트 메모리 동적 회수.

12.3 I/O

VirtIO + Multi-queue: 여러 큐로 병렬 처리.

IOThreads: QEMU에 별도 I/O 스레드. 메인 스레드 차단 방지.

O_DIRECT + Native AIO: 디스크 캐시 우회 + 비동기 I/O.

12.4 네트워크

Vhost-net: 커널 모드 VirtIO 백엔드.

SR-IOV: PCIe 디바이스를 여러 "VF"(가상 function)으로 분할. 각 VF를 VM에 직접 할당. 하드웨어 가상화.

DPDK: User space 네트워크 처리. VM 내부에서 더 빠른 성능.

12.5 PCIe Passthrough

특정 PCIe 디바이스를 VM에 완전히 할당. 호스트는 접근 불가.

VT-d (Intel IOMMU)가 필요. GPU 가상화, 고성능 NIC에 사용.


13. Live Migration

13.1 아이디어

실행 중인 VM을 다른 호스트로 이동. 다운타임 없이.

13.2 Pre-copy Migration

  1. VM 메모리 전체를 새 호스트로 복사.
  2. VM은 계속 실행 — 수정된 페이지를 "dirty"로 표시.
  3. Dirty 페이지만 다시 복사.
  4. Dirty가 충분히 적어지면 VM 정지.
  5. 마지막 dirty 페이지 복사 + CPU 상태 전송.
  6. 새 호스트에서 VM 재개.
  7. 원본 호스트에서 VM 제거.

대부분 경우 수백 ms 다운타임. 유저는 눈치 못 챔.

13.3 Post-copy Migration

  1. VM CPU 상태만 먼저 전송.
  2. 새 호스트에서 바로 재개.
  3. 메모리 페이지는 필요 시 원본 호스트에서 가져옴 (page fault).
  4. 모든 메모리가 transfer 되면 완료.

더 짧은 다운타임이지만 페이지 fault가 느림.

13.4 실전 사용

  • VMware vMotion: 엔터프라이즈 표준.
  • KVM + libvirt: virsh migrate.
  • Google GCP: "Live Migration for VMs" — hardware maintenance 중 무중단.
  • AWS: EC2 일부 인스턴스 타입.

13.5 제약

  • 네트워크 대역폭 필요 (VM memory 크기에 비례).
  • Shared storage 필수 (또는 storage migration도).
  • CPU 호환성: 게스트가 사용한 CPU 기능을 새 호스트도 지원해야.

14. 보안 고려사항

14.1 Side Channel Attacks

Spectre, Meltdown (2018): CPU의 투기적 실행을 악용해 다른 VM의 메모리 읽기.

하이퍼바이저도 완전히 막기 어려움. 완화책:

  • KPTI (Kernel Page Table Isolation).
  • IBRS, STIBP: 분기 예측 제어.
  • Cache flush on VMExit.

성능 손실 10-30%.

14.2 L1 Terminal Fault (Foreshadow)

Intel CPU 버그. L1 캐시로 다른 VM 메모리 leak.

완화: L1 flush on VMExit.

14.3 Rowhammer

DRAM 셀이 인접 셀에 영향 → 비트 flip. VM이 다른 VM의 메모리를 수정 가능.

완화: ECC 메모리, targeted refresh.

14.4 Escape

VM Escape: 게스트에서 하이퍼바이저로 breakout. 드물지만 치명적.

사례:

  • VENOM (2015): QEMU floppy 에뮬레이션 버그.
  • LibQEMU 버그: 여러 번.

대응:

  • Rust 재작성 (Firecracker).
  • Seccomp 제한.
  • IOMMU 사용.
  • 최소 디바이스 지원.

15. 클라우드의 가상화

15.1 AWS EC2

  • Xen (2006-2017): 초기.
  • Nitro (2017+): KVM 기반 + SmartNIC.
    • Nitro Controller: 관리.
    • Nitro Cards: 네트워크, 스토리지, 보안.
    • Nitro Hypervisor: 최소화된 하이퍼바이저.

Bare metal (i3.metal 등): 고객이 하이퍼바이저 없이 하드웨어 직접 제어. Nitro가 여전히 인프라 관리.

15.2 Google Cloud

  • KVM 기반.
  • Live Migration: 유지보수 시 VM 이동 → 다운타임 없음.
  • Shielded VMs: Secure Boot, vTPM 지원.

15.3 Azure

  • Hyper-V 기반. Microsoft 자체 하이퍼바이저.
  • Nested virtualization 지원.

15.4 Hypervisor의 스케일

각 클라우드 provider가 수백만 VM을 운영. 하이퍼바이저 효율이 인프라 비용에 직접 영향. 1% 오버헤드 = 수억 달러.

15.5 Serverless

  • AWS Lambda: Firecracker microVM.
  • Google Cloud Run: gVisor + ?
  • Azure Functions: 컨테이너 + Hyper-V 격리 옵션.

하이퍼바이저가 더 가벼워지며 서버리스가 가능해졌다.


16. 학습 리소스

:

  • "Virtualization Essentials" — Matthew Portnoy.
  • "Systems Performance" — Brendan Gregg. 가상화 섹션.

논문:

  • Popek & Goldberg, "Formal Requirements for Virtualizable Third Generation Architectures" (1974).
  • VMware 논문: "A Comparison of Software and Hardware Techniques for x86 Virtualization" (2006).
  • Xen 논문: "Xen and the Art of Virtualization" (2003).
  • Firecracker 논문: "Firecracker: Lightweight Virtualization for Serverless Applications" (2020).

공식 문서:

  • Intel VMX 문서 (Software Developer's Manual Volume 3).
  • KVM API 문서 (Documentation/virt/kvm/api.rst).
  • libvirt 튜토리얼.

실습:

  • QEMU + KVM 직접 써보기.
  • Firecracker quickstart.
  • kvmtool: 간단한 KVM client.

17. 요약 — 한 장 정리

┌─────────────────────────────────────────────────────┐
Virtualization Cheat Sheet├─────────────────────────────────────────────────────┤
│ 역사:IBM CP-40 (1967)VMware (1999) - x86 binary translation            │
Xen (2003) - paravirtualization                   │
Intel VT-x / AMD-V (2005-2006) - HW support       │
KVM (2007) - Linux 내부                            │
Firecracker (2018) - microVM                       │
│                                                       │
Type 1 vs 2:Type 1: bare metal (ESXi, Xen, Hyper-V)Type 2: hosted (VirtualBox, VMware Workstation)KVM: "Type 1.5" (Linux이 하이퍼바이저)│                                                       │
│ x86 난제:17 sensitive non-privileged instructions          │
VMware: binary translation                         │
Xen: paravirtualization                            │
Intel/AMD: 하드웨어 지원 (VT-x/AMD-V)│                                                       │
VT-x / AMD-V:VMX root / non-root modes                          │
VMCS (Virtual Machine Control Structure)VMLAUNCH/VMRESUMEVMExitExit reasons: CPUID, I/O, EPT violation 등         │
│                                                       │
│ 메모리 가상화:GVAGPAHPAShadow page tables (초기)EPT/NPT (하드웨어 중첩 변환)Huge pages로 TLB 압박 감소                         │
│                                                       │
KVM:Linux 커널 모듈                                     │
VM = 프로세스, VCPU = 스레드                       │
/dev/kvm ioctl APIQEMU와 함께 사용                                    │
│                                                       │
QEMU:│   원래 user-mode 에뮬레이터                           │
KVM + QEMU = 완전한 VMDevice emulation (VirtIO, NIC, disk)│                                                       │
VirtIO:Paravirtualized devices                            │
Shared memory ring buffer                          │
│   net, blk, scsi, console, balloon 등               │
Vhost (커널 모드)│                                                       │
Xen:Type 1, 독립 하이퍼바이저                            │
Dom0 (특권) + DomU (게스트)Split driver (frontend/backend)│                                                       │
Firecracker:AWS 2018, Rust 기반 microVM                        │
125 ms boot, 5 MB overhead                         │
Minimal devices, seccomp                           │
KVM 재사용                                          │
Lambda/Fargate 기반                                 │
│                                                       │
│ 격리 비교:Container: 빠름, 약한 격리                           │
VM: 느림, 강한 격리                                  │
│   gVisor: user-space kernel                          │
Kata: VM 격리 + 컨테이너 UXFirecracker: 빠른 VM│                                                       │
│ 최적화:CPU pinning, NUMAHuge pages, KSMVhost-net, SR-IOV, DPDKPCIe passthrough (IOMMU)│                                                       │
Live Migration:Pre-copy (dirty tracking)Post-copy (on-demand page fault)Shared storage 또는 storage migration              │
│                                                       │
│ 보안:Spectre/Meltdown 완화                              │
VM Escape 방어                                      │
Seccomp, Rust 재작성                               │
Nitro 같은 SmartNIC 오프로드                        │
└─────────────────────────────────────────────────────┘

18. 퀴즈

Q1. x86이 1974년 Popek-Goldberg 조건을 위반한 이유는?

A. 17개의 sensitive non-privileged instructions. Popek-Goldberg는 "모든 시스템 상태에 영향을 주는 명령(sensitive)은 반드시 privileged여야 한다"고 요구했다 — 그래야 ring 3의 게스트가 이를 시도하면 trap이 발생해 하이퍼바이저로 제어가 넘어간다. 하지만 x86은 POPF(EFLAGS restore), LAR(segment descriptor read), SGDT(global descriptor table read) 등 17개 명령이 ring 3에서도 trap 없이 조용히 실패한다. 예: 게스트가 POPF로 IF(interrupt flag)를 끄려 해도 ring 3에선 무시됨 → 게스트는 성공했다고 믿지만 실제로는 안 됨 → 동작 오류. 30년간 "x86은 가상화 불가능"이라는 통념의 기원. VMware가 1999년 binary translation으로 극복 — 실행 전에 문제 명령을 찾아 trap-inducing 명령으로 교체. 2005년 Intel VT-x가 하드웨어 수준에서 해결했지만 그 이전 6년간 VMware의 공학적 성취는 전설로 남는다.

Q2. KVM이 Xen이나 VMware와 다른 결정적 디자인 포인트는?

A. Linux 커널 자체를 하이퍼바이저로 만든 것. Xen(2003)은 독립적 마이크로커널 하이퍼바이저로 설계됐다 — 자체 스케줄러, 메모리 관리, I/O 관리. Dom0라는 특권 VM이 드라이버를 담당. VMware ESXi도 유사하게 자체 커널(VMkernel)을 가진다. KVM(2007)의 Avi Kivity가 한 결정: "별도 하이퍼바이저 OS를 쓰지 말고, Linux가 곧 하이퍼바이저다". KVM은 커널 모듈(kvm.ko)일 뿐이고, Linux의 스케줄러/메모리 관리/네트워크가 그대로 하이퍼바이저 역할을 한다. VM은 일반 Linux 프로세스이고 VCPU는 일반 스레드. ps aux로 qemu 프로세스가 VM으로 보인다. 이 선택이 KVM이 Xen을 이긴 이유: (1) Linux의 모든 기능 재사용, (2) 관리 도구 호환, (3) 커뮤니티 지원, (4) 성능 튜닝이 Linux 성능 튜닝과 같음. AWS가 Xen → Nitro(KVM)로 전환한 것도 이 철학의 승리. "재발명보다 재사용"이 시스템 엔지니어링의 원칙.

Q3. EPT (Extended Page Tables)가 Shadow Page Tables를 대체한 이유는?

A. 게스트 page table 수정마다 VMExit 유발하는 오버헤드 제거. Shadow page tables (초기 방법): 하이퍼바이저가 게스트의 페이지 테이블을 복사본으로 관리 — GVA → HPA 직접 매핑. 게스트가 자기 페이지 테이블을 수정하면 하이퍼바이저가 trap해서 shadow를 업데이트. 문제: (1) 매 수정마다 VMExit → 수백 cycle 낭비, (2) 페이지 폴트가 두 번(게스트 + 하이퍼바이저) 일어남, (3) 메모리 오버헤드 커짐. EPT/NPT (2007-2008): 하드웨어 MMU가 두 단계 주소 변환을 직접 수행 — GVA → (게스트 페이지 테이블) → GPA → (EPT) → HPA. 게스트가 자기 페이지 테이블을 자유롭게 수정할 수 있음(하이퍼바이저 개입 없음). 결과: 가상화 오버헤드가 10-30%에서 5% 이하로 감소. Huge pages와 결합하면 거의 native 성능. 2025년 모든 주요 클라우드가 EPT/NPT 필수. "하드웨어가 소프트웨어 복잡도를 흡수"의 전형적 사례.

Q4. VirtIO가 전체 디바이스 에뮬레이션보다 10배 빠른 이유는?

A. VMExit 횟수 최소화와 배치 처리. 전체 에뮬레이션(예: e1000 NIC): 게스트가 실제 하드웨어인 것처럼 MMIO 레지스터를 읽고 씀 → 매 접근이 VMExit → QEMU가 trap을 받아 해석 → 에뮬레이션 → 게스트 재개. 한 패킷 전송에 수십 번의 VMExit 가능. 각 VMExit는 ~500 ns + QEMU 처리 시간. VirtIO: 게스트가 "가상 환경임을 알고" 하이퍼바이저와 협력. 공유 메모리 ring buffer로 직접 통신 — 게스트가 request를 ring에 씀(VMExit 없음), 마지막에 한 번 "kick"(한 번의 VMExit) → 하이퍼바이저가 여러 요청을 배치로 처리. 한 번의 VMExit로 수십 패킷 처리 가능. 결과: VMExit 횟수 10-100배 감소, CPU 오버헤드 대폭 감소. 대가: 게스트 OS에 virtio 드라이버 필요(Linux 내장, Windows는 별도 설치). 현대 모든 VM(KVM, Xen, Hyper-V)이 VirtIO 또는 유사 기법 사용. "공유 메모리 + 배치"가 가상화 성능 최적화의 기본 패턴.

Q5. Firecracker가 125ms boot을 달성한 방법은?

A. 극도로 단순한 디바이스 모델과 최소화된 실행 경로. AWS Lambda 요구: cold start 125ms 이내. 전통 VM(수 초 boot)으로 불가능. Firecracker의 접근: (1) QEMU 제거: QEMU는 수백 개 디바이스를 에뮬레이션(PCI, USB, VGA, 사운드, ...). Firecracker는 PCI도 없음 — MMIO-only + virtio-net, virtio-blk, virtio-vsock, virtio-balloon만. ~50,000 줄의 Rust로 QEMU(수백만 줄)를 대체. (2) BIOS 없음: 보통 BIOS → bootloader → kernel 순서지만 Firecracker는 KVM이 커널을 직접 로드 (kexec 스타일). (3) Minimal kernel: Linux 커널을 Lambda 용도로 스트립다운. 불필요한 드라이버/기능 제거 → 더 빠른 초기화. (4) Rust 안전성: C 대신 Rust로 써서 공격 표면 감소 — seccomp로 더 제한 가능. (5) API-driven: CLI 없이 REST API로 VM 제어. 오버헤드 감소. 결과: 125ms boot, 5 MB 메모리 오버헤드. 이것이 Lambda의 "서버리스" UX를 가능하게 한다 — 요청이 올 때마다 새 VM을 띄워도 사용자가 체감 못함. AWS Fargate, Kata Containers, gVisor의 일부 경로도 Firecracker 채택. 오픈소스라 누구나 사용 가능. "필요한 것만 남기고 나머지는 제거"라는 원칙의 극단적 적용.

Q6. Live Migration의 "pre-copy"가 작동하는 원리는?

A. Dirty page tracking으로 반복 복사 + 최종 stop-and-copy. 목표: 실행 중인 VM을 다른 호스트로 옮기되 수백 ms 다운타임만 허용. 알고리즘: (1) Round 0: VM의 전체 메모리를 원본→대상 호스트로 복사. VM은 계속 실행. (2) 동시에 EPT의 dirty bit로 수정된 페이지 추적. (3) Round 1: Round 0 중 dirty된 페이지들을 다시 복사. VM은 여전히 실행 — 또 dirty가 생김. (4) Round 2, 3, ...: dirty가 충분히 작아질 때까지 반복. 전체 메모리의 ~1% 수준. (5) Stop phase: VM 정지, 마지막 dirty 페이지 + CPU 상태 + device state 전송. (6) 대상 호스트에서 재개. 전체 다운타임: 일반적으로 100-500 ms. 사용자는 TCP 타임아웃 없이 접속 유지. 문제: (a) dirty rate가 너무 높으면(VM이 memory-heavy writer) 수렴 실패 → fallback은 post-copy 또는 강제 stop. (b) 네트워크 대역폭 필요. (c) 양 호스트가 같은 storage 보거나 storage migration 병행. 응용: VMware vMotion, GCP live migration(하드웨어 유지보수 중 무중단), KVM virsh migrate. "데이터가 움직이는 동안 계산이 계속된다"는 멋진 엔지니어링 성취.

Q7. gVisor, Kata Containers, Firecracker는 무엇을 해결하려는가?

A. "컨테이너의 속도와 VM의 격리"라는 딜레마. 기본 Linux 컨테이너(runc + namespaces + cgroups)는 커널 공유: 한 컨테이너의 커널 exploit이 다른 컨테이너로 번짐. 멀티테넌트(여러 고객) 환경에서 위험. 전통 VM은 완벽한 격리지만 수 초 boot + 100+ MB overhead. 세 가지 솔루션: (1) gVisor (Google): user-space에서 Linux kernel을 흉내내는 application kernel. 컨테이너의 syscall을 가로채 gVisor가 처리 → 실제 host kernel 호출 최소화. 장점: 컨테이너 UX, 강한 보안. 단점: 성능 페널티(~20%), 일부 syscall 미지원. GKE, App Engine, Cloud Run. (2) Kata Containers: 각 컨테이너를 자체 VM으로 실행. OCI 호환(docker run 그대로), 내부는 경량 VM. 백엔드로 QEMU/Cloud Hypervisor/Firecracker. 장점: 완벽한 커널 격리, 컨테이너 API. 단점: VM 오버헤드(줄었지만 존재). (3) Firecracker (AWS): microVM manager, Kata의 일반적 백엔드. 125ms boot으로 "실질적으로 컨테이너만큼 빠름". 셋 모두 "실행 모델은 컨테이너, 격리는 VM급" 목표 공유. 2025년 멀티테넌트 serverless/FaaS 플랫폼의 사실상 표준 접근.


이 글이 도움이 됐다면 다음 포스트도 확인해 보세요:

  • "Container Internals Deep Dive" — namespaces, cgroups, runc의 대체 격리.
  • "Linux Virtual Memory Deep Dive" — EPT가 기반하는 MMU 기법.
  • "eBPF Deep Dive" — 또 다른 커널 가상화 기술.
  • "Docker BuildKit Deep Dive" — 컨테이너 이미지의 실제 구조.