Гипервизор на стероидах: FreeBSD + ZFS + cbsd
В этой инструкции я хочу пролить свет на то, насколько просто и элегантно возможно устанавливать FreeBSD в серверном окружении — на арендованном железе или в собственном датацентре, вручную или средствами оркестрации вроде Ansible. Шифрование дисков, удобное управление пространством, гипервизор для контейнеров и полных VM, удобный и понятный firewall — это всё и не только это доступно из коробки и занимает мало времени в настройке при правильном подходе.
Установка
Начнём с самого простого сценария. У нас стоит железяка или она где-то там стоит, но мы можем на CD-диске или средствами IPMI скормить .iso (или .img). Или даже еще проще — хостер предлагает образ mfsbsd прямо из интерфейса. В любом случае загружаемся с mfsbsd.
Linux rescue CD
Бывает, что хостер не дает FreeBSD ни в каком виде, но дает Linux Rescue. Загружаемся в линукс, скачиваем mfsbsd образ диска и накатываем на харды:
root@rescue:~# wget https://mfsbsd.vx.sk/files/images/12/amd64/mfsbsd-12.1-RELEASE-amd64.img
root@rescue:~# dd if=mfsbsd-12.1-RELEASE-amd64.img of=/dev/sda bs=4M
22+1 records in
22+1 records out
92307456 bytes (92 MB) copied, 0.386325 s, 239 MB/s
root@rescue:~# reboot
Настройка сетевого адаптера
Всё хорошо, когда у нас есть непосредственный доступ к монитору или получение сигнала по сети через IPMI/VNC/web. Но что делать если такой возможности нет и возможно только подключение по сети? Для этого нам нужен образ mfsbsd с уже выставленными настройками сети. Давайте соберем наш индивидуальный образ с настройками сетевого адаптера и демона SSH. Для этого нам нужен какойто FreeBSD хост для сборки образа.
Скачиваем оригинальный .iso
root@ns312777:~ # fetch https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/12.0/FreeBSD-12.0-RELEASE-amd64-dvd1.iso
Маунтим
root@ns312777:~ # mount -t cd9660 /dev/
mdconfig -f FreeBSD-12.0-RELEASE-amd64-dvd1.iso/root/cd-rom
Качаем mfsbsd
root@ns312777:~ # fetch https://github.com/mmatuska/mfsbsd/archive/master.zip
root@ns312777:~ # unzip master.zip && cd mfsbsd-master
Меняем конфигурационные файлы. Для конфигурации IP достаточно изменить создать conf/rc.conf
из conf/rc.conf.sample
. Так же следует создать conf/authorized_keys
и добавить свой ключ.
root@ns312777:~/mfsbsd-master # ls -la conf/
total 55
drwxr-xr-x 2 root wheel 13 Dec 8 10:06 .
drwxr-xr-x 7 root wheel 13 Dec 8 10:11 ..
-rw-r--r-- 1 root wheel 451 Dec 8 10:06 authorized_keys
-rw-r--r-- 1 root wheel 50 Nov 30 22:54 authorized_keys.sample
-rw-r--r-- 1 root wheel 3 Nov 30 22:54 boot.config.sample
-rw-r--r-- 1 root wheel 156 Nov 30 22:54 hosts.sample
-rw-r--r-- 1 root wheel 592 Nov 30 22:54 interfaces.conf.sample
-rw-r--r-- 1 root wheel 1310 Nov 30 22:54 loader.conf.sample
-rw-r--r-- 1 root wheel 739 Dec 8 10:06 rc.conf
-rw-r--r-- 1 root wheel 689 Nov 30 22:54 rc.conf.sample
-rw-r--r-- 1 root wheel 40 Nov 30 22:54 rc.local.sample
-rw-r--r-- 1 root wheel 108 Nov 30 22:54 resolv.conf.sample
-rw-r--r-- 1 root wheel 898 Nov 30 22:54 ttys.sample
Ну и поехали!
root@ns312777:~/mfsbsd-master # make BASE=/root/cd-rom/usr/freebsd-dist
Extracting base and kernel ... done
Removing selected files from distribution ... done
Installing configuration scripts and files ... done
Generating SSH host keys ... done
Configuring boot environment .п..x ./
x ./linker.hints
x ./kernel
done
Installing pkgng ... done
Compressing usr ... done
Creating and compressing mfsroot ... done
Creating image file ...87072+0 records in
87072+0 records out
89161728 bytes transferred in 0.905906 secs (98422727 bytes/sec)
87040+0 records in
87040+0 records out
89128960 bytes transferred in 0.877165 secs (101610229 bytes/sec)
md3 created
md3p1 added
partcode written to md3p1
bootcode written to md3
md3p2 added
Calculated size of `./mfsbsd-12.0-RELEASE-p10-amd64.img.a47DUe1j': 80281600 bytes, 65 inodes
Extent size set to 32768
./mfsbsd-12.0-RELEASE-p10-amd64.img.a47DUe1j: 76.6MB (156800 sectors) block size 32768, fragment size 4096
using 1 cylinder groups of 76.56MB, 2450 blks, 256 inodes.
super-block backups (for fsck -b #) at:
64,
Populating `./mfsbsd-12.0-RELEASE-p10-amd64.img.a47DUe1j'
Image `./mfsbsd-12.0-RELEASE-p10-amd64.img.a47DUe1j' complete
612+1 records in
612+1 records out
80281600 bytes transferred in 36.018812 secs (2228880 bytes/sec)
done
Почему mfsbsd?
Это компактная сборка ОС FreeBSD которая загружается прямо в память, а значит мы можем свободно делать что хотим со всеми доступными носителями. Что-то вроде Linux rescue CD. Вся прелесть этой сборки в том, что она позволяет установить FreeBSD буквально в одну команду.
После Linux rescue CD или загрузки с .iso/.img мы попадаем в шелл mfsbsd.
root@mfsbsd:~ # cd bin/
Чистим диски
root@mfsbsd:~/bin # gpart destroy -F /dev/ada0
root@mfsbsd:~/bin # gpart destroy -F /dev/ada1
или
root@mfsbsd:~/bin # ./destroygeom -d /dev/ada0 -d /dev/ada1
Destroying geom ada0:
Deleting partition 1 ... done
Deleting partition 2 ... done
Destroying geom ada1:
Deleting partition 1 ... done
Deleting partition 2 ... done
затем устанавливаем нашу систему.
root@mfsbsd:~/bin # ./zfsinstall
Usage: ./zfsinstall [-h] -d geom_provider [-d geom_provider ...] [ -u dist_url ] [-r mirror|raidz] [-m mount_point] [-p zfs_pool_name] [-s swap_partition_size] [-z zfs_partition_size] [-c] [-C] [-l] [-4] [-A]
т.к. на хостах до 3 дисков я предпочитаю ставить систему в зеркале на все диски на небольшой раздел. Благодаря ZFS оставшеесе пространство можно конфигурировать как душе угодно, но об этом позже. Вот она одна единственная команда, которой устанавливается вся система.
root@mfsbsd:~/bin # ./zfsinstall -d /dev/ada0 -d /dev/ada1 -d /dev/ada2 -r mirror -p zsys -z 25G
для установки системы на трёх дисках в зеркале
Fetching base files from: ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/12.0-RELEASE
/tmp/base.txz 147 MB 3670 kBps 41s
/tmp/kernel.txz 39 MB 2344 kBps 18s
Creating GUID partitions on ada0 ... done
Configuring ZFS bootcode on ada0 ... done
=> 40 937703008 ada0 GPT (447G)
40 472 1 freebsd-boot (236K)
512 52428800 2 freebsd-zfs (25G)
52429312 885273736 - free - (422G)
Creating GUID partitions on ada1 ... done
Configuring ZFS bootcode on ada1 ... done
=> 40 937703008 ada1 GPT (447G)
40 472 1 freebsd-boot (236K)
512 52428800 2 freebsd-zfs (25G)
52429312 885273736 - free - (422G)
Creating GUID partitions on ada2 ... done
Configuring ZFS bootcode on ada2 ... done
=> 40 937703008 ada2 GPT (447G)
40 472 1 freebsd-boot (236K)
512 52428800 2 freebsd-zfs (25G)
52429312 885273736 - free - (422G)
Creating ZFS pool zsys on ada0p2 ada1p2 ada2p2 ... done
Creating zsys root partition: ... done
Creating zsys partitions: var tmp ... done
Setting bootfs for zsys to zsys/root ... done
NAME USED AVAIL REFER MOUNTPOINT
zsys 712K 23.7G 88K none
zsys/root 264K 23.7G 88K /mnt
zsys/root/tmp 88K 23.7G 88K /mnt/tmp
zsys/root/var 88K 23.7G 88K /mnt/var
Extracting FreeBSD distribution ... done
Writing /boot/loader.conf... done
Writing /etc/fstab...Writing /etc/rc.conf... done
Copying /boot/zfs/zpool.cache ... done
Installation complete.
The system will boot from ZFS with clean install on next reboot
You may make adjustments to the installed system using chroot:
chroot /mnt
Some adjustments may require a mounted devfs:
mount -t devfs devfs /mnt/dev
WARNING - Don't export ZFS pool "zsys"!
После установки, файловая система установленной ОС доступна в /mnt/
, поэтому мы производим некоторую конфигурацию перед уходом в ребут:
Копируем уже добавленный в образ mfsbsd ключ в систему
root@mfsbsd:~/bin # mkdir /mnt/root/.ssh && cp /root/.ssh/authorized_keys /mnt/root/.ssh/
Редактируем конфиг SSH (например меняем порт и разрешаем логин рутом)
root@mfsbsd:~/bin # ee /mnt/etc/ssh/sshd_config
редактируем файл «загрузчика» системы
root@mfsbsd:~/bin # ee /mnt/etc/rc.conf
zfs_enable="YES"
sshd_enable="YES"
hostname="hyper.bitbsd.org"
#
# You need a gateway defined for a working network setup
defaultrouter="37.79.8.254"
ifconfig_em0="inet 37.79.8.111 netmask 255.255.255.0"
Обратите внимание на производителя вашего сетевого адептера, т.к. от этого зависит имя интерфейса. В некоторых случаях можно сделать так:
ifconfig_DEFAULT="DHCP"
в /etc/rc.conf
Выставляем DNS
root@mfsbsd:~/bin # ee /mnt/etc/resolv.conf
nameserver 8.8.8.8
Ну и можно вообще можно сделать так
root@mfsbsd:~ # chroot /mnt
и добавлять пользователя и вообще делать все из окружения уже свеже-установленной ОС.
ну, а мы поехали дальше
root@mfsbsd:~ # reboot
Больше красноглазия
Перед тем как приступить к конфигурации установленной ОС, я хочу отметить еще два способа загрузки в mfsbsd.
Стандартный установщик FreeBSD:
Выбираем Live CD
Создаем memory-disk
# mdconfig -a -t swap -s 2g -u 9
# newfs -U md9
# mount /dev/md9 /tmp
# cd /tmp
Диск в памяти на необходим для работы с файлами mfsbsd, более того, mfsbsd использует /tmp/
в процессе инсталяции, поэтому там должно быть достаточно свободного места.
# fetch https://github.com/mmatuska/mfsbsd/archive/master.zip
# unzip master.zip && cd mfsbsd-master/tools
ну, а дальше уже тоже самое — ./destroygeom
и ./zfsinstall
Установка из предустановленного образа Linux:
Если провайдер сервера дает уже установленный линукс, то мы так же можем «заинсталиться» по-своему, изменив конфигурацию загрузчика GRUB. Для этого на действующей системе нужно добавить grub.cfg следующие строки:
menuentry "mfsbsd-10.0-RELEASE-amd64.iso" {
# Path to the iso
set isofile=/boot/boot-isos/mfsbsd-12.0-RELEASE-amd64.iso
# (hd0,1) here may need to be adjusted of course depending where the partition is
loopback loop (hd0,1)$isofile
kfreebsd (loop)/boot/kernel/kernel.gz -v
# kfreebsd_loadenv (loop)/boot/device.hints
# kfreebsd_module (loop)/boot/kernel/geom_uzip.ko
kfreebsd_module (loop)/boot/kernel/ahci.ko
kfreebsd_module (loop)/mfsroot.gz type=mfs_root
set kFreeBSD.vfs.root.mountfrom="ufs:/dev/md0"
set kFreeBSD.mfsbsd.autodhcp="YES"
# Define a new root password
# set kFreeBSD.mfsbsd.rootpw="foobar"
# Alternatively define hashed root password
# set kFreeBSD.mfsbsd.rootpwhash=""
}
Ну и положить соответственно .iso в /boot/boot-isos/
Сам так никогда не делал, поэтому если найдете ошибку или недочет в конфиге — дайте знать.
Пожалуй как еще можно извернуться что бы установить FreeBSD я не знаю.
Конфигурация системы
Ну вот мы всё установили, загружаемся… Для начала работы нам нужно установить и настроить сеть — в нашем случае главную роль будет выполнять pf, мне нравится это решение за простоту и понятность. Нам нужно настроить файловую систему сервера — мы используем ZFS, потому что это невероятно гибкая и эффективная файловая система. Когда настроена сеть и ФС, мы установим гипервизор. Если нам необходим повышенный уровень безопасности, можно настроить дополнительные утилиты, я опущу их описание в этом туториале. В качестве демонстрации гибкости ZFS я опишу конфигурацию двух серверов, одного с 2-мя дисками и одного с 3-мя дисками. Так же не забываем провести апгрейд системы после установки, порой выходит так, что доступно для установки только что-то древнее.
freebsd-update -r 12.1-RELEASE upgrade
На момент написания этого руководства 12.1 является последней версией
Сервер с двумя дисками
Мы его создали в следующей конфигурации:
root@:~ # gpart show /dev/ada0
=> 40 5860533088 ada0 GPT (2.7T)
40 472 1 freebsd-boot (236K)
512 8388608 2 freebsd-swap (4.0G)
8389120 167772160 3 freebsd-zfs (80G)
176161280 2097152000 4 freebsd-zfs (1.0T)
2273313280 3587219848 5 freebsd-zfs (1.7T)
root@:~ # gpart show /dev/ada1
=> 40 5860533088 ada1 GPT (2.7T)
40 472 1 freebsd-boot (236K)
512 8388608 2 freebsd-swap (4.0G)
8389120 167772160 3 freebsd-zfs (80G)
176161280 2097152000 4 freebsd-zfs (1.0T)
2273313280 3587219848 5 freebsd-zfs (1.7T)
root@:~ # zpool status
pool: appdata
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
appdata ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
ada0p4.eli ONLINE 0 0 0
ada1p4.eli ONLINE 0 0 0
errors: No known data errors
pool: miscdata
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
miscdata ONLINE 0 0 0
ada0p5.eli ONLINE 0 0 0
ada1p5.eli ONLINE 0 0 0
errors: No known data errors
pool: zsys
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
zsys ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
ada0p3 ONLINE 0 0 0
ada1p3 ONLINE 0 0 0
errors: No known data errors
Что всё это значит? У нас железка с двумя дисками, эти два диска оба объемом 3ТБ. Мы на сервере собираемся запустить несколько приложений, часть из них условно критичны, часть «для поиграться», поэтому не жалко утратить данные. Для найболее эффективной конфигурации дискового пространства. Что мы получаем в результате недолгих манипуляций?
root@:~ # zfs list
NAME USED AVAIL REFER MOUNTPOINT
appdata 75.5K 961G 23K /appdata
miscdata 75.5K 3.21T 23K /miscdata
zsys 2.02G 75.0G 88K none
zsys/root 2.02G 75.0G 1.18G /
zsys/root/tmp 88K 75.0G 88K /tmp
zsys/root/var 862M 75.0G 862M /var
Пул appdata (зеркало из разделов /dev/ada0p4.eli и /dev/ada1p4.eli) для данных приложения критической важности, пул miscdata («страйп», т.е. просто последовательно расположены данные на /dev/ada0p5.eli и /dev/ada1p5.eli) для всякой ерунды. Так же есть системный пул и своп, это так же зеркала из разделов обоих дисков.
Итого мы имеем для наших приложений 1ТБ зеркалированного пространства и 3.2ТБ для экспериментов.
Как всё это настроить? Несложно. После начальной установки наш диск выглядит как-то так:
root@:~ # gpart show /dev/ada0
=> 40 5860533088 ada0 GPT (2.7T)
40 472 1 freebsd-boot (236K)
512 8388608 2 freebsd-swap (4.0G)
8389120 167772160 3 freebsd-zfs (80G)
176161280 684371848 free (2.7T)
Добавляем разделы на диски, сначала тот что будем зеркалировать
root@:~ # gpart add -t freebsd-zfs -s 1000g /dev/ada0
root@:~ # gpart add -t freebsd-zfs -s 1000g /dev/ada1
а затем всё остальное
root@:~ # gpart add -t freebsd-zfs /dev/ada0
root@:~ # gpart add -t freebsd-zfs /dev/ada1
теперь у нас есть разделы /dev/ada0p4, /dev/ada0p5, /dev/ada1p4 и /dev/ada0p5. Т.к. мы не имеем физического доступа к серверу у хостера в ДЦ, целесообразно разделы с нашими данными зашифровать.
root@:~ # geli init /dev/ada0p4
root@:~ # geli init /dev/ada0p5
root@:~ # geli init /dev/ada1p4
root@:~ # geli init /dev/ada1p5
вводим пароль шифрования, затем расшифровываем разделы:
root@:~ # geli attach /dev/ada0p4
root@:~ # geli attach /dev/ada0p5
root@:~ # geli attach /dev/ada1p4
root@:~ # geli attach /dev/ada1p5
теперь у нас есть разделы /dev/ada0p4.eli, /dev/ada0p5.eli, /dev/ada1p4.eli и /dev/ada0p5.eli, можем построить пулы из этих разделов
Можно это заскриптить:
root@:~ # cat /root/attach_disks.sh
#!/bin/sh
geli attach /dev/ada0p4
geli attach /dev/ada0p5
geli attach /dev/ada1p4
geli attach /dev/ada1p5
root@:~ # zpool create appdata mirror /dev/ada0p4.eli /dev/ada1p4.eli
и для данных без зеркалирования
root@:~ # zpool create miscdata /dev/ada0p5.eli /dev/ada1p5.eli
Ну вот и готов к бою наш сервер. Мы имеем пулы, которые в дальнейшем можем расширать посредством добавления доп. дисков. А еще есть снапшоты, как их применять опишу чуть ниже.
Сервер с тремя дисками
Тут немного другой конфиг. У нас есть железка с тремя SSD дисками по 450ГБ. Этот сервер будет использоваться в основном для полной виртуализации, поэтому нам нужно как можно больше пространства, но при это возможно продолжать работать после утраты одного из дисков. После начальной установки наши диски выглядят так:
root@:~ # gpart show /dev/ada[0-9]
=> 40 937703008 ada0 GPT (447G)
40 472 1 freebsd-boot (236K)
512 52428800 2 freebsd-zfs (25G)
52429312 885273736 - free - (422G)
=> 40 937703008 ada1 GPT (447G)
40 472 1 freebsd-boot (236K)
512 52428800 2 freebsd-zfs (25G)
52429312 885273736 - free - (422G)
=> 40 937703008 ada2 GPT (447G)
40 472 1 freebsd-boot (236K)
512 52428800 2 freebsd-zfs (25G)
52429312 885273736 - free - (422G)
Создаем разделы по 420 ГБ (до конца диска не создаем, т.к. при замене диска, его фактический размер может немного отличаться)
root@hyper:~ # gpart add -t freebsd-zfs -s 420g /dev/ada0
ada0p3 added
root@hyper:~ # gpart add -t freebsd-zfs -s 420g /dev/ada1
ada1p3 added
root@hyper:~ # gpart add -t freebsd-zfs -s 420g /dev/ada2
ada2p3 added
Шифруем
root@hyper:~ # geli init /dev/ada0p3
Enter new passphrase:
Reenter new passphrase:
Metadata backup for provider /dev/ada0p3 can be found in /var/backups/ada0p3.eli
and can be restored with the following command:
# geli restore /var/backups/ada0p3.eli /dev/ada0p3
root@hyper:~ # geli init /dev/ada1p3
Enter new passphrase:
Reenter new passphrase:
Metadata backup for provider /dev/ada1p3 can be found in /var/backups/ada1p3.eli
and can be restored with the following command:
# geli restore /var/backups/ada1p3.eli /dev/ada1p3
root@hyper:~ # geli init /dev/ada2p3
Enter new passphrase:
Reenter new passphrase:
Metadata backup for provider /dev/ada2p3 can be found in /var/backups/ada2p3.eli
and can be restored with the following command:
# geli restore /var/backups/ada2p3.eli /dev/ada2p3
Что за Metadata backup?
Это файл, при помощи которого можно сбросить пароль шифрования на тот, что мы ввели. Ну это на случай если потом поменял и забыл. В любом случае стоит иметь ввиду этот нюанс.
Аттачим диски и создаем пул
root@hyper:~ # geli attach /dev/ada0p3
Enter passphrase:
root@hyper:~ # geli attach /dev/ada1p3
Enter passphrase:
root@hyper:~ # geli attach /dev/ada2p3
Enter passphrase:
root@hyper:~ # zpool create safestore raidz1 /dev/ada0p3.eli /dev/ada1p3.eli /dev/ada2p3.eli
и на выходе мы получаем пул с 820 ГБ дискового пространства
root@hyper:~ # zfs list
NAME USED AVAIL REFER MOUNTPOINT
safestore 89.2K 810G 29.3K /safestore
...
Собирать ZFS пулы и разных дисков разных конфигураций возможно как угодно. Это своего рода конструктор хранилища, которое можно вечно увеличивать. ZFS создаёт слой абстракции над устройствами, который позволяет достигать крайне удивительных конфигураций.
Установка и настройка гипервизора
FreeBSD предлагает из коробки два решения для «гостевых ОС» — jails и bhyve. Есть cbsd, это враппер для FreeBSD Jails, bhyve и XEN. В данном руководстве я последний затрагивать не буду, т.к. никогда им сам не пользовался.
Клетки это клоны системы FreeBSD. Очень удобный инструмент для изоляции процессов. Хост система может хостить клетки любой версии ниже своей. Клетки можно переносить между хостами, снапшотить и откатывать хоть на лету. Стоит использовать клектки когда это возможно, таким образом оптимально использовать ресурс системы, не тратя его на полную виртуализацию.
bhyve в свою очередь это полноценный гипервизор, которым можно поднимать всякие Ubuntu и докеры.
root@:~ # pkg install cbsd
Стоит отметить, что на момент написания руководства cbsd тянет всего 11 зависимостей размеров 35 МБ.
New packages to be INSTALLED:
cbsd: 12.1.2
sudo: 1.8.28
gettext-runtime: 0.20.1
indexinfo: 0.3.1
libssh2: 1.8.2,3
ca_root_nss: 3.47.1
rsync: 3.1.3_1
libiconv: 1.14_11
pkgconf: 1.6.3,1
libedit: 3.1.20190324,1
sqlite3: 3.29.0
readline: 8.0.0
Number of packages to be installed: 12
The process will require 33 MiB more space.
8 MiB to be downloaded.
Proceed with this action? [y/N]: y
создаем отдельный раздел на пуле для клеток
root@:~ # zfs create miscdata/jails
инициализируем cbsd
root@:~ # env workdir="/miscdata/jails" /usr/local/cbsd/sudoexec/initenv
-------[CBSD v.12.1.2]-------
This is install/upgrade scripts for CBSD.
Don't forget to backup.
-----------------------------
Do you want prepare or upgrade hier environment for CBSD now?
[yes(1) or no(0)]
1
>>> Installing or upgrading
[Stage 1: account & dir hier]
* Check hier and permission...
./.rssh missing (created)
./.ssh missing (created)
./.ssh/sockets missing (created)
./basejail missing (created)
./etc missing (created)
./etc/defaults missing (created)
./export missing (created)
./ftmp missing (created)
./import missing (created)
./jails missing (created)
./jails-data missing (created)
./jails-fstab missing (created)
./jails-rcconf missing (created)
./jails-system missing (created)
./share missing (created)
./share/dialog missing (created)
./share/helpers missing (created)
./share/FreeBSD-jail-puppet-skel missing (created)
./share/FreeBSD-jail-skel missing (created)
./share/FreeBSD-jail-vnet-skel missing (created)
./share/emulators missing (created)
./src missing (created)
./tmp missing (created)
./var missing (created)
./var/cron missing (created)
./var/cron/tabs missing (created)
./var/db missing (created)
./var/log missing (created)
./var/mail missing (created)
./var/run missing (created)
./var/spool missing (created)
* write directory id: jaildatadir
* write directory id: jailsysdir
* write directory id: jailrcconfdir
* write directory id: dbdir
[Stage 2: build tools]
Shall i add cbsd user into /usr/local/etc/sudoers.d/cbsd_sudoers sudo file to obtain root privileges for the most cbsd commands?
[yes(1) or no(0)]
1
[Stage 3: local settings]
Shall i modify the /etc/rc.conf to sets cbsd_workdir="/miscdata/jails"?:
[yes(1) or no(0)]
1
/etc/rc.conf: cbsd_workdir: -> /miscdata/jails
[Stage 4: update default skel resolv.conf]
[Stage 5: refreshing inventory]
nodename: CBSD Nodename for this host e.g. the hostname. Warning: this operation will recreate the ssh keys in /miscdata/jails/.ssh dir: hostname.org
Empty inventory database created: /miscdata/jails/var/db/inv.hostname.org.sqlite
nodeip: Node management IPv4 or IPv6 address (used for node interconnection), e.g: 151.106.27.106
jnameserver: Jails default DNS name-server (for jails resolv.conf), e.g.: 9.9.9.9,149.112.112.112
8.8.8.8,8.8.4.4
nodeippool: Jail pool IP address range (networks for jails)
Hint: use space as delimiter for multiple networks, e.g.: 10.0.0.0/16 151.106.27.106/24
192.168.0.0/24
nat_enable: Enable NAT for RFC1918 networks?
[yes(1) or no(0)]
0
fbsdrepo: Use official FreeBSD repository? When no (0) the repository of CBSD is preferred (useful for stable=1) for fetching base/kernel?
[yes(1) or no(0)]
1
zfsfeat: You are running on a ZFS-based system. Enable ZFS feature?
[yes(1) or no(0)]
1
parallel: Parallel mode stop/start ?
(0 - no parallel or positive value (in seconds) as timeout for next parallel sequence) e.g: 5
0
stable: Use STABLE branch instead of RELEASE by default? Attention: only the CBSD repository has a binary base for STABLE branch ?
(STABLE_X instead of RELEASE_X_Y branch for base/kernel will be used), e.g.: 0 (use release)
0
sqlreplica: Enable sqlite3 replication to remote nodes ?
(0 - no replica, 1 - try to replicate all local events to remote nodes) e.g: 1
0
statsd_bhyve_enable: Configure CBSD statsd services for collect RACCT bhyve statistics? ?
(EXPERIMENTAL FEATURE)? e.g: 0
0
statsd_jail_enable: Configure CBSD statsd services for collect RACCT jail statistics? ?
(EXPERIMENTAL FEATURE)? e.g: 0
0
statsd_hoster_enable: Configure CBSD statsd services for collect RACCT hoster statistics? ?
(EXPERIMENTAL FEATURE)? e.g: 0
0
[Stage 6: authentication keys]
Generating public/private rsa key pair.
Your identification has been saved in /miscdata/jails/.ssh/8a3574aa0ec0ad3056e7dcf0f48adb01.id_rsa.
Your public key has been saved in /miscdata/jails/.ssh/8a3574aa0ec0ad3056e7dcf0f48adb01.id_rsa.pub.
The key fingerprint is:
SHA256:bZM/lo6lx40vE48MxZea1KQMKYIBq3HyPWQrF0xn980 root@hostname.org
The key's randomart image is:
+---[RSA 2048]----+
| ..ooo . . |
| +.o....oo . |
|o o = . ..+E+ . |
| * + o . .* + |
|. o = S =o + |
| o . ..o+. |
| +** |
| *O.o |
| o..+. |
+----[SHA256]-----+
[Stage 7: modules]
Installing module pkg.d cmd: pkg
Installing module bsdconf.d cmd: tzsetup
Installing module bsdconf.d cmd: ssh
Installing module bsdconf.d cmd: ftp
Installing module bsdconf.d cmd: adduser
Installing module bsdconf.d cmd: passwd
Installing module bsdconf.d cmd: service
Installing module bsdconf.d cmd: sysrc
Installing module bsdconf.d cmd: userlist
Installing module bsdconf.d cmd: grouplist
Installing module bsdconf.d cmd: adduser-tui
Installing module bsdconf.d cmd: pw
Installing module bsdconf.d cmd: cloudinit
Installing module zfsinstall.d cmd: zfsinstall
[Stage 9: cleanup]
* Remove obsolete files...
Configure RSYNC services for jail migration?
[yes(1) or no(0)]
0
cbsdrsyncd_enable: -> YES
Do you want to enable RACCT feature for resource accounting?
[yes(1) or no(0)]
0
Shall i modify the /etc/rc.conf to sets cbsdd_enable=YES ?
[yes(1) or no(0)]
0
cbsdd_enable: -> NO
Shall i modify the /etc/rc.conf to sets rcshutdown_timeout="900"?
[yes(1) or no(0)]
0
rcshutdown_timeout: 90 -> 900
[Stage X: upgrading]
* Insert default topology into vm_cpu_topology table
* Insert small1 group into vmpackage table
>>> Done
First CBSD initialization complete.
Now your can run:
service cbsdd start
to run CBSD services.
For change initenv settings in next time, use:
cbsd initenv-tui
Also don't forget to execute:
cbsd initenv
every time when you upgrade CBSD version.
preseedinit: Would you like a config for "cbsd init" preseed to be printed?
[yes(1) or no(0)]
0
Запускаем cbsd демона. Запускаем onestart, т.к. мы не добавили cbsd в автозапуск при загрузке системы, т.к. у нас зашифрованы разделы с данными гостевых ОС.
root@:~ # service cbsdd onestart
При загрузке сервера нужно к нему подключится по SSH (именно поэтому системный раздел не зашифрован, что бы загрузка системы происходила успешно без всяких там VNC/IPMI вводов паролей) и расшфифровать наши дисковые разделы скриптом ./root/attach_disks.sh
.
При инициализации cbsd мы указали что 192.168.0.0/24 будет подсетью для наших клеток. Ту же подсеть я планирую использовать и для полной виртуализации. Закрутим пару клеток на хосте, но перед этим настроим огненную стену, что бы NAT для клеток работал через хоста.
root@:~ # sysrc pf_enable=YES
pf_enable: NO -> YES
root@:~ # service pf start
/etc/rc.d/pf: WARNING: /etc/pf.conf is not readable.
создаем файл правил для фаервола
root@:~ # ee /etc/pf.conf
IF_PUBLIC="igb0"
IP_PUBLIC="X.X.X.X"
JAIL_IP_POOL="192.168.0.0/24"
icmp_types="echoreq"
set limit { states 100000, frags 20000, src-nodes 20000 }
set skip on lo0
scrub in all
#NAT for others
nat pass on $IF_PUBLIC from $JAIL_IP_POOL to any -> $IP_PUBLIC
## Jail HTTP/S port forward
IP_JAIL="192.168.0.2"
PORT_JAIL="{ 80, 443 }"
rdr pass on $IF_PUBLIC proto tcp from any to $IP_PUBLIC port $PORT_JAIL -> $IP_JAIL
загружаем правила
root@:~ # service pf start
Enabling pf.
root@:~ # pfctl -f /etc/pf.conf
Ну, а теперь создаем клетки
root@:~ # cbsd jconstruct-tui
Выбираем скачивание с репозитория базы для клеток
и еще пару клеток посредством root@:~ # cbsd jconstruct-tui
затем клетки запускаем
root@:~ # cbsd jstart webapp
и внутри уже делаем что хотим
root@:~ # cbsd jlogin webapp
webapp:/root@[12:58] # pkg install nginx
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
Updating database digests format: 100%
The following 2 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
nginx: 1.16.1_4,2
pcre: 8.43_2
Number of packages to be installed: 2
The process will require 8 MiB more space.
2 MiB to be downloaded.
Proceed with this action? [y/N]: y
[webapp.host.org] [1/2] Fetching nginx-1.16.1_4,2.txz: 100% 442 KiB 452.8kB/s 00:01
[webapp.host.org] [2/2] Fetching pcre-8.43_2.txz: 100% 1 MiB 638.0kB/s 00:02
Checking integrity... done (0 conflicting)
[webapp.host.org] [1/2] Installing pcre-8.43_2...
[webapp.host.org] [1/2] Extracting pcre-8.43_2: 100%
[webapp.host.org] [2/2] Installing nginx-1.16.1_4,2...
===> Creating groups.
Using existing group 'www'.
===> Creating users
Using existing user 'www'.
[webapp.host.org] [2/2] Extracting nginx-1.16.1_4,2: 100%
=====
Message from nginx-1.16.1_4,2:
--
Recent version of the NGINX introduces dynamic modules support. In
FreeBSD ports tree this feature was enabled by default with the DSO
knob. Several vendor's and third-party modules have been converted
to dynamic modules. Unset the DSO knob builds an NGINX without
dynamic modules support.
To load a module at runtime, include the new `load_module'
directive in the main context, specifying the path to the shared
object file for the module, enclosed in quotation marks. When you
reload the configuration or restart NGINX, the module is loaded in.
It is possible to specify a path relative to the source directory,
or a full path, please see
https://www.nginx.com/blog/dynamic-modules-nginx-1-9-11/ and
http://nginx.org/en/docs/ngx_core_module.html#load_module for
details.
Default path for the NGINX dynamic modules is
/usr/local/libexec/nginx.
webapp:/root@[12:59] # sysrc nginx_enable=YES
nginx_enable: -> YES
webapp:/root@[12:59] # service nginx start
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
Starting nginx.
webapp:/root@[DING!] # sockstat -l4
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS
www nginx 27982 6 tcp4 192.168.0.2:80 *:*
root nginx 27981 6 tcp4 192.168.0.2:80 *:*
root sendmail 25361 4 tcp4 192.168.0.2:25 *:*
webapp:/root@[13:00] #
Вот так выглядит типичный зоопарк клеток:
root@:~ # jls
JID IP Address Hostname Path
1 192.168.0.1 tor.host.org /miscdata/jails/jails/tor
2 192.168.0.2 webapp.host.org /miscdata/jails/jails/webapp
3 192.168.0.3 bitcoind.host.org /miscdata/jails/jails/bitcoind
4 192.168.0.4 ethd.host.org /miscdata/jails/jails/ethd
В webapp клетке у нас слушает nginx
, а доступен он извне как раз засчет записи в /etc/pf.conf
Полная виртуализация
Для полной виртуализации нужно подключить доп. модули ядра. Возьмем наш сервер с тремя дисками
В /etc/rc.conf
включим режим роутера и настроим ИП хоста в подсети клеток и виртуалок
ifconfig_igb0_alias="inet 192.168.0.1 netmask 255.255.255.0"
gateway_enable="YES"
root@hyper:~ # echo 'vmm_load="YES"' >> /boot/loader.conf
root@hyper:~ # echo 'kld_list="vmm if_tap if_bridge nmdm"' >> /etc/rc.conf
root@hyper:~ # reboot
Теперь настроим фаервол, похожим образом как с хостом только для клеток.
IF_PUBLIC=«igb0»
IP_PUBLIC=«Y.Y. Y.Y»
JAIL_IP_POOL=»192.168.0.0/24»
icmp_types=«echoreq»
set limit { states 100000, frags 20000, src-nodes 20000 }
set skip on lo0
scrub in all
nat pass on $IF_PUBLIC from $JAIL_IP_POOL to any → $IP_PUBLIC
Jail HTTP/S port forward
IP_JAIL=»192.168.0.2»
PORT_JAIL=»{ 80, 443 }»
rdr pass on $IF_PUBLIC proto tcp from any to $IP_PUBLIC port $PORT_JAIL → $IP_JAIL
root@hyper:~ # sysrc pf_enable=YES
pf_enable: NO -> YES
root@hyper:~ # service pf start
Enabling pf.
Такс, теперь создаем виртуалку
root@hyper:~ # cbsd bconstruct-tui
Затем нам надо подключится по VNC к порту хоста 5900, я для этого использую проброс портов SSH
ssh hyperhost -L 5900:localhost:5900
Устанавливаем систему. IP адрес можно выбрать какойнить 192.168.0.100.
Добавим в /etc/pf.conf
форвардинг SSH
## VM SSH port forward
IP_VM="192.168.0.100"
PORT_VM="{ 22100 }"
rdr pass on $IF_PUBLIC proto tcp from any to $IP_PUBLIC port $PORT_VM -> $IP_VM port 22
И вот мы можем подключится к нашему дебиану. Но перед этим, сделаем одну вещь.
root@hyper:~ # zfs list
NAME USED AVAIL REFER MOUNTPOINT
safestore 5.89G 804G 29.3K /safestore
safestore/jails 5.89G 804G 4.46G /safestore/jails
safestore/jails/debian 920M 804G 208K /safestore/jails/vm/debian
safestore/jails/debian/dsk1.vhd 919M 804G 919M -
safestore/jails/linuxjail 329M 804G 329M /safestore/jails/jails-data/linuxjail-data
safestore/jails/nginx 89.2M 804G 89.2M /safestore/jails/jails-data/nginx-data
safestore/jails/tor 117M 804G 117M /safestore/jails/jails-data/tor-data
zsystem 1.85G 21.9G 88K none
zsystem/root 1.85G 21.9G 1.20G /
zsystem/root/tmp 120K 21.9G 120K /tmp
zsystem/root/var 662M 21.9G 662M /var
root@hyper:~ # zfs snap safestore/jails/debian/dsk1.vhd@fresh
Виртулка дебиана сейчас занимает неполный гигабайт. Сделали снапшот свежей системы.
Чайная пауза — Ansible
Мы ведь хотим управлять нашим виртуальным хозяйством будь то 3, будь то 1300 инстансов. Для универсального управления нашими инстансами мы напишем простые сценарии.
[hyper-debian-group]
hyper-debian
[hyper-group]
hyper
[hyper-group:vars]
ansible_python_interpreter=/usr/local/bin/python3.7
Host hyper
Hostname Y.Y.Y.Y
User root
Port 33696
Host hyper-debian
Hostname Y.Y.Y.Y
User root
Port 22100
- hosts: hyper-debian
gather_facts: no
tasks:
- name: Install a list of misc packages
apt:
update_cache: yes
pkg:
- apt-transport-https
- ca-certificates
- curl
- gnupg2
- software-properties-common
- name: Add Docker repo
shell: curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
- name: Add Docker repo
shell: apt-key fingerprint 0EBFCD88
- name: Do some linux repo magic
shell: add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
- name: Install Docker CE suite & composer
apt:
update_cache: yes
pkg:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose
- name: Update all packages to the latest version
apt:
upgrade: dist
- hosts: hyper
gather_facts: no
tasks:
- name: stop debian
shell: cbsd bstop debian
- name: list ZFS datasets
shell: zfs list | grep debian
register: zfslist
- debug: var=zfslist.stdout_lines
- name: rollback debian
shell: zfs rollback safestore/jails/debian/dsk1.vhd@fresh
- name: list ZFS datasets
shell: zfs list | grep debian
register: zfslist
- debug: var=zfslist.stdout_lines
- name: start debian
shell: cbsd bstart debian
Вот так и получился свой маленький, но удаленький TerraForm
Ну, а теперь всё можно красиво управлять
[user@localhost ~]$ ansible-playbook deploy-docker.yml
PLAY [hyper-debian] *******************************************************************************************************
TASK [Install a list of misc packages] *********************************************************************************************
changed: [hyper-debian]
TASK [Add Docker repo] ********************************************************************************************************
changed: [hyper-debian]
TASK [Add Docker repo] ********************************************************************************************************
changed: [hyper-debian]
TASK [Do some linux repo magic] ********************************************************************************************************changed: [hyper-debian]
TASK [Install Docker CE suite & composer] ********************************************************************************************************changed: [hyper-debian]
TASK [Update all packages to the latest version] ********************************************************************************************************ok: [hyper-debian]
PLAY RECAP ********************************************************************************************************hyper-debian : ok=6 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Внутри нашего дебиана теперь можем запустит наше приложение или любой произвольный сервис в докере, например почтовый комплекс iRedMail.
docker run --privileged -p 80:80 -p 443:443 \
-e "DOMAIN=example.com" -e "HOSTNAME=mail" \
-e "MYSQL_ROOT_PASSWORD=password" \
-e "SOGO_WORKERS=1" \
-e "TIMEZONE=Europe/Prague" \
-e "POSTMASTER_PASSWORD=password" \
-e "IREDAPD_PLUGINS=['reject_null_sender', 'reject_sender_login_mismatch', 'greylisting', 'throttle', 'amavisd_wblist', 'sql_alias_access_policy']" \
-v PATH/mysql:/var/lib/mysql \
-v PATH/vmail:/var/vmail \
-v PATH/clamav:/var/lib/clamav \
--name=iredmail lejmr/iredmail:mysql-latest
Ну, а зачем нам нужен был снапшот? А что бы мы могли опять что-нибудь развернуть на чистом Debian.
[user@localhost ~]$ ansible-playbook rollback-debian.yml
PLAY [hyper] ********************************************************************************************************
TASK [stop debian] ********************************************************************************************************changed: [hyper]
TASK [list ZFS datasets] ********************************************************************************************************changed: [hyper]
TASK [debug] ********************************************************************************************************ok: [hyper] => {
"zfslist.stdout_lines": [
"safestore/jails/debian 2.65G 803G 206K /safestore/jails/vm/debian",
"safestore/jails/debian/dsk1.vhd 2.65G 803G 2.34G -"
]
}
TASK [rollback debian] ********************************************************************************************************changed: [hyper]
TASK [list ZFS datasets] ********************************************************************************************************changed: [hyper]
TASK [debug] ********************************************************************************************************ok: [hyper] => {
"zfslist.stdout_lines": [
"safestore/jails/debian 920M 804G 206K /safestore/jails/vm/debian",
"safestore/jails/debian/dsk1.vhd 919M 804G 919M -"
]
}
TASK [start debian] ********************************************************************************************************changed: [hyper]
PLAY RECAP ********************************************************************************************************hyper : ok=7 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Я опустил другие фишки гипервизора как учет потребления ресурса и клонирование инстансов, может я выделю время под написание еще одной части руководства.
Если понравился материал и он Вам пригодился, можно проголосовать рублём, заслав биткойна на 1baysxTdXkwZnBosDdL1veb2zWDo6DC5b
Удачи в экспериментах!