Написано: 2020-01-11.
Эта статья является примером установки FreeBSD без использования sysinstall, позволяя делать более тонкую настройку, а также понимая насколько просто и интуитивно просто работать с FreeBSD.
Использовать голый ZFS легко. Создаём pool на диске и копируем zfsboot загрузчик в зарезервированные пустые области ZFS ФС:
zpool create zroot ada0 dd if=/boot/zfsboot of=/dev/ada0 count=1 dd if=/boot/zfsboot of=/dev/ada0 iseek=1 oseek=1024
Выглядит красиво и минималистично, работает, но я бы не стал рекомендовать такой подход, как минимум, потому что swap придётся размещать внутри zvol-а, что существенный overhead. Если же swap захочется делать, то желательно использовать такие параметры для zvol-а:
volblocksize=4k # размер страницы памяти amd64 sync=always # сразу помещать данные на диск, не держа в памяти logbias=throughput # сразу писать на диск checkpoint-ом, без ZIL primarycache=metadata # держать в кэше только метаданные
Создаём GPT схему, разделы для загрузчика, swap, корня. Всё с метками (label). Выравниванием по 4 KiB границе, так как современные диски имеют 4K физические сектора и не выровненный доступ катастрофично может сказаться на производительности.
gpart create -s GPT diskid/DISK-SERIAL gpart add -t freebsd-boot -a 4K -s 512K -l SERIAL-BOOT diskid/DISK-SERIAL gpart add -t freebsd-swap -s 2G -l SERIAL-SWAP diskid/DISK-SERIAL gpart add -t freebsd-zfs -l SERIAL-ROOT diskid/DISK-SERIAL
Устанавливаем MBR загрузчик и загрузчик с ZFS раздела.
gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 diskid/DISK-SERIAL
UEFI режим загрузки может понадобиться например в таком случае:
modesetting
драйвер Xorg работает только если ОС
загружена в UEFI режиме. В противном случае, доступен только
VESA режим, ощутимо более медленный, без XrandR и ускорения.
В FreeBSD есть заранее подготовленный FAT32 образ с EFI загрузчиком для установки прямо в раздел диска. В целом всё аналогично предыдущему пункту, разница только в установке загрузчика:
gpart create -s GPT diskid/DISK-SERIAL gpart add -t efi -s 800K diskid/DISK-SERIAL gpart add -t freebsd-swap -a 4K -s 2G -l SERIAL-SWAP diskid/DISK-SERIAL gpart add -t freebsd-zfs -l SERIAL-ROOT diskid/DISK-SERIAL gpart bootcode -p /boot/boot1.efifat -i 1 diskid/DISK-SERIAL
Также у меня есть ещё заметка о том как я создавал EFI раздел загрузки:
gpart add -t efi -s XXX diskid/DISK-SERIAL newfs_msdos -F 32 -c 1 diskid/DISK-SERIALp1 mount -t msdosfs /dev/diskid/DISK-SERIALp1 /mnt mkdir -p /mnt/EFI/BOOT cp /boot/loader.efi /mnt/EFI/BOOT/BOOTx64.efi umount /mnt
Если хочется использовать UFS2 вместо ZFS, то тип разделов
freebsd-zfs
стоит заменить на freebsd-ufs
, а загрузчик
на gptboot.
sysctl vfs.zfs.min_auto_ashift=12 zpool create zroot gpt/SERIAL-ROOT zfs set checksum=sha256 compression=lz4 atime=off mountpoint=/mnt zroot
Если нужно создать сразу зеркало, то можно сразу указать: zpool create zroot mirror gpt/SERIAL1-ROOT gpt/SERIAL2-ROOT.
Всюду и везде я рекомендую использовать диски только с метками, без
нестабильной нумерации вида adaX или отталкивающиеся от
географии. Проблем с diskid/XXX (где используется серийный
номер диска) я с ходу не вижу, кроме как его смены, если я сделаю
dd
копию на другой диск. Метки записанные прямо на диск
будут точно неизменными.
Для меток можно использовать glabel
, который в последний
сектор диска запишет саму метку и диск будет доступен по
label/XXX пути. Но это BSD-специфичное решение. GPT раздел с
меткой будет точно работать и виден везде. Кроме того, раздел можно
выровнять по 4K секторам. Кроме того, некоторые люди задают раздел
немного меньшего размера (например на гигабайт), чтобы при
добавлении диска стороннего производителя, небольшое несовпадение
размеров нивелировалось. Поэтому GPT очень удобен всем этим. Solaris
документация рекомендует отдавать диск полностью под управление ZFS,
но, насколько слышал, это связано исключительно с тем, что только
так ZFS сможет отключить write кэш диска. В FreeBSD такой проблемы
нет. То есть, чтобы делать ZFS pool на дисках (не для установочной
системы, а для хранилищ), стоит создавать GPT и использовать метки:
gpart create -s GPT diskid/SERIAL1 gpart create -s GPT diskid/SERIAL2 gpart add -t freebsd-zfs -a 4K -l SERIAL1-STORAGE diskid/SERIAL1 gpart add -t freebsd-zfs -a 4K -l SERIAL2-STORAGE diskid/SERIAL2 vfs.zfs.min_auto_ashift=12 zpool create storage gpt/SERIAL1-STORAGE gpt/SERIAL2-STORAGE
Для zroot pool-а я сразу же указал использование сжатия LZ4. Не использовать сжатие (быстрое и с fallback-ом при несжимаемых данных – LZ4) почти никогда не имеет смысла. А для корневого раздела оно точно даст выигрыш, так как данные там хорошо сжимаемы.
atime тоже отключён, так как я с ходу и не вспомню где для корня системы он бы пригодился. Если какая-то программа опирается на него, то стоит для её рабочих директорий сделать отдельный dataset и в нём atime включить, не применяя для всего zroot.
Checksum-ы я всегда использую только криптографические. Для всех
pool-ов и dataset-ов кроме корневого я выбираю skein
или,
если бы его не было, sha512
– первый существенно быстрее
остальных, а второй быстрее работает на 64-бит системах чем
sha256
. Но FreeBSD загрузчик поддерживает только
sha256
. Для dataset-ов внутри zroot можно указать
более быстрые checksum-ы. Использование криптографически сильных, а
не fletcher4
, контрольных сумм это уверенность и возможность
дедупликации (если её захочется).
Критично использование корректного ashift. Его значение является
степенью двойки и указывает на размер сектора диска. Важно его
задать во время создания pool, так как позже его уже не изменить.
Проблема в том, что современные не SAS/enterprise диски врут по
поводу размера своего сектора: на самом деле он уже давно 4K, но
диски говорят, для совместимости с убогими Windows системами, что он
512 байт. ZFS честно смотрит на то, что говорят диски. Можно черезе
sysctl
указать минимальный ashift или обмануть ZFS можно
создав NOP диск с изменённым размером сектора:
gnop create -S4K gpt/LABEL zpool ... gpt/LABEL.nop
Если планируется использование зашифрованных GELI дисков, то тоже нельзя забывать про 4K размеры секторов уже самого GELI, так как это существенно сократит накладные расходы CPU на генерирование ключей для каждого сектора: geli init -s 4K ....
Если хочется использовать UFS2 (для некритичных слабых систем), то ФС я делаю просто: newfs -Ut [-E] /dev/gpt/SERIAL-ROOT. Включается soft-updates, существенно повышающий производительность и TRIM, актуальный для SSD дисков (в ZFS TRIM автоматически делается). -E форсирует полную TRIM очистку диска, что может быть полезно для SSD.
for what in base kernel doc src ports ; do tar xfC /usr/freebsd-dist/$what.txz /mnt done
Минимально нужны только base и kernel. Далее всё делается в пределах /mnt:
chroot /mnt
# cat > /boot/loader.conf <<EOF zfs_load="YES" vfs.root.mountfrom="zfs:zroot" # У HyperThreading всё очень плохо в плане стабильности и безопасности machdep.hyperthreading_allowed=0 # Meltdown/Spectre mitigation отключение vm.pmap.pti=0 hw.ibrs_disable=1 aesni_load="YES" # чтобы GELI сразу ускорялся # Можно забыть о том, что ipfw по умолчанию делает deny #net.inet.ip.fw.default_to_accept=0 EOF
# cat > /etc/fstab <<EOF tmpfs /tmp tmpfs rw,nosuid,mode=1777 0 0 fdescfs /dev/fd fdescfs rw 0 0 proc /proc procfs rw 0 0 /dev/gpt/SERIAL-SWAP.eli none swap sw 0 0 EOF
Важно указать .eli в конце пути к swap разделу – это заставит шифровать GELI этот раздел одноразовым ключом.
sysctl
# cat >> /etc/sysctl.conf <<EOF kern.msgbuf_show_timestamp=1 # время в dmesg выводе kern.cam.ada.write_cache=0 # с ZFS никакой буферизации не надо security.bsd.stack_guard_page=1 security.bsd.see_other_uids=0 security.bsd.unprivileged_idprio=1 # idprio я часто использую kern.randompid=1 #security.jail.allow_raw_sockets=1 # если хочется делать ping в jail net.inet.tcp.tso=0 # TSO на маршрутизаторах только вредит, отключаю везде net.inet.tcp.drop_synfin=1 net.inet6.ip6.use_tempaddr=0 # не делать рандомизацию link-local адресов # Отключить возможность сжатия трафика в IPsec, а также позволять # фильтровать его firewall-ом net.inet.ipcomp.ipcomp_enable=0 net.inet.ipsec.filtertunnel=1 net.inet6.ipsec6.filtertunnel=1 # Позволять пользователям открывать TAP интерфейсы и сразу их поднимать #net.link.tap.user_open=1 #net.link.tap.up_on_open=1 #vfs.usermount=1 # может захотеться делать mount-ы из под пользователя # Отключить prefetch, если мы на SSD #vfs.zfs.prefetch_disable=1 #vfs.zfs.no_scrub_prefetch=1 vfs.nfsd.server_min_nfsvers=4 # форсированное использование только NFSv4 EOF
# cat > /etc/rc.conf <<EOF hostname="stargrave.org" zfs_enable="YES" clear_tmp_enable="YES" # на всякий пожарный, хотя у нас tmpfs sshd_enable="YES" ntpd_enable="YES" # В приоритете использовать IPv6 адреса полученные от DNS ip6addrctl_enable="YES" ip6addrctl_policy="ipv6_prefer" ipv6_ipv4mapping="YES" # удобно иметь IPv4 доступными демонов слушающих на [::] nfsv4_server_enable="YES" # нужно для работы NFSv4 # Первым делом я ставлю Postfix вместо Sendmail postfix_enable="YES" sendmail_enable="NO" sendmail_submit_enable="NO" sendmail_outbound_enable="NO" sendmail_msp_queue_enable="NO" firewall_enable="YES" firewall_script="/etc/ipfw.rules" # Чтобы после перезагрузки поднялась сеть ifconfig_em0="up" ifconfig_em0_ipv6="inet6 -ifdisabled" EOF
tzsetup /usr/share/zoneinfo/Europe/Moscow
ipfw
firewall# cat > /etc/ipfw.rules <<EOF #!/bin/sh -x ipfw -f flush ipfw -f table all destroy ipfw zero ipfw disable one_pass add="ipfw add" \$add deny all from any to any frag \$add allow { icmp or icmp6 } from any to any keep-state \$add allow esp from any to any keep-state \$add allow udp from any to any isakmp keep-state \$add allow all from any to any via lo0 \$add deny all from any to any not verrevpath in via em0 \$add check-state \$add deny tcp from any to any established via em0 \$add allow tcp from any to me ssh keep-state \$add allow all from me to any out keep-state #\$add deny log all from any to any EOF # chmod 600 /etc/ipfw.rules
sed -i.tmp "/periodic/ s/^/#/" /etc/crontab
echo server gw.stargrave.org iburst >> /etc/ntp.conf
echo V4: / > /etc/exports
zfs umount zroot zfs set mountpoint=none zroot zpool export zroot reboot
После перезагрузки можно установить Postfix и сделать его тривиальную настройку, использующую relay для всей корреспонденции:
# cat >> /usr/local/etc/postfix/main.cfg <<EOF inet_interfaces = loopback-only mynetworks_style = host relayhost = [gw.stargrave.org] EOF