[가상화] QEMU로 Ubuntu VM에 커스텀 커널 올리기
QEMU를 사용하여 Ubuntu VM에 커스텀 리눅스 커널을 설치하고 부팅하는 방법
QEMU로 Ubuntu VM에 커스텀 커널 올리기
가상화(Virtualization)를 하는 방법에는 하이퍼바이저를 통한 서버 가상화와 Docker로 익숙한 OS레벨의 컨테이너 가상화가 있습니다. 최근에는 컨테이너 가상화의 경량성과 이식성을 통해, 쿠버네티스를 필두로 한 마이크로서비스 운영 방식이 표준으로 자리잡았습니다. 하지만, 컨테이너 환경은 커널을 공유한다는 특징으로 인해 유연성이 떨어지고, 격리(Compartmentalization)의 측면에서 보안성이 떨어집니다. 따라서 컨테이너를 맹목적으로 선호하기보다는 VM도 다룰 줄 아는 것이 좋은 방향이라고 생각합니다.
다만, QEMU를 통해 Ubuntu VM을 실행하고 커스텀 커널을 올리는 것은 실제 서비스 환경에서 VM을 운영하기 위한 것은 아닙니다. VM을 실행하는 것보다 커스텀 커널을 올리는 과정이 더 복잡하며, 이는 커널 개발을 위한 환경 설정을 하는 과정이라고 생각하면 될 것 같습니다. 호스트 머신의 성능을 통해 커널을 빌드하고, Ubuntu라는 mainstream 배포판에서 해당 커널을 테스트하는 환경을 설정하는 방법을 알아보도록 하겠습니다.
QEMU
이 글에서는 QEMU(https://www.qemu.org)를 통해 VM을 실행하는 방법을 설명합니다.
QEMU란 오픈소스 가상화 솔루션 중 하나로, 하드웨어의 기능을 통해 리눅스 커널을 고성능 하이퍼바이저로 사용하는 KVM을 지원합니다. 또한 이 글에서 다루지는 않지만, 에뮬레이션(예를 들어, x86 시스템에서 arm, risc-v 아키텍쳐 소프트웨어를 실행)기능도 제공합니다.
UTM, Parallels와 같은 Type-2 하이퍼바이저를 주로 사용한 경험이 있을 테지만, 좀 더 네이티브하고, 유연성이 높은 QEMU의 사용법을 익혀 두는 것을 추천합니다. 실제로 UTM의 백엔드는 QEMU로 이루어져 있습니다.
사전 준비
x86 시스템, Rocky Linux 9.5 운영체제를 기준으로 설명합니다.
30GB 이상의 여유공간이 필요합니다.
필요한 패키지 설치
1
2
3
4
5
# Rocky Linux 기준
sudo dnf update
sudo dnf groupinstall "Development Tools"
sudo dnf install qemu-kvm qemu-img libvirt virt-manager
sudo dnf install elfutils-libelf-devel openssl-devel ncurses-devel flex bison
QEMU 설치 확인
1
qemu-kvm --version
1. Ubuntu VM 생성
작업 디렉토리 생성
1
2
mkdir qemu-ubuntu
cd qemu-ubuntu
필수는 아니지만, 편의를 위해 작업 디렉토리를 생성합니다.
VM 디스크 이미지 생성
1
2
# 20GB 크기의 가상 디스크 생성
qemu-img create -f qcow2 ubuntu-vm.qcow2 20G
qemu-img
: QEMU에서 사용하는 가상 디스크 이미지를 생성/관리하는 도구입니다.f qcow2
: 디스크 이미지의 포맷을 지정합니다. qcow2는 ‘QEMU Copy-On-Write 2’의 약자로, QEMU 가상화 환경에서 가장 널리 사용되는 포맷입니다. 디스크의 최대 크기를 지정하면 가변적으로 용량이 조절됩니다.
Ubuntu ISO 다운로드
1
2
# Ubuntu 24.04 LTS 다운로드
wget https://releases.ubuntu.com/noble/ubuntu-24.04.2-live-server-amd64.iso
OS 설치
1
2
3
4
5
6
qemu-kvm \
-m 4G \
-smp 4 \
-cdrom ubuntu-24.04.2-live-server-amd64.iso \
-drive file=ubuntu-vm.qcow2,if=virtio,format=qcow2 \
-nographic
linux 커맨드 끝에
console=ttyS0
옵션을 추가합니다.
ctrl + x
를 입력하여 부팅합니다.
설치 과정 중 옵션을 모두 기본값으로 설정하나, storage 설정에서 LVM 옵션을 해제합니다.
설치가 완료되면 ctrl + a
이후 x
를 입력하여 QEMU에서 빠져나옵니다.
2. 리눅스 커스텀 빌드
리눅스 리포지토리 클론
1
git clone --depth=1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
커밋 로그 등 깃 히스토리의 용량이 원본 소스의 용량보다 많기 때문에 --depth=1
옵션을 통해 소스만 클론합니다.
커널 빌드
1
2
3
4
5
6
cd linux
make defconfig # .config 생성
make menuconfig # GUI를 통해 configuration 수정
make -j$(nproc)
리눅스 커널의 빌드 옵션을 수정하고, 직접 빌드합니다.
이 단계에서 새로운 시스템 콜을 추가하거나, 스케줄러를 커스텀하는 등 커널 개발의 단계를 진행할 수 있습니다.
모듈 추출
1
2
mkdir ../tmp_modules
make INSTALL_MOD_PATH=../tmp_modules modules_install
컴파일된 커널 모듈을 VM에 이식하기 위해 임시 디렉토리로 추출합니다.
커널 버전 확인 및 환경변수 설정
1
export KERNEL_VERSION=$(make kernelrelease)
이후 작업을 위해 현재 빌드한 커널의 버전을 환경변수로 export 합니다.
1
cd ..
작업 디렉토리로 나갑니다.
3. Ubuntu에 커널 이식
NBD(네트워크 블록 장치) 커널 모듈을 로드
1
sudo modprobe nbd max_part=8
qcow2 이미지를 /dev/nbd0 장치에 연결
1
sudo qemu-nbd --connect=/dev/nbd0 ubuntu-vm.qcow2
qcow2 디스크 이미지 파일을 호스트에서 직접 다룰 수 있도록 설정합니다.
마운트할 디렉토리 생성
1
sudo mkdir -p /mnt/ubuntu
우분투 루트 파티션을 마운트
1
sudo mount /dev/nbd0p2 /mnt/ubuntu
연결된 가상 디스크의 루트 파티션을 호스트에 마운트합니다.
장치 넘버는 위 예시와 다를 수 있습니다. fdisk
명령어를 통해 정확한 장치명을 확인해야 합니다.
커널 이미지 및 모듈 복사
1
2
sudo cp linux/arch/x86/boot/bzImage /mnt/ubuntu/boot/vmlinuz-${KERNEL_VERSION}
sudo cp -r tmp_modules/lib/modules/${KERNEL_VERSION} /mnt/ubuntu/lib/modules
chroot 및 initrd 생성
1
2
3
4
5
6
sudo chroot /mnt/ubuntu
ls /lib/modules
update-initramfs -c -k ${KERNEL_VERSION}
exit
우분투 환경 안으로 들어가 새로운 커널에 맞는 initial ramdisk를 생성합니다.
해당 환경에서는 KERNEL_VERSION 환경변수가 설정되어있지 않으므로, ls /lib/modules
의 출력으로 나온 버전을 확인하여 명령어를 직접 입력합니다.
initrd 복사
1
sudo cp /mnt/ubuntu/boot/initrd.img-${KERNEL_VERSION} ./initrd-ubuntu.img
생성된 initrd를 qemu에서 사용하기 위해 현재 작업 디렉토리로 복사합니다.
마운트 해제 및 NBD 정리
1
2
sudo umount /mnt/ubuntu
sudo qemu-nbd --disconnect /dev/nbd0
4. VM 실행
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
sudo qemu-kvm \
-cpu host \
-m 4G \
-smp 4 \
-drive file=ubuntu-vm.qcow2,if=virtio,format=qcow2 \
-nographic \
-kernel linux/arch/x86/boot/bzImage \
-initrd initrd-ubuntu.img \
-append "root=/dev/vda2 console=ttyS0"
위 예시처럼 커맨드를 스크립트 파일로 저장해두고 사용하면 편리하게 VM을 실행할 수 있습니다.
커널 버전 확인
직접 빌드한 최신 버전의 커널이 적용된 것을 확인할 수 있습니다.
결론
지금까지 QEMU를 통해 Ubuntu VM을 실행하고 커스텀 커널을 올리는 과정을 살펴보았습니다. 처음에는 복잡하다고 생각할 수 있지만, 이 흐름 정도만 알고 있어도 Ubuntu 가상환경 구축은 도커 컨테이너로 하는 것 만큼 쉽게 할 수 있을 것이라 생각됩니다. 또, 앞으로 관심이 있다면 커널 코드를 보고 직접 수정해보는 것도 재미있는 과정이 될 것입니다.