В данной статье рассматривается идея и практическое руководство по создания многопользовательской рабочей станции (multiseat) с использованием виртуализации KVM, с возможностью удаленного управления через libvirt. С помощью этого руководства возможно создать два и более рабочих мест как в офисе, так и дома, которым может потребоваться возможность полного использования графической подсистемы компьютера. Причем можно на одном системном блоке реализовать одновременно работу различных операционных систем Windows, Linux, Mac OS X, остальное по вкусу.
Введение
Желание реализовать возможность совместного использования одного компьютера, хотя бы двуx людей появилась давным-давно. Идея сделать из домашнего или офисного компьютера, как минимум два рабочих места раньше реализовывалась в основном на той же операционной системе со всеми вытекающими недостатками. Трудности возникали особенно тогда, когда одному человеку нравилось работать в windows, а другому в Linux и приходилось кому-то терпеть, если решение было реализовано только через одну ОС. Впрочем были и другие трудности.
Сейчас компьютеры стали более производительные, рост частоты новых процессоров уже нас давно не радует, но зато радует рост ядер. А это значит, что мы в одном системном блоке потенциально уже имеем несколько компьютеров. И тенденция эта будет только расти. Работа нескольких человек с одним компьютером в стандартной конфигурации скоро будет вполне востребованной на рынке и уже сейчас доступна в реализации.
Установка Gentoo
Настройка
Настройка BIOS
Проверяем, что в BIOS VT-d включён, для Z87 «usb intel XHCI» нужно отключить для проброса USB контролеров.
Настройка звука
usermod -G kvm,audio,pulse-access -d /home/qemu qemu
usermod -G wheel,audio,pulse-access,users testuser
Запустим pulseaudio.
<span class="keyword">echo</span> <span class="string">'PULSEAUDIO_SHOULD_NOT_GO_SYSTEMWIDE="1"'</span> >> /etc/conf.d/pulseaudio
rc-update add pulseaudio default
/etc/init.d/pulseaudio start
su - testuser
<span class="comment"># C помощью alsamixer выставляем требуемую громкость, если ММ то нажимаем М.</span>
alsamixer
<span class="comment"># обратно к root</span>
<span class="keyword">exit</span>
<span class="comment"># сохраняем настройки громкости</span>
/etc/init.d/alsasound save
/etc/init.d/alsasound start
rc-update add alsasound default
Настройка libvirt
В файл /etc/libvirt/qemu.conf определим пользователя, от которого будет запуск виртуалок
user = <span class="string">"qemu"</span>
group = <span class="string">"qemu"</span>
<span class="comment"># Если все будет плохо, то можно раскомментировать</span>
<span class="comment"># clear_emulator_capabilities = 0</span>
В момент написания статьи seabios в gentoo 1.7.3, а нам нужен свежий, по этому качаем и распаковываем
cd /etc/libvirt
wget http://code.coreboot.org/p/seabios/downloads/get/bios.bin-1.7.4.gz
gzip -d bios.bin-1.7.4.gz
Создадим вспомогательный скрипт для проброса устройств
cat > /etc/libvirt/vfio-bind << <span class="string">"EOF"</span>
<span class="shebang">#!/bin/sh</span>
<span class="keyword">for</span> dev <span class="keyword">in</span> <span class="string">"$@"</span>; <span class="keyword">do</span>
vendor=$(cat /sys/bus/pci/devices/<span class="variable">$dev</span>/vendor)
device=$(cat /sys/bus/pci/devices/<span class="variable">$dev</span>/device)
<span class="keyword">if</span> <span class="test_condition">[ -e /sys/bus/pci/devices/<span class="variable">$dev</span>/driver ]</span>; <span class="keyword">then</span>
<span class="keyword">echo</span> <span class="variable">$dev</span> > /sys/bus/pci/devices/<span class="variable">$dev</span>/driver/unbind
<span class="keyword">fi</span>
<span class="keyword">echo</span> <span class="variable">$vendor</span> <span class="variable">$device</span> > /sys/bus/pci/drivers/vfio-pci/new_id
<span class="keyword">done</span>
EOF
chmod +x /etc/libvirt/vfio-bind
Запускаем lspci и выбираем жертву для ручного проброса
03:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Turks PRO [Radeon HD 6570/7570]
03:00.1 Audio device: Advanced Micro Devices, Inc. [AMD/ATI] Turks/Whistler HDMI Audio [Radeon HD 6000 Series
00:1a.0 USB controller: Intel Corporation 8 Series/C220 Series Chipset Family USB EHCI <span class="comment">#2 (rev 04)</span>
Создадим скрипт проброса конкретной видеокарты
cat > /etc/libvirt/bind-vga-1 << <span class="string">"EOF"</span>
<span class="shebang">#!/bin/sh</span>
/etc/libvirt/vfio-bind 0000:03:00.0 0000:03:00.1
<span class="comment"># ставим права на группу 6. Найти можно здесь /sys/kernel/iommu_groups/6/devices</span>
chown qemu /dev/vfio/6
EOF
chmod +x /etc/libvirt/bind-vga-1
<span class="comment"># запускаем</span>
/etc/libvirt/bind-vga-1
<span class="comment"># создадим скрипт автозагрузки</span>
cat > /etc/init.d/bind-vga-1 << <span class="string">"EOF"</span>
<span class="comment">#!/sbin/runscript </span>
start() {
ebegin <span class="string">"Starting vfio-bind"</span>
/etc/libvirt/bind-vga-1
eend $? <span class="string">"Failed to start vfio-bind"</span>
}
EOF
<span class="comment"># если нужно, то в автозагрузку его</span>
rc-update add bind-vga-1 default
Теперь создадим раздел где будет лежать образ виртуальной машины
vcreate -L70G -nwks1 vg
У меня есть машина, где лежат образы уже подготовленных виртуалок с софтом, надеюсь и у Вас такая появится. Как подготовить образ напишу ниже.
ssh [email protected] <span class="string">"dd if=/dev/vg_archive/windows7 bs=1M |gzip -"</span> |gunzip - | dd of=/dev/vg/wks1 bs=1M
Настройка домена
Ниже приведен файл настройки конечной рабочей конфигурации с комментариями.
<span class="tag"><<span class="title">domain</span> <span class="attribute">type</span>=<span class="value">'kvm'</span> <span class="attribute">xmlns:qemu</span>=<span class="value">'http://libvirt.org/schemas/domain/qemu/1.0'</span>></span>
<span class="tag"><<span class="title">name</span>></span>wks1<span class="tag"></<span class="title">name</span>></span>
<span class="tag"><<span class="title">uuid</span>></span>2811e544-bf4d-baf6-1135-ec5acd139999<span class="tag"></<span class="title">uuid</span>></span>
<span class="tag"><<span class="title">memory</span> <span class="attribute">unit</span>=<span class="value">'KiB'</span>></span>4145152<span class="tag"></<span class="title">memory</span>></span>
<span class="tag"><<span class="title">currentMemory</span> <span class="attribute">unit</span>=<span class="value">'KiB'</span>></span>4145152<span class="tag"></<span class="title">currentMemory</span>></span>
<span class="tag"><<span class="title">cpu</span> <span class="attribute">mode</span>=<span class="value">'host-passthrough'</span>/></span>
<span class="tag"><<span class="title">os</span>></span>
<span class="tag"><<span class="title">type</span> <span class="attribute">arch</span>=<span class="value">'x86_64'</span> <span class="attribute">machine</span>=<span class="value">'pc-q35-2.0'</span>></span>hvm<span class="tag"></<span class="title">type</span>></span>
<span class="tag"><<span class="title">loader</span>></span>/etc/libvirt/bios.bin-1.7.4<span class="tag"></<span class="title">loader</span>></span>
<span class="tag"><<span class="title">boot</span> <span class="attribute">dev</span>=<span class="value">'hd'</span>/></span>
<span class="tag"><<span class="title">bootmenu</span> <span class="attribute">enable</span>=<span class="value">'yes'</span>/></span>
<span class="tag"></<span class="title">os</span>></span>
<span class="tag"><<span class="title">features</span>></span>
<span class="tag"><<span class="title">acpi</span>/></span>
<span class="tag"><<span class="title">apic</span>/></span>
<span class="tag"><<span class="title">pae</span>/></span>
<span class="tag"></<span class="title">features</span>></span>
<span class="tag"><<span class="title">clock</span> <span class="attribute">offset</span>=<span class="value">'localtime'</span>/></span>
<span class="tag"><<span class="title">on_poweroff</span>></span>destroy<span class="tag"></<span class="title">on_poweroff</span>></span>
<span class="tag"><<span class="title">on_reboot</span>></span>restart<span class="tag"></<span class="title">on_reboot</span>></span>
<span class="tag"><<span class="title">on_crash</span>></span>restart<span class="tag"></<span class="title">on_crash</span>></span>
<span class="tag"><<span class="title">devices</span>></span>
<span class="tag"><<span class="title">emulator</span>></span>/usr/bin/qemu-kvm<span class="tag"></<span class="title">emulator</span>></span>
<span class="tag"><<span class="title">disk</span> <span class="attribute">type</span>=<span class="value">'block'</span> <span class="attribute">device</span>=<span class="value">'disk'</span>></span>
<span class="tag"><<span class="title">driver</span> <span class="attribute">name</span>=<span class="value">'qemu'</span> <span class="attribute">type</span>=<span class="value">'raw'</span> <span class="attribute">cache</span>=<span class="value">'none'</span>/></span>
<span class="tag"><<span class="title">source</span> <span class="attribute">dev</span>=<span class="value">'/dev/vg/wks1'</span>/></span>
<span class="comment"><!-- Вначале установки windows нужно использовать следующий target
<target dev='sda' bus='sata'/>
--></span>
<span class="tag"><<span class="title">target</span> <span class="attribute">dev</span>=<span class="value">'vda'</span> <span class="attribute">bus</span>=<span class="value">'virtio'</span>/></span>
<span class="tag"></<span class="title">disk</span>></span>
<span class="comment"><!-- секция для CD образов
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file='/usr/win_7.iso'/>
<target dev='hdc' bus='sata'/>
<readonly/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
--></span>
<span class="tag"><<span class="title">controller</span> <span class="attribute">type</span>=<span class="value">'pci'</span> <span class="attribute">index</span>=<span class="value">'0'</span> <span class="attribute">model</span>=<span class="value">'pcie-root'</span>></span>
<span class="tag"><<span class="title">alias</span> <span class="attribute">name</span>=<span class="value">'pcie.0'</span>/></span>
<span class="tag"></<span class="title">controller</span>></span>
<span class="tag"><<span class="title">controller</span> <span class="attribute">type</span>=<span class="value">'pci'</span> <span class="attribute">index</span>=<span class="value">'1'</span> <span class="attribute">model</span>=<span class="value">'dmi-to-pci-bridge'</span>></span>
<span class="tag"><<span class="title">alias</span> <span class="attribute">name</span>=<span class="value">'pci.1'</span>/></span>
<span class="tag"></<span class="title">controller</span>></span>
<span class="tag"><<span class="title">controller</span> <span class="attribute">type</span>=<span class="value">'pci'</span> <span class="attribute">index</span>=<span class="value">'2'</span> <span class="attribute">model</span>=<span class="value">'pci-bridge'</span>></span>
<span class="tag"><<span class="title">alias</span> <span class="attribute">name</span>=<span class="value">'pci.2'</span>/></span>
<span class="tag"></<span class="title">controller</span>></span>
<span class="tag"><<span class="title">interface</span> <span class="attribute">type</span>=<span class="value">'bridge'</span>></span>
<span class="tag"><<span class="title">mac</span> <span class="attribute">address</span>=<span class="value">'52:54:00:12:50:01'</span>/></span>
<span class="tag"><<span class="title">source</span> <span class="attribute">bridge</span>=<span class="value">'br0'</span>/></span>
<span class="comment"><!-- Вначале установки windows нужно закомментировать модель --></span>
<span class="tag"><<span class="title">model</span> <span class="attribute">type</span>=<span class="value">'virtio'</span>/></span>
<span class="tag"><<span class="title">address</span> <span class="attribute">type</span>=<span class="value">'pci'</span> <span class="attribute">domain</span>=<span class="value">'0x0000'</span> <span class="attribute">bus</span>=<span class="value">'0x02'</span> <span class="attribute">slot</span>=<span class="value">'0x03'</span> <span class="attribute">function</span>=<span class="value">'0x0'</span>/></span>
<span class="tag"></<span class="title">interface</span>></span>
<span class="comment"><!-- секция для первоночальной установки windows, через vnc
<controller type='usb'/>
<input type='tablet' bus='usb'/>
<input type='mouse' bus='ps2'/>
<graphics type='vnc' port='5900' autoport='no' listen='192.168.1.2' passwd='mypassword'/>
--></span>
<span class="comment"><!-- BEGIN Если устанавливаем по vnс убираем этот блок --></span>
<span class="tag"><<span class="title">hostdev</span> <span class="attribute">mode</span>=<span class="value">'subsystem'</span> <span class="attribute">type</span>=<span class="value">'pci'</span> <span class="attribute">managed</span>=<span class="value">'yes'</span>></span>
<span class="comment"><!-- Вместо source1 должно быть source. С хабра-разметкой не могу справится --></span>
<span class="tag"><<span class="title">source1</span>></span>
<span class="tag"><<span class="title">address</span> <span class="attribute">domain</span>=<span class="value">'0x0000'</span> <span class="attribute">bus</span>=<span class="value">'0x00'</span> <span class="attribute">slot</span>=<span class="value">'0x1a'</span> <span class="attribute">function</span>=<span class="value">'0x0'</span>/></span>
<span class="tag"></<span class="title">source1</span>></span>
<span class="tag"><<span class="title">address</span> <span class="attribute">type</span>=<span class="value">'pci'</span> <span class="attribute">domain</span>=<span class="value">'0x0000'</span> <span class="attribute">bus</span>=<span class="value">'0x02'</span> <span class="attribute">slot</span>=<span class="value">'0x04'</span> <span class="attribute">function</span>=<span class="value">'0x0'</span>/></span>
<span class="tag"></<span class="title">hostdev</span>></span>
<span class="comment"><!-- END Eсли устанавливаем по vnс убираем этот блок --></span>
<span class="tag"><<span class="title">sound</span> <span class="attribute">model</span>=<span class="value">'ich9'</span>/></span>
<span class="tag"><<span class="title">memballoon</span> <span class="attribute">model</span>=<span class="value">'virtio'</span>></span>
<span class="tag"><<span class="title">address</span> <span class="attribute">type</span>=<span class="value">'pci'</span> <span class="attribute">domain</span>=<span class="value">'0x0000'</span> <span class="attribute">bus</span>=<span class="value">'0x02'</span> <span class="attribute">slot</span>=<span class="value">'0x08'</span> <span class="attribute">function</span>=<span class="value">'0x0'</span>/></span>
<span class="tag"></<span class="title">memballoon</span>></span>
<span class="tag"></<span class="title">devices</span>></span>
<span class="tag"><<span class="title">seclabel</span> <span class="attribute">type</span>=<span class="value">'none'</span>/></span>
<span class="tag"><<span class="title">qemu:commandline</span>></span>
<span class="tag"><<span class="title">qemu:env</span> <span class="attribute">name</span>=<span class="value">"HOME"</span> <span class="attribute">value</span>=<span class="value">"/home/qemu"</span>/></span>
<span class="tag"><<span class="title">qemu:env</span> <span class="attribute">name</span>=<span class="value">"QEMU_AUDIO_DRV"</span> <span class="attribute">value</span>=<span class="value">"pa"</span>/></span>
<span class="comment"><!-- BEGIN Если мы устанавливаем по vnc, то нужно закомментировать этот блок --></span>
<span class="tag"><<span class="title">qemu:arg</span> <span class="attribute">value</span>=<span class="value">'-vga'</span>/></span>
<span class="tag"><<span class="title">qemu:arg</span> <span class="attribute">value</span>=<span class="value">'none'</span>/></span>
<span class="tag"><<span class="title">qemu:arg</span> <span class="attribute">value</span>=<span class="value">'-device'</span>/></span>
<span class="tag"><<span class="title">qemu:arg</span> <span class="attribute">value</span>=<span class="value">'vfio-pci,host=03:00.0,bus=pcie.0,addr=02.0,x-vga=on'</span>/></span>
<span class="comment"><!-- END Если мы устанавливаем по vnc, то нужно закомментировать этот блок --></span>
<span class="tag"></<span class="title">qemu:commandline</span>></span>
<span class="tag"></<span class="title">domain</span>></span>
Данную конфигурацию записываем в файл /etc/libvirt/qemu/wks1.xml
Перечитываем файл конфигурации
/etc/init.d/libvirtd restart
Запускаем домен
virsh start wks1
<span class="comment"># если все будет хорошо, то можно в автозагрузку добавить</span>
<span class="comment"># virsh autostart wks1</span>
Первоначальная установка
Для первоначальной установки записываем нужный iso образ и раскомментируем секцию cdrom. Так же убираем все настройки virtio и пробросы устройств 03:00.0 — видео и 00:1a.0 usb контроллера. Включаем доступ по vnc. После того когда вы систему установите нужно установить драйвера virtio в гостевую OC. Драйвера под windows.
Без драйверов все работает очень медленно.
Известные засады
- Я встречался с тем, что без полного обновления windows не вставали последние драйвера virtio.
- Перед тем как ставить драйвер-virtio основного диска, нужно подключить второй пустой диск с уже определенным типом virtio
lvcreate -nzero -L1M
<span class="tag"><<span class="title">disk</span> <span class="attribute">type</span>=<span class="value">'block'</span> <span class="attribute">device</span>=<span class="value">'disk'</span>></span> <span class="tag"><<span class="title">driver</span> <span class="attribute">name</span>=<span class="value">'qemu'</span> <span class="attribute">type</span>=<span class="value">'raw'</span> <span class="attribute">cache</span>=<span class="value">'writethrough'</span>/></span> <span class="tag"><<span class="title">source</span> <span class="attribute">dev</span>=<span class="value">'/dev/vg/wks1'</span>/></span> <span class="tag"><<span class="title">target</span> <span class="attribute">dev</span>=<span class="value">'sda'</span> <span class="attribute">bus</span>=<span class="value">'sata'</span>/></span> <span class="tag"></<span class="title">disk</span>></span> <span class="tag"><<span class="title">disk</span> <span class="attribute">type</span>=<span class="value">'block'</span> <span class="attribute">device</span>=<span class="value">'disk'</span>></span> <span class="tag"><<span class="title">driver</span> <span class="attribute">name</span>=<span class="value">'qemu'</span> <span class="attribute">type</span>=<span class="value">'raw'</span> <span class="attribute">cache</span>=<span class="value">'writethrough'</span>/></span> <span class="tag"><<span class="title">source</span> <span class="attribute">dev</span>=<span class="value">'/dev/vg/zero'</span>/></span> <span class="tag"><<span class="title">target</span> <span class="attribute">dev</span>=<span class="value">'vda'</span> <span class="attribute">bus</span>=<span class="value">'virtio'</span>/></span> <span class="tag"></<span class="title">disk</span>></span>
- В windows изменение параметров энергосбережения -> настройка перехода в спящий режим -> переводить компьютер в спящий режим ->
«никогда»
Этот скрипт можно в crontab установить.
cat > /etc/libvirtd/shutdown_if_not_start.sh << <span class="string">"EOF"</span>
<span class="shebang">#!/bin/sh</span>
LIST_VM=`virsh list | awk <span class="string">'{if($3=="running")print $2}'</span>|wc -l`
<span class="keyword">if</span> <span class="test_condition">[ <span class="variable">${LIST_VM}</span> -ne 0 ]</span> ; <span class="keyword">then</span>
<span class="keyword">exit</span> 0
<span class="keyword">fi</span>
awk <span class="string">'{if(int($1)>300){exit 0}else{ exit 1}}'</span> /proc/uptime
<span class="keyword">if</span> <span class="test_condition">[ $? -ne 0 ]</span>; <span class="keyword">then</span>
<span class="keyword">exit</span> 0
<span class="keyword">fi</span>
/sbin/shutdown -h now
EOF
chmod +x /etc/libvirtd/shutdown_if_not_start.sh
Заключение
Вроде основные моменты описал. Здесь можно найти пути решения, если у Вас валится windows в BSOD. Если вы будете организовывать моментальные снимки, то не забудьте поставить QEMU Guest Agent и научитесь с ним работать.
У нас на боевых компах стоит апач, через скрипт пользователь сам может производить действия с VM с соседнего компьютера или виртуалки. Можно так же реализовать запуск виртуалок по USB ключу.
Уверен, что кто-то сможет эту конфигурацию улучшить. Пожалуйста, напишите об это в комментарии, чтобы помочь остальным.
Данную конфигурацию можно затарить и распространять на другие машины, так же как и образы гостевых OC. Не забудьте после разворачивания хостового имиджа, поправить адреса, названия хостов, интерфейсов и сгенерировать ssh ключи.