Библиотека — FreeBSD — sysctl и конфигурация ядра [X]
Шифр
freebsd-sysctl-and-kernel
Разделы
freebsd tooling
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
19 433 знака · 26 разделов · чтения около 20 минут

FreeBSD — sysctl и конфигурация ядра


Обзор

В 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.

Источники


Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.