가상 머신: QEMU-KVM

July 1, 2019

    QEMU-KVM

    리눅스의 표준적인 가상 머신 프로그램으로 QEMU이 있다. QEMU는 커널 레벨에서 하드웨어 가상화를 담당하는 KVM과 함께 작동해서 성능을 높일 수 있다. 아래에는 QEMU-KVM으로 가상 머신을 생성하고, 게스트 OS로 윈도우 10을 설치하고, 원격 데스크톱 연결 (RDP) 을 구성하고, 윈도우 10에 우분투 서버의 SMB 디렉토리를 연결하는 것을 다룬다.

    CPU 확인

    egrep -o '(vmx|svm)' /proc/cpuinfo

    홈 서버 컴퓨터의 CPU가 Intel VT 또는 AMD-V 가상화를 지원하는지 확인한다. vmx 또는 svm가 하이퍼스레딩 등의 가상 쓰레드를 포함한 코어 개수 만큼 출력되면 정상 작동하는 것이다. 아무것도 출력되지 않는다면 CPU에서는 지원하지만 메인보드 BIOS 설정에서 꺼져있는 경우도 있으므로 확인한다. CPU 가상화가 작동하지 않으면 가상 머신의 성능이 낮아진다.

    QEMU-KVM 설치

    우분투 서버에 가상 머신을 구축할 때 QEMU-KVM은 가상 머신 그 자체를 담당하게 하고, 가상 머신을 관리하고 운영하는 데에는 libvirt를 사용하는 것이 좋다. 가상 머신에 게스트 OS를 설치할 때에는 virt-install 툴을 이용한다. 이상의 모든 것은 서로 유기적으로 작동하게 만들어져 있다.

    sudo apt update
    sudo apt install qemu-kvm libvirt-bin virtinst
    

    패키지 저장소 정보를 갱신하고 필요한 패키지를 설치한다.

    kvm-ok

    KVM이 작동 중인지 확인한다. 아래와 같이 출력되는지 확인한다.
    INFO: /dev/kvm exists
    KVM acceleration can be used
     

    virsh list --all

    libvirt가 작동하는지 확인한다. 위 명령을 실행했을 때 아래처럼 출력되면 작동하는 것이다. (아직 생성한 가상 머신이 없으므로 빈 목록이 출력된다.)

    Id    Name                           State
    ----------------------------------------------------
    

     

    virsh net-list --all

    libvirt의 기본 네트워크 default가 작동하는지 확인한다. 위 명령을 실행했을 때 아래처럼 default가 출력되면 작동하는 것이다. 만일 아무 것도 출력되지 않으면 서버 컴퓨터를 한 번 재시작하고 확인해본다.

    Name                 State      Autostart     Persistent
    ----------------------------------------------------------
    default              active     yes           yes
    

    Guest OS – Windows 10 설치

    가상 머신을 생성하면서 동시에 게스트 OS로 윈도우 10을 설치한다.

    sudo cp win10.iso /var/lib/libvirt/boot/

    윈도우 10 설치 iso 이미지를 /var/lib/libvirt/boot 디렉토리에 넣는다. 본 글에서는 iso 파일 이름이 win10.iso 인 것으로 가정한다.

    wget -P /tmp --content-disposition "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.141-1/virtio-win-0.1.141.iso"
    

    가상 디스크와 가상 랜카드에 더 좋은 성능을 내는 VirtIO를 사용하기 위해서 VirtIO 드라이버가 담긴 iso 파일을 /tmp 디렉토리에 내려받는다. VirtIO 디스크는 SCSI 방식이며, VirtIO 랜카드는 10Gbps를 지원한다. 이 예시에 사용된 다운로드 주소는 이 글을 작성하는 현재의 최신 stable 버전의 주소이며, 다른 버전을 내려받으려면 https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/에서 찾을 수 있다.

    sudo mv /tmp/virtio-win-0.1.141.iso /var/lib/libvirt/images/

    /tmp 디렉토리에 내려받은 iso 파일을 /var/lib/libvirt/images/ 디렉토리에 옮긴다.

    sudo iptables -A INPUT -p tcp -m tcp --dport 5900 -j ACCEPT

    윈도우 10 설치 과정을 VNC로 접속해서 진행할 것인데, VNC 접속에 사용할 포트를 iptables 방화벽에서 열어준다. (기본적인 방화벽 세팅은 앞선 글 우분투 서버 기본 설정에서 완료했다.)

    sudo netfilter-persistent save
    sudo netfilter-persistent reload
    

    iptables 방화벽을 저장하고 리로드한다. (iptables-persistent는 앞선 글 우분투 서버 기본 설정에서 설치했다.)

    이제 가상 머신을 생성하고 게스트 OS를 설치하는 virt-install 명령을 실행하는데, virt-install은 아주 다양한 옵션을 제공하기 때문에 자신의 컴퓨터 하드웨어나 게스트 OS의 종류에 따라서 여러 가지 옵션을 선택할 수 있다. 현재 설치된 virt-install에 사용 가능한 옵션들은 man virt-install 명령을 사용하면 상세한 매뉴얼이 출력되므로 참고한다.

    sudo virt-install \
    --virt-type kvm \
    --name windows10 \
    --vcpus 2 \
    --cpu host-passthrough,cache.mode=passthrough \
    --memory 2048 \
    --os-variant win10 \
    --disk path=/var/lib/libvirt/boot/win10.iso,device=cdrom,bus=ide \
    --disk path=/var/lib/libvirt/images/virtio-win-0.1.141.iso,device=cdrom,bus=ide \
    --disk path=/var/lib/libvirt/images/win10.qcow2,format=qcow2,size=40,bus=virtio \
    --network network=default,model=virtio \
    --graphics vnc,port=5900,listen=0.0.0.0 \
    --sound default
    

    이 옵션들을 살펴보면 아래와 같다:

    --virt-type kvm
    KVM의 가상화를 이용한다.
    --name windows10
    이 가상 머신의 이름으로, 원하는 대로 지정한다.
    --vcpus 2
    가상 머신에 할당한 CPU 코어 개수이다.
    --cpu host-passthrough,cache.mode=passthrough
    실제 CPU의 정보를 그대로 사용하도록 한다. 홈 서버 컴퓨터에 직접 설치할 수 있는 종류의 OS를 게스트 OS로 설치할 때에만 가능한 옵션이다. 가상 머신이 CPU 정보를 생성하도록 하려면 이 줄을 삭제한다.
    --memory 2048
    RAM을 2GB 할당했다.
    --os-variant win10
    설치하는 게스트 OS의 종류로, 윈도우 10은 win10 / 윈도우 8.1은 win8.1 / 우분투 16.04는 ubuntu16.04 를 입력한다. 더 다양한 OS 종류는 우분투 서버에 libosinfo-bin을 설치하고 osinfo-query os 명령을 실행하면 확인할 수 있다.
    --disk path=/var/lib/libvirt/boot/win10.iso,device=cdrom,bus=ide
    앞서 준비한 윈도우 10 설치 iso 파일을 disk에 연결하고 cdrom으로 지정한다. 첫 번째 cdrom으로 부팅되므로 아래의 VirtIO 드라이버가 담긴 cdrom 보다 먼저 작성해야 한다.
    --disk path=/var/lib/libvirt/images/virtio-win-0.1.141.iso,device=cdrom,bus=ide
    앞서 준비한 VirtIO 드라이버가 담긴 iso 파일을 disk에 연결하고 cdrom으로 지정한다.
    --disk path=/var/lib/libvirt/images/win10.qcow2,format=qcow2,size=40,bus=virtio
    윈도우 10이 설치될 디스크를 win10.qcow2 이름의 qcow2 형식으로 생성하고, 40GB를 할당하고, VirtIO 방식을 사용한다.
    --network network=default,model=virtio
    libvirt의 기본 NAT 네트워크인 default 네트워크를 사용하고, VirtIO 랜카드를 사용한다.
    --graphics vnc,port=5900,listen=0.0.0.0
    화면을 출력할 방식으로 VNC를 사용하고, VNC 접속 포트를 앞서 iptables 방화벽에서 열어준 5900으로 지정하고, listen=0.0.0.0 옵션으로 내부 네트워크에서 접속할 수 있게 한다.
    --sound default
    가상 사운드 카드로 적절한 것이 자동 설치되도록 한다.

    그밖에 다른 옵션이 필요하면 man virt-install 명령의 매뉴얼을 참고해서 조정한다. 참고로 위의 명령은 한 줄로 작성하기에는 너무 길어서 \를 사용해서 여러 줄로 나눈 것으로, 전체를 한꺼번에 복사해서 PuTTY 등에 붙여넣으면 된다.

    위 명령을 실행하면 가상 머신이 실행되고 win10.iso 파일로 부팅한 상태가 된다. 이제 내부 네트워크에서 Real VNC Viewer 등의 VNC 클라이언트 프로그램으로 접속하면 윈도우 10 설치 화면을 볼 수 있다. VNC 접속 정보는 홈 서버 컴퓨터의 내부(사설) IP 주소와 위에서 지정한 VNC 접속 포트이다.

    윈도우 10 설치 과정 중에 드라이브를 찾을 수 없다는 메시지가 나오면 3가지 VirtIO 드라이버를 설치해준다. 드라이버 로드를 클릭하고, 찾아보기를 클릭하고, VirtIO 드라이버가 담겨있는 CD 드라이브를 더블클릭하면 CD의 내용물이 나타난다. 이 중에서 3가지 드라이버 Balloon, NetKVM, viostor 아래에 있는 w10 폴더에서, 윈도우 10이 64비트이면 amd64, 32비트이면 x86 폴더를 선택해서 드라이버를 설치한다. 한 번에 하나씩만 설치할 수 있으므로 설치 작업을 3번 진행한다. 이후 윈도우 10 설치 과정을 마무리하면 우분투 서버의 게스트 OS로 윈도우 10이 정상 작동하게 된다.

    CD 추출 / 삽입

    윈도우 10 설치를 위해서 삽입했던 CD를 추출하려면 윈도우 10의 파일 탐색기에서 CD 드라이브를 오른쪽 클릭하고 꺼내기를 선택하면 된다. 우분투 서버 상에서 CD를 추출하거나 삽입하려면 아래의 명령을 실행한다.

    virsh domblklist windows10

    먼저 가상 머신을 생성할 때 지정했던 이름인 windows10 가상 머신에 현재 장착되어 있는 디스크 목록을 확인한다.

    Target     Source
    ------------------------------------------------
    hda        /var/lib/libvirt/boot/win10.iso
    hdb        /var/lib/libvirt/images/virtio-win-0.1.141.iso
    vda        /var/lib/libvirt/images/win10.qcow2
    
    virsh change-media windows10 hda --eject

    위 목록에서 win10.iso CD를 추출하려면 이 명령어를 실행한다.

    virsh change-media windows10 hda /var/lib/libvirt/boot/win10.iso --insert

    반대로 CD를 삽입하려면 이런 형태의 명령을 실행하면 된다. 더 자세한 사항은 virsh change-media --help 명령을 실행해서 도움말을 참고한다.

    주요 명령어

    아래는 자주 쓰이는 명령어의 예시이며, virsh --helpman virsh 명령을 실행하면 아주 상세한 사용 방법을 확인할 수 있다.

    virsh list --all
    모든 가상 머신 목록 출력
    
    virsh start windows10
    windows10 이름의 가상 머신을 시작 (부팅)
    
    virsh shutdown windows10
    종료
    
    virsh reboot windows10
    재시작
    
    virsh reset windows10
    강제 재시작
    
    virsh suspend windows10
    중지
    
    virsh resume windows10
    재개
    
    virsh destroy windows10
    virsh undefine windows10
    가상 머신 삭제
    (디스크는 삭제되지 않으므로 직접 삭제해야 한다.)
    
    virsh autostart windows10
    홈 서버 컴퓨터가 시작되면 windows10 이름의 가상 머신을 자동으로 시작 (부팅)
    
    virsh autostart windows10 --disable
    autostart 해제
    

    스냅샷 명령어

    가상 머신의 현재 상태를 스냅샷으로 저장하고, 언제든지 저장해둔 스냅샷으로 가상 머신을 복원할 수 있다.

    virsh snapshot-create-as windows10 --name win10-snap-01
    windows10 이름의 가상 머신의 현재 상태를 win10-snap-01 이름의 스냅샷으로 저장
    
    virsh snapshot-list windows10
    스냅샷 목록 출력
    
    virsh snapshot-revert windows10 --snapshotname win10-snap-01
    스냅샷 win10-snap-01으로 windows10 이름의 가상 머신을 복원
    
    virsh snapshot-delete windows10 --snapshotname win10-snap-01
    스냅샷 win10-snap-01 삭제
    

    가상 머신 백업

    우분투 서버를 재설치하는 경우 등, 스냅샷이 아닌 가상 머신 자체를 백업해야 할 때는 아래의 절차를 따른다.

    echo '' > /백업할-경로/windows10.xml

    백업할 경로에 xml 파일을 하나 생성한다.

    virsh dumpxml windows10 > /백업할-경로/windows10.xml

    백업할 가상 머신의 설정을 앞서 생성한 xml 파일에 덤프한다. 빨간색 windows10은 백업할 가상 머신의 이름이다.

    sudo cp /var/lib/libvirt/images/win10.qcow2 /백업할-경로/

    가상 머신의 디스크를 백업할 경로에 복사한다.

    백업한 가상 머신의 복원 절차는 아래와 같다.

    sudo cp /백업한-경로/win10.qcow2 /var/lib/libvirt/images/

    백업한 가상 머신의 디스크를 /var/lib/libvirt/images/ 경로에 복사한다.

    virsh define --file /백업한-경로/windows10.xml

    덤프해서 백업했던 xml파일을 적용한다. 이후 autostart 지정 여부 및 아래에서 설명하는 가상 머신 IP 주소 고정, RDP 방화벽 설정 등을 다시 해주면 복원이 완료된다.

    가상 머신 구성 편집

    virsh edit windows10

    이 명령을 실행하면 windows10 이름의 가상 머신의 구성 내용이 xml 형태로 nano 편집기를 통해서 출력된다. libvirt 웹사이트의 매뉴얼을 참고해서 원하는 대로 편집할 수 있다. 가령 가상 머신에서 CD 드라이브를 없애고 싶다면 해당 CD 드라이브 부분을 찾아서 <disk type=’file’ device=’cdrom’> 부터 </disk> 까지 삭제하고 저장하면 된다. 가상 머신이 실행 중인 상태라면 종료한 다음에 시작하면 반영된다. (종료 대신에 재시작하면 반영이 안 된다.)

    가상 머신 IP 주소 고정

    원격 데스크톱 연결 (RDP) 을 원활하게 사용하려면 가상 머신에 부여되는 IP 주소를 고정할 필요가 있다.

    virsh dumpxml windows10 | grep 'mac address'

    이 명령으로 windows10 이름의 가상 머신의 MAC 어드레스를 확인한다.

    virsh net-update default add ip-dhcp-host "<host mac='52:54:00:00:00:00' name='windows10' ip='192.168.122.000' />" --live --config

    libvirt의 기본 네트워크인 default는 192.168.122.xxx 대역의 IP 주소를 사용한다. 확인한 MAC 어드레스와 192.168.122.2 ~ 192.168.122.254 중에서 고정 하기를 원하는 IP 주소를 넣고 위의 명령을 실행한다.

    가상 머신 종료 후 시작

    가상 머신이 실행 중이라면 먼저 종료 한 다음에 시작한다. 종료 대신에 재시작을 하면 반영이 안 된다.

    virsh net-edit default

    이 명령으로 default 네트워크의 구성 상태를 확인할 수 있다.

    RDP 방화벽 설정

    libvirt의 기본 네트워크인 default는 NAT 방식으로, 우분투 서버와 게스트 OS 사이의 데이터 전송이 가능하고, 게스트 OS에서 외부로 나오는 데이터 전송은 가능하지만, 외부에서 게스트 OS로 직접 들어가는 데이터 전송은 불가능하다.

    그래서 외부에서 게스트 OS에 원격 데스크톱 연결 (RDP) 로 접속하려면 데이터가 먼저 우분투 서버를 통해서 게스트 OS에 전달되도록 iptables 방화벽에 설정을 해야 한다.

    그런데 libvirt는 우분투 서버가 재시작 될 때 보안 상의 이유로 자신과 관련된 iptables 규칙을 차단하기 때문에 libvirt가 스스로 iptables 규칙을 생성하도록 설정해야 한다. 이에 libvirt의 후크 기능을 이용해서 가상 머신이 실행될 때 RDP 관련 방화벽 규칙을 iptables에 생성하고 가상 머신이 종료되면 iptables의 규칙을 삭제하는 스크립트를 적용한다.

    가상 머신 종료

    실행 중인 가상 머신이 있다면 먼저 종료한다.

    sudo nano /etc/libvirt/hooks/qemu

    후크 내용이 기록될 파일을 nano 편집기로 생성한다.

    #!/bin/bash
    
    GUEST_NAME=windows10
    GUEST_IP=192.168.122.000
    GUEST_PORT=3389
    HOST_PORT=3389
    
    if [ "${1}" = "$GUEST_NAME" ]; then
     if [ "${2}" = "stopped" ] || [ "${2}" = "reconnect" ]; then
      /sbin/iptables -D FORWARD -d $GUEST_IP/32 -m conntrack --ctstate NEW -j ACCEPT
      /sbin/iptables -t nat -D PREROUTING -p udp -m udp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
      /sbin/iptables -t nat -D PREROUTING -p tcp -m tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
     fi
     if [ "${2}" = "start" ] || [ "${2}" = "reconnect" ]; then
      /sbin/iptables -I FORWARD -d $GUEST_IP/32 -m conntrack --ctstate NEW -j ACCEPT
      /sbin/iptables -t nat -I PREROUTING -p udp -m udp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
      /sbin/iptables -t nat -I PREROUTING -p tcp -m tcp --dport $HOST_PORT -j DNAT --to $GUEST_IP:$GUEST_PORT
     fi
    fi
    

    빨간색 부분을 자신의 정보로 수정하는데, GUEST_NAME=에 가상 머신 이름을 입력하고, GUEST_IP=에 앞서 가상 머신에 고정한 IP 주소를 입력한다. GUEST_PORT=HOST_PORT=에 RDP에 사용되는 포트 번호를 기록하는데 특별히 바꾸지 않았다면 3389가 기본 포트이다. 그 아래의 내용은 가상 머신이 실행되면 RDP 접속에 필요한 규칙을 iptables 방화벽에 생성하고, 가상 머신이 종료되면 생성했던 규칙을 삭제하는 명령이다. 작성했으면 Ctrl키와 x키를 동시에 눌러서 nano 편집기를 빠져나오면서 저장한다.

    sudo chmod u+x /etc/libvirt/hooks/qemu

    작성한 파일에 실행 권한을 부여한다.

    sudo sh -c 'service libvirt-bin stop; service libvirt-bin start'

    libvirt를 재실행한다.

    라우터 (인터넷 공유기) 에서 RDP 포트가 홈 서버 컴퓨터로 향하도록 포워딩

    외부 네트워크에서의 RDP 접속을 허용하려면 RDP 포트 (위의 예시에서는 3389번 포트) 가 홈 서버 컴퓨터로 향하도록 라우터 (인터넷 공유기) 에서 포트 포워딩 설정을 한다. 포트 포워딩을 하지 않고 VPN으로 접속해서 이용할 수도 있다.

    이제 가상 머신을 시작하면 RDP 접속이 가능해진다. 내부 네트워크에서 홈 서버 컴퓨터의 내부(사설) IP 주소로 접속이 가능하고, 라우터 (인터넷 공유기) 에서 포트 포워딩을 했다면 홈 서버 컴퓨터의 공인 IP 주소 및 공인 IP 주소에 연결된 도메인으로도 모두 접속이 가능하다. 물론 원격 접속 대상인 윈도우 10에서 원격 데스크톱 기능을 활성화해야 하며, 위치는 설정시스템원격 데스크톱원격 데스크톱 활성화에 있다.

    SMB 디렉토리 연결

    앞선 글 네트워크 파일 공유: SMB를 참고해서 우분투 서버에 SMB 디렉토리를 생성하고 홈 네트워크의 중앙 파일 저장소로 사용한다면 이 디렉토리를 게스트 OS인 윈도우 10에도 연결해서 홈 네트워크 전체와 파일을 공유할 수 있다. 별다른 추가 설정이 필요한 것은 아니고 앞선 글의 SMB 접속 부분을 참고하면 된다.

     

    본 글의 저작권은 작성자 Varins에게 있습니다.
    Varins의 사전 서면 동의 없이는 본 글의 전부 또는 일부를 무단으로 전재, 게시, 배포하는 것을 금지합니다.
    

Leave a comment

댓글은 관리자의 승인 이후에 게시됩니다.