Microsoft 社는 2022년 6월 15일 Internet Explorer 11의 지원을 종료했습니다.

qemu on ubuntu

제목

qemu on ubuntu

1. qemu.sh 파일

Ubuntu 19.04 에서 테스트했다.

각자의 상황에 맞게 DISK_IMAGE_FILE (3번째 줄) 부터 NETWORK_ADDR (9번째 줄) 까지 변경해서 사용하면 되고, 상세한 내용은 후술하기로 한다.

#!/bin/bash
DISK_IMAGE_FILE=CentOS-7-x86_64.qcow2
DISK_IMAGE_FILE_SIZE=10G
INSTALL_ISO_FILE=CentOS-7-x86_64-Minimal-1908.iso
GATEWAY_ADDR=192.168.1.1
INET_ADDR=192.168.1.100
INET_DEV=enp0s25
NETWORK_ADDR=192.168.1.0/24
BRIDGE_DEV=br0
TAP_DEV=tap0
MEM_SIZE=640
L2CACHE_SIZE=2621440
RETVAL=0
print() {
echo "DISK_IMAGE_FILE=${DISK_IMAGE_FILE}"
echo "DISK_IMAGE_FILE_SIZE=${DISK_IMAGE_FILE_SIZE}"
echo "INSTALL_ISO_FILE=${INSTALL_ISO_FILE}"
echo "GATEWAY_ADDR=${GATEWAY_ADDR}"
echo "INET_ADDR=${INET_ADDR}"
echo "INET_DEV=${INET_DEV}"
echo "NETWORK_ADDR=${NETWORK_ADDR}"
echo "BRIDGE_DEV=${BRIDGE_DEV}"
echo "TAP_DEV=${TAP_DEV}"
echo "MEM_SIZE=${MEM_SIZE}"
echo "L2CACHE_SIZE=${L2CACHE_SIZE}"
}
qemu() {
sudo apt-get install qemu-kvm qemu
}
backup() {
# gzip -9 -k ${DISK_IMAGE_FILE}
qemu-img convert -O qcow2 ${DISK_IMAGE_FILE} "${DISK_IMAGE_FILE}.1"
gzip -9 "${DISK_IMAGE_FILE}.1"
}
create() {
if [ -f "${DISK_IMAGE_FILE}" ]; then
echo "${DISK_IMAGE_FILE} exist"
else
qemu-img create -f qcow2 ${DISK_IMAGE_FILE} ${DISK_IMAGE_FILE_SIZE}
RETVAL=$?
return ${RETVAL}
fi
}
install_with_kernel_image() {
qemu-system-x86_64 \
-nographic \
-hda ${DISK_IMAGE_FILE} \
-kernel vmlinuz \
-initrd initrd.img \
-boot d \
-cdrom ${INSTALL_ISO_FILE} \
-m ${MEM_SIZE} \
-net nic \
-net user \
-append "console=tty0 console=ttyS0,115200n8 edd=off"
RETVAL=$?
return ${RETVAL}
}
install() {
qemu-system-x86_64 \
-nographic \
-hda ${DISK_IMAGE_FILE} \
-boot d \
-cdrom ${INSTALL_ISO_FILE} \
-m ${MEM_SIZE} \
-net nic -net user
RETVAL=$?
return ${RETVAL}
}
boot() {
qemu-system-x86_64 \
-nographic \
-m ${MEM_SIZE} \
-net nic \
-net tap,ifname=${TAP_DEV},script=no \
-drive file=${DISK_IMAGE_FILE},l2-cache-size=${L2CACHE_SIZE},media=disk \
-boot order=c \
-vga none
RETVAL=$?
return ${RETVAL}
}
tap_with_zeroconf() {
sudo modprobe tun tap && \
sudo ip link add ${BRIDGE_DEV} type bridge && \
sudo ip tuntap add dev ${TAP_DEV} mode tap && \
sudo ip link set dev ${TAP_DEV} master ${BRIDGE_DEV} && \
sudo ip link set dev ${INET_DEV} master ${BRIDGE_DEV} && \
sudo ip link set dev ${BRIDGE_DEV} up && \
sudo ip link set dev ${TAP_DEV} up && \
sudo ip route flush dev ${INET_DEV} && \
sudo ip address delete ${INET_ADDR} dev ${INET_DEV} && \
sudo ip address add ${INET_ADDR} dev ${BRIDGE_DEV} && \
sudo ip route add ${NETWORK_ADDR} dev ${BRIDGE_DEV} && \
sudo ip route add 169.254.0.0/16 dev ${BRIDGE_DEV} && \
sudo ip route add 0.0.0.0/0 via ${GATEWAY_ADDR}
}
untap_with_zeroconf() {
sudo ip route flush dev ${BRIDGE_DEV} && \
sudo ip address delete ${INET_ADDR} dev ${BRIDGE_DEV} && \
sudo ip address add ${INET_ADDR} dev ${INET_DEV} && \
sudo ip route flush dev ${INET_DEV} && \
sudo ip route add ${NETWORK_ADDR} dev ${INET_DEV} && \
sudo ip route add 169.254.0.0/16 dev ${INET_DEV} && \
sudo ip route add 0.0.0.0/0 via ${GATEWAY_ADDR}
sudo ip link set dev ${TAP_DEV} down && \
sudo ip link set dev ${BRIDGE_DEV} down && \
sudo ip link set dev ${INET_DEV} nomaster && \
sudo ip link set dev ${TAP_DEV} nomaster && \
sudo ip tuntap delete dev ${TAP_DEV} mode tap && \
sudo ip link delete ${BRIDGE_DEV} type bridge
}
tap() {
sudo modprobe tun tap && \
sudo ip link add ${BRIDGE_DEV} type bridge && \
sudo ip tuntap add dev ${TAP_DEV} mode tap && \
sudo ip link set dev ${TAP_DEV} master ${BRIDGE_DEV} && \
sudo ip link set dev ${INET_DEV} master ${BRIDGE_DEV} && \
sudo ip link set dev ${BRIDGE_DEV} up && \
sudo ip link set dev ${TAP_DEV} up && \
sudo ip route flush dev ${INET_DEV} && \
sudo ip address delete ${INET_ADDR} dev ${INET_DEV} && \
sudo ip address add ${INET_ADDR} dev ${BRIDGE_DEV} && \
sudo ip route add ${NETWORK_ADDR} dev ${BRIDGE_DEV} && \
sudo ip route add 0.0.0.0/0 via ${GATEWAY_ADDR}
}
untap() {
sudo ip route flush dev ${BRIDGE_DEV} && \
sudo ip address delete ${INET_ADDR} dev ${BRIDGE_DEV} && \
sudo ip address add ${INET_ADDR} dev ${INET_DEV} && \
sudo ip route flush dev ${INET_DEV} && \
sudo ip route add ${NETWORK_ADDR} dev ${INET_DEV} && \
sudo ip route add 0.0.0.0/0 via ${GATEWAY_ADDR} && \
sudo ip link set dev ${TAP_DEV} down && \
sudo ip link set dev ${BRIDGE_DEV} down && \
sudo ip link set dev ${INET_DEV} nomaster && \
sudo ip link set dev ${TAP_DEV} nomaster && \
sudo ip tuntap delete dev ${TAP_DEV} mode tap && \
sudo ip link delete ${BRIDGE_DEV} type bridge
}
case "$1" in
qemu)
qemu
;;
backup)
backup
;;
start)
boot
;;
boot)
boot
;;
install)
install
;;
create)
create
;;
tap)
tap
;;
untap)
untap
;;
print)
print
;;
*)
echo $"Usage: $0 {print|qemu|backup|start|boot|install|create|tap|untap}"
esac
exit ${RETVAL}

2. qemu.sh 사용법

2.1. ./qemu.sh print

DISK_IMAGE_FILE (3번째 줄) 부터 L2CACHE_SIZE (14번째 줄) 까지의 환경변수 값을 출력한다.

2.2. ./qemu.sh qemu

qemu 패키지를 설치한다.

만약, HOST 가 Ubuntu 가 아닌 경우, qemu.sh 에서 이 부분을 변경해야 한다.

2.3. ./qemu.sh create

DISK_IMAGE_FILE 로 정의된 디스크 이미지 파일을 생성한다.

파일이 있다면 안내메시지만 출력한다.

디스크 이미지 생성 및 GUEST 운영체제 설치를 생략하는 방법에 대해서는 후술을 참조한다.

2.4. ./qemu.sh tap

GUEST 운영체제의 인터넷 연결을 위한 tap 네트워크를 구성한다.

이 명령이 실패하면, HOST 운영체제의 인터넷 연결이 끊어질 가능성이 높다.

처음이라면 반드시 후술하는 설명을 참조한다.

다음과 같은 경고는 무시하기로 한다.

Warning: Executing wildcard deletion to stay compatible with old scripts.
Explicitly specify the prefix length (192.168.1.100/32) to avoid this warning.
This special behaviour is likely to disappear in further releases,
fix your scripts!

2.5. ./qemu.sh install

GUEST 운영체제를 설치한다.

이 명령을 실행하기 위해서는 INSTALL_ISO_FILE 이 있어야 한다.

./qemu.sh tap 을 선행하여 실행할 필요는 없다.

문제가 발생한다면 후술하는 설명을 참조한다.

설치가 완료 된 후에 리부팅을 하고 있다면, Ctrl+a x 로 종료하고, ./qemu.sh start 를 실행해서 GUEST 운영체제를 시작(부팅)한다.

디스크 이미지 생성 및 GUEST 운영체제 설치를 생략하는 방법에 대해서는 후술을 참조한다.

2.5. ./qemu.sh start

GUEST 운영체제를 시작(부팅)한다.

./qemu.sh boot 로 실행해도 된다.

이 명령을 처음으로 실행한다면, GUEST 운영체제에서 네트워크를 설정하는 등의 작업을 해야 하는데, 이에 대해서는 필자가 작성한 다른 글들을 참조한다.

2.6. 구체적인 상황에 따른 실행방법

2.6.1. 주의사항

qemu.sh 는 중복실행이나 선행한 명령어가 실패한 경우 등의 예외 처리가 없으므로, 같은 명령이 2번 이상 실행되지 않도록 주의한다.

2.6.2. 처음인 경우

먼저 DISK_IMAGE_FILE (3번째 줄) 부터 L2CACHE_SIZE (43번째 줄) 까지의 환경변수 값을 변경한다.

이에 대해서는 후술하기로 한다.

다음의 순서에 따라 실행한다.

./qemu.sh qemu
./qemu.sh create
./qemu.sh install
./qemu.sh tap
./qemu.sh start

2.6.3. 이미 설치한 경우

./qemu.sh start

HOST 운영체제를 재부팅했거나, 네트워크를 원래대로 돌려놓았다면, 다음의 순서에 의한다.

./qemu.sh tap
./qemu.sh start

3. 후술

3.1. 환경변수 값 변경

  • DISK_IMAGE_FILE : ./qemu.sh create 로 생성하는 파일이므로, 파일이름이 중복 되지만 않도록 한다.

  • DISK_IMAGE_FILE_SIZE

  • INSTALL_ISO_FILE : 인터넷에서 다운로드 받은 GUEST 운영체제 설치 파일이다.

  • GATEWAY_ADDR

  • INET_ADDR

  • INET_DEV

  • NETWORK_ADDR

  • BRIDGE_DEV

  • TAP_DEV

  • MEM_SIZE : GUEST 운영체제가 사용할 메모리 크기

  • L2CACHE_SIZE : GUEST 운영체제가 사용할 L2CACHE 크기

3.1.1. GATEWAY_ADDR 부터 TAP_DEV 까지

다음의 명령어로 Host 운영체제의 네트워크 구성을 확인한다.

ifconfig -a
netstat -nr
  • ifconfig 가 없다면, ip a 로 확인한다.
  • netstat 가 없다면, route -n 으로 확인한다.

ifconfig -a 의 결과는 다음과 같다.

enp0s25: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.100 netmask 255.255.255.0 broadcast 192.168.1.255

netstat -nr 의 결과는 다음과 같다.

Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 enp0s25
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 enp0s25

netstat -nr 실행결과에 169.254.0.0 가 있는 경우 후술을 참조한다.

ifconfig 와 netstat 명령어의 실행결과로부터 INET_DEV(=enp0s25), INET_ADDR(=192.168.1.100), GATEWAY_ADDR(=192.168.1.1)를 각각 얻어온다.

NETWORK_ADDR 는 netstat -nr 의 2번째 줄에서 얻어오는데, Genmask 가 255.255.255.0 가 아니라면, 192.168.1.0 뒤에 붙은 /24를 변경해야 한다.

ifconfig -a 혹은 netstat -nr 실행결과에서 br0, tap0 docker0 와 같은 것들이 이미 있다면, 전문가의 도움을 받아야 한다.

필자의 경험에 의하면 전문가의 도움은 쉽지 않다. 포기하거나, reboot 을 반복하면서 결과를 확신 할 수 없는 삽질을 반복해야 하는데, 포기 와 삽질 중에 무엇을 선택할지는 개인의 자유다.

3.1.2. MEM_SIZE 와 L2CACHE_SIZE

MEM_SIZE 는 HOST 운영체제의 남아 있는 메모리를 참작해서 정하면 된다.

MEM_SIZE = 640 은 최소값에 가깝고, 큰 값을 설정하면 좋겠지만, HOST 운영체제에서 Memory Swapping 이 발생하면, HOST 운영체제의 성능에도 부정적인 영향이 있으므로 신중을 기해야 한다.

Memory Swapping 이란 운영체제가 하드디스크의 Swap 파티션을 메모리 처럼 사용하는 것을 말하는데, 메모리 부족을 원인으로 Swap 파티션을 사용하게 되면, 수반되는 성능저하가 치명적인 수준이다.

L2CACHE_SIZE 는 큰 값을 정하는 것이 별 의미가 없으므로 DISK_IMAGE_FILE 의 크기에 따라 적당히 설정한다.

3.1.3. DISK_IMAGE_FILE_SIZE

DISK_IMAGE_FILE_SIZE 는 DISK_IMAGE_FILE 의 최대값이고, 늘릴 수 있다.

DISK_IMAGE_FILE 의 크기를 늘렸을 경우, GUEST 운영체제에서도 파티션을 변경해야 하는데, GUEST 운영체제가 물리적인 파티션을 사용한다면, DISK_IMAGE_FILE을 1개 더 추가하는 것과 차이가 없다.

3.1.4. CentOS 7 GUEST 운영체제 에서 DISK_IMAGE_FILE 크기 변경

HOST 운영체제에서 다음과 같이 DISK_IMAGE_FILE 의 크기를 확인한다.

qemu-img info CentOS-7-x86_64.qcow2

HOST 운영체제에서 다음과 같이 DISK_IMAGE_FILE 의 크기를 늘린다.

qemu-img resize CentOS-7-x86_64.qcow2 +10G

GUEST 운영체제에서 다음과 같이 파티션 할당 현황을 확인한다.

$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 18G 0 disk
└─sda1 8:1 0 8G 0 part /
sr0 11:0 1 1024M 0 rom

GUEST 운영체제에서 다음과 같이 프로그램을 설치한다.

$ yum -y install cloud-utils-growpart

GUEST 운영체제에서 다음과 같이 파티션을 변경한다.

$ growpart /dev/sda 1

GUEST 운영체제에서 다음과 같이 변경된 파티션 할당 현황을 확인한다.

$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 18G 0 disk
└─sda1 8:1 0 18G 0 part /
sr0 11:0 1 1024M 0 rom

GUEST 운영체제에서 다음과 같이 변경된 파티션을 반영한다.

$ sudo xfs_growfs /

혹은

$ sudo resize2fs /dev/sda1

3.2. tap 네트워크 설정

3.2.0. 실패 사례

  1. Wifi 네트워크(wlan0) : br0 장치를 wlan0 장치의 master 로 설정할 수 없다.
  2. Fedora 11 : 너무 오래되었다.

3.2.1. 개요 및 주의사항

tap 네트워크로 설정하면, 여유분의 IP 주소가 있어야 하지만, Host 이외의 다른 장치에서 Guset 운영체제로 접근할 수 있다.

vmware의 bridge 모드와 기능적으로 동일하다.

HOST 에서 tap 네트워크 설정은 다음과 같은 절차에 의한다.

  • br0, tap0 장치를 추가하고
  • br0 를 tap0 와 enp0s25 의 master 로 설정한다.
  • enp0s25 장치에 설정되어 있는 IP 와 라우팅 정보를 삭제한다.
  • enp0s25 에 설정되어 있던 정보를 전부 br0 에 설정한다.

ssh로 접속한 상태에서 명령을 1개씩 실행하다 보면, enp0s25 장치에 네트워크 정보를 삭제할 때, HOST 의 네트워크는 먹통이 되고, HOST 장비 앞으로 사람이 가야 문제를 해결할 수 있다.

qemu.sh 와 같이 스크립트를 작성하거나 한 번에 붙여넣기를 하는 방법으로 명령어를 한 번에 전송해야 한다.

./qemu.sh tap 명령이 실패하면, HOST 장치의 네트워크가 먹통이 될 가능성이 높으므로, qemu.sh 를 처음으로 실행하는 경우에는 HOST 장비 앞에서 사람이 리부팅 정도는 할수 있는 상태에서 작업할 것을 권한다.

3.2.2. 설명

중요한 것은 실행결과의 차이가 그대로 유지되는 것이다.

./qemu.sh tap 을 실행한 후에 ifconfig -a 결과는 다음과 같다.

br0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.100 netmask 255.255.255.255 broadcast 0.0.0.0
enp0s25: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
tap0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500

차이는 다음과 같다.

  • br0 장치가 새로 생겼고, br0의 인터넷 주소(inet) 가 192.168.1.100 로 되어 있다(앞선 실행결과에서는 enp0s25 에 할당되어 있었다).
  • enp0s25 장치의 인터넷 주소(inet)는 항목 자체가 없어졌다.
  • tap0 장치가 새로 생겼다.

ip a 명령어를 사용한다면, link 정보도 볼 수 있다.

netstat -nr 결과는 다음과 같다.

Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 br0
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 br0

차이는 다음과 같다.

  • 가장 오른쪽의 Iface 항목이 enp0s25 에서 br0 로 변경되었다.

3.2.4. /etc/resolv.conf 를 반드시 확인한다.

nameserver 가 127.0.0.53 혹은 127.0.1.1 와 같다면, 다음 작업을 해야 하고, 그렇지 않다면 nslookup, host, apt 혹은 DNS 쿼리를 수행하는 모든 프로그램에서 에러가 발생한다.

3.2.4.1. Ubuntu 19.04

Ubuntu 19.04 의 /etc/resolv.conf 는 다음과 같다.

nameserver 127.0.0.53
options edns0

이 경우 다음과 같이 ${INET_DEV} (enp0s25) 의 "DNS Servers" 를 알아낸다.

systemd-resolve --status

위에서 알아낸 "DNS Servers" (8.8.8.8 라고 가정한다) 를 다음과 같이 ${BRIDGE_DEV} (br0) 에 설정한다.

sudo systemd-resolve --interface br0 --set-dns 8.8.8.8
3.2.4.2. Ubuntu 16.04.7

Ubuntu 16.04.7 는 다음과 같다.

nameserver 127.0.1.1

nmcli 명령에서 dns 변경은 (장치가 아니라) 연결에 대해서만 할 수 있고, (실시간으로 반영되는 것도 아니어서) dns 변경을 반영하려면 연결을 내렸다가 다시 올려야 하는데, 원격에서 작업을 하고 있는 경우, 새롭고 중대한 위험이 발생하므로, resolvconf 서비스 자체를 중단하는 방법을 제시한다.

resolvconf 를 중단한다.

sudo systemctl stop resolvconf

/etc/resolv.conf 파일에 nameserver 설정을 변경한다.

nameserver 8.8.8.8

3.2.5. netstat -nr 실행결과에 169.254.0.0 가 있는 경우``

다음과 같이 netstat -nr 결과에 169.254.0.0 로 시작하는 줄이 있는 경우도 있다.

Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
0.0.0.0 192.168.1.1 0.0.0.0 UG 0 0 0 eth0

이 경우 qemu.sh 를 다음과 같이 변경한다.

변경 전은 다음과 같다.

tap)
tap
;;

변경 후는 다음과 같다.

tap)
tap_with_zeroconf
;;

3.3. ./qemu.sh install 에서 문제가 발생하는 경우

3.3.1. 현상

CentOS 7 과 CentOS 8 은 문제가 없었다.

Fedora 11 은 edd=off 로 하라고 하면서 설치가 중단되었고, CentOS 5 는 화면이 전혀 출력되지 않아서 설치를 진행할 수 없었다.

CentOS 5 는 -curses 를 추가하면 화면은 출력되지만, 설치가 곤란한건 마찬가지이다.

3.3.2. 해결방법

이런 건 kernel 파라미터를 추가하는 방법으로 해결 할 수 있는데, iso 파일 내의 grub.conf 를 편집하거나, qemu-system-x86_64 의 -append 라는 명령행 파라미터를 이용하는 방법 정도가 있는데, 후자만 설명하기로 한다.

-append "console=tty0 console=ttyS0,115200n8 edd=off"

다만, -append 파라미터는 -kernel 파라미터와 함께 사용해야 하므로, (오직 kernel 파라미터를 지정할 목적으로) iso 파일 내에서 vmlinuz 파일과 initrd.img 파일을 꺼낸 뒤에, 그 파일을 -kernel-initrd 파라미터로 추가한다.

3.3.3. iso 에서 vmlinuz 파일과 initrd.img 꺼내기

iso 파일을 mount 한다.

sudo mount ./CentOS-7-x86_64-Minimal-1908.iso /media -o loop
  • /media 는 비어있는 디렉토리이면 된다.

파일을 복사한다.

cp /media/isolinux/vmlinuz ./
cp /media/isolinux/initrd.img ./

mount 를 해제한다.

sudo umount /media

3.3.4. qemu.sh 변경

변경 전은 다음과 같다.

install)
install
;;

변경 후는 다음과 같다.

install)
install_with_kernel_image
;;

3.4. 디스크 이미지 생성 및 GUEST 운영체제 설치를 생략하는 방법

OpenStack > Get images 에서 운영체제가 설치된 디스크 이미지를 얻을 수 있다.

OpenStack 을 설치하지 않고, (이 글에서 소개하는 것과 같이 qemu 만 설치해서) 위의 디스크 이미지들을 (정상적으로) 이용하기 위해서는 (HOST 운영체제에게 guestfish 를 이용해서) 패스워드를 설정하는 등의 작업을 해야 할 수도 있다.

GUEST 운영체제로 로그인 한 이후에 cloud-init 를 삭제하는 경우도 있다.

제목

첨부파일