Перенос FreeBSD сервера с ZFS root файловой системой на новый pool

Попались мне под руку пара винтов побольше и еще один компьютер. Наслышан я о чудесах и простоте переноса ZFS и решил попробовать.

Задача перенести сервер под FreBSD, что уже работает, на новый сервер с помощью ZFS.

Ну и так сказать своеобразный бекап.

Бекап это сомнительно. Потому как на исходном тоже стоит всё на «мирроре»

Создаем ключ

# ssh-keygen

# cat /root/.ssh/id_rsa.pub | ssh root@127.0.0.1 ‘cat >> /root/.ssh/authorized_keys’

[root@server ~]# zpool list -v

NAME                                     SIZE  ALLOC   FREE    CAP  DEDUP  HEALTH  ALTROOT

Грузимся с CD и работаем в Live-режиме mfsBSD

# mkdir /root/.ssh

# camcontrol devlist

vi /etc/ssh/sshd_config 

В конфиге разрешаем root-у логиниться. 

Отключаем DNS проверку, чтобы побыстрее было немного. 

PermitRootLogin yes 

UseDNS no 

Задаем пароль для пользователя root: 

# passwd root 

Указываем интерфейсу нужный IP адрес 

ifconfig re0 add 127.0.0.0.1/24 

или dhclient re0

запускаем sshd: /etc/rc.d/sshd onestart

Далее создаём разделы…

# gpart create -s gpt ada0

# gpart add -b 40 -s 512k -t freebsd-boot ada0

# gpart add -s 8g -t freebsd-swap ada0

# gpart add -t freebsd-zfs ada0

[root@mfsbsd ~]# gpart show

=>        40  1953522976  ada0  GPT  (932G)

          40        1024     1  freebsd-boot  (512K)

        1064    16777216     2  freebsd-swap  (8.0G)

    16778280  1936744736     3  freebsd-zfs  (924G)

Создаем пул:

# zpool create -f tank /dev/ada0p3

Пул готов к приёму данных.

Переходим на сервер-источник. 

Создаем рекурсивный снапшот, который и будем передавать:

# zfs snapshot -r tank@21-04-13 

Передаем его на сервер приемник:

# screen

# zfs send -vR tank@21-04-13 | ssh 127.0.0.0.1 zfs recv -F tank

Ждем окончания передачи. У меня примерно 213 GB передавалось около 12 часов.

Переходим опять на сервер-приемник root@mfsbsd:~ 

# csh

# zfs list -t snapshot 

# zpool list

# mkdir /tmp/tank

# mount -t zfs tank/root /tmp/tank

# gpart bootcode -b /tmp/tank/boot/pmbr -p /tmp/tank/boot/gptzfsboot -i 1 ada0

# chroot /tmp/tank

Ввиду того, что с сервера-источника нам передалось всё, то поправим немного /etc/fstab. Дело в том, что своп на источнике подключен в fstab по меткам gpt. 

Настройка по меткам

# cat /etc/fstab

/dev/gptid/dadde41e-913d-11eb-92f9-ac1f6bd74a62 none swap sw 0 0

# gpart list

2. Name: ada0p2

   Mediasize: 8589934592 (8.0G)

   Sector Size: 512

   Stripe Size: 4096

   Stripe Offset: 0

   Mode: r0w0e0

   defimedia: HD(2,GPT,fffe5673-d7ad-11dd-a24d-50465d8ab2bb,0x428,0x1000000)

   rawuuid: fffe5673-d7ad-11dd-a24d-50465d8ab2bb

   rawtype: 516e7cb5-6ecf-11d6-8ff8-00022d09712b

   label: (null)

   length: 8589934592

   offset: 544768

   type: freebsd-swap

   index: 2

   end: 16778279

   start: 1064

# vi /etc/fstab 

/dev/gptid/dadde41e-913d-11eb-92f9-ac1f6bd74a62 none swap sw 0 0 >

fffe5673-d7ad-11dd-a24d-50465d8ab2bb

Второй вариант без меток

[root@msfbsd ~]# cat /etc/fstab 

# Device Mountpoint FStype Options Dump Pass#

### /dev/gpt/swap0 none swap sw 0 0

>

/dev/ada0p2 none swap sw 0 0

Правим файлы в скопированной системе.

[root@mfsbsd ~]#

# zfs umount -a

# exit

Скажем кто у нас bootfs и сделаем экспорт:

# zpool set bootfs=tank/root tank

# zpool export tank

# shutdown -r now

На источнике можно удалить ненужный снапшот: 

# zfs destroy -r tank@move

Взял на afabla немного подправил на свой лад. Автору респект!

msfBSD — FreeBSD + ZFS + cbsd

mfsBSD и mfslinux
Это набор сценариев, который генерирует загрузочный образ (и/или ISO-файл), который создает рабочую минимальную установку FreeBSD (mfsBSD) или Linux (mfslinux).
Он полностью загружен в память. Mfslinux основан на OpenWRT.

https://github.com/mmatuska/mfsbsd
Martin Matuška
https://mfsbsd.vx.sk/
https://blog.vx.sk/archives/category/1-freebsd

В этой инструкции я хочу пролить свет на то, насколько просто и элегантно возможно устанавливать 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. Очень удобный инструмент для изоляции процессов. Хост система может хостить клетки любой версии ниже своей. Клетки можно переносить между хостами, снапшотить и откатывать хоть на лету. Стоит использовать клектки когда это возможно, таким образом оптимально использовать ресурс системы, не тратя его на полную виртуализацию.

image

bhyve в свою очередь это полноценный гипервизор, которым можно поднимать всякие Ubuntu и докеры.

image

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 демона. Запускаем 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
например установить nginx

Вот так выглядит типичный зоопарк клеток:

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

Теперь настроим фаервол, похожим образом как с хостом только для клеток.
/etc/pf.conf

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 инстансов. Для универсального управления нашими инстансами мы напишем простые сценарии.
/etc/ansible/hosts
~/.ssh/config
~/deploy-docker.yml
~/rollback-debian.yml

Вот так и получился свой маленький, но удаленький 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   

Я опустил другие фишки гипервизора как учет потребления ресурса и клонирование инстансов, может я выделю время под написание еще одной части руководства. Источник инструкции: https://habr.com/en/post/479192/