Обзор
В FreeBSD есть три уровня конфигурации ядра, и важно понимать, когда какой применяется. Первый — sysctl(8): runtime-тюнинг параметров работающего ядра, без перезагрузки и пересборки. Второй — loader.conf: параметры, задаваемые на этапе загрузки (loader tunables), когда ядро уже загружено, но ещё не запущено полностью. Третий — пересборка ядра из исходников с кастомным конфигурационным файлом.
На практике 95% задач решаются на первых двух уровнях. Пересборка ядра — редкая операция, особенно на VPS, но понимание её механики полезно для осознания архитектуры системы. Статья построена от повседневного к редкому: sysctl → loader.conf → кастомное ядро.
sysctl: runtime-тюнинг ядра
Архитектура
sysctl(8) — интерфейс к дереву параметров ядра, организованных в иерархическую структуру MIB (Management Information Base). Каждый параметр — это точка в дереве, доступная для чтения, а часто и для записи в runtime. В современном ядре FreeBSD доступно более 5000 переменных.
Параметры организованы по подсистемам: kern (ядро: процессы, лимиты, планировщик), vm (виртуальная память, swap), net (сетевой стек), vfs (файловые системы, включая ZFS), hw (оборудование), security (политики безопасности), debug (отладка), machdep (платформо-специфичные).
Базовое использование
sysctl -a # все переменные (5000+)
sysctl kern.ostype # конкретная переменная
sysctl kern # все переменные в ветке kern.*
sysctl -d kern.maxfiles # описание переменной
sysctl kern.maxfiles=200000 # установить значение (до перезагрузки)
sysctl -t kern.maxfiles # показать тип переменной
Флаг -d особенно полезен — многие переменные имеют встроенное описание, что избавляет от поиска в документации.
Персистентность: sysctl.conf и loader.conf
Изменения через sysctl живут до перезагрузки. Для персистентности:
/etc/sysctl.conf — применяется при загрузке rc-системой (после старта ядра). Сюда попадают параметры, которые можно менять в runtime.
# /etc/sysctl.conf
kern.maxfiles=200000
net.inet.ip.forwarding=1
security.bsd.see_other_uids=0
/boot/loader.conf — применяется загрузчиком до полного старта ядра. Сюда попадают read-only параметры (tunables), которые нельзя изменить в runtime. Их можно узнать по тому, что sysctl покажет значение, но попытка записи вернёт ошибку "read-only".
# /boot/loader.conf
kern.maxusers=256
kern.ipc.nmbclusters=65536
vfs.zfs.arc.max="4G"
Как определить, куда класть параметр: попробовать sysctl переменная=значение. Если сработало — /etc/sysctl.conf. Если "read-only" — /boot/loader.conf. Деление не строгое: часть переменных помечена CTLFLAG_RWTUN и задаётся обоими способами — так устроены vfs.zfs.arc.max и kern.ipc.nmbclusters. Полный список того, что вообще читается загрузчиком, даёт sysctl -T, а дефолтные значения loader tunables — /boot/defaults/loader.conf.
Практически важные sysctl по категориям
Сеть (net.inet.*)
# Размер очереди входящих соединений (дефолт 128 — мало для web-сервера)
kern.ipc.soacceptqueue=4096
# Буферы TCP
net.inet.tcp.recvspace=65536 # буфер приёма (дефолт 65536)
net.inet.tcp.sendspace=65536 # буфер отправки
# Диапазон эфемерных портов (для серверов с большим числом исходящих соединений)
net.inet.ip.portrange.first=1024
net.inet.ip.portrange.last=65535
# Быстрый recycling TIME_WAIT сокетов
net.inet.tcp.fast_finwait2_recycle=1
net.inet.tcp.finwait2_timeout=5000 # мс (дефолт 60000)
# IP forwarding (для роутера/firewall)
net.inet.ip.forwarding=1
# Congestion control алгоритм
net.inet.tcp.cc.algorithm=cubic # дефолт с 14.0; ещё newreno, htcp, dctcp, vegas, cdg
kern.ipc.soacceptqueue — один из первых параметров, который стоит увеличить на web-сервере. Дефолтное значение 128 может быть мало для обработки всплесков входящих соединений; старое имя kern.ipc.somaxconn осталось рабочим синонимом, но скрыто из вывода sysctl -a, и tuning(7) говорит уже только о soacceptqueue.
С FreeBSD 14.0 алгоритмом управления перегрузкой по умолчанию стал CUBIC вместо NewReno. BBR в этот список не входит — это не congestion control, а альтернативный TCP-стек (модуль tcp_bbr), который выбирается через net.inet.tcp.functions_default.
Лимиты ресурсов (kern.*)
# Максимальное число открытых файлов в системе
kern.maxfiles=200000
kern.maxfilesperproc=100000
# kern.maxusers — read-only, но влияет на автотюнинг многих параметров
# Устанавливается в /boot/loader.conf; автоматически вычисляется по RAM
# Явно задавать имеет смысл только если автотюнинг не устраивает
# Shared memory (для PostgreSQL, Redis и др.)
kern.ipc.shmmax=1073741824 # 1GB максимальный сегмент
kern.ipc.shmall=262144 # макс. страниц shared memory
kern.maxusers — историческая переменная, от которой масштабируются десятки других лимитов (таблицы процессов, сетевые буферы, mbuf'ы). Автоматически вычисляется по объёму RAM. Обычно трогать не нужно, но понимать её влияние полезно.
Безопасность (security.bsd.*)
# Непривилегированные пользователи не видят процессы других пользователей
security.bsd.see_other_uids=0
security.bsd.see_other_gids=0
# Не видят процессы в других jail'ах
security.bsd.see_jail_proc=0
# Непривилегированные пользователи не могут читать dmesg
security.bsd.unprivileged_read_msgbuf=0
# ASLR — с 13.2 включён по умолчанию для 64-битных бинарников
kern.elf64.aslr.enable=1
kern.elf64.aslr.pie_enable=1
# Рандомизация PID
kern.randompid=1
Эти параметры — low-hanging fruit для hardening'а VPS. see_other_uids=0 и see_jail_proc=0 существенно ограничивают разведку для потенциального атакующего.
ZFS (vfs.zfs.*)
# Максимальный размер ARC (кэш чтения ZFS)
# Меняется и в runtime, но обычно задаётся в loader.conf
vfs.zfs.arc.max="4G" # ограничить, если ZFS съедает всю RAM
vfs.zfs.arc.min="512M" # минимальный размер ARC
# Prefetch (полезен для последовательного чтения)
vfs.zfs.prefetch.disable=0 # 0 = включён (дефолт)
# Лимит транзакционных групп (влияет на latency записи)
vfs.zfs.txg.timeout=5 # секунд (дефолт 5)
vfs.zfs.arc.max — возможно, самый востребованный tunable на сервере с ZFS. По умолчанию ARC может занять почти всю RAM, что нормально для чистого файл-сервера, но проблематично если на той же машине работают приложения с собственными кэшами (PostgreSQL, Redis). Со времён OpenZFS 2.x у него два имени: старое vfs.zfs.arc_max продолжает работать, но помечено как LEGACY и с 15.1 печатает в лог предупреждение с советом перейти на vfs.zfs.arc.max. Вопреки распространённому убеждению, оба варианта пишутся не только из loader.conf: это CTLFLAG_RWTUN, то есть значение меняется и в runtime (уменьшить фактический размер ARC без давления на память при этом всё равно не выйдет).
Виртуальная память (vm.*)
# Overcommit: битовая маска, дефолт 0. Бит 0 — отказывать, когда резерв свопа
# превысит vm.swap_total; бит 1 — соблюдать RLIMIT_SWAP; бит 2 — считать
# доступной и физическую память, кроме wired и резерва
vm.overcommit=0
# Давление на swap (когда начинать использовать swap)
vm.swappiness=60 # нет прямого аналога в FreeBSD,
# но vm.pageout_oom_seq влияет на OOM
FreeBSD агрессивнее Linux в использовании RAM для кэширования, что обычно хорошо для серверных нагрузок.
Исследование параметров
# Найти все параметры, связанные с TCP
sysctl -a | grep tcp
# Показать все изменяемые параметры с описаниями в ветке net
sysctl -ad net
# Посмотреть текущие значения vs дефолтные
# (нет встроенной команды, но можно сравнить с чистой системой)
# Все loader-тюнабли (то, что имеет смысл класть в loader.conf)
sysctl -T
# Все переменные, изменяемые в runtime
sysctl -W
Полная документация: man 8 sysctl, man 5 sysctl.conf, а также man tuning — специализированная man-страница по тюнингу FreeBSD.
loader.conf: конфигурация загрузчика
Роль загрузчика
FreeBSD boot loader (loader(8)) — это этап между BIOS/UEFI и запуском ядра. Он загружает ядро, kernel modules и устанавливает начальные значения tunables. Конфигурация — в /boot/loader.conf, дефолты — в /boot/defaults/loader.conf.
Это единственное место для: параметров, которые должны быть установлены до старта ядра (read-only sysctl); загрузки kernel modules; выбора альтернативного ядра.
Типичные настройки
# /boot/loader.conf
# Загрузка модулей
if_bridge_load="YES" # модуль бриджинга
pf_load="YES" # модуль PF (можно и через rc.conf)
accf_http_load="YES" # accept filter для HTTP (nginx/Apache)
accf_data_load="YES" # accept filter для данных
# Tunables
kern.maxusers=256
kern.ipc.nmbclusters=65536 # сетевые mbuf-кластеры (для нагруженной сети)
vfs.zfs.arc.max="4294967296" # 4GB в байтах (или "4G")
# Задержка автозагрузки (секунды до автоматической загрузки ядра)
autoboot_delay="3" # дефолт 10; -1 = без задержки и без приглашения
# Console
kern.vty=vt # виртуальный терминал (vt vs sc)
Accept Filters
Accept filters — FreeBSD-специфичная оптимизация: ядро откладывает доставку входящего соединения приложению, пока не получит полный HTTP-запрос или блок данных. Это снижает количество переключений контекста и системных вызовов. Для web-серверов (nginx, Apache) accf_http даёт заметный выигрыш при высокой нагрузке.
# /boot/loader.conf
accf_http_load="YES"
accf_data_load="YES"
После этого nginx может использовать listen 80 accept_filter=httpready.
Kernel Modules
Большинство функциональности FreeBSD-ядра реализовано в загружаемых модулях (KLD — Kernel Loadable Modules). Это ключевое архитектурное решение: GENERIC-ядро содержит минимум статически вкомпилированного кода, а всё остальное загружается по необходимости.
kldstat # список загруженных модулей
kldload pf # загрузить модуль вручную
kldunload pf # выгрузить
Постоянная загрузка — через loader.conf (modulename_load="YES") или через rc-систему (многие rc-скрипты сами загружают нужные модули).
Модули хранятся в /boot/kernel/. При обновлении ядра через freebsd-update (или, на pkgbase-системе, через pkg upgrade) модули обновляются автоматически. Критично: модули должны быть синхронизированы с ядром — загрузка модуля от другой версии ядра может привести к панике.
Конфигурация и пересборка ядра
Когда это действительно нужно
На VPS — практически никогда. GENERIC-ядро покрывает подавляющее большинство сценариев, а загружаемые модули и sysctl обеспечивают необходимую гибкость.
Реальные причины для кастомного ядра: поддержка железа, отсутствующего в GENERIC (экзотические embedded-платформы); hardening через статическую компиляцию только нужных компонентов и запрет загрузки модулей (securelevel); включение функциональности, не существующей в виде модуля (дисциплины ALTQ, например, модулями не загружаются в принципе — это прямо сказано в altq(4)); радикальное уменьшение footprint для embedded-систем с ограниченной RAM.
Классические кандидаты из старых руководств — IPsec и MAC — из этого списка давно выпали: в GENERIC есть и options MAC, и options IPSEC_SUPPORT, который специально существует ради kldload ipsec. Для серверных задач на стандартном железе или VPS — sysctl + loader.conf достаточно.
Архитектура сборки
Исходники ядра — в /usr/src/sys/. Конфигурационные файлы — в /usr/src/sys/<arch>/conf/:
GENERIC — конфигурация ядра, которая поставляется по умолчанию. Никогда не редактируется напрямую — перезаписывается при обновлении исходников.
NOTES — документация всех доступных опций для данной архитектуры. ../../conf/NOTES — платформо-независимые опции. Вместе — полный справочник.
Кастомная конфигурация — копия GENERIC с модификациями:
cd /usr/src/sys/amd64/conf
cp GENERIC MYKERNEL
Рекомендуется хранить кастомный конфиг вне /usr/src (например, /root/kernels/MYKERNEL) и создавать симлинк — иначе можно потерять конфиг при пересоздании /usr/src.
Формат конфигурационного файла
ident MYKERNEL
# Включить всё из GENERIC как базу
include GENERIC
# Отключить ненужное
nooptions COMPAT_FREEBSD32 # убрать 32-bit совместимость
nodevice fdc # убрать floppy-контроллер
# Добавить нужное
options ALTQ # ALTQ для QoS в PF
options ALTQ_HFSC # HFSC scheduler
options IPSTEALTH # не декрементировать TTL при форвардинге
device pf # PF статически (вместо модуля)
device pflog
device pfsync # синхронизация состояний PF (для CARP)
include GENERIC + nooptions/nodevice — современный подход: базируемся на GENERIC и точечно модифицируем, вместо создания конфига с нуля. Это упрощает обновление — при изменении GENERIC кастомный конфиг автоматически наследует изменения.
Процесс сборки
cd /usr/src
make buildkernel KERNCONF=MYKERNEL # сборка (~20-90 мин в зависимости от CPU)
make installkernel KERNCONF=MYKERNEL # установка в /boot/kernel/
Старое ядро автоматически сохраняется в /boot/kernel.old/. Если новое ядро не загружается — в меню загрузчика можно выбрать kernel.old.
На системе, установленной пакетами базы (pkgbase; с 15.1 bsdinstall ставит систему так по умолчанию), make installkernel намеренно заблокирован — иначе база пакетов разошлась бы с содержимым /boot. Собранное ядро в этом случае либо упаковывают целью make update-packages KERNCONF=MYKERNEL и ставят через pkg, либо кладут в отдельный каталог через INSTKERNNAME и пробуют загрузиться в него через nextboot(8). Проверку можно снять переменной ALLOW_PKGBASE_INSTALLKERNEL в /etc/make.conf, но тогда pkg перестаёт отражать реальное состояние системы.
Для ускорения итераций: MODULES_OVERRIDE в /etc/make.conf позволяет собирать только нужные модули вместо всех.
securelevel: уровни безопасности ядра
FreeBSD поддерживает концепцию securelevel — монотонно возрастающий уровень ограничений ядра. После повышения securelevel его нельзя понизить без перезагрузки.
-1 — permanently insecure (дефолт). Никаких ограничений.
0 — insecure. Нет ограничений, но securelevel повышается до 1 при переходе в multi-user.
1 — secure. Запрещена загрузка/выгрузка модулей ядра, запись в /dev/mem и /dev/kmem, изменение файловых флагов immutable/append-only.
2 — highly secure. Всё из уровня 1, плюс диски нельзя открыть на запись вообще — ни смонтированные, ни размонтированные (кроме как через mount(2)), из-за чего newfs в multi-user становится невозможен; изменение времени ядра ограничено шагом в одну секунду.
3 — network secure. Всё из уровня 2, плюс запрет изменения правил пакетного фильтра.
# /etc/rc.conf
kern_securelevel_enable="YES"
kern_securelevel="1"
На production-сервере securelevel 1 — разумный выбор: предотвращает загрузку вредоносных модулей ядра и модификацию критичных файлов, при этом не мешает нормальной работе.
Практические рецепты
Минимальный hardening для VPS
# /etc/sysctl.conf
security.bsd.see_other_uids=0
security.bsd.see_other_gids=0
security.bsd.see_jail_proc=0
security.bsd.unprivileged_read_msgbuf=0
kern.randompid=1
kern.elf64.aslr.enable=1
kern.elf64.aslr.pie_enable=1
kern.coredump=0
Тюнинг для web-сервера (nginx/PostgreSQL)
# /etc/sysctl.conf
kern.ipc.soacceptqueue=4096
kern.ipc.shmmax=1073741824
kern.maxfiles=200000
kern.maxfilesperproc=100000
net.inet.tcp.fast_finwait2_recycle=1
# /boot/loader.conf
accf_http_load="YES"
accf_data_load="YES"
kern.ipc.nmbclusters=65536
Тюнинг для ZFS с ограниченной RAM (2-4GB VPS)
# /boot/loader.conf
vfs.zfs.arc.max="1073741824" # 1GB — оставить RAM приложениям
Диагностика и мониторинг
sysctl -a | wc -l # сколько всего переменных
sysctl kern.boottime # когда система загрузилась
sysctl kern.version # версия ядра
sysctl hw.machine # архитектура
sysctl hw.model # модель CPU
sysctl hw.ncpu # число CPU
sysctl hw.physmem # физическая память (байты)
sysctl hw.realmem # реальная память до резервирования ядром
sysctl vm.loadavg # load average
sysctl vm.vmtotal # общая картина использования VM
kldstat # загруженные модули ядра
kenv # переменные окружения загрузчика
dmesg # сообщения ядра (буфер в памяти)
cat /var/run/dmesg.boot # dmesg на момент загрузки (сохраняется)
Отличия от Linux
В Linux sysctl организован аналогично (/proc/sys/), но с другим пространством имён. Ближайшие аналоги: net.ipv4.ip_forward → net.inet.ip.forwarding, vm.swappiness → прямого аналога нет, fs.file-max → kern.maxfiles.
В Linux нет аналога /boot/loader.conf как отдельного уровня конфигурации — параметры ядра передаются через командную строку загрузки (GRUB), что менее структурировано.
Securelevel не имеет прямого аналога в Linux. Closest — LSM (AppArmor, SELinux), но это другая модель: политики доступа vs глобальные уровни ограничений.
Kernel modules в обеих системах работают похоже, но во FreeBSD KLD и kldload/kldstat, в Linux — .ko и modprobe/lsmod.
Источники
- FreeBSD Handbook, "Tuning with sysctl": docs.freebsd.org/en/books/handbook/config/#configtuning-sysctl
- FreeBSD Handbook, "Configuring the FreeBSD Kernel": docs.freebsd.org/en/books/handbook/kernelconfig/
- Klara Systems, "Customizing the FreeBSD Kernel": klarasystems.com/articles/customizing-the-freebsd-kernel/
sysctl(8): man.freebsd.org/cgi/man.cgi?query=sysctl&sektion=8loader.conf(5): man.freebsd.org/cgi/man.cgi?query=loader.conf&sektion=5tuning(7): man.freebsd.org/cgi/man.cgi?query=tuning&sektion=7security(7): man.freebsd.org/cgi/man.cgi?query=security&sektion=7
Связанные заметки
- FreeBSD — безопасность — securelevel и hardening-переменные sysctl
- FreeBSD — мониторинг и отладка — sysctl как источник счётчиков производительности
- FreeBSD — обновление системы — пересборка ядра при обновлении из исходников
- Linux — ядро: сборка, модули, параметры — Kconfig и модули Linux, для сравнения
- FreeBSD — сводный cheatsheet — ключевые переменные sysctl
Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.
Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.