Pull to refresh

Два и более рабочих места на одном компьютере — бесплатное решение

Reading time 12 min
Views 102K

В данной статье рассматривается идея и практическое руководство по создания многопользовательской рабочей станции (multiseat) с использованием виртуализации KVM, с возможностью удаленного управления через libvirt. С помощью этого руководства возможно создать два и более рабочих мест как в офисе, так и дома, которым может потребоваться возможность полного использования графической подсистемы компьютера. Причем можно на одном системном блоке реализовать одновременно работу различных операционных систем Windows, Linux, Mac OS X, остальное по вкусу.

Введение


Желание реализовать возможность совместного использования одного компьютера, хотя бы двуx людей появилась давным-давно. Идея сделать из домашнего или офисного компьютера, как минимум два рабочих места раньше реализовывалась в основном на той же операционной системе со всеми вытекающими недостатками. Трудности возникали особенно тогда, когда одному человеку нравилось работать в windows, а другому в Linux и приходилось кому-то терпеть, если решение было реализовано только через одну ОС. Впрочем были и другие трудности.

Сейчас компьютеры стали более производительные, рост частоты новых процессоров уже нас давно не радует, но зато радует рост ядер. А это значит, что мы в одном системном блоке потенциально уже имеем несколько компьютеров. И тенденция эта будет только расти. Работа нескольких человек с одним компьютером в стандартной конфигурации скоро будет вполне востребованной на рынке и уже сейчас доступна в реализации.

Установка Gentoo


Подготовка
Качаем текущий livecd и загружаемся.
Поднимаем сеть. Как зовут сетевой интерфейс?
ifconfig
  eno1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

Для тех кто любит окошки настраиваем сеть так
net-setup eno1

Я лично привык по старинке. Прописываем свой ip
ifconfig eno1 192.168.1.2/24

Установим маршрутизатор по умолчанию
route add default gw 192.168.1.1

Укажем наши DNS
echo "nameserver 8.8.8.8" >> /etc/resolv.conf

Задаем пароль для root
passwd root

Запускаем ssh, для того, чтобы продолжать установку удаленно
/etc/init.d/sshd start

C любимого рабочего места заходим по ssh
ssh root@192.168.1.186


Разметка диска
Предполагается, что у Вас один пустой диск с именем /dev/sda
parted -a opt  /dev/sda
(parted) mklabel msdos

Создадим раздел для загрузки. 150M с очень большим запасом. Может быть мигрируем в будущем разделы в GPT и поставим EFI.
(parted) mkpart primary ext2 1 150M
(parted) set 1 boot on

Все остальное отдадим под lvm
(parted) mkpart primary 150M -1s
(parted) #Is this still acceptable to you? Yes/No? yes 
(parted) set 2 lvm on                                                     
(parted) quit

LVM нам понадобится для того, чтобы делать моментальные снимки дисков виртуальных машин, без остановки системы, для организации резервных копий. Так же удобнее будет производить изменение разделов, добавление новых.
Разметка диска для LVM. Размеры ставьте на Ваш вкус
pvcreate /dev/sda2
vgcreate vg /dev/sda2
lvcreate -L 1G -n root vg
lvcreate -L 1G -n tmp vg
lvcreate -L 5G -n var vg
lvcreate -L 10G -n usr vg

Если уже был настроен LVM, то его нужно активировать
vgchange -ay

Отформатируем все разделы
mkfs.ext2 /dev/sda1
mkfs.ext2 /dev/vg/tmp
for p in root var usr ; do mkfs.ext4 /dev/vg/$p ; done

Монтируем наш будущий корневой раздел
mount /dev/vg/root /mnt/gentoo

Создаем каталоги для остальных точек монтирования
cd /mnt/gentoo
mkdir boot usr var tmp sys proc dev
chmod 1777 tmp

Монтируем остальные разделы
mount /dev/vg/usr /mnt/gentoo/usr
mount /dev/vg/tmp /mnt/gentoo/tmp
mount /dev/vg/var /mnt/gentoo/var
mount /dev/sda2 /mnt/gentoo/boot


Разворачивание готового образа и подготовка к chroot
cd /mnt/gentoo

Качаем свежую систему (проверьте дату в названии файла)
wget http://mirror.yandex.ru/gentoo-distfiles/releases/amd64/current-stage3/stage3-amd64-nomultilib-20140123.tar.bz2

Заберем portage. Portage — описание как собирать и устанавливать необходимые нам программы
wget http://mirror.yandex.ru/gentoo-distfiles/snapshots/portage-latest.tar.bz2 

Распакуем все это
tar xvjpf stage3-amd64-nomultilib-20140123.tar.bz2
tar xvjf portage-latest.tar.bz2 -C /mnt/gentoo/usr 

Скопируем resolv.conf в будущий корневой раздел
cp /etc/resolv.conf /mnt/gentoo/etc/resolv.conf 

Монтируем системные разделы
mount --rbind /dev /mnt/gentoo/dev
mount -t proc none /mnt/gentoo/proc
mount --rbind /sys /mnt/gentoo/sys

Попадаем внутрь созданного корневого раздела (будущей системы)
chroot /mnt/gentoo
env-update
source /etc/profile


Обживаем новую систему
nano /etc/portage/make.conf

Если есть необходимость, то меняем CFLAGS — опции для компилятора. Опции компилятора, практически стандартные, кроме -march=native (вместо -march=i686), что обязывает компилятор использовать все возможности процессора. Если стоит задача сделать железо независимую установку, то лучше не использовать, т.к. при восстановлении на новом железе (возможно) придется пересобрать весь мир.
USE — перечисляются глобально основные опции программ, которые будут установлены.
Если будут тонкие клиенты, то добавьте в USE «spice», если нужно пробрасывать USB порты тонких клиентов, то добавьте «usbredir»
CFLAGS="-O2 -pipe -march=native"
# количество процессов при сборке пакетов
MAKEOPTS="-j3"
USE="bindist mmx sse sse2 -ipv6 unicode device-mapper -X slang udev pulseaudio"
GENTOO_MIRRORS=http://mirror.yandex.ru/gentoo-distfiles/
SYNC="rsync://rsync.ru.gentoo.org/gentoo-portage"
LINGUAS="ru en" 

Пропишем наши разделы для авто-монтирования
cat > /etc/fstab << "EOF"
/dev/sda1 /boot ext2 noauto,noatime 1 2
/dev/vg/root / ext4 noatime 0 1 
/dev/vg/tmp /tmp ext2 noatime 0 0
/dev/vg/usr /usr ext4 noatime 0 0 
/dev/vg/var /var ext4 noatime 0 0
EOF

Обновим дерево portage
emerge --sync

Если нужно сменить профиль, то как раз самое время
eselect profile list
eselect profile set <цифра>

Создадим файл-конфигурации сети
cat > /etc/conf.d/net << "EOF"
config_eno1="null"
config_br0="192.168.1.2/24"
routes_br0="default via 192.168.1.1"
bridge_br0="eno1"
rc_net_br0_need="net.eno1"
ethtool_offload_eno1="gro off"
dns_domain="mydomain.ru"
dns_servers="8.8.8.8 8.8.4.4"
EOF
ln -s /etc/init.d/net.lo /etc/init.d/net.eno1
ln -s /etc/init.d/net.lo /etc/init.d/net.br0

Зададим пароль для root
passwd root

Добавим себя (testuser), как пользователя и зададим пароль. Добавим в группы users,wheel,audio
useradd -m -G users,wheel,audio -s /bin/bash testuser
passwd testuser

К моменту написания статьи не вышла еще qemu-1.7.1, поэтому ставим две звезды, чтобы забрать девелоперскую версию
echo "app-emulation/qemu ** ~amd64" >>/etc/portage/package.accept_keywords

Данный флаг нужен, чтобы pulseaudio работал как независимый процесс. К сожалению, по умолчанию заставить его стабильно работать с libvirt мне не удалось, используя переменную «nographics_allow_host_audio=1» в libvirtd.conf. Может быть у Вас получится?
echo "media-sound/pulseaudio -system-wide" >>/etc/portage/make.profile/package.use.mask
echo "media-sound/pulseaudio -dbus system-wide" >>/etc/portage/package.use 

Укажем, что нужно будет собирать новые версии пакетов
for p in sys-kernel/gentoo-sources app-emulation/libvirt sys-apps/dtc sys-firmware/seabios; do echo $p" ~amd64" >>/etc/portage/package.accept_keywords ; done 

Установим необходимые программы. Ключ -av позволяет посмотреть опции сборки, если что-то не устраивает, то можно включить опцию в USE переменную, которая содержится в файле /etc/portage/make.conf. app-misc/mc — если кому-то он удобен
emerge -av gentoo-sources vixie-cron lvm2 genkernel syslog-ng iproute2 libvirt ethtool bridge-utils grub terminus-font radeon-ucode app-misc/mc

Добавим программы в автозагрузку
for s in sshd vixie-cron syslog-ng net.br0 libvirtd ; do rc-update add $s default; done

Соберем ядро Linux, причем в разделах
> Device Drivers > IOMMU Hardware Support > IOMMU Hardware Support
Отмечаем все звёздочками
> Device Drivers > VFIO Non-Privileged userspace driver framework
Отмечаем все звёздочками
>Device Drivers > Networking support > Universal TUN/TAP device driver support
Ставим звёздочку.
Все остальное по вкусу ;-)
genkernel --lvm --menuconfig all

Добавим флаг «dolvm», для автозагрузки LVM
nano /etc/default/grub
# пропишем переменную
GRUB_CMDLINE_LINUX="dolvm"

Установим загрузчик Grub
grub2-install /dev/sda
grub2-mkconfig -o /boot/grub/grub.cfg

Настраиваем локализацию
cat /usr/share/i18n/SUPPORTED | grep -E 'ru_RU|en_US' >> /etc/locale.gen && locale-gen

Копируем новый файл временной зоны в /etc/localtime
cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime

Настроим переключение клавиатуры
nano /etc/conf.d/keymaps
# изменим параметры
dumpkeys_charset="utf8"
keymap="-u ru"

Фонт в консоли пропишем
nano /etc/conf.d/consolefont
# изменим параметр
consolefont="cyr-sun16"

Локализация сообщений
cat > /etc/env.d/02locale << "EOF"
LANG="ru_RU.UTF-8"
LC_ALL="ru_RU.UTF-8"
LANG="ru_RU.UTF-8"
LC_MESSAGES="POSIX"
LC_NUMERIC="POSIX"
LC_TIME="POSIX"
EOF
env-update && source /etc/profile
rc-update add keymap default
rc-update add consolefont default


Если необходимо, то можно обновить и пересобрать пакеты с новыми USE флагами
emerge -avuDN system world

Задаем имя хоста
nano /etc/conf.d/hostname
hostname="testhost"

Правим /etc/hosts
nano /etc/hosts
127.0.0.1 localhost testhost testhost.mydomain.ru

Перегружаемся, если все хорошо, то Вам повезло ;-) Все-таки железо у всех разное, возможно что-то нужно будет включить в ядро.
Если все плохо, то разбираемся в документации или пользуемся google. Документации на русском языке в сети достаточно.

Настройка


Настройка 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.
echo 'PULSEAUDIO_SHOULD_NOT_GO_SYSTEMWIDE="1"' >> /etc/conf.d/pulseaudio 
rc-update add pulseaudio default 
/etc/init.d/pulseaudio start
su - testuser 
# C помощью alsamixer выставляем требуемую громкость, если ММ то нажимаем М.
alsamixer
# обратно к root
exit
# сохраняем настройки громкости
/etc/init.d/alsasound save 
/etc/init.d/alsasound start
rc-update add alsasound default

Настройка libvirt

В файл /etc/libvirt/qemu.conf определим пользователя, от которого будет запуск виртуалок
user = "qemu"
group = "qemu"
# Если все будет плохо, то можно раскомментировать
# clear_emulator_capabilities = 0

В момент написания статьи 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 << "EOF"
#!/bin/sh
for dev in "$@"; do 
  vendor=$(cat /sys/bus/pci/devices/$dev/vendor) 
  device=$(cat /sys/bus/pci/devices/$dev/device)
  if [ -e /sys/bus/pci/devices/$dev/driver ]; then
    echo $dev > /sys/bus/pci/devices/$dev/driver/unbind
  fi                                                                                                         
  echo $vendor $device > /sys/bus/pci/drivers/vfio-pci/new_id
done
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 #2 (rev 04)

Создадим скрипт проброса конкретной видеокарты
cat > /etc/libvirt/bind-vga-1 << "EOF"
#!/bin/sh                                                                                                    
/etc/libvirt/vfio-bind 0000:03:00.0 0000:03:00.1                                                                    
# ставим права на группу 6. Найти можно здесь /sys/kernel/iommu_groups/6/devices
chown qemu /dev/vfio/6
EOF
chmod +x /etc/libvirt/bind-vga-1
# запускаем
/etc/libvirt/bind-vga-1
# создадим скрипт автозагрузки
cat > /etc/init.d/bind-vga-1 << "EOF"
#!/sbin/runscript                                                                                            
start() {
  ebegin "Starting vfio-bind"
  /etc/libvirt/bind-vga-1
  eend $? "Failed to start vfio-bind"
}
EOF
# если нужно, то в автозагрузку его
rc-update add bind-vga-1 default

Теперь создадим раздел где будет лежать образ виртуальной машины
lvcreate -L70G -nwks1 vg

У меня есть машина, где лежат образы уже подготовленных виртуалок с софтом, надеюсь и у Вас такая появится. Как подготовить образ напишу ниже.
ssh root@192.168.1.3 "dd if=/dev/vg_archive/windows7 bs=1M |gzip -" |gunzip - | dd of=/dev/vg/wks1 bs=1M

Настройка домена


Ниже приведен файл настройки конечной рабочей конфигурации с комментариями.

<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
  <name>wks1</name> 
  <uuid>2811e544-bf4d-baf6-1135-ec5acd139999</uuid>
  <memory unit='KiB'>4145152</memory>
  <currentMemory unit='KiB'>4145152</currentMemory>
  <cpu mode='host-passthrough'/>
  <os>
    <type arch='x86_64' machine='pc-q35-2.0'>hvm</type>
    <loader>/etc/libvirt/bios.bin-1.7.4</loader>
    <boot dev='hd'/>
    <bootmenu enable='yes'/>
  </os>
  <features>
    <acpi/>
    <apic/>
    <pae/>
  </features>
  <clock offset='localtime'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>restart</on_crash>
  <devices>
    <emulator>/usr/bin/qemu-kvm</emulator>
    <disk type='block' device='disk'>
      <driver name='qemu' type='raw' cache='none'/>
      <source dev='/dev/vg/wks1'/>
      <!-- Вначале установки windows нужно использовать следующий target
         <target dev='sda' bus='sata'/>
      -->
      <target dev='vda' bus='virtio'/>
    </disk>
    <!-- секция для 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>
     -->
    <controller type='pci' index='0' model='pcie-root'>
      <alias name='pcie.0'/>
    </controller>
    <controller type='pci' index='1' model='dmi-to-pci-bridge'>
      <alias name='pci.1'/>
    </controller>
    <controller type='pci' index='2' model='pci-bridge'>
      <alias name='pci.2'/>
    </controller>
    <interface type='bridge'>
      <mac address='52:54:00:12:50:01'/>
      <source bridge='br0'/>
      <!-- Вначале установки windows нужно закомментировать модель -->
      <model type='virtio'/>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x03' function='0x0'/>
    </interface>
    <!-- секция для первоночальной установки 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'/>
    -->
    <!-- BEGIN Если устанавливаем по vnс убираем этот блок -->
    <hostdev mode='subsystem' type='pci' managed='yes'>
       <!-- Вместо source1 должно быть source. С хабра-разметкой не могу справится -->
       <source1>
        <address domain='0x0000' bus='0x00' slot='0x1a' function='0x0'/>                                     
      </source1>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x04' function='0x0'/>                            
    </hostdev>
    <!-- END Eсли устанавливаем по vnс убираем этот блок -->
    <sound model='ich9'/>
    <memballoon model='virtio'>
      <address type='pci' domain='0x0000' bus='0x02' slot='0x08' function='0x0'/>
    </memballoon>
  </devices>
  <seclabel type='none'/>
  <qemu:commandline>
    <qemu:env name="HOME" value="/home/qemu"/>
    <qemu:env name="QEMU_AUDIO_DRV" value="pa"/>
    <!-- BEGIN  Если мы устанавливаем по vnc, то нужно закомментировать этот блок -->
    <qemu:arg value='-vga'/>
    <qemu:arg value='none'/>
    <qemu:arg value='-device'/>
    <qemu:arg value='vfio-pci,host=03:00.0,bus=pcie.0,addr=02.0,x-vga=on'/>
    <!-- END  Если мы устанавливаем по vnc, то нужно закомментировать этот блок -->
  </qemu:commandline>
</domain>

Данную конфигурацию записываем в файл /etc/libvirt/qemu/wks1.xml
Перечитываем файл конфигурации
/etc/init.d/libvirtd restart

Запускаем домен
virsh start wks1
# если все будет хорошо, то можно в автозагрузку добавить
# virsh autostart wks1

Первоначальная установка


Для первоначальной установки записываем нужный iso образ и раскомментируем секцию cdrom. Так же убираем все настройки virtio и пробросы устройств 03:00.0 — видео и 00:1a.0 usb контроллера. Включаем доступ по vnc. После того когда вы систему установите нужно установить драйвера virtio в гостевую OC. Драйвера под windows.
Без драйверов все работает очень медленно.

Известные засады


  • Я встречался с тем, что без полного обновления windows не вставали последние драйвера virtio.
  • Перед тем как ставить драйвер-virtio основного диска, нужно подключить второй пустой диск с уже определенным типом virtio
    lvcreate -nzero -L1M
    

    <disk type='block' device='disk'>
          <driver name='qemu' type='raw' cache='writethrough'/>
          <source dev='/dev/vg/wks1'/>
          <target dev='sda' bus='sata'/>
        </disk>
    <disk type='block' device='disk'>
          <driver name='qemu' type='raw' cache='writethrough'/>
          <source dev='/dev/vg/zero'/>
          <target dev='vda' bus='virtio'/>
        </disk>
    

  • В windows изменение параметров энергосбережения -> настройка перехода в спящий режим -> переводить компьютер в спящий режим ->
    «никогда»


Этот скрипт можно в crontab установить.
cat > /etc/libvirtd/shutdown_if_not_start.sh << "EOF"
#!/bin/sh                                                                                                    
LIST_VM=`virsh list | awk '{if($3=="running")print $2}'|wc -l`                                               
if [ ${LIST_VM} -ne  0 ] ; then                                                                              
  exit 0                                                                                                     
fi                                                                                                           
awk '{if(int($1)>300){exit 0}else{ exit 1}}' /proc/uptime                                                    
if [ $? -ne 0 ]; then                                                                                        
  exit 0                                                                                                     
fi                                                                                                           
/sbin/shutdown -h now
EOF
chmod +x /etc/libvirtd/shutdown_if_not_start.sh

Заключение


Вроде основные моменты описал. Здесь можно найти пути решения, если у Вас валится windows в BSOD. Если вы будете организовывать моментальные снимки, то не забудьте поставить QEMU Guest Agent и научитесь с ним работать.
У нас на боевых компах стоит апач, через скрипт пользователь сам может производить действия с VM с соседнего компьютера или виртуалки. Можно так же реализовать запуск виртуалок по USB ключу.
Уверен, что кто-то сможет эту конфигурацию улучшить. Пожалуйста, напишите об это в комментарии, чтобы помочь остальным.
Данную конфигурацию можно затарить и распространять на другие машины, так же как и образы гостевых OC. Не забудьте после разворачивания хостового имиджа, поправить адреса, названия хостов, интерфейсов и сгенерировать ssh ключи.
Про spice и ARM, если будет интересно напишу отдельно.
Tags:
Hubs:
+59
Comments 53
Comments Comments 53

Articles