도커 환경에 VM 설치하기: QEMU/KVM으로 가상머신 실행하는 방법

반응형

도커 환경에 VM 설치하기: QEMU/KVM으로 가상머신 실행하는 방법

도커 컨테이너 안에서 Ubuntu 가상머신을 실행하는 일반적인 예제를 설명한다. 구성의 핵심은 도커가 VM을 만드는 것이 아니라, 컨테이너에서 QEMU를 실행하고 호스트의 KVM 장치를 전달하는 것이다.

1. 도커 컨테이너와 VM의 차이

도커 컨테이너는 호스트의 커널을 공유한다. 반면 VM은 자체 운영체제와 커널을 실행한다. 따라서 일반적인 Ubuntu 컨테이너를 실행하는 것만으로 Ubuntu VM이 만들어지지는 않는다. VM이 필요하면 QEMU/KVM 같은 가상화 프로그램을 사용해야 한다. :chatgpt-content-reference{index="0"}

도커에서 VM을 실행하는 구성 요소
구성 요소역할
Linux 호스트CPU 가상화 기능과 /dev/kvm 제공
Docker 컨테이너QEMU 실행 환경 제공
QEMU/KVM실제 가상머신 실행
ISO 및 가상 디스크게스트 OS 설치 파일과 영구 저장 공간

2. 설치 전 확인 사항

아래 예제는 Docker Engine이 설치된 Linux 호스트를 기준으로 한다. 호스트 자체가 VMware나 다른 VM이라면 중첩 가상화가 활성화되어 있어야 /dev/kvm을 사용할 수 있다. Docker Desktop을 쓰는 Windows PC에서는 도커 엔진이 별도 Linux VM 안에서 실행되므로 장치 접근 구성이 다르다. :chatgpt-content-reference{index="1"}

# Linux 호스트에서 실행
docker --version
ls -l /dev/kvm
grep -E 'vmx|svm' /proc/cpuinfo | head

/dev/kvm이 보이지 않으면 먼저 BIOS/UEFI의 가상화 기능, 호스트의 KVM 모듈, 상위 하이퍼바이저의 중첩 가상화 설정을 확인한다. 이 장치가 없을 때 QEMU의 소프트웨어 에뮬레이션으로 실행하는 방법도 있지만 속도가 크게 떨어질 수 있다.

3. 작업 디렉터리 준비

예제에서는 Ubuntu Server ISO를 사용한다. 공식 사이트에서 받은 ISO 파일을 iso/ubuntu-server.iso라는 이름으로 배치한다. 다운로드한 버전에 따라 설치 화면과 요구 자원은 달라질 수 있다.

mkdir -p docker-vm/iso docker-vm/vm-data
cd docker-vm

# 이 위치에 Ubuntu Server ISO를 복사
ls -lh iso/ubuntu-server.iso

가상 디스크는 vm-data 디렉터리에 저장한다. 컨테이너를 다시 만들더라도 이 디렉터리를 유지하면 VM 디스크를 재사용할 수 있다. :chatgpt-content-reference{index="2"}

4. Dockerfile 작성

작업 디렉터리에 Dockerfile을 만든다. 예제는 Ubuntu 기반 컨테이너에 QEMU를 설치한다.

FROM ubuntu:24.04

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
       qemu-system-x86 qemu-utils \
    && rm -rf /var/lib/apt/lists/*

COPY start-vm.sh /usr/local/bin/start-vm.sh
RUN chmod +x /usr/local/bin/start-vm.sh

ENTRYPOINT ["/usr/local/bin/start-vm.sh"]

5. VM 시작 스크립트 작성

같은 위치에 start-vm.sh를 만든다. 처음 실행할 때 40GB 가상 디스크를 생성하고, ISO가 있으면 설치 매체로 연결한다. VM은 VNC 포트로 설치 화면을 제공하며, 호스트의 2222 포트를 게스트의 SSH 22번 포트로 전달한다.

#!/usr/bin/env bash
set -euo pipefail

DISK=/vm/ubuntu.qcow2
ISO=/iso/ubuntu-server.iso

if [ ! -f "$DISK" ]; then
  qemu-img create -f qcow2 "$DISK" 40G
fi

if [ -f "$ISO" ]; then
  exec qemu-system-x86_64 \
    -enable-kvm \
    -machine q35 \
    -cpu host \
    -smp 2 \
    -m 4096 \
    -drive file="$DISK",format=qcow2,if=virtio \
    -cdrom "$ISO" \
    -boot order=dc \
    -display vnc=0.0.0.0:0 \
    -netdev user,id=net0,hostfwd=tcp::2222-:22 \
    -device virtio-net-pci,netdev=net0
fi

exec qemu-system-x86_64 \
  -enable-kvm \
  -machine q35 \
  -cpu host \
  -smp 2 \
  -m 4096 \
  -drive file="$DISK",format=qcow2,if=virtio \
  -boot order=c \
  -display vnc=0.0.0.0:0 \
  -netdev user,id=net0,hostfwd=tcp::2222-:22 \
  -device virtio-net-pci,netdev=net0

설치 완료 후에는 ISO 파일을 iso/ 밖으로 옮기고 컨테이너를 재시작한다. 그러면 스크립트가 설치된 가상 디스크로 부팅한다. ISO를 계속 연결해 두면 ISO로 다시 부팅할 수 있다.

6. Docker Compose 파일 작성

compose.yaml을 작성한다. Docker의 devices 설정으로 호스트의 /dev/kvm을 컨테이너에 전달한다. 전체 호스트 권한을 부여하는 privileged: true는 이 예제에 사용하지 않는다. :chatgpt-content-reference{index="3"}

services:
  ubuntu-vm:
    build: .
    container_name: ubuntu-vm
    restart: unless-stopped
    devices:
      - /dev/kvm:/dev/kvm
    volumes:
      - ./vm-data:/vm
      - ./iso:/iso:ro
    ports:
      - "127.0.0.1:5900:5900"
      - "127.0.0.1:2222:2222"

VNC와 SSH 포트는 우선 호스트의 127.0.0.1에만 공개한다. 다른 PC에서 접속해야 한다면 SSH 터널 등을 이용하거나, 방화벽과 접근 범위를 정한 뒤 포트 바인딩을 조정한다. VNC 자체를 외부 네트워크에 그대로 공개하지 않는 것이 좋다.

7. VM 실행 및 OS 설치

docker compose up -d --build
docker compose ps
docker compose logs -f ubuntu-vm

호스트 PC에서 VNC 클라이언트로 127.0.0.1:5900에 접속하여 Ubuntu Server 설치를 진행한다. 설치 중 SSH 서버를 활성화하면 설치 후 포트 전달을 통해 접속할 수 있다.

# 설치 완료 후 호스트에서 실행
mv iso/ubuntu-server.iso iso/ubuntu-server.iso.installed
docker compose restart ubuntu-vm

# Ubuntu 설치 중 만든 계정으로 접속
ssh -p 2222 사용자명@127.0.0.1

iso/에 설치 ISO가 더 이상 없으므로 이후에는 가상 디스크로 부팅한다. SSH 접속은 게스트 OS에서 SSH 서버가 실행 중이고 방화벽이 허용할 때 가능하다.

8. 운영 시 알아둘 점

  • 자원 사용: 예제 VM에는 2 vCPU와 4GB 메모리를 지정했다. 호스트의 다른 컨테이너와 VM 사용량을 고려해 조정한다.
  • 백업: vm-data/ubuntu.qcow2가 VM의 디스크다. 일관된 백업을 위해 VM을 정상 종료한 뒤 복사하거나 VM 전용 백업 절차를 사용한다.
  • 네트워크: 예제의 QEMU 사용자 모드 네트워크는 게스트의 외부 통신과 지정한 포트 전달에 적합하다. 게스트가 사내망에서 독립 IP를 가져야 한다면 브리지 네트워크와 별도 네트워크 설계가 필요하다.
  • 관리: 여러 VM을 장기간 운영하거나 스냅샷, 네트워크, 스토리지 관리를 체계화해야 한다면 호스트에 KVM/libvirt를 직접 구성하는 방식도 검토한다.

자주 발생하는 문제

/dev/kvm이 없다

호스트의 가상화 지원과 KVM 모듈을 확인한다. Docker Engine 자체가 VM 안에서 실행된다면 상위 가상화 환경의 중첩 가상화 설정도 확인한다.

컨테이너는 실행되지만 VNC에 접속할 수 없다

docker compose ps와 docker compose logs ubuntu-vm을 확인한다. 예제의 포트는 호스트 로컬에만 열려 있으므로 다른 PC에서 호스트 IP의 5900번 포트에 직접 접속할 수 없다.

설치 후에도 설치 화면이 다시 나온다

ISO가 iso/ubuntu-server.iso 경로에 남아 있는지 확인하고, 해당 파일을 다른 위치로 옮긴 뒤 컨테이너를 재시작한다.

정리

도커 환경에서 VM을 실행하는 기본 흐름은 Linux 호스트의 KVM 확인 → QEMU 컨테이너 구성 → ISO와 가상 디스크 연결 → VNC로 OS 설치 → ISO 분리 후 디스크 부팅이다. 이 방식은 테스트용 단일 VM에 적합하다. 운영 환경에서 VM을 여러 대 관리한다면 호스트 기반 가상화 플랫폼의 관리 기능까지 함께 검토하는 것이 좋다.

참고 문서

반응형