Русский
Русский
English
Статистика
Реклама

Настройка linux

Перевод Заметки о Unix ограничения опции -exec команды find и стремление к удобству при реализации команд

06.05.2021 16:19:55 | Автор: admin
В материале о том, что в наши дни find, как правило, не нуждается в xargs, я отметил, что в конструкции '-exec ... {} +' скобки ('{}') (для имён файлов, генерируемых find) должны находиться в конце команды. В комментарии к той публикации анонимный читатель сказал, что это неприменимо к -exec-версии, которая запускает отдельную команду для каждого имени файла. В результате можно поместить заменяемое имя файла в любом месте команды. Это, как оказалось, относится не только к GNU Find, являясь стандартной возможностью, и я полагаю, что этого даже требует Single Unix Specification (SUS) для find.



(SUS, в отношении аргументов -exec, вводит ограничения лишь на форму '+', предписывая размещать '{}' непосредственно перед '+'. Спецификация же для формы ';' просто говорит об использовании обычного списка аргументов, после чего в описании сказано, что '{}' в списке аргументов заменяется на текущий путь. Понять это всё довольно сложно, хотя подобное в SUS и POSIX обычное дело.)

Разница между двумя формами -exec это интересный вопрос, и эта разница, вероятно, существует из-за удобства реализации формы '+'. Поэтому предлагаю начать с самого начала. Когда применяют любую из форм -exec, команда find выполняет соответствующие команды, задействуя семейство системных вызовов exec() (и библиотечные функции), которым нужно, чтобы им передавался бы массив с командами и с аргументами (то есть это argv для новой команды). Реализация этого в конструкции, где выполняется одна замена ('-exec ... ;') проста: создают и заполняют массив argv, содержащий все аргументы -exec (и команды), и запоминают индекс параметра '{}' (если такой параметр есть; он не является обязательным). Каждый раз, когда выполняют команду, текущий путь помещают в ячейку argv и дело сделано.

При использовании ограниченной формы множественной замены это, в некотором роде, тоже можно сделать. Создаётся массив argv некоего размера, начало которого заполняют всеми фиксированными опциями, а потом присоединяют каждый путь к его концу в виде дополнительной опции, следя за общим размером всех аргументов до тех пор, пока не понадобится выполнить команду, что позволяет ограничивать размер массива. Когда всё готово, индекс, по которому должен быть размещён следующий путь, сбрасывается к стартовой позиции, к месту, где заканчиваются фиксированные опции, после чего всё повторяется.

Правда, если конструкция '{}' может размещаться где угодно понадобится более сложная реализация, в которой надо будет делить фиксированные аргументы на две части, одна из которых идёт до '{}', а вторая после. Начало argv тогда заполняется предшествующими фиксированными аргументами, потом в массив попадают пути, в виде дополнительных аргументов, до достижения лимита, а затем, до exec(), присоединяются следующие фиксированные аргументы, если такие имеются. Для реализации этого всего нужно не так уж и много дополнительного труда, но над этим, всё равно, надо поработать. Поэтому я вынужден выдвинуть теорию о том, что объём этого дополнительного труда оказался именно таким, чтобы программисты, реализующие команду в System V R4 (там эта возможность появилась впервые) выбрали бы ограниченную форму ради удобства разработки и ради того, чтобы в их коде было бы меньше ошибок (ведь код, который не надо писать, определённо, совсем не содержит ошибок).

(Уверен, что это не единственная область Unix-команд, в которой можно заметить признаки стремления разработчиков к реализации того, что реализовывать удобнее. Но в случае с двумя версиями -exec команды find перед нами яркий пример такого подхода.)

Сталкивались ли вы с какими-то особенностями Unix-команд, которые можно объяснить стремлением их создателей к удобству их реализации?

Подробнее..

Перевод Необычный способ использования встроенных команд оболочки для выяснения того, какие директории существуют в системе

07.05.2021 16:08:17 | Автор: admin
В давние времена многоархитектурных Unix-окружений разработчики дистрибутивов не могли прийти к единому мнению о том, что должно быть в $PATH. Базовые вещи, вроде /bin и /usr/bin, были везде одинаковыми, но у каждого дистрибутива был собственный набор дополнительных директорий (у Solaris их, например, было много). Кроме того у разных локальных вычислительных групп было различное видение того, где должны размещаться локальные программы. Например в /usr/local/bin, в /local/bin, в /opt/<something>/bin, в /<group>/bin и так далее. Всё это усложняло мне жизнь, так как я занимался поддержкой общего набора dot-файлов, используемых во всех Unix-системах, за которые я отвечал, и мне не хотелось бы, чтобы моя переменная $PATH представляла бы собой огромный список, содержащий пути ко всем необходимым директориям каждой из систем. Поэтому мне нужно было убирать всё лишнее из гигантского базового списка директорий, которые могли присутствовать в $PATH, оставляя там лишь те директории, которые существовали в текущей системе. А чтобы ещё сильнее усложнить эту задачу, мне хотелось использовать для этого только команды, встроенные в оболочку, и это при работе с оболочкой, где test встроенной командой не является.



К моему счастью, есть одна команда, которая должна быть встроенной в оболочку и при этом даёт сбой в том случае, если директории не существует (или если пользователь не может с ней работать). Это команда cd. Использование cd в качестве замены 'test -d' это решение немного странное, но работоспособное. В моей оболочке были настоящие списки, поэтому я мог добиться того, что мне нужно, примерно так:

# то, что может попасть в $PATH, находится в $candidatespath=`{ tpath=()for (pe in $candidates)builtin cd $pe >[1=] >[2=] && tpath=($tpath $pe)echo $tpath }

(С относительными путями этот код не работает, но в моей переменной $PATH таких путей не было.)

Так как во всех оболочках обязательно должна быть встроенная команда cd, тот же подход можно было использовать практически во всех оболочках. Bourne-подобные оболочки, правда, усложняли задачу по сборке $PATH. Там, как минимум, нужно было добавлять :'s между элементами (cf) и, возможно, в эквиваленте $candidates для таких оболочек нужно было бы использовать :'s между записями, что привело бы к необходимости разделять записи, основываясь на этой конструкции.

(В оболочке Bourne я представил бы $candidates в виде строки, заключённой в кавычки, элементы которой разделены пробелами, так как работать с таким списком директорий гораздо проще. Правда, при таком подходе я не смог бы обрабатывать $PATH-записи, содержащие пробелы, но таких записей в $PATH обычно не бывает.)

Использование cd вышеописанным образом это, по сути, хак, но хаки это то, к чему мы вынуждены прибегать в минималистичных окружениях оболочек, когда необходимо, для решения неких задач, обойтись без внешних программ. Я же, на самом деле, написал на C маленькую программу, isdirs, которая решала вышеописанную задачу, и использовал её в тех системах, с которыми я работал достаточно часто для того, чтобы оправдать компиляцию для них этой программы. А код, в котором использовалась команда cd, был чем-то вроде запасного варианта, применяемого в системах и в ситуациях, в которых я не мог воспользоваться моей isdirs.

(Этот материал можно счесть чем-то вроде продолжения одной моей статьи про хак командной оболочки Unix, которая тоже связана с кросс-архитектурной средой и с dot-файлами.)

P.S. То, о чём я рассказал, происходило в те времена, когда системы были достаточно медленными для того чтобы тот, кто их обслуживает, стремился бы к тому, чтобы без крайней нужды не пользоваться дополнительными внешними программами в dot-файлах оболочки. Именно поэтому я и решил пользоваться только встроенными командами оболочки вместо того, чтобы выполнять множество вызовов test или чего-то подобного. А в большинстве современных оболочек test это встроенная в них команда.

Приходилось ли вам, при работе в Unix, решать какие-то задачи, необычным образом пользуясь исключительно встроенными командами оболочки?

Подробнее..

Recovery mode Сборка ядра Linux 5.12.10 c LLVM 12 Clang и LTO оптимизацией

14.06.2021 18:13:31 | Автор: admin

Технический прогресс не стоит на месте, появляются новые компьютерные архитектуры, компиляторы становятся умнее и генерируют более быстрый машинный код. Современные задачи требуют все более креативного и эффективного решения. В данной статье пойдет речь, на мой взгляд, про один из самых прогрессивных тулчейнов LLVM и компиляторы на его основе Clang и Clang++, для языков программирования С и C++ соответственно. Хоть GCC конкурент Clang, может агрессивнее оптимизировать циклы и рекурсию, Clang дает на выходе более корректный машинный код, и чаще всего не ломает поведение приложений. Плюс оптимизация программ не заканчивается только оптимизацией циклов, поэтому Clang местами дает лучшую производительность. В GCC же за счет переоптимизации вероятность получить unpredictable behavior значительно выше. По этой причине на многих ресурсах не рекомендуют использовать -O3 и LTO(Link Time Optimization) оптимизации для сборки программ. Плюс в случае агрессивной оптимизации, размер исполняемых файлов может сильно увеличиться и программы на практике будут работать даже медленнее. Поэтому мы остановились на Clang не просто так и опции компиляции -O3 и LTO работают в нем более корректно. Плюс современные компиляторы более зрелые, и сейчас уже нет тех детских болячек переоптимизации и LTO.

Что меня побудило написать эту статью? В первую очередь это несколько фактов:

  1. Впервые прочел про сборку ядра Linux с LTO оптимизацией и Clang из новостей, где упоминалась компания Google. Она использует Clang и LTO оптимизацию для сборки ядра Linux и получения лучшей производительности. Компания Google для меня является синонимом инноваций, лучших программистов в мире и поэтому для меня ее опыт является самым авторитетным. Плюс она привнесла очень много в развитие open source, и ее наработками пользуются тысячи компаний во всем мире.
  2. Хоть компания Google начала использовать Clang и LTO оптимизацию раньше, только с выходом ядра Linux 5.12.6 и 5.12.7 было закрыто большое количество багов, и сборка ядра c LTO оптимизаций стала доступна многим. До этого при сборке ядра с LTO оптимизацией многие драйвера давали сбой.
  3. Мною уже протестирована работа ядра с LTO на Ryzen 9 3900x + AMD Radeon 5700 XT. Плюс уже давно использую LLVM 12 и Clang для сборки системных программ. Инструментарий LLVM12 и Clang стали основными в моей системе по причине лучшей поддержки моего процессора и нужные мне программы работают быстрее при сборке с помощью Clang. Для программистов Clang дает лучший контроль ошибок, оптимизации и unpredictable behavior. -fdebug-macro, -fsanitize=address, -fsanitize=memory, -fsanitize=undefined, -fsanitize=thread, -fsanitize=cfi, -fstack-protector, -fstack-protector-strong, -fstack-protector-all, -Rpass=inline, -Rpass=unroll, -Rpass=loop-vectorize, -Rpass-missed=loop-vectorize, -Rpass-analysis=loop-vectorize и т.д.
  4. Данная возможность толком нигде не была описана в связи с п.2 и есть подводные моменты, которые будут рассмотрены в данной статье.


В этой статье будет описана сборка ядра Linux 5.12.10 c LLVM 12 + Clang и LTO оптимизацией. Но так как статья получилась бы короткой, то так же бонусом будет рассмотрен вопрос как сделать утилиты LLVM 12 и Clang сборочным инструментарием по умолчанию, и какие программы и библиотеки имеет смысл собрать вручную, чтобы получить лучший отклик и производительность от системы. GCC имеет более лояльную лицензию на использование, и поэтому он установлен во многих дистрибутивах по умолчанию.

Так как в новом ядре фиксится немалое количество багов для работы с моим оборудованием(Ryzen 9 3900x + AMD Radeon 5700 XT) будет рассмотрен вопрос автоматизации сборки и установки нового ядра, чтобы это сильно не отвлекало и занимало минимум времени. Думаю многим это будет полезно. Будет рассмотрен принцип работы моего сборочного скрипта. Все действия будут проводиться в Arch Linux. Если статья будет хорошо оценена, то она станет вводной частью в серию статей про оптимизацию Linux, где будут рассмотрены внутренние механизмы ОС, и как оптимизировать их работу, будут рассмотрены вредные советы и ошибки оптимизации, и будет дан ответ на вопрос оптимизации системы Что для русского хорошо, то для немца смерть!.

Хоть тема оптимизации описывалась многократно, не мало где дают вредные советы, и некоторые механизмы ОС описаны с ошибками. Чаще всего это происходит из-за сложностей перевода или минимальной документации в интернете к компонентам ядра Linux. Где-то информация вовсе устарела. Плюс некоторые вещи понимают программисты, но не понимают системные администраторы, и наоборот. Изначально после установки Linux работает относительно медленно, но благодаря оптимизации и гибкой настройке, можно добиться более высокой производительности и значительно улучшить отклик системы. Arch Linux у меня используется как основная система, и отклик системы, производительность лучше, чем в Windows 10.
Внимание, автор статьи не несет ответственность за причиненный вред в следствии использования данной статьи! Все действия вы выполняете на свой страх и риск! Все действия должны выполнять только профессионалы!


Немного теории



LTO или Link Time Optimization это оптимизация на этапе линковки(компоновки). Чтобы понять, что такое LTO рассмотрим как работают компиляторы. В большинстве компиляторов используется двух этапная модель: этап компиляции и этап линковки.

На этапе компиляции:

Парсятся исходные тексты программ, строится AST Абстрактное Синтаксическое Дерево.

  • Оптимизируется Абстрактное Синтаксическое Дерево. Оптимизируются циклы, удаляется мертвый код, результат которого нигде не используется. Раскрываются выражения, например 2+5 можно заменить на 7, чтобы при работе приложения не вычислять его значение каждый раз и тем самым сделать его быстрее и т.д.
  • Оптимизированное дерево может быть преобразовано в машинный псевдокод понятный компилятору. Псевдокод используется для дополнительной оптимизации, упрощает разработку универсального компилятора для разных архитектур процессора, например для x86-64 и ARMv7\. Так же как ASM листинг, этот псевдокод еще используется, чтобы понять, как компилятор генерирует машинный код, и служит для понимания работы компилятора, поиска ошибок, например, ошибок оптимизации и unpredictable behavior. Стоит заметить этот этап не является обязательным и в некоторых компиляторах отсутствует.
  • Происходит векторизация. Векторизация ,Automatic Vectorization, SIMD
  • Генерируется объектный файл. Объектный файл содержит в себе машинный код для компьютера, и специальные служебные структуры, в которых все еще есть неизвестные адреса функций и данных, поэтому этот файл все еще не может быть запущен на исполнение. Чтобы разрешить неизвестные адреса, был добавлен этап линковки.


Компьютер работает только с бинарными данными, и может оперировать только адресам, поэтому имя функции и переменной ему ничего не говорят. Имя это лишь формальность для программистов, чтобы удобнее было читать исходный код. Во многих компилируемых языках программирования невозможно вычислить адрес функций на этапе компиляции, поэтому в них был придуман механизм описания функций.
Если мы используем функцию и она реализована в каком-то другом файле, то мы должны описать ее имя, параметры и возвращаемое значение. Тем самым мы скажем компилятору, что не надо искать ее в этом файле и она будет добавлена на этапе линковки. Так же, это упрощает парсинг исходных файлов. Нам больше не надо для компиляции и разрешения адресов читать все файлы за один раз. Представьте себе, что у вас исходный код программы занимает несколько гигабайт, такой размер нередко встречается в серьезных программах, тогда оперативной памяти большинства компьютеров не хватило бы, чтобы хранить все служебные структуры компилятора, что значительно повысило бы стоимость разработки и самих программ.

На этапе линковки:

  • Происходит подстановка адресов
  • Добавляются дополнительных данных для работы программы, например ресурсы
  • Происходит сборка всех объектных файлов в конечный исполняемый файл или распространяемую библиотеку, которая может быть использована в других программах


Понимая это мы можем понять, что LTO оптимизация это дополнительная оптимизация исполняемых файлов, которая не может быть произведена на этапе компиляции и происходит на этапе линковки.

В Clang используется два вида LTO Оптимизации: Full LTO и Thin LTO. Full LTO это классическая реализация LTO оптимизации, которая обрабатывает конечный исполняемый файл за раз целиком и использует много оперативной памяти. Отсюда эта оптимизация занимает много времени, но дает на выходе самый быстрый код. Thin LTO это развитие LTO оптимизации, в которой нет оптимизации всего файла целиком, а вместо этого вместе с объектными файлами записывают дополнительные метаданные, и LTO оптимизатор работает с этими данными, что дает более высокую скорость получения оптимизированного исполняемого файла (скорость сравнима с линковкой файла без LTO оптимизации) и код сравнимый или чуть уступающий в производительности Full LTO. Но самое главное Full LTO может значительно увеличить размер файла, и код наоборот может из-за этого работать медленнее. Thin LTO лишен этого недостатка и в некоторых приложениях на практике мы можем получить лучшую производительность! Поэтому наш выбор будет сборка ядра Linux с Thin LTO.

Дополнительная информация:



Установка LLVM 12 и Clang



Поставить llvm и clang можно выполнив в консоли под root команду:

pacman -Syu base-devel llvm clang lld vim

Это самый простой вариант установки, но лично предпочитают новые версии ПО и git версия закрыла часть багов компилятора и даже стабильнее релиза. Так как за время написания статьи многое поменялось, вышел официальный пакет llvm 12, то чтобы понять ход мыслей, рекомендуется к прочтению прошлая версия по установке.

Прошлая версия
На момент написания статьи, в дистрибутиве Arch Linux используются LLVM и Clang версии 11\. А LLVM и Clang версии 12 находятся в staging репозитории Arch Linux [LLVM](http://personeltest.ru/aways/archlinux.org/packages/staging/x86_64/llvm/). Staging репозиторий это репозиторий, где находятся версии пакетов, которые ломают приложения, зависящие от прошлой версии. Он используется для компиляции всех зависящих программ, и когда все они будут собраны, все пакеты за раз переходит в общий репозиторий. Например, в Arch Linux от LLVM и Clang версии 11 зависят blender, rust и qt creator и т.д. Если мы поставим LLVM и Clang версии 12, то они перестанут работать.
Upd. Пакет уже перешел в основной репозиторий. Так как мною одним из первых была произведена миграция на LLVM и Clang 12, то было придумано простое решение, создать пакет [llvm11-libs](http://personeltest.ru/aways/aur.archlinux.org/packages/llvm11-libs-bin/) с необходимыми библиотеками для обратной совместимости, который позволяет оставить зависимые программы рабочими. Но данный пакет работает только с моим сборочным пакетом [llvm12-git](http://personeltest.ru/aways/aur.archlinux.org/packages/llvm12-git/). Поэтому мы будем собирать LLVM и Clang 12 из исходников. Но вы можете дождаться, когда LLVM и Clang 12 появятся в основном репозитории Arch Linux или использовать 11 версию. Лично предпочитают новые версии ПО, и LLVM и Clang 12 лучше поддерживают мой процессор Ryzen 9 3900X. Плюс git версия закрыла часть багов компилятора и даже стабильнее релиза. Релизный архив с официального сайта у меня не проходит больше тестов при сборке чем git версия. Не стоит пугаться того, что часть тестов компилятор провалил, там нет критических багов для x84-64 архитектуры, и большая часть затрагивают другие компоненты, например openmp и lldb. За очень долгое время тестирования llvm и clang 12 мною не было замечено ни одного бага влияющего на работу системы. Стоит заметить, на данный момент 13 версия является очень сырой и нам не подходит!

Поставим llvm и clang 11 версии(Если 12 версия появилась в основном репозитории, то поставится 12я версия) можно выполнив в консоли под root команду:

pacman -Syu base-devel llvm clang lld libclc vim

Обновить Arch Linux и поставить новые версии программ можно командой(это будет полезно тем кто будет ждать официального выхода 12 версии, думаю это произойдет уже через пару дней):

pacman -Syu

Кто остановился на этом варианте можно пропустить следующий пункт. Для остальных, будет дана инструкция по установке моего пакета.


Cборка LLVM 12 из Arch User Repository



Для сборки нам понадобиться git и нам надо будет собрать программу yay.

Поставим необходимые зависимости, для этого нам будут нужны права root: pacman -Syu base-devel git go vim

Если вы хотите собрать llvm 12 с помощью clang 11, то надо поставить еще их: pacman -S llvm clang

Отредактируем конфигурационный файл сборщика пакетов makepkg в Arch Linux и увеличим количество потоков для сборки программ. Это ускорит скорость сборки. Под root выполним: vim /etc/makepkg.conf

Найдем строки MAKEFLAGS и NINJAFLAGS. Нажмем латинскую букву A. Нам после -j надо указать количество потоков для сборки. Рекомендуется ставить ваше количество ядер или потоков процессора, если ядер 4, то ставим 4 или 8\. У меня это 20, 12 ядер 24 потока, 4 остаются запасными для других задач. Или используем автоматическое определение $(nproc).

В итоге получим:

MAKEFLAGS="-j20"NINJAFLAGS="-j20"

или

MAKEFLAGS="-j$(nproc)"NINJAFLAGS="-j$(nproc)"


Нажмем ESC, дальше SHIFT + :(буква Ж). Внизу появится : строка для ввода команд, вводим wq. w write, записать изменения в файл. q quit, выйти из vim. q! выход из vim без сохранения файла. Кому сложно разобраться с vim, в Linux есть замечательная программа, называется она vimtutor. Если у вас настроена правильно локаль, то vimtutor будет на русском, запустить его можно командой vimtutor. Стоит заметить, вопреки распространенному мнению, обучение у вас не займет много времени. Обычно новичков пугают мифом: vi и vim люди изучают очень долго, и осилить их могут только единицы. На самом деле это не так и там нет ничего сложного.

Под обычным пользователем клонируем репозиторий yay, собираем и устанавливаем:
git clone https://aur.archlinux.org/yay.git && cd yay && makepkg -cfi

Импортирует открытый gpg ключ, он необходим для проверки подписи llvm12-git:
gpg --keyserver pgp.mit.edu --recv-keys 33ED753E14757D79FA17E57DC4C1F715B2B66B95

Поставим LLVM 12 и библиотеки совместимости с 11 версией. Стоит заметить, мой пакет LLVM 12 уже содержит все необходимые утилиты, включая Clang и LLD и их не надо ставить отдельно. Под обычным пользователем выполним команду: yay -Syu llvm12-git. Если llvm 12 есть в официальном репозитории, то llvm11-libs-bin не нужно ставить. Команда yay задаст вам несколько вопросов, нажмите Enter в ответ на все. Сборщик LLVM задаст 3 вопроса:

  • Build with clang and llvm toolchain? Собрать с помощью llvm и clang? Отвечаем Y или Enter если да, и N если нет. Рекомендую собирать LLVM с помощью Clang.
  • Skip build tests? Пропустить сборку тестов? Отвечаем Y или Enter. Так как во время сборки, не все тесты проходят проверку, то сборка будет прекращена. Поэтому мы пропускаем сборку тестов, и на самом деле сборка будет идти даже быстрее.
  • Skip build documentation? Пропустить сборку документации? Отвечаем Y или Enter если да, и N если нет. Если вам не нужна документация, то можно пропустить, это ускорит сборку. Лучше читать документацию на официальном сайте, это удобнее.
  • Skip build OCaml and Go bindings? Пропустить сборку OCaml и Go биндингов? Отвечаем Y или Enter если да, и N если нет. Для большинства ответ Y и их сборку можно смело пропустить в угоду скорости сборки. Для тех кому они нужны, а это очень маленькое количество людей могут ответить N.


Сборка может занять от 20 минут до пары часов. Ждете и в конце отвечаете Y на вопрос: хотите ли вы поставить собранные пакеты?

После установка LLVM надо собрать libclc12-git yay -S libclc12-git. libclc необходим для компиляции opencl и для сборки mesa.

Делаем LLVM и Clang сборочным тулчейном по умолчанию в Arch Linux



Большинство программ в Arch Linux собираются с помощью команды makepkg: man makepkg и PKGBUILD файлов. Поэтому в первую очередь внесем изменения в конфигурационный файл /etc/makepkg.conf. Выполним под root в консоли команду: vim /etc/makepkg.conf. Перейдем к строке CHOST="x86_64-pc-linux-gnu" поставим курсор на следующей пустой строке и нажмем латинскую букву A, и вставим после строки:

export CC=clangexport CXX=clang++export LD=ld.lldexport CC_LD=lldexport CXX_LD=lldexport AR=llvm-arexport NM=llvm-nmexport STRIP=llvm-stripexport OBJCOPY=llvm-objcopyexport OBJDUMP=llvm-objdumpexport READELF=llvm-readelfexport RANLIB=llvm-ranlibexport HOSTCC=clangexport HOSTCXX=clang++export HOSTAR=llvm-arexport HOSTLD=ld.lld

Дальше заменим строки CPPFLAGS, CXXFLAGS, LDFLAGS на содержимое ниже:

CFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"CXXFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"

Если вкратце мы используем -O2 оптимизацию для всех программ, -fstack-protector-strong используем улучшенную защиту стека, что снижает вероятность потенциально опасных ошибок при работе со стеком в программах, она же включена у меня в ядре. Плюс на моем процессоре при сборке с Clang с -fstack-protector-strong код при работе с целыми числами работает чуть быстрее, при работе с числами с плавающей запятой есть небольшой оверхед. В GCC наоборот есть более заметный оверхед и производительность снижается. -march=native есть смысл заменить на ваш, у меня это -march=znver2 gcc.gnu.org/onlinedocs/gcc/x86-Options.html.

Изменим количество потоков в MAKEFLAGS и NINJAFLAGS для сборки программ. Это помогает ускорить сборку программ. После -j надо указать количество потоков для сборки. Рекомендуется ставить ваше количество ядер или потоков процессора, если ядер 4, то ставим 4 или 8\. У меня это 20, 12 ядер, 24 потока, 4 остаются запасными для других задач. Или используем автоматическое определение $(nproc).

В итоге получим:

MAKEFLAGS="-j20"
NINJAFLAGS="-j20"


или

MAKEFLAGS="-j$(nproc)"
NINJAFLAGS="-j$(nproc)"


Из DEBUG_CFLAGS и DEBUG_CXXFLAGS надо удалить -fvar-tracking-assignments. LLVM не поддерживает данный параметр.

Файл должен будет принять примерно такой вид:

CARCH="x86_64"CHOST="x86_64-pc-linux-gnu"CARCH="x86_64"CHOST="x86_64-pc-linux-gnu"#-- Compiler and Linker Flagsexport CC=clangexport CXX=clang++export LD=ld.lldexport CC_LD=lldexport CXX_LD=lldexport AR=llvm-arexport NM=llvm-nmexport STRIP=llvm-stripexport OBJCOPY=llvm-objcopyexport OBJDUMP=llvm-objdumpexport READELF=llvm-readelfexport RANLIB=llvm-ranlibexport HOSTCC=clangexport HOSTCXX=clang++export HOSTAR=llvm-arexport HOSTLD=ld.lldCPPFLAGS="-D_FORTIFY_SOURCE=2"CFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"CXXFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"RUSTFLAGS="-C opt-level=2"#-- Make Flags: change this for DistCC/SMP systemsMAKEFLAGS="-j20"NINJAFLAGS="-j20"#-- Debugging flagsDEBUG_CFLAGS="-g"DEBUG_CXXFLAGS="-g"#DEBUG_CFLAGS="-g -fvar-tracking-assignments"#DEBUG_CXXFLAGS="-g -fvar-tracking-assignments"#DEBUG_RUSTFLAGS="-C debuginfo=2"

Нажмем ESC, дальше SHIFT + :(буква Ж). Внизу появится: строка для ввода команд, вводим wq. w write, записать изменения в файл. q quit, выйти из vim. q! выход из vim без сохранения файла. Кому сложно разобраться с vim, в Linux есть замечательная программа, называется она vimtutor. Если у вас настроена правильно локаль, то vimtutor будет на русском, запустить его можно командой `vimtutor`. Стоит заметить, вопреки распространенному мнению, обучение у вас не займет много времени. Обычно новичков пугают мифом: vi и vim люди изучают очень долго, и осилить их могут только единицы. На самом деле это не так и там нет ничего сложного.

Следующим этапом можно добавить настройки в файл .bashrc текущего пользователя. Не root, сборка программ под root очень плохая идея! Это относительно вредный совет и с помощью clang будут собираться все программы! Поэтому делайте это только если хорошо понимаете зачем это вам. Это можно сделать командой:

cat << 'EOF' >> "${HOME}/.bashrc"export CARCH="x86_64"export CHOST="x86_64-pc-linux-gnu"export CC=clangexport CXX=clang++export LD=ld.lldexport CC_LD=lldexport CXX_LD=lldexport AR=llvm-arexport NM=llvm-nmexport STRIP=llvm-stripexport OBJCOPY=llvm-objcopyexport OBJDUMP=llvm-objdumpexport READELF=llvm-readelfexport RANLIB=llvm-ranlibexport HOSTCC=clangexport HOSTCXX=clang++export HOSTAR=llvm-arexport HOSTLD=ld.lldexport CPPFLAGS="-D_FORTIFY_SOURCE=2"export CFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"export CXXFLAGS="-fdiagnostics-color=always -pipe -O2 -march=native -fstack-protector-strong"export LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"export RUSTFLAGS="-C opt-level=2"export MAKEFLAGS="-j20"export NINJAFLAGS="-j20"export DEBUG_CFLAGS="-g"export DEBUG_CXXFLAGS="-g"EOF


Список системных библиотек и программ которые стоит собирать вручную


Внимание, сборка всех программ и все консольные команды надо выполнять под обычным пользователем, перед установкой у вас попросит пароль root. Сборка всех библиотек и программ из списка не занимает много времени. Все кроме Mesa у меня собирается в районе 1 минуты. Список дан в той в последовательности в которой рекомендуется сборка! К примеру от zlib-ng и zstd зависит Mesa, а от Mesa зависит xorg-server.

Самое первое, что надо сделать в Arch Linux это заменить zlib на zlib-ng. Это дает хороший выигрыш производительности в приложениях, которые зависят от zlib. Больше всего это заметно на веб браузерах и веб серверах, которые используют gzip сжатие для передачи данных. На высоко нагруженных серверах это дает очень значительную прибавку к производительности. Сборка довольно быстрая. Поставить можно командой(под обычным пользователем): yay -Syu zlib-ng. На вопрос хотите ли вы удалить zlib отвечайте Y. Не бойтесь библиотеки полностью взаимозаменяемы, и ничего не сломается!

Дальше у нас идет zstd это вторая по популярности библиотека используемая в ядре и в программах для сжатия данных. Поэтому имеет смысл собрать так же ее. Чтобы собрать, вам нужно скопировать содержимое zstd, создать директорию, например zstd, а в ней создать файл PKGBUILD и в него вставить содержимое по ссылке. Дальше в консоли перейти в директорию содержащую PKGBUILD, выполнить команду makepkg -cfi .

libjpeg-turbo Библиотека для работы c jpeg файлами. Ее очень часто используют браузеры и программы рабочего стола. libjpeg-turbo собранный с clang дает у меня лучшую производительность. Действия такие же, как в zstd. Создать директорию, и вставить в файл PKGBUILD содержимое по ссылке libjpeg-turbo. Дальше в консоли перейдите в директорию содержащую PKGBUILD, выполнить команду makepkg -cfi.

libpng Библиотека для работы с PNG файлами. По сборке и установке все то же самое. libpng. Для сборки вам понадобится патч: 72fa126446460347a504f3d9b90f24aed1365595.patch, его надо положить в одну директорию с файлом PKGBUILD. Для сборки надо внести изменения в PKGBUILD, заменить source и sha256sums на строки ниже, и добавить функцию prepare.

source=("https://downloads.sourceforge.net/sourceforge/$pkgname/$pkgname-$pkgver.tar.xz"  "72fa126446460347a504f3d9b90f24aed1365595.patch")sha256sums=('505e70834d35383537b6491e7ae8641f1a4bed1876dbfe361201fc80868d88ca'  '84298548e43976265f414c53dfda1b035882f2bdcacb96ed1bc0a795e430e6a8')prepare() {  cd $pkgname-$pkgver  patch --forward --strip=1 --input="${srcdir:?}/72fa126446460347a504f3d9b90f24aed1365595.patch"}


Mesa это святой грааль для всех графических приложений. Стоит собирать всегда вручную, дает хорошую прибавку в десктоп приложениях, улучшается отклик рабочего стола. Одно время сидел на git версии, чтобы получить лучшую поддержку новых видеокарт AMD. Вот мой PKGBUILD оптимизированный для сборки с помощью Clang.

Для сборки вам надо отредактировать файл mesa.conf и установить необходимые вам драйвера dri, gallium, vulkan для сборки. У меня сборка только под новые видеокарты AMD. Подглядеть можно тут: Mesa OpenGL, mesa-git package, Mesa Documentation. При выходе новой версии Mesa не забудьте сменить 21.1.2 на новую версию. А после смены версии обновите контрольные суммы файлов, выполнив в директории с PKGBUILD команду updpkgsums.

xorg-server X сервер с которым взаимодействуют почти все среды рабочего стола. Сборка дает заметное улучшение отклика рабочего стола. Сборка такая же через mapkepkg -cfi. Скачать необходимые файлы для сборки можно тут: xorg-server Сборочный пакет немного кривой и собирает пакет без оптимизаций. Поэтому его надо пропатчить. Для это после строки arch-meson ${pkgbase}-$pkgver build \ надо добавить строки:

  -D debug=false \  -D optimization=2 \  -D b_ndebug=true \  -D b_lto=true \  -D b_lto_mode=thin \  -D b_pie=true \

Полный список критических важных программ влияющих на производительность системы вы можете посмотреть в поем github репозитории arch-packages. Список был создан с помощью системного профилировщика perf. Все сборочные файлы оптимизированы для сборки с помощью llvm и сборка полностью автоматизирована. На моем ryzen 9 3900x сборка всего занимает около 20 минут. Единственный пакет который невозможно собрать с помощью clang и llvm это glibc. Его надо собирать вручную, и с оптимизацией -march= под ваш процессор, это самая часто вызываемая библиотека. Сборку glibc могут проводить только профессионалы, понимающие, что они делают. Не правильная сборка может сломать систему!

Для того, что бы воспользоваться автоматизированной сборкой надо выполнить(под обычным пользователем):
git clone https://github.com/h0tc0d3/arch-packages.git && cd arch-packages && chmod +x build.sh

Дальше нам надо установить все gpg сертификаты и зависимости необходимые для сборки, выполним ./build.sh --install-keys, а затем ./build.sh --install-deps

Для сборки программ достаточно просто запустить скрипт: ./build.sh --install, скрипт вам будет задавать вопросы, какие программы хотите собрать и поставить. На вопрос: хотите ли вы отправить все ваши деньги и пароли автору статьи? хотите ли вы заменить программы?(например, zlib-ng и zlib конфликтуют. Удалить zlib? [y/N] ) ответьте Y . Если вам нужна принудительная пересборка всех программ, то надо выполнить ./build.sh --install --force. По умолчанию, если пакет был уже собран и найден с нужной версией, то он не собирается, а просто устанавливается.

Для сборки mesa надо отредактировать файл mesa/mesa.conf и установить необходимые вам драйвера dri, gallium, vulkan для сборки.

С помощью команды ./build.sh --check можно проверить различия версий в моем репозитории и в официальном, помогает быстро адаптировать сборочные файлы и собрать актуальные версии программ. Слева версия в моем репозитории, справа от стрелки в официальном. Мой репозиторий может служить удобной тренировочной точкой на пути к созданию своего дистрибутива, создания LFS и развитию навыка пересборки ПО не ломая систему.

[+] zstd 1.5.0-1[+] libpng 1.6.37-3[+] libjpeg-turbo 2.1.0-1[+] mesa 21.1.2-1[+] pixman 0.40.0-1[-] glib2 2.68.3-1 -> 2.68.2-1[+] gtk2 2.24.33-2[+] gtk3 1:3.24.29-2[+] gtk4 1:4.2.1-2[+] qt5-base 5.15.2+kde+r196-1[+] icu 69.1-1[+] freetype2 2.10.4-1[+] pango 1:1.48.5-1[+] fontconfig 2:2.13.93-4[+] harfbuzz 2.8.1-1[+] cairo 1.17.4-5[+] wayland-protocols 1.21-1[+] egl-wayland 1.1.7-1[+] xorg-server 1.20.11-1[+] xorgproto 2021.4-1[+] xorg-xauth 1.1-2[+] xorg-util-macros 1.19.3-1[+] xorg-xkbcomp 1.4.5-1[+] xorg-setxkbmap 1.3.2-2[+] kwin 5.22.0-1[+] plasma-workspace 5.22.0-2[+] glibc 2.33-5


Сборка Ядра с помощью LLVM и Clang с LTO оптимизацией


Внимание! Сборку ядра необходимо выполнять под обычным пользователем. Перед установкой ядра у вас попросит sudo пароль. Не рекомендуется использовать патчи ядра linux-ck, linux-zen, MuQSS и т.д. Мною были протестированы все, при кажущемся увеличении производительности системы, происходят кратковременные лаги и снижается стабильность системы, некоторые подсистемы ядра работают не стабильно! С выходом ядра 5.11 стандартный планировщик работает не хуже и значительно стабильнее! Единственный патч который мною применяется это патч для применения оптимизации под процессор github.com/graysky2/kernel_gcc_patch Выбрать ваш процессор можно в меню конфигуратора ядра Processor type and features-->Processor family.

Сборка ядра с помощью LLVM описана в официальной документации Linux Kernel Build with LLVM. Но там есть несколько подводных моментов, которые не описаны. Первый подводный момент заключается в OBJDUMP=llvm-objdump, тут идет переопределение objdump, но так как параметры objdump в llvm имеет другой синтаксис, то при сборке будет пропущена часть тестов для проверки корректности сборки, и будет warning ругающийся на objdump. Правильно будет оставить родной objdump OBJDUMP=objdump

Неправильно:

make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \  READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ \  HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=llvm-objdump


Правильно:

make CC=clang LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip \  READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ \  HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=objdump

Второй подводный момент заключается в том, что если мы не добавим LLVM_IAS=1 в строку make, то нам не будет доступна LTO оптимизация в конфигураторе ядра!

Поэтому полная строка для сборки с LTO будет:

export BUILD_FLAGS="LLVM=1 LLVM_IAS=1 CC=clang CXX=clang++ LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=objdump"make ${BUILD_FLAGS} -j$(nproc)

Полный список команд для сборки ядра. /tmp
надо заменить на вашу директорию куда будут распакованы исходные файлы ядра, а mykernel
надо заменить на ваш постфикс для имени ядра.

export BUILD_FLAGS="LLVM=1 LLVM_IAS=1 CC=clang CXX=clang++ LD=ld.lld AR=llvm-ar NM=llvm-nm STRIP=llvm-strip READELF=llvm-readelf HOSTCC=clang HOSTCXX=clang++ HOSTAR=llvm-ar HOSTLD=ld.lld OBJCOPY=llvm-objcopy OBJDUMP=objdump"tar -xf linux-5.12.10.tar.xz -C /tmpcd /tmp/linux-5.12.10zcat /proc/config.gz > .config # Берем конфигурацию запущенного ядра из /proc/config.gz и используем ее для сборкиecho "-mykernel" > .scmversionmake ${BUILD_FLAGS} oldconfigmake ${BUILD_FLAGS} -j$(nproc) nconfig

C помощью oldconfig конфигурация адаптируется под новое ядро и запускается конфигуратор nconfig. Подробнее о конфигураторах ядра можно прочесть в официальной документации [Kernel configurator](http://personeltest.ru/aways/www.kernel.org/doc/html/latest/kbuild/kconfig.html).

В конфигураторе переходим в General architecture-dependent option --> Link Time Optimization (LTO) и выбираем Clang ThinLTO (EXPERIMENTAL). Для дополнительной защиты стека в General architecture-dependent options ставим \* напротив Stack Protector buffer overflow detection и Strong Stack Protector. Жмем F9 и сохраняем новый конфигурационный файл. Далее идет список команд для сборки и установки нового ядра.

make ${BUILD_FLAGS} -j$(nproc)make ${BUILD_FLAGS} -j$(nproc) modulessudo make ${BUILD_FLAGS} -j$(nproc) modules_installsudo cp -v arch/x86_64/boot/bzImage /boot/vmlinuz-mykernel

Следующий подводный момент заключается в DKMS, после установки ядра собранного с помощью Clang, DKMS пытается собрать модули ядра с помощью GCC. По этой причине сборка и установка DKMS модулей в новое ядро завершается ошибкой. Решение проблемы заключается в передаче DKMS компилятора Clang таким образом:

sudo ${BUILD_FLAGS} dkms install ${dkms_module} -k 5.12.10-mykernel


Автоматизация сборки ядра Linux



Для автоматизации сборки ядра мы будем использовать мой bash скрипт github.com/h0tc0d3/kbuild. Клонируем репозиторий и перейдем в рабочую директорию: git clone https://github.com/h0tc0d3/kbuild.git && cd kbuild && chmod +x kbuild.sh

Отредактируем файл build.sh или поместим содержимое ниже в файл ${HOME}/.kbuild. Рекомендуется второй способ vim "${HOME}/.kbuild" т.к. при обновлении скрипта наши настройки сохранятся. Если использовалось клонирование репозитория git, то в директории со скриптом можно выполнить команду git pull, чтобы обновить скрипт. Ниже даны параметры по умолчанию, они формируют поведение скрипта по умолчанию, если соответствующий параметр не был передан. Эти параметры в дальнейшем можно будет переопределить с помощью параметров командной строки для скрипта. Так же можно добавить команду в ваш .bashrc. Для этого в директории со скриптом kbuild.sh надо выполнить echo "alias kbuild='${PWD}/kbuild.sh" >> "${HOME}/.bashrc", ${PWD} автоматом заменит на текущую директорию. Или из любой другой директории можно указать полный пусть к скрипту echo "alias kbuild='полный-путь/kbuild.sh'" >> "${HOME}/.bashrc" После редактирования .bashrc необходимо перезапустить терминал! Теперь можно будет запускать скрипт командой kbuild --help .

KERNEL_VERSION='5.12.10'         # Версия Linux для сборки. Любая версия с официального сайта kernel.org, включая rc версии.KERNEL_POSTFIX='noname'         # Постфикс для названия ядра. Ядро будет иметь имя версия-постфикс, 5.12.10-noname, нужно для разделения в системе ядер с одной версией.KERNEL_CONFIG='/proc/config.gz' # Конфигурационный файл ядра. Поддерживает любые текстовые файлы и с жатые с расширением gz.KERNEL_CONFIGURATOR='nconfig'   # Конфигуратор ядра nconfig, menuconfig, xconfig.# Рекомендую использовать nconfig, он лучше menuconfig.# Можно писать полную строку, например MENUCONFIG_COLOR=blackbg menuconfig# Дополнительную информацию можно найти в документации к ядру https://www.kernel.org/doc/html/latest/kbuild/kconfig.htmlMKINITCPIO=1 # Запускать "mkinitcpio -p конфигурационный_файл" После сборки? 0 - Нет, 1 - Да.MKINITCPIO_CONFIG="${KERNEL_POSTFIX}" # Имя конфигурационного файла mkinitcpio, по умолчанию равно постфиксу.CONFIGURATOR=0      # Запускать конфигуратор ядра? 0 - Нет, 1 - Да. Если вам не нужно конфигурировать ядро, то можно поставить 0.LLVM=0              # Использовать LLVM Для сборки? 1 - Да, 0 - Нет(Будет использован GCC или другой системный компилятор по умолчанию)THREADS=8           # Количество поток для сборки. Ускоряет сборку. Для автоматического определения надо заменить на $(nproc)BUILD_DIR='/tmp'    # Директория в которой будет проходить сборки ядра. У меня 32gb оперативной памяти и сборка происходит в tmpfs.DOWNLOAD_DIR=${PWD} # Директория для сохранения архивных файлов с исходниками ядра. ${PWD} - в папке из которой запущен скрипт сборки.DIST_CLEAN=0    # Если директория с исходниками существует выполнять make disclean перед сборкой? 0 - Нет, 1 - ДаCLEAN_SOURCE=0  # Выполнять make clean после сборки ядра? 0 - Нет, 1 - ДаREMOVE_SOURCE=1 # Удалять директорию с исходными файлами ядра после сборки? 0 - Нет, 1 - Да.SYSTEM_MAP=0    # Копировать System.map в /boot После сборки? 0 - Нет, 1 - Да.PATCH_SOURCE=1                          # Применять патчи ядра? 0 - Нет, 1 - Да.PATCHES=("${HOME}/confstore/gcc.patch") # Список патчей ядра. Нельзя поменять с помощью параметров скрипта.DKMS_INSTALL=1                                        # Выполнять DKMS Install? 0 - Нет, 1 - Да.DKMS_UNINSTALL=1                                      # Выполнять DKMS Uninstall? 0 - Нет, 1 - Да.DKMS_MODULES=('openrazer-driver/3.0.1' 'digimend/10') # Список DKMS модулей, который нужно собрать и установить. Нельзя поменять с помощью параметров скрипта.

Внимание! Сборку ядра необходимо выполнять под обычным пользователем. Перед установкой ядра у вас попросит sudo пароль. Не рекомендуется использовать патчи ядра linux-ck, linux-zen, MuQSS и т.д. Мною были протестированы все, при кажущемся увеличении производительности системы, происходят кратковременные лаги и снижается стабильность системы, некоторые подсистемы ядра работают не стабильно. С выходом ядра 5.11 стандартный планировщик работает не хуже и значительно стабильнее! Единственный патч который мною применяется это патч для применения оптимизации под процессор github.com/graysky2/kernel_gcc_patch. Нас интересует файл more-uarches-for-kernel-5.8+.patch. Путь к нему имеет смысл указать в PATCHES. Выбрать ваш процессор можно в меню конфигуратора ядра Processor type and features-->Processor family.

Принцип работы скрипта:

1) set -euo pipefail скрипт переходит в строгий режим, в случае ошибок скрипт завершается с ошибкой. Является хорошим тоном, при написании bash скриптов. Скрипт проверяет запущен ли он под рут, если запущен под рут, то выдает ошибку и завершается. Загружается настройки пользователя из файла ${HOME}/.kbuild

2) Скрипт проверяет существование директории linux-версия в директории BUILD_DIR. Если существует, то исходники распакованы. Перед сборкой может выполняться команда make distclean, поведение задается переменной DIST_CLEAN. Если этой директории не существует, то проверяется существование файла linux-версия.tar.gz

или linux-версия.tar.xz. Если файл найден, то он распаковывается в BUILD_DIR. Иначе файл скачивается с kernel.org в директорию DOWNLOAD_DIR.

3) Скрипт применяет патчи ядра и устанавливает постфикс для версии ядра(записывает его в файл .scmversion ).

4) Скрипт копирует настройки ядра из файла KERNEL_CONFIG в .config и выполняет make oldcofig для адаптации настроек под новое ядро и запускает конфигуратор ядра.

5) Скрипт собирает ядро и модули.

6) Скрипт удаляет модули DKMS из ядра которое сейчас запущено, если это необходимо. Это необходимо, чтобы в списке dkms status не отображались мертвые ядра. Удаляет директорию `/lib/modules/версия-постфикс` если она существует. Она существует в том случае, если мы собираем одну и туже версию несколько раз. Это дополнительная защита от unpredictable behavior .

7) Скрипт устанавливает модули ядра, копирует ядро в /boot/vmlinuz-постфикс.

8) Скрипт собирает DKMS модули и устанавливает их. Копирует System.map в /boot/System-постфикс.map, если это необходимо.

9) Обновляет загрузочный img файл для ядра. Выполняет mkinitcpio -p конфиг.

10) Выполняет make clean если необходимо. Удаляет директорию linux-версия в директории BUILD_DIR, если это необходимо.

Собрать ядро с llvm можно командой ./kbuild.sh -v 5.12.10 --llvm --start или kbuild -v 5.12.10 --llvm --start, если был установлен alias. -v 5.12.10 указывает версию ядра для сборки, --llvm указывает собирать ядро с помощью llvm и clang. --start указывает, что надо запускать конфигуратор ядра. Получить справку по параметрам скрипта можно выполнив команду kbuild --help.

Русская справка
Параметры: Описание: Пример:
--version, -v Версия ядра для сборки --version 5.12.10 | -v 5.13-rc4
--postfix, -p Постфикс ядра --postfix noname | -p noname
--config, -c Файл конфигурации ядра --config /proc/config.gz | -c /proc/config.gz
--dir, -d Директории сборки --dir /tmp | -d /tmp
--download, -z Директория загрузки --download /tmp | -z /tmp
--threads, -t Количество потоков сборки --threads 8 | -t 8
--configurator, -x Конфигуратор ядра --configurator nconfig | -x "MENUCONFIG_COLOR=blackbg menuconfig"

--start, -s Запускать конфигуратор
--disable-start, -ds Не запускать конфигуратор

--mkinitcpio, -mk Запускать mkinitcpio после установки ядра
--disable-mkinitcpio, -dmk Не запускать mkinitcpio после установки ядра
--mkinitcpio-config, -mc Конфиг mkinitcpio --mkinitcpio-config noname | -mc noname

--llvm, -l Использовать LLVM
--disable-llvm, -dl Не использовать LLVM

--patch, -ps Применять патчи ядра
--disable-patch, -dp Не применять патчи ядра

--map, -m Копировать System.map в /boot/System-постфикс.map
--disable-map, -dm Не копировать System.map

--clean, -cs Чистить исходники после сборки. make clean
--disable-clean, -dc Не чистить исходники после сборки.
--distclean, -cd Чистить исходники перед сборкой. make distclean
--disable-distclean, -dd Не чистить исходники перед сборкой.
--remove, -r Удалять директорию с исходниками после сборки
--disable-remove, -dr Не удалять директорию с исходниками после сборки

--dkms-install, -di Устанавливать DKMS модули
--disable-dkms-install, -ddi Не устанавливать DKMS модули
--dkms-uninstall, -du Деинсталлировать DKMS модули перед их установкой
--disable-dkms-uninstall, -ddu Не деинсталлировать DKMS модули перед их установкой

Список параметров которые помогают отлавливать ошибки на разных этапах и продолжить вручную:

--stop-download, -sd Стоп посл загрузки файла
--stop-extract, -se Стоп после распаковки архива с исходниками
--stop-patch, -sp Стоп после применения патчей ядрей
--stop-config, -sc Стоп после конфигуратора ядра
--stop-build, -sb Стоп после сборки ядра
--stop-install, -si Стоп после установки нового ядра и модулей




Как можно понять из статьи сборка ядра с LLVM и Clang относительно простая. И самое главное можно автоматизировать сборку и установку ядра, и в дальнейшем не тратить много времени на сборку новых ядер.

Всем кто дочитал до конца, спасибо! Комментарии и замечания приветствуются!


Подробнее..

1 CPU 1 Гб а я хочу мониторинг, как у больших дядей

10.05.2021 16:07:24 | Автор: admin


Я обожаю читать на хабре статьи про то, как устроены системы больших интернет-компаний. Кластеры SQL-серверов, монг и редисов. Тут у нас кластер ELK собирает трейсинг, там сборка логов, здесь балансер выдает входящим запросам traceID и можно отслеживать, как запрос ходит по всем нашим микросервисам. Класс. Но, допустим, у вас совсем маленький проект и вы можете себе позволить лишь VPS минимальной конфигурации. Реально ли на ней сделать мониторинг не хуже, чем у больших проектов? Я решил надо попробовать.


Создаем VPS


Сразу оговорюсь, что я ни разу не devops и не особо глубоко разбираюсь в Linux, поэтому, если что-то сделал неправильно, или у вас есть идеи, как можно было сделать то, что я делаю в этой статье проще и лучше пишите в комментариях, буду рад любым вашим советам и замечаниям!

Для экспериментов я создал на Маклауде VPS следующей конфигурации: 1 CPU, 1 Гб RAM и 20 Гб диск.


Для удобства я загрузил свой SSH ключ, и мог заходить в консоль сразу после запуска сервера. Также по умолчанию включено резервное копирование, я его отключил, так как в целях эксперимента мне оно было не нужно. Далее требовалось выбрать ОС. Для этого хотелось понять, какие ресурсы будут доступны на VPS сразу после создания. Меня интересовала свободная память и место на диске. Для этого, в панели управления можно инициировать переустановку ОС. Я поочередно установил доступные ОС и для каждой посмотрел, какие параметры она дает на старте:

CentOS 8:

[root@v54405 ~]# dfFilesystem     1K-blocks    Used Available Use% Mounted ondevtmpfs          406744       0    406744   0% /devtmpfs             420480       0    420480   0% /dev/shmtmpfs             420480    5636    414844   2% /runtmpfs             420480       0    420480   0% /sys/fs/cgroup/dev/vda1       20582864 1395760  18300472   8% /tmpfs              84096       0     84096   0% /run/user/0[root@v54405 ~]# free              total        used        free      shared  buff/cache   availableMem:         840960      106420      525884        5632      208656      600868Swap:             0           0           0


Debian 10

root@v54405:~# dfFilesystem     1K-blocks    Used Available Use% Mounted onudev              490584       0    490584   0% /devtmpfs             101092    1608     99484   2% /run/dev/vda1       20608592 1001560  18736224   6% /tmpfs             505448       0    505448   0% /dev/shmtmpfs               5120       0      5120   0% /run/locktmpfs             505448       0    505448   0% /sys/fs/cgrouptmpfs             101088       0    101088   0% /run/user/0root@v54405:~# free              total        used        free      shared  buff/cache   availableMem:        1010900       43992      903260        1608       63648      862952Swap:             0           0           0


Ubuntu 20.04

root@v54405:~# dfFilesystem     1K-blocks    Used Available Use% Mounted onudev              473920       0    473920   0% /devtmpfs             100480     592     99888   1% /run/dev/vda1       20575824 1931420  17757864  10% /tmpfs             502396       0    502396   0% /dev/shmtmpfs               5120       0      5120   0% /run/locktmpfs             502396       0    502396   0% /sys/fs/cgrouptmpfs             100476       0    100476   0% /run/user/0root@v54405:~# free              total        used        free      shared  buff/cache   availableMem:        1004796       65800      606824         592      332172      799692Swap:        142288           0      142288

Итак, в CentOS не доложили оперативной памяти (кстати почему хороший вопрос сервису), а Убунту занял на гигабайт больше места на диске. Так что я остановил свой выбор на Debian 10.

Для начала обновим систему:

apt-get updateapt-get upgrade

Также установим sudo

apt-get install sudo


Для того, чтобы реализовать мою задумку первым делом я установил докер по инструкции с официального сайта.

Проверяем, что докер установлен

# docker -vDocker version 20.10.6, build 370c289


Также понадобится docker-compose. Процесс установки можно посмотреть тут.

Проверим, что докер установился:

# docker-compose -vdocker-compose version 1.29.1, build c34c88b2

Итак, все приготовления выполнены, посмотрим, сколько места осталось на диске:

Filesystem     1K-blocks    Used Available Use% Mounted onudev              490584       0    490584   0% /devtmpfs             101092    2892     98200   3% /run/dev/vda1       20608592 1781756  17956028  10% /tmpfs             505448       0    505448   0% /dev/shmtmpfs               5120       0      5120   0% /run/locktmpfs             505448       0    505448   0% /sys/fs/cgrouptmpfs             101088       0    101088   0% /run/user/0


Запускаем проект


Для эксперимента я написал на NestJS небольшой веб-сервис, который работает с изображениями. Он позволяет загружать изображения на сервер, извлекает из них метаданные, записывает их в MongoDB, а информация о сохраненных изображениях пишется в Postgres. Для каждого загруженного изображения можно получить метаданные и скачать само изображение. Изображения, к которым не обращались более 10 минут удаляются с сервера при помощи функции очистки, которая запускается раз в минуту.

Исходный код проекта на githab.

Я клонировал его на сервер при помощи команды:

git clone https://github.com/debagger/observable-backend.git

Чтобы было удобно разворачивать сервис на сервере я написал файл docker-compose.nomon.yml следующего содержания:
version: "3.9"volumes:  imagesdata:  grafanadata:  postgresdata:  mongodata:  tempodata:services:  backend:    image: node:lts    volumes:      - ./backend:/home/backend      - imagesdata:/images    working_dir: /home/backend    environment:      OT_TRACING_ENABLED: "false"      PROM_METRICS_ENABLE: "false"    ports:      - 3000:3000    entrypoint: ["/bin/sh"]    command: ["prod.sh"]    restart: always  db:    image: postgres    restart: always    expose:      - "5432"    volumes:      - postgresdata:/var/lib/postgresql/data    environment:      POSTGRES_PASSWORD: password      POSTGRES_USER: images  adminer:    image: adminer    restart: always    ports:      - 8080:8080  mongo:    image: mongo    restart: always    volumes:      - mongodata:/data/db  mongo-express:    image: mongo-express    restart: always    ports:      - 8081:8081


Для запуска проекта переходим в его директорию

cd observable-backend


И запускаем:

docker-compose -f docker-compose.nomon.yml up -d


Сервису понадобится некоторое время чтобы стартовать, я настроил его таким образом, чтобы он при запуске автоматически загружал зависимости и собирался.

После запуска можно проверить что он работает в браузере по ссылке
http://<ip сервера>:3000/

Должна вывестись строка Hello World!

Для того, чтобы испытывать производительность сервиса при помощи библиотеки autocannon я написал нагрузочный тест. Он находится в том же репозитории, в директории autocannon. Его надо запускать на машине с установленным node.js предварительно установив адрес сервера, где запущен проект в .env файле.

После запуска двухминутного теста я получил следующий результат:


В процессе теста я мог наблюдать за поведением системы при помощи стандартной команды linux top, а также docker stats. Помимо этого можно смотреть логи, при помощи команды docker logs. Но этого недостаточно, хочется лучше понимать, что происходит с моим сервисом под нагрузкой. Поэтому следующим шагом я решил добавить к проекту сбор метрик.

Настраиваем метрики


После недолгого гугления решений для сбора метрик я остановил свой выбор на связке Prometheus + Grafana.

Для использования этой связки я добавил в конфигурацию docker-compose следующее:

  prometheus:    image: prom/prometheus    ports:      - 9090:9090    volumes:       - ./prometheus.yml:/etc/prometheus/prometheus.yml  mongo-exporter:    image: bitnami/mongodb-exporter    ports:      - 9091:9091    command: [--mongodb.uri=mongodb://mongo, --web.listen-address=0.0.0.0:9091]  pg-exporter:    image: bitnami/postgres-exporter    ports:      - 9092:9092    environment:       DATA_SOURCE_NAME: sslmode=disable user=images password=password host=db      PG_EXPORTER_WEB_LISTEN_ADDRESS: 0.0.0.0:9092  grafana:    image: grafana/grafana    ports:       - 3001:3000    volumes:       - grafanadata:/var/lib/grafana


Здесь минимальная конфигурация для запуска Prometheus и Grafana, а также экспортеры для метрик из Postgres и Mongo. Для Prometheus я написал конфиг prometheus.yml со следующим содержимым.

global:  scrape_interval:     10sscrape_configs:  - job_name: 'nodejs'    honor_labels: true    static_configs:      - targets: ['backend:3000']  - job_name: mongodb    honor_labels: true    static_configs:      - targets: ['mongo-exporter:9091']  - job_name: postgres    scrape_timeout: 9s    honor_labels: true    static_configs:      - targets: ['pg-exporter:9092']


Чтобы собирать метрики из своего приложения я использовал библиотеку express-prom-bundle, которая позволяет собирать стандартные метрики и создавать свои собственные. Также я добавил в свой сервис переменную окружения PROM_METRICS_ENABLE для того, чтобы можно было включать и отключать метрики из конфигурации контейнера. Если активировать данную функцию, метрики, собираемые приложением, будут доступны по адресу http://<ip сервера>:3000/metrics.

Я включил сбор дефолтных системных метрик, метрики по запросам, обрабатываемым сервером Express, а также несколько своих, которые позволяют контролировать скорость и количество загружаемых и скачиваемых изображений, количество хранимых изображений и занимаемое ими место на диске.

Получившуюся конфигурацию я сохранил под именем docker-compose.metrics.yml.

Запустить эту конфигурацию можно командой

docker-compose -f docker-compose.metrics.yml up -d


После запуска можно зайти в интерфейс Grafana по адресу http://<ip сервера>:3001/

Логин/пароль по умолчанию admin/admin.

Здесь в настройках я добавил источник данных Prometheus


После этого нам доступны все метрики, которые собирает Prometheus.

Для примера выведем графики загрузки процессора по всем сервисам:


Для своих целей я настроил такую панель:


Теперь мне стало гораздо проще разобраться, что происходит с сервисом.

ELK неудача


Итак, я настроил метрики, и теперь мне хотелось заняться сбором логов. Я решил попробовать поднять для этих целей связку Elasticsearch + Logstash. Это просто первое, что пришло в голову, ибо читал много хорошего про эти инструменты. Особенно интересовало, удастся ли сделать сбор логов прямо с контейнеров, потому что у докера для этой целей есть встроенный плагин, позволяющий экспортировать вывод консоли сервисов в формате gelf, который поддерживает Logstash. Я добавил в docker-compose следующее

  elasticsearch:    image: elasticsearch:7.12.1    environment:      - discovery.type=single-node      - ES_JAVA_OPTS=-Xms250m -Xmx250m    ports:      - 9200:9200      - 9300:9300  logstash:    image: logstash:7.12.1    links:      - elasticsearch    volumes:      - ./logstash.conf:/etc/logstash/logstash.conf    command: logstash -f /etc/logstash/logstash.conf    ports:     - 12201:12201/udp    depends_on:      - elasticsearch


Также для начала настроил экспорт логов из Mongo. Для этого описание сервиса mongo в файле docker-compose я дополнил следующим образом:

  mongo:    image: mongo    restart: always    logging:      driver: gelf      options:        gelf-address: "udp://localhost:12201"


Когда я запустил новую конфигурацию я понял, что это конец. Ничего не работало. Сервер стал жутко тормозить, а kswapd0 периодически выходил на первое место по загрузке процессора, а свободная память была почти на нуле. Памяти для такой конфигурации явно не хватало.

Забегая вперед, когда я активировал файл подкачки, мне удалось запустить проект. Но все равно всё работало очень медленно, причем дольше всего запускался Logstash. Инструмент, задача которого всего лишь на всего грузить логи стартовал минут 20. Хотя, когда он наконец запустился, работал как предполагалось, и я даже смог посмотреть в Grafana кусочек лога Mongo, так что, в принципе решение работало, просто для системы с таким объемом оперативной памяти оно не подходило, что не удивительно, ведь если погуглить, каковы минимальные требования для Elasticsearch, то ответ будет таким:



Я действительно этого не знал, поэтому немного приуныл, поскольку я хотел позже использовать Elasticsearch в качестве хранилища данных для jaeger, чтобы реализовать сбор трейсов приложения и поставить Kibana чтобы добить ELK стек. Но, как говорится, на нет и суда нет, поэтому я стал искать альтернативу.

Loki


И альтернатива нашлась! Искать, к слову, долго не пришлось, потому что в списке поддерживаемых источников данных Grafana обнаружился зверь под названием Loki. Это сборщик логов из той же эко-системы, что Prometheus и Grafana. Напомню, что моя идея была в том, чтобы писать логи из стандартного потока контейнеров. И для этого сценария тоже быстро нашлось решение. Оказалось, для докера есть плагин, который позволяет делать именно то, что мне надо отправлять потоки стандартного вывода в Loki. Поставить его можно следующей командой:

# docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all-permissions

Я добавил в конфигурацию docker-compose сервис loki:

  loki:    image: grafana/loki:2.0.0    ports:      - 3100:3100    command: -config.file=/etc/loki/local-config.yaml

Кроме этого, я добавил ко всем сервисам, логи с которых хотел собрать, следующую секцию:

    logging:      driver: loki      options:       loki-url: http://localhost:3100/loki/api/v1/push

А к своему приложению добавил еще

        loki-pipeline-stages: |          - json:              expressions:                output: msg                level: level                timestamp: time                pid: pid                hostname: hostname                context: context                traceID: traceID


Чтобы из лога, который у меня в формате json парсились важные поля.

Получившийся конфиг я сохранил под именем docker-compose.metrics_logs.yml.

Теперь результат можно запустить при помощи команды

docker-compose -f docker-compose.metrics_logs.yml up -d

После запуска я понял, что что-то идет не так, потому что команда вылетела с сообщением Killed. Я попробовал еще раз сервисы запустились частично. На третий раз все заработало, но когда я заглянул в top то увидел, что там периодически проскакивает kswapd0, а это значило, что системе жестко не хватало памяти.


Простой выход добавить в конфигурацию хотя бы гигабайт оперативной памяти, но по условиям эксперимента я хотел запустить все на VPS минимальной конфигурации. Поэтому я решил активировать swap-файл, который в системе по умолчанию отключен. Сколько было эпичных баттлов в комментариях про то, нужен ли файл подкачки в Linux, но у меня выбора особо не было. Заодно я решил проверить, как будет влиять наличие файла подкачки на производительность системы.

Включаем swap:

# sudo fallocate -l 1G /swapfile# sudo chmod 600 /swapfile# sudo mkswap /swapfile# sudo swapon /swapfile

Проверяем про помощи команды free:

              total        used        free      shared  buff/cache   availableMem:        1010900      501760      202344       26500      306796      353952Swap:       4194300           0     4194300

В системе появился файл подкачки размером 4Гб. Должно хватить!

Снова пытаемся запустить нашу систему:

# docker-compose -f docker-compose.metrics_logs.yml up -d


Все работает! Теперь в Grafana добавляем в качестве источника логов Loki


Идем в Explore и видим, что логи начали подгружаться.


Проверим, что стало с производительностью.



Раз все работает, осталось закрепить файл подкачки в системе. Для этого надо в файле /etc/fstab добавить строку

/swapfile swap swap defaults 0 0

После этого файл подкачки останется в системе даже после перезагрузки.

Добавляем сбор трейсов при помощи Tempo


Для полного счастья мне нужна была система сбора трейсов. Чтобы не мудрить, раз уж так вышло, что я использую стек Grafana, можно добавить в качестве сборщика данных еще один инструмент от Grafana Lab сервер для сбора трейсов Tempo. Он из коробки поддерживается Grafana, поэтому попробуем его добавить его в систему.

Для того, чтобы приложение стало генерировать трейсы, его надо специальным образом инструментировать. Для этого есть замечательный проект под названием OpenTelemetry, который развивает систему спецификаций и библиотек для реализации трейсинга под различные платформы и системы. В нем есть готовые библиотеки для автоматической инструментации Node.js и сервера express.js, который работает под капотом у nest.js. Их я и добавил в свой проект.

Tempo может принимать трейсы про всем распространённым протоколам. Я выбрал протокол Jagger Trift binary простой двоичный формат, передаваемый по UDP. Также, как и в случае с метриками, я в своем приложение я добавил переменную окружения OT_TRACING_ENABLED, которая, если ее установить в true включает в приложении телеметрию.

Для запуска Tempo я добавил в файл конфигурации docker-compose следующее:

  tempo:    image: grafana/tempo:latest    command: [-config.file=/etc/tempo.yaml]    volumes:      - ./tempo-local.yaml:/etc/tempo.yaml      - tempodata:/tmp/tempo    ports:      - 6832/udp   # Jaeger - Thrift Binary

и сохранил его под названием docker-compose.metrics_logs_tempo.yml

Для настройки Tempo я создал файл конфигурации tempo-local.yaml (на самом деле просто скопировал из репозитория Tempo подходящий и немного поправил). Запустим его командой

docker-compose -f docker-compose.metrics_logs_tempo.yml up -d


Теперь осталось в Grafana настроить источник данных:


Чтобы было удобно переходить к просмотру трейсов из логов надо настроить источник данных Loki:


После такой настройки рядом с полем traceID появится ссылка:


По этой ссылке будет открываться окно с данным трейсом:


Испытываем производительность нашего сервиса.


Здесь уже видно заметное падение производительности сервиса, но надо понимать, что эта плата за детальную телеметрию.

Дополнение: уже когда я прогнал нагрузочные тесты, результаты которых приведены ниже и дописывал статью, изучая документацию Jaeger я выяснил, что он может использовать для хранения данных локальное хранилище на основе key-value базы данных Badger, и, таким образом, может работать без Elasticsearch. Я добавил в репозиторий файл конфигурации для docker-compose где вместо tempo используется jaeger (docker-compose.metrics_logs_jaeger.yml), но не проводил всего набора тестов. Я запустил тест производительности только на базовой конфигурации, и в этом режиме получилось 19,92 запроса в секунду, что несколько больше по сравнению с вариантом, где используется tempo 18,84.

В отличии от tempo, который позволяет искать трейсы только по traceID, jeaeger дает возможность поиска по различным параметрам и у него есть собственный, достаточно удобный интерфейс для просмотра трейсов.

Результаты тестов


Итак, мне удалость запустить все необходимые компоненты мониторинга и телеметрии. Осталось понять, насколько использование различных компонентов влияет на производительность системы.

Для каждого из перечисленных выше вариантов я запускал нагрузочный тест продолжительностью 20 минут. Для того, чтобы задействовать все компоненты системы, включая сетевой интерфейс я запускал тест autocannon со своей VPS размещённой у другого провайдера, предварительно проверив скорость соединения при помощи iperf она составила 90 Мбит/сек. Так же между запусками тестов я дожидался, пока отработает функция удаления старых изображений, полностью удалив загруженные в предыдущем тесте файлы с диска и информацию из баз данных.

Результаты я свел в таблицу. Для оценки производительности я решил ориентироваться на число запросов в секунду, которое может обрабатывать система. Конечно, есть куча других метрик, но именно эта наиболее наглядно, на мой взгляд, показывает общее влияние различных факторов на производительность системы. Вот что получилось.

Запросов в секунду Снижение производительности
Без мониторинга 28,07 100%
Prometheus 27,19 97%
Prometheus+Loki 25,47 91%
Prometheus+Loki+Tempo 18,84 67%

Выходит, что только использование трейсинга приводит к значительным потерям производительности, а в случае со сбором метрик и логов потери составляют менее 10%.

Добавляем ядра и память


Также я решил посмотреть, как будет влиять на производительность сервиса увеличение объема памяти и количества ядер процессора. Macloud.ru позволяет менять параметры тарифа и я решил посмотреть как работает эта функция. Первым делом я добавил еще 1 Гб оперативной памяти.


После нажатия кнопки Сменить тариф сервер перезагрузился и вот что получилось:

              total        used        free      shared  buff/cache   availableMem:        2043092      309876     1190416       14284      542800     1576804Swap:       4194300           0     4194300

Все правильно. Теперь можно отключить файл подкачки.

swapoff /swapfile

Посмотрим, что покажут тесты:

Запросов в секунду Снижение производительности
Без мониторинга 27,52 100%
Prometheus 24,78 90%
Prometheus+Loki 21,58 78%
Prometheus+Loki+Tempo 21,44 78%

Почему-то производительность в целом стала немного ниже, но не так сильно зависит от включения функций мониторинга. Я предположил, что может быть дело все-таки в файле подкачки и включил его обратно. Вот что получилось:

Запросов в секунду Снижение производительности
Без мониторинга 29,64 100%
Prometheus 26,97 91%
Prometheus+Loki 25,7 87%
Prometheus+Loki+Tempo 22,95 77%

Выводы такие добавление памяти улучшает производительность системы, наличие файла подкачки также влияет положительно. Почему так происходит, требует более детального изучения, могу лишь предположить, что менеджер памяти при наличии файла подкачки имеет возможность вытеснить на диск малоиспользуемые данные чтобы дать больше места активным приложениям. Я оставил файл подкачки включенным несмотря на то, что памяти в принципе хватало для работы системы и без него.

После этого мне стало интересно а как повлияет на производительность добавление второго ядра CPU? Сказано-сделано:


После добавления второго ядра я погнал всё те же тесты и вот какой результат получился.

Запросов в секунду Снижение производительности
Без мониторинга 49,05 100%
Prometheus 44,52 91%
Prometheus+Loki 45,64 93%
Prometheus+Loki+Tempo 40,34 82%

Производительность увеличилась в 1,75 раза если сравнивать с базовым вариантом. Это хорошо, ведь если нагрузка на сервер будет расти, я могу просто докупить второе ядро, когда в этом появится необходимость.

Дальше я экспериментировать не стал, мне понятно, что, если продолжить добавлять память и процессорные ядра, производительность системы не будет расти так заметно. Чтобы обеспечить прирост производительности в этом случае нужно будет организовать запуск приложения в режиме кластера и внести еще ряд архитектурных изменений.

Выводы


Начиная этот эксперимент, я задался целью проверить, возможно ли на VPS c очень ограниченными ресурсами (я знаю, что можно найти предложения с еще более скромными параметрами, но 1 CPU + 1 Гб RAM это доступный минимум у большей части провайдеров) запустить полноценную систему мониторинга для приложения. Как видите, это оказалось вполне возможно. Конечно, не все инструменты, которые используют крупные компании, применимы, но вполне можно найти такой набор, который позволит организовать мониторинг вашей системы не сильно влияя на ее производительность.

Также в ходе эксперимента я смог ответить для себя на ряд вопросов:

Стоит ли заморачиваться с настройкой мониторинга для совсем небольшого проекта?

Я считаю, что да, стоит. Мониторинг помогает понять, как ведет себя ваш проект в рабочей среде и дает информацию для дальнейшего улучшения качества кода. Я в процессе данного эксперимента, благодаря изучению данных мониторинга я смог заметить ряд недостатков в своем приложении, которые мне бы вряд ли удалось выявить другим способом.

Нужно ли делать нагрузочное тестирование?

Однозначно да. Во-первых, это позволяет найти узкие места в вашем приложении. Во-вторых, позволяет оценить, с какой нагрузкой сможет справиться система. Ну и в-третьих, имея настроенный мониторинг очень интересно наблюдать как система ведет себя под нагрузкой, это дает очень много пищи для размышлений и позволяет улучшить понимание того, как взаимодействуют компоненты системы.

Сложно ли настроить мониторинг

Ох, хотел бы я сказать, что это просто, но нет. Если делаешь это впервые, скорее всего придется вдоволь походить по граблям. Тут нет единого рецепта, как построить работающую систему, которая будет делать то, что вам нужно. Часто приходится собирать необходимую информацию по крупицам, и действовать интуитивно, потому что какие-то важные для тебя моменты не озвучиваются в документации. Если вы решите пойти этим путем, надеюсь, что данная статья и прилагающийся репозиторий поможет вам пройти его быстрее, чем мне.

А почему не облачные решения?

Мне просто спокойней платить фиксированную сумму и иметь в своем распоряжении все ресурсы, которые дает VPS. Истории, как разработчик что-то сделал неправильно и получил многотысячный счет пугают. Хотя возможно, что в облаке все, что я описываю в этой статье настраивается за пару кликов мышкой, и тогда это, конечно, огромный плюс.

В заключении хотелось бы сказать даже если у вас небольшой проект, и еще нет мониторинга, имеет смысл задуматься о его внедрении. Если вы опасаетесь, что это сложно сделать, или что у вашей системы недостаточно ресурсов для его реализации можете использовать в качестве отправной точки материалы данной статьи и пример реализации, который я разместил в репозитории.

Репозиторий можно посмотреть по этому адресу: github.com/debagger/observable-backend



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Почему клавиатура всегда быстрее мыши

27.05.2021 10:19:54 | Автор: admin

Тепловая карта с клавиатуры высокоинтеллектуальных программистов, источник: r/ProgrammerHumor/

Много говорилось об удовольствии, которое испытываешь при работе в консоли. Это не случайно: так задумано отцами-основателями Unix. Возникает странное ощущение, что это самый правильный способ взаимодействия с компьютером.

Более того, такое же приятное ощущение возникает при использовании горячих клавиш в любых приложениях, даже с графическим интерфейсом.

В чём же дело?

Экзотический манипулятор


Изначально трекболы использовались для особых профессиональных приложений: например, для систем управления радарами. Мышь как перевёрнутый трекбол, накрытый коробкой, придумали в 1980-е годы. В то время программисты не совсем понимали, зачем компьютеру мышь, если код пишется в виде машинных команд, а взаимодействие с мейнфреймом целиком происходит в консоли.

Необычные манипуляторы с колёсиком стоили в районе 400 долларов. Затем вышел революционный компьютер Apple Lisa один из первых ПК с графическим интерфейсом. Компания Apple демпинговала она снизила стоимость манипулятора до 25 долларов и сделала сексуальный дизайн с одной кнопкой. Мышь из профессионального аксессуара превратилась в массовый гаджет.


Apple Lisa. Очень элегантный дизайн для своего времени

С тех пор мышь и GUI стали прочно ассоциироваться с компьютерами Apple и модным оконным интерфейсом.

Вскоре появилась Windows. Затем первые игры с поддержкой мыши, например, для более точного движения персонажа Возникает впечатление, что компьютерные игры постоянно стимулировали технический прогресс, в том числе производительность CPU, GPU, ёмкость накопителей, популярность мышей и других экзотических манипуляторов.

Геймеры вечно недовольны текущей производительностью персональных компьютеров и непрерывно требуют от индустрии наращивать мощность железа. Если посмотреть, кто собирает самые производительные конфигурации ПК и покупает самые мощные версии десктопных процессоров, то это будут сотни любителей рекордных FPS, парочка учёных с машинным обучением и один программист, который каждый день что-то билдит и компилирует. Да и то, трём последним персонажам компьютер нужен конкретно для работы, так что они не всегда вкладываются собственными деньгами, как геймеры.

Если бы не компьютерные игры, то производители процессоров могли сосредоточиться исключительно на серверных CPU. В самом деле, армия бухгалтеров, экономистов и прочих офисных клерков спокойно посидят на компьютерах 20-летней давности, которые их полностью устраивают. Они вообще не в курсе, какое железо стоит внутри процессора (так они называют системный блок). Зато не отрывают руку от любимой мышки. Отними у офисного клерка мышь и он будет несколько минут тупо пялиться в монитор и бессмысленно дёргать рукой, не в силах совершить ни одного полезного действия, словно под седативными веществами.

В наше время редко встретишь компьютер без мыши. А вот удовольствие от работы в консоли осталось.

На форумах и в блогах пользователи Linux делятся любимыми однострочниками и другими трюками, которые повышают эффективность рабочего процесса. Есть даже отличный сайт с голосованием за однострочники.

Крутые однострочники


Вот некоторые примеры интересного использования программ Linux.

ps aux | convert label:@- process.png

Команда конвертирует выдачу из консоли в изображение. Например, чтобы поделиться с коллегами в виде скриншота. Сама идея скриншотить текст некоторым кажется концептуально неправильной, но в некоторых случаях так просто быстрее и удобнее. В наше время с распространением смартфонов и планшетов скриншоты становятся чуть ли не стандартом для копирования текста с экрана, что немного печалит, если воспринимать это как намёк на закат универсальных компьютеров

Примечание. Утилита convert входит в пакет ImageMagick, так что нужно сначала его установить.

А вообще, текст из консоли можно быстро запостить через интернет-сервис вроде termbin.com (это как pastebin, только для консоли):

ps aux | nc termbin.com 9999

Как обычно, с алиасом для частого использования:

alias tb='nc termbin.com 9999'

Следующая:

curl ipinfo.io

Это если хотите узнать свой внешний IP-адрес через сервис ipinfo.io.

git log --format='%aN' | sort -u

Очень удобная команда, если работаете над опенсорсным проектом и хотите посмотреть контрибуторов.

history | awk '{print $2}' | sort | uniq -c | sort -rn | head

Отсортированный список самых часто запускаемых команд (тоже полезно добавить в алиасы, чтобы запускать в пару нажатий).

ls -d */

Листинг только директорий. Полезная команда, которой тоже хорошо бы присвоить алиас для быстрого использования.

du -hs */ | sort -hr | head

Эта команда показывает только 10 крупнейших директорий в текущем каталоге.

ss -p

Просмотр, какие приложения потребляют трафик (утилиты iftop и nethogs дают более подробную информацию).

rm -f !(test.txt)

Команда удаляет из директории все файлы, кроме одного, указанного в скобках. Это работает после включения расширенной глобуляции в баше (shopt -s extglob).

python3 -m http.server

Запускает http-сервер и начинает отдавать файлы. Удобно, если хотите пошарить какой-то html-файл по сети.

screen -S the-screen-name

Создание экран-сессии.

screen -x the-screen-name

Подключение к экран-сессии. Такую сессию можно принудительно создать у пользователя после его авторизации в системе и наблюдать, что происходит на его экране. Полезно, если кто-то подключился к вашему серверу, а вы хотите посмотреть, чем он занимается.

Утилита screen поставляется по умолчанию со многими дистрибутивами Linux, хотя не со всеми.

alias copy='xclip -i -selection clipboard'

cat file.txt | copy

Копирование файла в буфер обмена, когда первый однострочник прописан как алиас copy в баше.

sudo !!

Запустить последнюю команду под рутом, если в предыдущей команде вы забыли набрать sudo. У этой команды первое место в рейтинге однострочников.

Конечно, здесь просто примеры. Прелесть линуксовой консоли в том, что можно самому придумать такую последовательность команд, которую никто вообще никогда не использовал, но вам нужен именно этот конвейер в данной ситуации.

Горячие клавиши как наследие консоли


В графических программах под Windows тоже зачастую хочется сделать работу горячими клавишами, не трогая этот эппловский манипулятор. На самом деле использование горячих клавиш очень ускоряет рабочий процесс в любой программе.

Алиасы bash служат той же цели: выполнить команду с наименьшим количеством усилий, то есть с наименьшим количеством нажатий клавиш.

Взаимодействие с машиной у опытного специалиста происходит практически со световой скоростью. Скорость ограничивается только временем обработки команд на CPU и скоростью вывода текста в консоль, то есть скоростью графической подсистемы. А с сетевыми задержками компьютер работает медленнее, чем человек. Это идеал производительности человека.

Если подняться на более высокий уровень абстракций, то горячие клавиши в графических интерфейсах это в каком-то смысле дальний потомок консоли, некое историческое эхо. С горячими клавишами, однострочниками и алиасами в баше человек становится быстрее компьютера приходится делать паузы, чтобы дождаться результата выполнения команд. CPU загружен практически на 100%.

Это фундаментальное преимущество клавиатуры как инструмента для ввода команд по сравнению с любыми манипуляторами. В этом же и сила консоли.

Конечно, не все команды вызываются хоткеями. Иногда требуется переместить графический объект на экране Тогда нужен курсор. Но если хоткей существует, он без вариантов быстрее, чем аналогичное действие мышью в меню. Горячие клавиши живое доказательство, что команды с клавиатуры запускаются быстрее.

В общем, из этого факта может родиться предположение, что отцы-основатели Unix всё-таки были правы, а их наследие живёт во всех операционных системах. Графическая оболочка просто тонкий слой абстракции поверх мощного фундамента, который они построили. Ведь мы помним, что macOS тоже основана на Unix и относится к семейству *nix-систем.

Ну а окошки и другие элементы графического интерфейса Windows, по мнению Apple, это вторичный продукт, скопированный с интерфейса Lisa (см. судебный процесс Apple против Microsoft c 1988 по 1994 гг).

Суд отклонил иск Apple к Microsoft. Но некоторые вещи обращают на себя внимание. Например, команда open . в консоли macOS открывает Finder в текущей директории. В Windows то же самое делает команда start . (Finder здесь называется Explorer). Окна в macOS закрываются крестиком в левом верхнем углу, а в Windows в правом углу. Возможно, на примере таких деталей Билл Гейтс доказал суду, что у него оригинальный графический интерфейс, который сильно отличается от macOS.

Скопирован графический интерфейс или нет, формально неизвестно. Но факт в том, что это просто тонкая оболочка над системой. Так же как и мышка удобный, но совсем не обязательный аксессуар.



На правах рекламы


Наша компания предлагает аренду VPS для совершенно любых проектов. Создайте собственный тарифный план в пару кликов, максимальная конфигурация позволит разместить практически любой проект 128 ядер CPU, 512 ГБ RAM, 4000 ГБ NVMe!

Присоединяйтесь к нашему чату в Telegram.

Подробнее..

Немного об использовании regex в map nginx

21.05.2021 18:18:17 | Автор: admin

Давно ничего не писал, поэтому разбавим конец пятницы простыми, но не всегда очевидными иcканиями в Nginx.

В этом веб-сервере есть замечательная директива map, которая позволяет существенно упростить и сократить конфиги. Суть директивы в том, что она позволяет создать новую переменную, значение которой зависит от значений одной или нескольких исходных переменных. Ещё большую силу директива приобретает при использовании регулярных выражений, но при этом забывается, об одном важном моменте. Выдержка из мануала:

Поскольку переменные вычисляются только в момент использования, само по себе наличие даже большого числа объявлений переменных map не влечёт за собой никаких дополнительных расходов на обработку запросов.

И здесь важным является не только, то что "map не влечёт за собой никаких дополнительных расходов на обработку запросов", а и то, что "переменные вычисляются только в момент использования".

Как известно, конфиг Nginx, в основном, декларативен. Это касается и директивы map, и, не смотря на то, что она расположена в контексте http, её вычисление не происходит до момента обработки запроса. То есть при использовании результирующей переменной в контекстах server, location, if и т.п. мы "подставляем" не готовый результат вычисления, а лишь "формулу" по который этот результат будет вычислен в нужный момент. В этой конфигурационной казуистике не возникает проблем до того момента, пока мы не используем регулярные выражения. А именно регулярные выражения с выделениями. А ещё точнее, регулярные выражения с неименованными выделениями. Проще показать на примере.

Допустим у нас есть домен example.com с множеством поддоменов 3-го уровня, а-ля ru.example.com, en.example.com, de.example.com и т.д., и мы хотим их перенаправить на новые поддомены ru.example.org, en.example.org, de.example.org и т.п. Вместо того чтобы описывать сотни строк редиректов мы поступим вот так:

map $host $redirect_host {  default "example.org";  "~^(\S+)\.example\.com$"  $1.example.org;}server {    listen       *:80;    server_name  .example.com;  location / {        rewrite ^(.*)$ https://$redirect_host$1 permanent;    }

Здесь мы ошибочно ожидали, что при запросе ru.example.com произойдет вычисление регулярки в map и, соответственно, попав в location переменная $redirect_host будет содержать значение ru.example.org, однако на деле это не так:

$ GET -Sd ru.example.comGET http://ru.example.com301 Moved PermanentlyGET https://ru.example.orgru

Оказалось, что на момент исполнения запроса наша переменная равна ru.example.orgru. Всё из-за того, что мы пренебрегли предупреждением "переменные вычисляются только в момент использования", а в нашем rewrite оказалась некая регулярка вложенная в регулярку.

Самый простой вариант решения - не использовать regexp одновременно и в map и в месте вычисления переменной, например, для данного конкретного случая это может выглядеть так:

map $host $redirect_host {  default "example.org";  "~^(\S+)\.example\.com$"  $1.example.org;}server {    listen       *:80;    server_name  .example.com;    location / {        return 301 https://$redirect_host$request_uri;    }}

Но что делать, если альтернативного решения без регулярок нет (ну или просто очень хочется).
Пробуем использовать именованные выделения в map:

map $host $redirect_host {  default "example.org";  "~^(?<domainlevel3>\S+)\.example\.com$"  $domainlevel3.example.org;}server {    listen       *:80;    server_name  .example.com;    location / {        rewrite ^(.*)$ https://$redirect_host$1 permanent;    }}

Попытка не увенчалась успехом:

$ GET -Sd ru.example.comGET http://ru.example.com301 Moved PermanentlyGET https://ru.example.orgru

так как наше неименованное выделение $1 получит результат именованного $domainlevel3. То есть необходимо использовать именованные выделения в обеих регулярках:

map $host $redirect_host {  default "example.org";  "~^(?<domainlevel3>\S+)\.example\.com$"  $domainlevel3.example.org;}server {    listen       *:80;    server_name  .example.com;    location / {        rewrite ^(?<requri>.*)$ https://$redirect_host$requri permanent;    }}

И теперь всё работает как ожидалось:

$ GET -Sd ru.example.comGET http://ru.example.com301 Moved PermanentlyGET https://ru.example.org/
Подробнее..

Мониторинг Virtuozzo Hybrid Server с помощь Prometheus

01.06.2021 14:09:01 | Автор: admin

Пользователи продуктов Virtuozzo и OpenVZ обычно работают с множеством развернутых машин под управлением нашего ПО. Поэтому для них логично запустить централизованный мониторинг всего парка таких серверов. И сегодня мы расскажем о том, как использовать для этого встроенные сервисы мониторинга Virtuozzo Hybrid Infrastructure на основе Prometheus.

Схожие сервисы доступны и в VHS, но только если вы используете графические интерфейс для управления кластером Virtuozzo Storage. Если же у вас нет кластера Storage, или вы не используете GUI для его администрирования (либо работаете с OpenVZ, где подключение Virtuozzo Storage является возможным, но редко встречающимся сценарием), то для мониторинга приходится обращаться к сторонним решениям.

Анализ предпочтений пользователей (публично доступный для OpenVZ здесь) показывает следующее: как и для мониторинга серверов с Linux и основанных на нем решениями, для продуктов Virtuozzo популярны Zabbix и Prometheus.

Мы поговорим сегодня про Prometheus более молодой, но активно набирающий популярность инструментарий.

Непосредственно Prometheus занимается сбором данных от подвластных ему экспортеров - в репозиториях VHS доступны node_exporter (для сбора общих характеристик сервера, таких как использование ресурсов и состояние дисков) и libvirt_exporter (для сбора информации о виртуальных машинах, управляемых libvirt). Экспортеры основаны на соответствующих стандартных проектах, но содержат ряд специфичных для Virtuozzo изменений.

Для удобного изучения собираемой информации, в связке с Prometheus обычно устанавливается инструментарий визуализации данных Grafana, а для возможности получения оповещений о требующих внимания администратора событиях Alertmanager.

Готовим плацдарм

Prometheus, Grafana и Alertmanager можно развернуть на отдельной машине, не обязательно физической. Раз уж мы говорим о Virtuozzo Hybrid Server, то логично развернуть их внутри контейнера на одной из машин VHS. Например, внутри контейнера с Virtuozzo Linux 8; для функционирования вполне достаточно двух ядер ЦПУ и пары гигабайт памяти:

# prlctl create promct --vmtype=ct --ostemplate=vzlinux-8-x86_64# prlctl set promct --cpu 2# prlctl set promct --memsize 2G

Не забудьте выставить необходимый IP адрес и доменное имя, по которым будет доступен ваш контейнер.

Внутри контейнера не помешает выставить правильный часовой пояс:

# timedatectl set-timezone 'Europe/Moscow'

А также настроить firewall порт 9090 для веб-интерфейса Prometheus, 9093 для AlertManager и 3000 для Grafana. Обратите внимание, что веб Prometheus и Alertmanager доступны без пароля, так что выставляйте их только в ваши внутренние сети.

# firewall-cmd --zone=public --permanent --add-port=9090/tcp# firewall-cmd --zone=public --permanent --add-port=9093/tcp# firewall-cmd --zone=public --permanent --add-port=3000/tcp# firewall-cmd --reload

Устанавливаем Prometheus & co.

Prometheus входит в репозитории многих дистрибутивов, в том числе и Virtuozzo Linux 8, поэтому можно его установить штатными средствами:

# yum install prometheus

Если вас не устраивает версия из дистрибутива (например, хочется самую свежую, либо наоборот - свежая имеет какие-то раздражающие вас проблемы), то можно просто скачать бинарные файлы с GitHub и положить их в директорию /opt (или в другую на ваш выбор). Например, для версии 2.21.0:

# cd /opt# wget https://github.com/prometheus/prometheus/releases/download/v2.21.0/prometheus-2.21.0.linux-amd64.tar.gz# tar -xzf prometheus*.tar.gz

Единственный нюанс при ручной установке - не забыть создать (либо скорректировать) service-файл для systemd, чтобы там были правильные пути:

# cat /lib/systemd/system/prometheus.service[Unit]Description=PrometheusWants=network-online.targetAfter=network-online.target[Service]Type=simpleWorkingDirectory=/opt/prometheus-2.21.0.linux-amd64Restart=on-failureExecStart=/opt/prometheus-2.21.0.linux-amd64/prometheus[Install]WantedBy=multi-user.target

Аналогично с Alertmanager - можно установить из репозиториев:

# yum install alertmanager

... А можно и с сайта, точно также не забыв service-файл:

# wget https://github.com/prometheus/alertmanager/releases/download/v0.21.0/alertmanager-0.21.0.linux-amd64.tar.gz# cd /opt# tar -xzf alertmanager*.tar.gz# cat /usr/lib/systemd/system/alertmanager.service[Unit]Description=AlertmanagerWants=network-online.targetAfter=network-online.targetAfter=prometheus.service[Service]Type=simpleWorkingDirectory=/root/alertmanager-0.21.0.linux-amd64Restart=on-failureExecStart=/root/alertmanager-0.21.0.linux-amd64/alertmanager --config.file=alertmanager.yml[Install]WantedBy=multi-user.target

Grafana сразу предоставляет собранные rpm-пакеты, так что можно, опять же, поставить либо из дистрибутива:

# yum install grafana

либо с сайта проекта:

# yum install https://dl.grafana.com/oss/release/grafana-7.1.5-1.x86_64.rpm

Подготавливаем машины с Virtuozzo Hybrid Server

На каждом сервере VHS, который предполагается мониторить, необходимо установить пакеты с экспортерами:

# yum install node_exporter libvirt_exporter

Чтобы экспортеры могли отдавать данные в Prometheus, необходимо открыть соответствующие порты - 9177 для libvirt_exporter и 9100 для node_exporter. Доступ к ним желательно ограничить адресом машины с Prometheus, чтобы посторонние люди не делали попыток снять метрики с ваших серверов:

# firewall-cmd --permanent --zone=public --add-rich-rule=' rule family="ipv4" source address="1.2.3.4/32" port protocol="tcp" port="9177" accept'# firewall-cmd --permanent --zone=public --add-rich-rule=' rule family="ipv4" source address="1.2.3.4/32" port protocol="tcp" port="9100" accept'# firewall-cmd --reload

Здесь "1.2.3.4" необходимо поменять на реальный адрес Prometheus.

Наконец, смело включаем и запускаем сервисы экспортеров:

# systemctl enable node_exporter# systemctl enable libvirt-exporter# systemctl start node_exporter# systemctl start libvirt-exporter

Дело за малым - надо сообщить Прометею, откуда ему собирать информацию.

Настройка Prometheus

Конфигурация самого Prometheus содержится в наборе Yaml-файлов. Писать их с нуля, даже с помощью примеров из документации то еще развлечение. Именно этот процесс и решили улучшить в Virtuozzo, положив в репозитории VHS 7 пакет vz-prometheus-cfg с шаблонами файлов конфигурации. Его можно установить на любой машине VHS 7, а если вы развернули Prometheus внутри VzLinux 8 то можно его поставить прямо на сервере из репозиториев этого дистрибутива:

# yum install vz-prometheus-cfg

После чего изучать директорию /usr/share/vz-prometheus-cfg/, начав с файла prometheus-example.yml.

Этот файл необходимо отредактировать под ваши нужды и под именем prometheus.yml сохранить в машину или контейнер, где у вас расположен Prometheus. Главное, что там необходимо изменить это местоположение файлов *rules.yml и *alerts.yml, которые также можно скопировать из директории /usr/share/vz-prometheus-cfg/ на сервер Prometheus. Можно их изучить и даже отредактировать, однако они вполне работоспособны и в исходном варианте.

В первую очередь здесь интересны vz-rules, содержащие правила сбора информации о потреблении ресурсов (диска, процессора, памяти и сети) виртуальными окружениями и предупреждения о проблемах с ресурсами (останов ВМ из-за ошибки диска, исчерпание диска на сервере и тому подобное).

Непосредственно машины, с которых надо собирать информацию, указываются в target-файлах. В первом приближении достаточно одного такого файла со списком машин для каждого экспортера (напомним, node_exporter отдает информацию на порту 9100, а libvirt - 9177):

# cat my-vz-libvirt.yml- labels: group: my-vz-deployment targets: - my.node1:9177 - my.node2:9177# cat my-vz-node.yml- labels: group: my-vz-deployment targets: - my.node1:9100 - my.node2:9100

Пути к этим файлам необходимо указать в соответствующих разделах секции scrape_configs в prometheus.yml:

scrape_configs: ... - job_name: node ... file_sd_configs: - files: - /root/prometheus-2.21.0.linux-amd64/targets/my-vz-node.yml - job_name: libvirt ... file_sd_configs: - files: - /root/prometheus-2.21.0.linux-amd64/targets/my-vz-libvirt.yml

Пример полного файла конфигурации можно посмотреть в документации: https://docs.virtuozzo.com/virtuozzo_hybrid_server_7_users_guide/advanced-tasks/monitoring-via-prometheus.html. Обратите внимание, что важными параметрами являются job_name на них идет отсылка в файлах с правилами и алертами. Так что если задумаете менять эти имена не забудьте пройтись и по другим файлами конфигурации и провести соответствующие замены.

Как только все необходимые файлы отредактированы - пора стартовать сервисы:

# systemctl start prometheus# systemctl start alertmanager# systemctl start grafana-server

После чего продолжать уже с настройкой двух других инструментов

Grafana и Alertmanager

Конфигурация Grafana осуществляется через ее веб-интерфейс по адресу http://<ваш-сервер>:3000. Логин-пароль по умолчанию - "admin" / "admin".

Настройка достаточно проста и стандартна - сначала необходимо указать "Prometheus" как источник данных, пройдя в Configuration -> Data Sources -> "Add data source", выбрав "Prometheus" и указав http://localhost:9090 в качестве его адреса.

Далее можно импортировать готовые json-файлы, опять же поставляемые с пакетов vz-prometheus-cfg - grafana_hn_dashboard.json и grafana_ve_dashboard.json - служащие соответственно для отображения информации о серверах и о виртуальных окружениях. Импорт осуществляется в меню "Dashboards" -> "Manage" -> "Import", в качестве источника данных необходимо добавить настроенный ранее Prometheus.

И, вуаля, к этому моменту Prometheus уже вполне мог получить какие-то данные с ваших машин, так что можно практически сразу изучать графики.

Наконец, настройка оповещений Alertmanager тут все на ваше усмотрение, готовых рецептов а-ля отправить отчет в саппорт Virtuozzo не предусмотрено. Так что можно, например, просто настроить типичные оповещения по email в /etc/alertmanager/alertmanager.yaml:

route:   receiver: 'email'  group_by: ['alertname', 'cluster']   group_wait: 30s   group_interval: 5m   repeat_interval: 3h   receivers:  - name: 'email'   email_configs:   - to: 'admin@myserver.com'   from: 'vz-alert@myserver.com'   smarthost: smtp.myserver.com:587

Всего алертов из коробки предусмотрено почти три десятка полный перечень доступен в документации.

Надеемся, обилие и нетривиальность параметров не отпугнет вас от использования Prometheus. Тем более что настроить его достаточно один раз, а шаблоны от Virtuozzo должны в этом неплохо помочь. В результате вы получаете красивую, удобную и функциональную систему мониторинга, без которой современному системному администратору будет намного сложнее работать.

Подробнее..

Перевод Находим и устраняем уязвимости бинарных файлов в Linux с утилитой checksec и компилятором gcc

12.06.2021 16:15:59 | Автор: admin

Изображение: Internet Archive Book Images. Modified by Opensource.com. CC BY-SA 4.0

После компиляции одного и того же исходного кода мы можем получить разные бинарные файлы. Это зависит от того, какие флаги мы передадим в руки компилятору. Часть этих флагов позволяет включать или отключать ряд свойств бинарника, имеющих отношение к безопасности.

Некоторые из них компилятор включает или отключает по умолчанию. Так в бинарных файлах могут возникать уязвимости, о которых мы не знаем.

Checksec это простая утилита, позволяющая определить, какие свойства были включены при компиляции. В этой статье я расскажу:

  • как использовать утилиту checksec для поиска уязвимостей;
  • как использовать компилятор gcc для устранения найденных уязвимостей.

Установка checksec


Для Fedora OS и других систем на базе RPM:

$ sudo dnf install checksec

Для систем на базе Debian используйте apt.

Быстрый старт с checksec


Утилита сhecksec состоит из единственного скриптового файла, который, впрочем, довольно большой. Благодаря такой прозрачности вы можете узнать, какие системные команды для поиска уязвимостей в бинарных файлах выполняются под капотом:

$ file /usr/bin/checksec/usr/bin/checksec: Bourne-Again shell script, ASCII text executable, with very long lines$ wc -l /usr/bin/checksec2111 /usr/bin/checksec

Давайте запустим checksec для утилиты просмотра содержимого каталогов (ls):

$ checksec --file=/usr/bin/ls<strong>RELRO      STACK CANARY   NX      PIE       RPATH   RUNPATH   Symbols    FORTIFY Fortified    Fortifiable  FILE</strong>Full RELRO   Canary found   NX enabled  PIE enabled   No RPATH  No RUNPATH  No Symbols    Yes  5    17       /usr/bin/ls

Выполнив команду в терминале, вы получите отчёт о том, какими полезными свойствами обладает этот бинарник, и какими не обладает.

Первая строка это шапка таблицы, в которой перечислены различные свойства безопасности RELRO, STACK CANARY, NX и так далее. Вторая строка показывает значения этих свойств для бинарного файла утилиты ls.

Hello, бинарник!


Я скомпилирую бинарный файл из простейшего кода на языке С:

#include <stdio.h>int main(){printf(Hello World\n);return 0;}

Обратите внимание, что пока я не передал компилятору ни одного флага, за исключением -o (он не относится к делу, а просто говорит, куда выводить результат компиляции):

$ gcc hello.c -o hello$ file hellohello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped$ ./helloHello World

Теперь запущу утилиту checksec для моего бинарника. Некоторые свойства отличаются от свойств

ls (для него я запускал утилиту выше):$ checksec --file=./hello<strong>RELRO      STACK CANARY   NX      PIE       RPATH   RUNPATH   Symbols     FORTIFY Fortified    Fortifiable   FILE</strong>Partial RELRO  No canary found  NX enabled  No PIE     No RPATH  No RUNPATH  85) Symbols    No  0    0./hello

Checksec позволяет использовать различные форматы вывода, которые вы можете указать с помощью опции --output. Я выберу формат JSON и сделаю вывод более наглядным с помощью утилиты jq:

$ checksec --file=./hello --output=json | jq{./hello: {relro: partial,canary: no,nx: yes,pie: no,rpath: no,runpath: no,symbols: yes,fortify_source: no,fortified: 0,fortify-able: 0}}

Анализ (checksec) и устранение (gcc) уязвимостей


Созданный выше бинарный файл имеет несколько свойств, определяющих, скажем так, степень его уязвимости. Я сравню свойства этого файла со свойствами бинарника ls (тоже указаны выше) и объясню, как это сделать с помощью утилиты checksec.

Для каждого пункта я дополнительно буду показывать, как устранить найденные уязвимости.

1. Отладочные символы


Начну с простого. Во время компиляции в бинарный файл включаются определённые символы. Эти символы используются при разработке программного обеспечения: они нужны для отладки и исправления ошибок.

Отладочные символы обычно удаляются из той версии бинарника, которую разработчики выпускают для широкого использования. Это никак не влияет на работу программы. Такая очистка (она обозначена словом strip) часто выполняется для экономии места, так как файл становится легче после удаления символов. А в проприетарном программном обеспечении эти символы часто удаляют ещё и потому, что у злоумышленников есть возможность прочитать их в двоичном формате и использовать в своих целях.

Сhecksec показывает, что отладочные символы присутствуют в моём бинарнике, но их нет в файле ls.

$ checksec --file=/bin/ls --output=json | jq | grep symbolssymbols: no,$ checksec --file=./hello --output=json | jq | grep symbolssymbols: yes,


То же самое может показать запуск команды file. Символы не удалены (not stripped).

$ file hellohello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, <strong>not stripped</strong>

Как работает checksec


Запустим эту команду с опцией --debug:

$ checksec --debug --file=./hello

Так как утилита checksec это один длинный скрипт, то для его изучения можно использовать функции Bash. Выведем команды, которые запускает скрипт для моего файла hello:

$ bash -x /usr/bin/checksec --file=./hello

Особое внимание обратите на echo_message вывод сообщения о том, содержит ли бинарник отладочные символы:

+ readelf -W --symbols ./hello+ grep -q '\.symtab'+ echo_message '\033[31m96) Symbols\t\033[m ' Symbols, ' symbols=yes' 'symbols:yes,'

Утилита checksec использует для чтения двоичного файла команду readelf со специальным флагом --symbols. Она выводит все отладочные символы, которые содержатся в бинарнике.

$ readelf -W --symbols ./hello

Из содержимого раздела .symtab можно узнать количество найденных символов:

$ readelf -W --symbols ./hello | grep -i symtab

Как удалить отладочные символы после компиляции


В этом нам поможет утилита strip.

$ gcc hello.c -o hello$$ file hellohello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, <strong>not stripped</strong>$$ strip hello$$ file hellohello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, <strong>stripped</strong>

Как удалить отладочные символы во время компиляции


При компиляции используйте флаг -s:

$ gcc -s hello.c -o hello$$ file hellohello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=247de82a8ad84e7d8f20751ce79ea9e0cf4bd263, for GNU/Linux 3.2.0, <strong>stripped</strong>

Убедиться, что символы удалены, можно и с помощью утилиты checksec:

$ checksec --file=./hello --output=json | jq | grep symbolssymbols: no,

2. Canary


Canary (осведомители) это секретные значения, которые хранятся в стеке между буфером и управляющими данными. Они используются для защиты от атаки переполнения буфера: если эти значения оказываются изменены, то стоит бить тревогу. Когда приложение запускается, для него создаётся свой стек. В данном случае это просто структура данных с операциями push и pop. Злоумышленник может подготовить вредоносные данные и записать их в стек. В этом случае буфер может быть переполнен, а стек повреждён. В дальнейшем это приведёт к сбою работы программы. Анализ значений canary позволяет быстро понять, что произошёл взлом и принять меры.

$ checksec --file=/bin/ls --output=json | jq | grep canarycanary: yes,$$ checksec --file=./hello --output=json | jq | grep canarycanary: no,$Чтобы проверить, включен ли механизм canary, скрипт checksec запускает следующую команду:$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'

Включаем canary


Для этого при компиляции используем флаг -stack-protector-all:

$ gcc -fstack-protector-all hello.c -o hello$ checksec --file=./hello --output=json | jq | grep canarycanary: yes,

Вот теперь сhecksec может с чистой совестью сообщить нам, что механизм canary включён:

$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'2: 0000000000000000   0 FUNC  GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3)83: 0000000000000000   0 FUNC  GLOBAL DEFAULT UND __stack_chk_fail@@GLIBC_2.4$

3. PIE


Включённое свойство PIE позволяет произвольно размещать в памяти исполняемый код независимо от его абсолютного адреса:

PIE (Position Independent Executable) исполняемый позиционно-независимый код. Возможность предсказать, где и какие области памяти находятся в адресном пространстве процесса играет на руку взломщикам. Пользовательские программы загружаются и выполняются с предопределённого адреса виртуальной памяти процесса, если они не скомпилированы с опцией PIE. Использование PIE позволяет операционной системе загружать секции исполняемого кода в произвольные участки памяти, что существенно усложняет взлом.

$ checksec --file=/bin/ls --output=json | jq | grep piepie: yes,$ checksec --file=./hello --output=json | jq | grep piepie: no,

Часто свойство PIE включают только при компиляции библиотек. В выводе ниже hello помечен как LSB executable, а файл стандартной библиотеки libc (.so) как LSB shared object:

$ file hellohello: ELF 64-bit <strong>LSB executable</strong>, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped$ file /lib64/libc-2.32.so/lib64/libc-2.32.so: ELF 64-bit <strong>LSB shared object</strong>, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4a7fb374097fb927fb93d35ef98ba89262d0c4a4, for GNU/Linux 3.2.0, not stripped

Checksec получает эту информацию следующим образом:

$ readelf -W -h ./hello | grep EXECType:               EXEC (Executable file)

Если вы запустите эту же команду для библиотеки, то вместо EXEC увидите DYN:

$ readelf -W -h /lib64/libc-2.32.so | grep DYNType:               DYN (Shared object file)

Включаем PIE


При компиляции программы нужно указать следующие флаги:

$ gcc -pie -fpie hello.c -o hello

Чтобы убедиться, что свойство PIE включено, выполним такую команду:

$ checksec --file=./hello --output=json | jq | grep piepie: yes,$

Теперь у нашего бинарного файла (hello) тип сменится с EXEC на DYN:

$ file hellohello: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb039adf2530d97e02f534a94f0f668cd540f940, for GNU/Linux 3.2.0, not stripped$ readelf -W -h ./hello | grep DYNType:               DYN (Shared object file)

4. NX


Средства операционной системы и процессора позволяют гибко настраивать права доступа к страницам виртуальной памяти. Включив свойство NX (No Execute), мы можем запретить воспринимать данные в качестве инструкций процессора. Часто при атаках переполнения буфера злоумышленники помещают код в стек, а затем пытаются его выполнить. Однако, если запретить выполнение кода в этих сегментах памяти, можно предотвратить такие атаки. При обычной компиляции с использованием gcc это свойство включено по умолчанию:

$ checksec --file=/bin/ls --output=json | jq | grep nxnx: yes,$ checksec --file=./hello --output=json | jq | grep nxnx: yes,

Чтобы получить информацию о свойстве NX, checksec вновь использует команду readelf. В данном случае RW означает, что стек доступен для чтения и записи. Но так как в этой комбинации отсутствует символ E, на выполнение кода из этого стека стоит запрет:

$ readelf -W -l ./hello | grep GNU_STACKGNU_STACK   0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10

Отключение NX


Отключать свойство NX не рекомендуется, но сделать это можно так:

$ gcc -z execstack hello.c -o hello$ checksec --file=./hello --output=json | jq | grep nxnx: no,

После компиляции мы увидим, что права доступа к стеку изменились на RWE:

$ readelf -W -l ./hello | grep GNU_STACKGNU_STACK   0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

5. RELRO


В динамически слинкованных бинарниках для вызова функций из библиотек используется специальная таблица GOT (Global Offset Table). К этой таблице обращаются бинарные файлы формата ELF (Executable Linkable Format). Когда защита RELRO (Relocation Read-Only) включена, таблица GOT становится доступной только для чтения. Это позволяет защититься от некоторых видов атак, изменяющих записи таблицы:

$ checksec --file=/bin/ls --output=json | jq | grep relrorelro: full,$ checksec --file=./hello --output=json | jq | grep relrorelro: partial,

В данном случае включено только одно из свойств RELRO, поэтому checksec выводит значение partial. Для отображения настроек сhecksec использует команду readelf.

$ readelf -W -l ./hello | grep GNU_RELROGNU_RELRO   0x002e10 0x0000000000403e10 0x0000000000403e10 0x0001f0 0x0001f0 R  0x1$ readelf -W -d ./hello | grep BIND_NOW

Включаем полную защиту (FULL RELRO)


Для этого при компиляции нужно использовать соответствующие флаги:

$ gcc -Wl,-z,relro,-z,now hello.c -o hello$ checksec --file=./hello --output=json | jq | grep relrorelro: full,

Всё, теперь наш бинарник получил почётное звание FULL RELRO:

$ readelf -W -l ./hello | grep GNU_RELROGNU_RELRO   0x002dd0 0x0000000000403dd0 0x0000000000403dd0 0x000230 0x000230 R  0x1$ readelf -W -d ./hello | grep BIND_NOW0x0000000000000018 (BIND_NOW) 

Другие возможности checksec


Тему безопасности можно изучать бесконечно. Даже, рассказывая про простую утилиту checksec в этой статье, я не смогу охватить всё. Тем не менее, упомяну ещё несколько интересных возможностей.

Проверка нескольких файлов


Нет необходимости для каждого файла запускать отдельную команду. Можно запустить одну команду сразу для нескольких бинарных файлов:

$ checksec --dir=/usr/bin

Проверка процессов


Утилита checksec также позволяет анализировать безопасность процессов. Следующая команда отображает свойства всех запущенных программ в вашей системе (для этого нужно использовать опцию --proc-all):

$ checksec --proc-all

Вы также можете выбрать для проверки один процесс, указав его имя:

$ checksec --proc=bash

Проверка ядра


Аналогично вы можете анализировать уязвимости в ядре вашей системы.

$ checksec --kernel

Предупреждён значит вооружён


Подробно изучите свойства безопасности и попытайтесь понять, на что именно влияет каждое из них и какие виды атак это позволяет предотвратить. Checksec вам в помощь!



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Rust в ядре Linux

13.06.2021 16:20:24 | Автор: admin


Вболее ранней публикации компания Google объявила, что в Android теперь поддерживается язык программирования Rust, применяемый в разработке этой ОС как таковой. В связи с этим авторы данной публикации также решили оценить, насколько язык Rust востребован в разработке ядра Linux. В этом посте на нескольких простых примерах рассмотрены технические аспекты этой работы.

На протяжении почти полувека C оставался основным языком для разработки ядер, так как C обеспечивает такую степень управляемости и такую предсказуемую производительность, какие и требуются в столь критичном компоненте. Плотность багов, связанных с безопасностью памяти, в ядре Linux обычно весьма низка, поскольку код очень качественный, ревью кода соответствует строгим стандартам, а также в нем тщательно реализуются предохранительные механизмы. Тем не менее,баги, связанные с безопасностью памяти, все равно регулярно возникают. В Android уязвимости ядра обычно считаются серьезным изъяном, так как иногда позволяют обходить модель безопасности в силу того, что ядро работает в привилегированном режиме.

Предполагается, что Rust уже дозрел до того, чтобы составить компанию C в качестве языка для практической реализации ядра. Rust помогает сократить количество потенциальных багов и уязвимостей безопасности в привилегированном коде, при этом он хорошо сочетается с основой ядра и позволяет поддерживать хорошие показатели производительности.

Поддержка Rust


Был разработанпервичный прототипдрайвера Binder, позволяющий адекватно сравнивать характеристики безопасности и производительности имеющейся версии на C и ее аналога на Rust. В ядре Linux более 30 миллионов строк кода, поэтому мы не ставим перед собой цель переписать весь его на Rust, а обеспечить возможность дописывать нужный код на Rust. Мы полагаем, что такой инкрементный подход помогает эффективно использовать имеющуюся в ядре высокопроизводительную реализацию, в то же время предоставляют разработчикам ядра новые инструменты для повышения безопасности памяти и поддержания производительности на уровне в ходе работы.

Мы присоединились к организацииRust для Linux, где сообщество уже много сделало и продолжает делать для добавления поддержки Rust в систему сборки ядра Linux. Также нам нужно проектировать системы таким образом, чтобы фрагменты кода, написанные на двух языках, могли взаимодействовать друг с другом: нас особенно интересуют безопасные абстракции без издержек, которые позволяют коду Rust использовать функционал ядра, написанный на C, а также возможность реализовывать функционал на идиоматическом Rust, который можно гладко вызывать из тех частей ядра, что написаны на C.

Поскольку Rust новый язык в ядре, у нас также есть возможность обязывать разработчиков придерживаться наилучших практик в области составления документации и соблюдения единообразия. Например, у нас есть конкретные машинно-проверяемые требования, касающиеся использования небезопасного кода: для каждой небезопасной функции разработчик должен документировать те требования, которым должна удовлетворять вызывающая сторона таким образом, гарантируется безопасность использования. Кроме того, при каждом вызове небезопасных функций разработчик обязан документировать те требования, которые должны удовлетворяться вызывающей стороной в качестве гарантии того, что использование будет безопасным. Кроме того, для каждого вызова к небезопасным функциям (или использования небезопасных конструкций, например, при разыменовании сырого указателя) разработчик должен документировать обоснование, почему делать так безопасно.

Rust славится не только своей безопасностью, но и тем, насколько он полезен и удобен для разработчиков. Далее рассмотрим несколько примеров, демонстрирующих, в чем Rust может пригодиться разработчикам ядра при написании безопасных и корректных драйверов.

Пример драйвера


Рассмотрим реализацию семафорного символьного устройства. У каждого устройства есть актуальное значение; при записи nбайт значение устройства увеличивается на n; при каждом считывании это значение понижается на 1, пока значение не достигнет 0, в случае чего это устройство блокируется, пока такая операция декремента не сможет быть выполнена на нем без ухода ниже 0.

Допустим,semaphore это файл, представляющий наше устройство. Мы можем взаимодействовать с ним из оболочки следующим образом:

> cat semaphore

Когдаsemaphore это устройство, которое только что инициализировано, команда, показанная выше, блокируется, поскольку текущее значение устройства равно 0. Оно будет разблокировано, если мы запустим следующую команду из другой оболочки, так как она увеличит значение на 1, тем самым позволив исходной операции считывания завершиться:

> echo -n a > semaphore

Мы также сможем увеличить счетчик более чем на 1, если запишем больше данных, например:

> echo -n abc > semaphore

увеличивает счетчик на 3, поэтому следующие 3 считывания не приведут к блокированию.

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

Теперь покажем, как такой драйвер был быреализован на Rust, сравнив этот вариант с реализацией на C. Правда, отметим, что разработка этой темы в Google только начинается, и в будущем все может измениться. Мы хотели бы подчеркнуть, как Rust может пригодиться разработчику в каждом из аспектов. Например, во время компиляции он позволяет нам устранить или сильно снизить вероятность того, что в код вкрадутся целые классы багов, притом, что код останется гиьким и будет работать с минимальными издержками.

Символьные устройства


Разработчику необходимо сделать следующее, чтобы реализовать на Rust драйвер для нового символьного устройства:

  1. Реализовать типажFileOperations: все связанные с ним функции опциональны, поэтому разработчику требуется реализовать лишь те, что релевантны для данного сценария. Они соотносятся с полями в структуре Cstruct file_operations.
  2. Реализовать типажFileOpenerэто типобезопасный эквивалент применяемого в C поляopenиз структурыstruct file_operations.
  3. Зарегистрировать для ядра новый тип устройства: так ядру будет рассказано, какие функции нужно будет вызывать в ответ на операции над файлами нового типа.

Далее показано сравнение двух первых этапов нашего первого примера на Rust и C:



Символьные устройства в Rust отличаются целым рядом достоинств по части безопасности:

  • Пофайловое управление состоянием жизненного цикла:FileOpener::openвозвращает объект, чьим временем жизни с того момента владеет вызывающая сторона. Может быть возвращен любой объект, реализующий типаж PointerWrapper, и мы предоставляем реализации дляBoxиArc, так что разработчики, реализующие идиоматические указатели Rust, выделяемые из кучи или предоставляемые путем подсчета ссылок, обеспечены всем необходимым.

    Все ассоциированные функции вFileOperationsполучают неизменяемые ссылки на self(подробнее об этом ниже), кроме функцииrelease, которая вызывается последней и получает в ответ обычный объект (а впридачу и владение им). Тогда реализация releaseможет отложить деструкцию объекта, передав владение им куда-нибудь еще, либо сразу разрушить его. При работе с объектом с применением подсчета ссылок деструкция означает декремент количества ссылок (фактическое разрушение объекта происходит, когда количество ссылок падает до нуля).

    То есть, здесь мы опираемся на принятую в Rust систему владения, взаимодействуя с кодом на C. Мы обрабатываем написанную на C часть кода, которым владеет объект Rust, разрешая ему вызывать функции, реализованные на Rust, а затем, в конце концов, возвращаем владение обратно. Таким образом, коль скоро код на C, время жизни файловых объектов Rust также обрабатывается гладко, и компилятор обязывает поддерживать правильное управление временем жизни объекта на стороне Rust. Например, open не может возвращать в стек указатели, выделенные в стеке, или объекты, выделенные в куче, ioctl/read/writeне может высвобождать (или изменять без синхронизации) содержимое объекта, сохраненное вfilp->private_data, т.д.


    Неизменяемые ссылки: все ассоциированные функции, вызываемые между openиreleaseполучают неизменяемые ссылки наself, так как они могут конкурентно вызываться сразу множеством потоков, а действующие в Rust правила псевдонимов не допускают, чтобы в любой момент времени на объект указывало более одной изменяемой ссылки.

    Если разработчику требуется изменить некоторое состояние (а это в порядке вещей), то это можно сделать при помощивнутренней изменяемости : изменяемое состояние можно обернуть в MutexилиSpinLock(илиatomics) и безопасно через них изменить.

    Во время компиляции это препятствует возникновению багов, при которых разработчику не удается приобрести нужную блокировку при доступе к полю (поле недоступно), или когда разработчику не удается обернуть поле в блокировку (поле предназначено только для чтения).


    Состояние для каждого устройства отдельно: когда экземпляры файлов должны совместно использовать состояние конкретного устройства, что при работе с драйверами бывает очень часто, в Rust это можно делать полностью безопасно. Когда устройство зарегистрировано, может быть предоставлен типизированный объект, для которого затем выдается неизменяемая ссылка, когда вызываетсяFileOperation::open. В нашем примере разделяемый объект обертывается вArc, поэтому объекты могут безопасно клонировать и удерживать ссылки на такие объекты.

    Причина, по которойFileOperation
    выделен в собственный типаж (в отличие, например, отopen, входящего в состав типажаFileOperations) так можно разрешить выполнять регистрацию конкретной реализации файла разными способами.

    Таким образом исключается, что разработчик может получить неверные данные, попытавшись извлечь разделяемое состояние. Например, когда в C зарегистрировано miscdevice, указатель на него доступен вfilp->private_data; когда зарегистрированоcdev, указатель на него доступен в inode->i_cdev. Обычно эти структуры встраиваются в объемлющую структуру, которая содержит разделяемое состояние, поэтому разработчики обычно прибегают к макросуcontainer_of, чтобы восстановить разделяемое состояние. Rust инкапсулирует все это и потенциально проблемные приведения указателей в безопасную абстракцию.


    Статическая типизация: мы пользуемся тем, что в Rust поддерживаются дженерики, поэтому реализуем все вышеупомянутые функции и типы в виде статических типов. Поэтому у разработчика просто нет возможности преобразовать нетипизированную переменную или поле в неправильный тип. В коде на C в вышеприведенной таблице есть приведения от, в сущности, нетипизированного указателя (void*) к желаемому типу в начале каждой функции: вероятно, в свеженаписанном виде это работает нормально, но также может приводить к багам по мере развития кода и изменения допущений. Rust отловит все такие ошибки еще во время компиляции.


    Операции над файлами: как упоминалось выше, разработчику требуется реализовать типажFileOperations, чтобы настраивать поведение устройства на свое усмотрение. Это делается при помощи блока, начинающегося с impl FileOperations for Device, гдеDevice это тип, реализующий поведение файла (в нашем случае это FileState). Оказавшись внутри блока, инструменты уловят, что здесь может быть определено лишь ограниченное количество функций, поэтому смогут автоматически вставить прототипы. (лично я используюneovimи LSP-серверrust-analyzer.)

    При использовании этого типажа в Rust, та часть ядра, что написана на C, все равно требует экземпляр struct file_operations. Контейнер ядра автоматически генерирует такой экземпляр из реализации типажа (а опционально также макросdeclare_file_operations): хотя, в нем и есть код, чтобы сгенерировать корректную структуру, здесь все равно всеconst, поэтому интерпретируется во время компиляции, и во время выполнения не дает никаких издержек.

    Обработка Ioctl

    Чтобы драйвер предоставлял собственный обработчикioctl, он должен реализовывать функцию ioctl, входящую в состав типажа FileOperations, как показано в следующей таблице.



    Команды Ioctl стандартизированы таким образом, что, имея команду, мы знаем, предоставляется ли под нее пользовательский буфер, как ее предполагается использовать (чтение, запись, и то, и другое, ничего из этого) и ее размер. В Rust предоставляется диспетчер(для доступа к которому требуется вызватьcmd.dispatch), который на основе этой информации автоматически создает помощников для доступа к памяти и передает их вызывающей стороне.

    Но от драйвера нетребуетсяэтим пользоваться. Если, например, он не использует стандартную кодировку ioctl, то Rust подходит к этому гибко: просто вызывает cmd.rawдля извлечения сырых аргументов и использует их для обработки ioctl (потенциально с небезопасным кодом, что требуется обосновать).

    Но, если в реализации драйвера действительно используется стандартный диспетчер, то ситуация значительно улучшается, поскольку ему вообще не придется реализовывать какой-либо небезопасный код, и:

    • Указатель на пользовательскую память никогда не является нативным, поэтому пользователь не может случайно разыменовать его.
    • Типы, позволяющие драйверу считывать из пользовательского пространства, допускают лишь однократное считывание, поэтому мы не рискуем получить баги TOCTOU (время проверки до времени использования). Ведь когда драйверу требуется обратиться к данным дважды, он должен скопировать их в память ядра, где злоумышленник не в силах ее изменить. Если исключить небезопасные блоки, то допустить баги такого класса в Rust попросту невозможно.
    • Не будет случайного переполнения пользовательского буфера: считывание или запись ни в коем случае не выйдут за пределы пользовательского буфера, которые задаются автоматически на основе размера, записанного в команде ioctl. В вышеприведенном примере реализацияIOCTL_GET_READ_COUNTобладает доступом лишь к экземпляру UserSlicePtrWriter, что ограничивает количество доступных для записи байт величиной sizeof(u64), как закодировано в команде ioctl.
    • Не смешиваются операции считывания и записи: в ioctl мы никогда не записываем буферы, предназначенные для считывания, и наоборот. Это контролируется га уровне обработчиков чтения и записи, которые получают только экземплярыUserSlicePtrWriterиUserSlicePtrReaderсоответственно.

    Все вышеперечисленное потенциально также можно сделать и в C, но разработчику ничего не стоит (скорее всего, ненамеренно) нарушить контракт и спровоцировать небезопасность; для таких случаев Rust требует блокиunsafe, которые следует использовать лишь изредка и проверяться особенно пристально. А вот что еще предлагает Rust:

    • Типы, применяемые для чтения и записи пользовательской памяти, не реализуют типажиSendиSync, и поэтому они (и указатели на них) небезопасны при использовании в другом контексте. В Rust, если бы разработчик попытался написать код, который передавал бы один из этих объектов в другой поток (где использовать их было бы небезопасно, поскольку контекст менеджера памяти там мог быть неподходящим), то получил бы ошибку компиляции.
    • ВызываяIoctlCommand::dispatch, логично предположить, что нам потребуется динамическая диспетчеризация, чтобы добраться до фактической реализации обработчика (и это повлечет дополнительные издержки, которых не было бы в C), но это не так. Благодаря использованию дженериков, компилятор сделает функцию мономорфной, что обеспечит нам статические вызовы функции. Функцию можно будет даже оформить внутристрочно, если оптимизатор сочтет это целесообразным.

    Блокировка и условные переменные

    Разработчикам разрешено использовать мьютексы и спинлоки для обеспечения внутренней изменяемости. В нашем примере мьютекс используется для защиты изменяемых данных; в приведенной ниже таблице показаны структуры данных, используемые в C и Rust соответственно, а также показано, как реализовать ожидание и дождаться, пока счет станет ненулевым, и мы сможем выполнить требуемое считывание:



    Отметим, что такие операции ожидание вполне обычны в существующем коде на C, например, когдаканал ожидаетпартнера для записи,сокет домена unix ожидаетданные, апоиск индексного дескриптора ожидает завершения удаления, либо помощник пользовательского режима ожидаетизменения состояния.

    А вот какими достоинствами обладает соответствующая реализация на Rust:

    • ПолеSemaphore::innerдоступно только во время удержания блокировки, при помощи ограничителя, возвращаемого функциейlock. Поэтому разработчики не могут случайно прочитать или записать защищенные данные, предварительно их не заблокировав. В примере на C, приведенном выше,countиmax_seenвsemaphore_stateзащищены мьютексом, но программа не обязывает держать блокировку во время доступа к ним. there is no enforcement that the lock is held while they're accessed.
    • Получение ресурса есть инициализация (RAII): блокировка снимается автоматически, когда ограничитель (innerв данном случае) выходит из области видимости. Таким образом, все блокировки всегда снимаются: если разработчику нужно, чтобы блокировка оставалась на месте, он может продлевать существование ограничителя, например, возвращая его; верно и обратное: если необходимо снять блокировку ранее, чем ограничитель выйдет из области видимости, это можно сделать явно, вызвав функцию drop.
    • Разработчики могут использовать любую блокировку, использующую типажLock, в состав которого, кстати, входят MutexиSpinLock, и, в отличие от реализации на C, это не повлечет никаких дополнительных издержек во время выполнения. Другие конструкции для синхронизации, в том числе, условные переменные, также работают прозрачно и без дополнительных издержек времени выполнения.
    • Rust реализует условные переменные при помощи очередей ожидания, предусмотренных в ядре. Благодаря этому разработчики могут пользоваться атомарным снятием блокировки и погружать поток в сон, не задумываясь о том, как это отразится на низкоуровневом планировщике функций ядра. В вышеприведенном примере на C вsemaphore_consumeвидим смесь семафорной логики и тонкого планирования в стиле Linux: например, код получится неправильным, еслиmutex_unlockбудет вызван доprepare_to_wait, поскольку таким образом можно забыть об операции пробуждения.
    • Никакого несинхронизированного доступа: как упоминалось выше, переменные, совместно используемые несколькими потоками или процессорами, должны предоставляться только для чтения, и внутренняя изменяемость пригодится для тех случаев, когда изменяемость действительно нужна. Кроме вышеприведенного примера с блокировками, есть еще пример с ioctl из предыдущего раздела, где также демонстрируется, как использовать атомарную переменную. В Rust от разработчиков также требуется указывать, как память должна синхронизироватьсяпри атомарных обращениях. В той части примера, что написана на C, нам довелось использовать atomic64_t, но компилятор не предупредит разработчика о том, что это нужно сделать.

    Обработка ошибок и поток выполнения

    В следующих примерах показано, как в нашем драйвере реализованы операцииopen,read иwrite:







    Здесь видны и еще некоторые достоинства Rust:

    • Оператор?operator: используется реализациями openиreadв Rust для неявного выполнения обработки ошибок; разработчик может сосредоточиться на семафорной логике, и код, который у него получится, будет весьма компактным и удобочитаемым. В версиях на C необходимая обработка ошибок немного замусоривает код, из-за чего читать его сложнее.
    • Обязательная инициализация: Rust требует, чтобы все поля структуры были инициализированы при ее создании, чтобы разработчик не мог где-нибудь нечаянно забыть об инициализации поля. В C такая возможность не предоставляется. В нашем примере с open, показанном выше, разработчик версии на C мог легко забыть вызвать kref_get(пусть даже все поля и были инициализированы); в Rust же пользователь обязан вызватьclone(повышающую счет ссылок на единицу), в противном случае он получит ошибку компиляции.
    • Область видимости при RAII: при реализации записи в Rust используется блок выражений, контролирующий, когда innerвыходит из области видимости и, следовательно, когда снимается блокировка.
    • Поведение при целочисленном переполнении: Rust стимулирует разработчиков всегда продумывать, как должны обрабатываться переполнения. В нашем примере с write, мы хотим обеспечить насыщение, чтобы не пришлось плюсовать к нашему семафору нулевое значение. В C приходится вручную проверять, нет ли переполнений, компилятор не оказывает дополнительной поддержки при такой операции.




    Облачные серверы от Маклауд быстрые и безопасные.

    Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Измеряем расходы на память у Postgres процессов

10.06.2021 18:12:02 | Автор: admin

Это вольный перевод поста одного из сильных разработчиков Postgres - Andres Freund. Кроме того что разработчик сильный, так еще и статья довольно интересная и раскрывает детали того как работает ОС Linux.

Довольно часто можно слышать заявления что постгресовые соединения используют слишком много памяти. Об этом часто упоминают сравнивая процессную модель обработки клиентских соединений с другой моделью, где каждое соединение обслуживается в отдельном потоке (thread).

Как по мне, здесь есть что обсудить. Кроме того можно сделать несколько улучшений и уменьшить использование памяти.

Я думаю что эта обеспокоенность оверхэдом на память вызвана одной общей причиной, это то что самый простой способ измерения потребления памяти через утилиты типа top и ps является довольно обманчивым.

И действительно, особенно довольно сложно измерить то как увеличивается используемая память при каждом новом соединении.

В этом посте я буду говорить о Postgres работающем на Linux, т.к. именно в этом направлении у меня больше всего опыта.

И перед тем как продолжить я хочу акцентировать внимание, что при точном и аккуратном измерении, одно соединение имеет накладные расходы на уровне меньше 2MiB (см. выводы в конце поста).

первый взгляд

Если использовать стандартные утилиты операционной системы, то можно сделать вывод, что накладные расходы существенно больше (чем есть на самом деле). Особенно если не использовать большие страницы (huge pages), то использование памяти каждый процессом действительно выглядит слишком большим. Отмечу что настоятельно рекомендуется использовать большие страницы. Давайте взглянем на только что установленное соединение, в только что запущенном Postgres:

andres@awork3:~$ psqlpostgres[2003213][1]=# SELECT pg_backend_pid(); pg_backend_pid         2003213 (1 row)andres@awork3:~/src/postgresql$ ps -q 2003213 -eo pid,rss    PID   RSS2003213 16944

Около 16MiB.

Утечки памяти!?! К счастью, нет.

При этом со временем памяти используется все больше и больше. Чтобы продемонстрировать это, я воспользуюсь расширением pgprewarm, чтобы загрузить таблицу в буфер (shared buffers):

postgres[2003213][1]=# SHOW shared_buffers ; shared_buffers  16GB           (1 row)postgres[2003213][1]=# SELECT SUM(pg_prewarm(oid, 'buffer')) FROM pg_class WHERE relfilenode <> 0;  sum    383341 andres@awork3:~$ ps -q 2003213 -eo pid,rss    PID   RSS2003213 3169144

Теперь использование памяти достигло уровня 3GB. При том что, на самом деле, в этой сессии не потребовалось выделять дополнительную память. Объем используемой памяти, увеличился пропорционально объему используемого буфера:

postgres[2003213][1]=# SELECT pg_size_pretty(SUM(pg_relation_size(oid))) FROM pg_class WHERE relfilenode <> 0; pg_size_pretty  2995 MB        (1 row)

Что еще хуже, даже если эти страницы будут использовать в других сессиях, это также будет отображаться как использование большого объема памяти:

postgres[3244960][1]=# SELECT sum(abalance) FROM pgbench_accounts ; sum    0 (1 row)andres@awork3:~/src/postgresql$ ps -q 3244960 -eo pid,rss    PID   RSS3244960 2700372

Конечно, Postgres на самом деле не использует 3GB и 2.7GB памяти в данном случае. На самом деле, в случае huge_pages=off, утилита ps отображает объем разделяемой (shаred - память совместно используемая с другими процессами) памяти, включая и страницы в буфере которые используются в каждой сессии. Очевидно это приводит к значительной переоценке величины используемой памяти.

На помощь внезапно приходят большие страницы

Множество процессорных микро-архитектур обычно используют страницы размером 4KiB, но также могут использовать и страницы большего размера, например широко распространенный вариант это 2MiB.

В зависимости от операционной системы, конфигурации и типа приложений, такие большие страницы могут использоваться и самой операционной системой и приложениями. Для больших подробностей можно в качестве примера взять статью из Debian wiki об использовании больших страниц.

Если повторить вышеописанный эксперимент с huge_pages=on, можно увидеть гораздо более приятные глазу результаты. Для начала, взглянем на "новый процесс":

andres@awork3:~$ ps -q 3245907 -eo pid,rss    PID   RSS3245907  7612

Теперь, новый процесс использует всего около 7MiB. Такое уменьшение вызвано тем что таблицы управления страницами (page table) теперь требуют меньше места, из-за того что используются большие страницы, для управления тем же объемом памяти нужно в 512 раз меньше элементов чем раньше (4KiB * 512 = 2MiB).

Теперь давайте посмотрим что произойдет при доступе к большим объемам данных в памяти:

postgres[3245843][1]=# ;SELECT SUM(pg_prewarm(oid, 'buffer')) FROM pg_class WHERE relfilenode <> 0;postgres[3245851][1]=# SELECT sum(abalance) FROM pgbench_accounts ;andres@awork3:~$ ps -q 3245907,3245974 -eo pid,rss    PID   RSS3245907 122603245974  8936

В отличие от самого первого эксперимента, эти процессы используют всего 12MiB и 9MiB соответственно, в то время как в прошлый раз использовалось 3GiB и 2.7GiB.

Разница довольно очевидна ;)

Это следствие того, как в Linux реализован учёт использования больших страниц, а не потому, что мы использовали на порядки меньше памяти: используемые большие страницы не отображаются как часть значения RSS в выводе ps и top.

Чудес не бывает

Начиная с версии ядра 4.5, появился файл /proc/$pid/status в котором отображается более подробная статистики об использование памяти процессом:

  • VmRSS общий размер используемой памяти. Значение является суммой трех других значений (VmRSS = RssAnon + RssFile + RssShmem)

  • RssAnon размер используемой анонимной памяти.

  • RssFile размер используемой памяти ассоциированной с файлами.

  • RssShmem размер используемой разделяемой памяти (включая SysV shm, сегменты в tmpfs и анонимные разделяемые сегменты)

andres@awork3:~$ grep -E '^(Rss|HugetlbPages)' /proc/3247901/statusRssAnon:    2476 kBRssFile:    5072 kBRssShmem:    8520 kBHugetlbPages:       0 kBpostgres[3247901][1]=# SELECT SUM(pg_prewarm(oid, 'buffer')) FROM pg_class WHERE relfilenode <> 0;andres@awork3:~$ ps -q 3247901 -eo pid,rss    PID   RSS3247901 3167164andres@awork3:~$ grep -E '^(Rss|HugetlbPages)' /proc/3247901/statusRssAnon:    3148 kBRssFile:    9212 kBRssShmem: 3154804 kBHugetlbPages:       0 kB

RssAnon отображает объем "анонимной" памяти, т.е. участки рабочей памяти которые не являются отображанием файлов на диске. RssFile это как раз отображение в памяти конкретных файлов на диске, включая даже исполняемый файл postgres. И последнее RssShmem отображает доступную разделяемую память без учета больших страниц.

Это хорошо показывает причину того почему ps и подобные утилиты показывают большие значения используемой памяти - из-за того что учитывается разделяемая память.

И теперь взглянем на эту же статистику, но с huge_pages=on:

andres@awork3:~$ grep -E '^(Rss|HugetlbPages)' /proc/3248101/statusRssAnon:    2476 kBRssFile:    4664 kBRssShmem:       0 kBHugetlbPages:  778240 kBpostgres[3248101][1]=# SELECT SUM(pg_prewarm(oid, 'buffer')) FROM pg_class WHERE relfilenode <> 0;andres@awork3:~$ grep -E '^(Rss|HugetlbPages)' /proc/3248101/statusRssAnon:    3136 kBRssFile:    8756 kBRssShmem:       0 kBHugetlbPages:    3846144 kB

Увеличиваем точность

Однако даже если считать использование памяти без учета разделяемой памяти, это всё еще является переоценкой реального использования памяти. Тут есть две причины:

Первое, на самом деле учет RssFile в использовании памяти не играет роли - по факту это всего лишь исполняемый файл и используемые им разделяемые библиотеки (Postgres не использует mmap() для каких-либо файлов). Все эти файлы являются общими для всех процессов в системе и практически не дают накладных расходов для постгресовых процессов.

Второе, RssAnon также переоценивает использование памяти. Смысл тут в том что ps показывает всю память процесса целиком, при том что большая часть этой памяти в случае создания нового процесса делится между пользовательским соединением и памятью родительского процесса postgres (так же известен как postmaster). Это следует из того что Linux не копирует всю память целиком когда создает новый процесс (при выполнении операции fork()), вместо этого используется механизм Copy-on-Write для копирования в новый процесс, набора только измененных страниц.

Таким образом, пока нет хорошего способа аккуратно и точно измерить использование памяти отдельно взятого нового процесса. Но все не так плохо, начиная с версии 4.14 ядро предоставляет еще одну статистику (коммит с описанием) процесса в /proc/$pid/smaps_rollup файле. Pss показывает "принадлежащую процессу пропорциональную долю отображения" среди всех отображений этого процесса (детали можно найти в документации поиском по smaps_rollups и Pss которые сами по себе не имеют прямых ссылок). Для сегмента памяти используемого совместно между несколькими процессами, доля будет представлять собой отношение размера этого сегмента на количество процессов которые используют этот сегмент.

postgres[2004042][1]=# SELECT SUM(pg_prewarm(oid, 'buffer')) FROM pg_class WHERE relfilenode <> 0;  sum    383341 (1 row)postgres[2004042][1]=# SHOW huge_pages ; huge_pages  off        (1 row)andres@awork3:~$ grep ^Pss /proc/2004042/smaps_rollupPss:             3113967 kBPss_Anon:           2153 kBPss_File:           3128 kBPss_Shmem:       3108684 kB

Pss_Anon включает в себя анонимную память используемую процессом, Pss_File включает память используемую под разделяемые библиотеки задействование процессом, и Pss_Shmem (если не используются большие страницы) показывает использование общей памяти разделенное на все процессы которые обращались к соответствующим страницам.

Но у пропорциональных значений есть небольшой недостаток, это использование делителя который зависит от числа подключений к серверу. Здесь я использовал pgbench (scale 1000, -S -M prepared -c 1024) чтобы создать большое число подключений:

postgres[2004042][1]=# SELECT count(*) FROM pg_stat_activity ; count   1030 (1 row)postgres[2004042][1]=# SELECT pid FROM pg_stat_activity WHERE application_name = 'pgbench' ORDER BY random() LIMIT 1;   pid    3249913 (1 row)andres@awork3:~$ grep ^Pss /proc/3249913/smaps_rollupPss:                4055 kBPss_Anon:           1185 kBPss_File:              6 kBPss_Shmem:          2863 kB

И с использованием huge_pages=on:

andres@awork3:~$ grep ^Pss /proc/2007379/smaps_rollupPss:                1179 kBPss_Anon:           1173 kBPss_File:              6 kBPss_Shmem:             0 kB

Как я понимаю Андрес намекает здесь на то что из-за большого количества процессов и соответственно большого делителя, значение Pss_Shmem становится маленьким, отчего может складываться впечатление что процесс использует мало разделяемой памяти хотя по факту мог использовать ее гораздо больше (Прим. переводчика).

К сожалению Pss значения учитывают только те ресурсы, что видны приложению. Например, размер таблицы страниц не учитывается. Размер таблицы страниц можно увидеть в уже упоминавшемся `/proc/$pid/status`.

Я не уверен, но насколько я знаю, VmPTE (размер таблицы страниц) полностью приватный для каждого процесса, но остальное большинство Vm* значений, включая стек VmStk являются общими через copy-on-write.

Учитывая всё это, накладные расходы с учетом таблицы страниц и с huge_pages=off:

andres@awork3:~$ grep ^VmPTE /proc/2004042/statusVmPTE:      6480 kB

и с huge_pages=on:

VmPTE:     132 kB

Конечно обслуживание каждого процесса в ядре наверняка предполагаются еще какие-то отдельные небольшие накладные расходы, но они настолько малы, что они даже не представлены в файле статистики.

Вывод

На основе проделанных измерений, мы можем представить что процесс выполняющий достаточно простую read-only OLTP нагрузку имеет накладные расходы около 7.6MiB с huge_pages=off и около 1.3MiB с huge_pages=on включая Pss_Anon в VmPTE.

Даже если представить что есть некий "невидимый" оверхэд, и большой объем данных в буфере и т.д., я думаю мы вернемся к моему раннему утверждению что накладные расходы на соединение меньше чем 2MiB.

Дополнение от переводчика. В версии Postgres 14 появилось новое представление pg_backend_memory_contexts которое показывает подробную утилизацию памяти текущим процессом с точки зрения самого Postgres.

Подробнее..

Включение гибридной графики в Ubuntu на ноутбуках Nvidia Intel (OpenGL, Vulkan)

07.05.2021 00:04:44 | Автор: admin

Введение

Это простая инструкция как включить гибридную графику intel-nvidia на ноутбуке. Чтобы определенные приложения запускались на дискретном чипе, а другие на встроенном. На свое удивление в интернете не нашел простую инструкцию того, как запускать определенные приложения, используя дискретную графику. Так что напишу так просто, на сколько считаю нужным

У меня система KDE Neon 5.21 - по большому счету - Ubuntu LTS с окружением рабочего стола KDE Plasma 5.21, видеочип GeForce MX150

1. Устанавливаем драйвер

a) Если у вас система на Qt (Как правило окружение KDE или LXQt), то с помощью данной команды через терминал загрузим программу для установки драйверов:

sudo apt install software-properties-qt

Если у вас система на GTK то с помощью это команды:

sudo apt install software-properties-gtk

Хотя разницы принципиальной нет

b) Затем запускаем ее с правами root

sudo software-properties-qt
Можно так же добавить ярлык для запуска в меню приложений

Инструкция для KDE

В папке ~/.local/share/applications/ создадим файл software properties qt.desktop с таким содержанием

[Desktop Entry]Categories=System;Settings;Comment[ru_RU]=driversComment=driversExec=konsole -e "~/.local/share/applications/software-properties-qt.sh"GenericName[ru_RU]=Установка драйверов\sGenericName=Установка драйверов\sIcon=systemsettingsMimeType=Name[ru_RU]=software properties qt\nName=software properties qt\nPath=StartupNotify=trueTerminal=falseTerminalOptions=Type=ApplicationX-DBUS-ServiceName=X-DBUS-StartupType=X-KDE-SubstituteUID=falseX-KDE-Username=

И файл software properties qt.sh в той же папке:

#! /bin/bashecho software-properties-qtsudo /usr/bin/software-properties-qt

После перезагрузки ярлык появится в меню

Но это далеко не обязательно, вполне достаточно запустить из консоли для наших целей настройки гибридной графики

c) Переходим на последнюю вкладку Additional drivers и устанавливаем нужный драйвер. Я выбрал самой последней версии, который не tested и не server

d) После установки перезагружаем устройство

2. Настраиваем видеокарту

a) Загружаем следующую программу:

sudo apt install nvidia-settings

И запускаем

b) Переходим в PRIME Profiles Здесь мы видим три пункта:

  1. NVIDIA (Performance Mode) - работать только на дискретной графике. Сильно потребляет батарею в несложных задачах, а так же ноутбук начинает греться. Зато система работает намного быстрее, но это того не стоит. У меня после установки драйвера этот пункт включился автоматически

  2. NVIDIA On-Demand - некоторые приложения будут использовать дискретную графику nvidia, но по-умолчанию встроенная intel. Как запустить конкретное приложение с дискретной графикой напишу дальше

  3. NVIDIA (Power Saving Mode) - отключение дискретной графики

Выбираем второй вариант - NVIDIA On-Demand, и перезагружаем систему

3. Запуск приложения с использованием дискретной графикой

Это то, что сложнее всего гуглилось...

Для запуска приложения с использованием графики nvidia нужно задать для OpenGL две переменные среды:

__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia

для Vulkan только:

__NV_PRIME_RENDER_OFFLOAD=1

Делать это надо перед командой для запуска приложения. Например, нам нужно запустить из терминала приложение program с использованием дискретной графики. Нужно вызвать его так:

__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia program

Соответственно, если у приложения есть ярлык (.desktop) в меню приложений, то надо изменить команду запуска в ярлыке. В KDE Plasma нужно нажать на него ПКМ, открыть свойства (или "изменить приложение..."), перейти во вкладку "приложение" и перед командой приписать данную приставку. В других средах похожего стола примерно так же

Пример: ярлык игры Wolfenstein - Blade of AgonyПример: ярлык игры Wolfenstein - Blade of Agony

Можно сделать это же действие через текстовый редактор. Открываем ярлык, находим Exec=, и приписываем перед коммандой данную приставку __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia

Например, Minecraft
__NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia minecraft-launcher __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia minecraft-launcher

Заключение

Данный метод, как я понял, точно работают для програм, использующих библиотеки OpenGL и Vulkan. У меня, к сожалению, не получилось запустить так Windows приложение через Wine, которое использует DirectX, но это уже совсем другая история.

Подробнее..

GentooArchLFS как путь в мир Linux

20.05.2021 10:09:10 | Автор: admin

Долго держался, не публиковал этот материал. Последний каплей стало вот это: Как Unix-way убивает десктопный Linux


Попалось мне обсуждение на habr Q&A habr Q&A
Очень понравился вот этот ответ от пользователя xolst9.


Стал вспоминать свой опыт и решил развернуто выразить свой взгляд на проблему.
В основном буду писать о Gentoo-linux, но во многом это верно и для Arch linux. С ним у меня опыт по-меньше, но этот дистриб я также считаю отличной учебной партой.


О себе работаю с linux более 10 лет, имеют опыт эксплуатации различных серверов(почта, файловые, DNS, маршрутизаторы, радиотрансляции, кластеры виртуализации и тд) на уровне провайдеров услуг(это несколько отличается от варианта "Фирма в 100 человек"), различных дистрибутивов( включая debian,ubuntu,gentoo, centos/oracle, arch), разные сферы деятельность (включая радиовещание и телеком), есть опыт работы как простым ведущим инженером, так и на руководящей должности. Есть что вспомнить, есть каким опытом поделиться.


Замечу, что каждый дистриб хорош на своем месте. Если мне потребуется создать сервер для длительной эксплуатации, то в 99% случаев я возьму Centos/RedHat. Нужно сделать VPN-сервер Zentyal. Настроить себе десктоп для работы mint или fedora, manjaro. Собрать небольшой дистрибутив/live flash с узкими задачами gentoo/arch. Создать кроссплатформенную прошивку начну собирать свой дистриб в buildroot.


Да, все это можно делать на ЛЮБОМ дистрибутиве, на самом деле. Будут вопросы в версиях пакетов, но в том или ином дистрибе есть свои плюсы и минусы.

PS Прекрасно знаю о сборках вроде zentyal/proxmox/esxi/прочие. Так или иначе они мне попадали в руки. Да, они позволяют упростить многие вещи, сэкономить время, дать возможность работать с ними людям, далёким от мира Linux. Но всё это до тех пор, пока вас устраивают их возможности и ограничения. Или пока что-то не сломалось.


PS Ещё я знаю про LFS/YOCTO/buildroot. Последний меня особенно интересует.


PSS И для любителей ткнуть в комментах да, я знаю, что такое проект GNU, что Linux это только ядро. Для желающих рекомендую прочитить вот это


Кто же такой Linux-профи


Меня давно удивляет ситуация, когда работодатель удивляется:"Вы специалист по Linux, а ни разу не настраивали EXIM4/NGINX/МОЙ_ОГРОМНЙ_СОФТ_НАПИСАННЙ_СТУДЕНТОМ_ЗА_ЕДУ?".


Так вот, следуют все таки отличать навыки работы в самой ОС и настройку ПРИКЛАДНОГО программного обеспечения на этой ОС. Да, linux популярен в хостингах. Но мне чаще приходилось делать почтовики и шлюзы с прокси, чем LAMP(или эквивалент). Отсюда вывод умение настраивать конкретный прикладной софт может быть преимуществом при выборе кандидата на вакансию.


Во-первых, этот мир многообразен. В рамках одной компании мне попадались почтовики на postfix/exim4/zimbra/sendmail. Не видел проблемы разобраться с каждым из них и продолжить их успешную эксплуатацию.


Во-вторых, этот мир развивается. Кто-то вспомнит времена, когда web-сервер это однозначно Apache. Сейчас большинству (из личного опроса) в голову приходит NGINX. Всё больше вокруг меня postgresql, все меньше mysql.


Личный пример. Работал в одном интернет-провайдере, не маленьком. Потребовалось переделать DNS-систему. Не внутреннюю, а для предоставления услуг. Первое что приходит в голову bind! И тут же понимаю, что не всё так просто. Например удобный интерфейс управления зонами с возможностью разделения прав доступа.


Решил всё же посмотреть аналоги. И наткнулся на powerDNS. И сделал всё на нём. Почему потому что удобно:


  • Относительно простая настройка (справедливо и для bind)
  • Наличие "из коробки" веб-интерфейса для редактирования зон, с разделением прав доступа
  • Разделение на recursor и Authoritative серверы
  • Хранение зон в базе данных

Итого, имея опыт настройки и эксплуатации bind, я открыл для себя альтернативную систему, внедрение которой сэкономило мне приличное время и при внедрении, и при последующей эксплуатации.


Второй пример. Коллеги, имеющие опыт работы с Proxmox, смогли создать удивительно тормозящий кластер. У меня такого опыта не было, я работал только с KVM + qemu + libvirt. Замечу, что внутри proxmox находится правильно, kvm + qemu + libvirt. Тем не менее, проблему нашел и решал именно я. Оказалось, что никто не прочитал отличие паравиртуализированного драйвера от эмуляции IDE. А для дальнейшего перевода виртуалок на паравиртуализированные драйверы потребовалось уметь работать с fstab/grub, тк после смены драйвера виртуалки перестали загружаться(тот случай, когда в fstab и в загрузчике использовались привязки не по UUID/LABEL, а по именам устройств. И когда быстрее было загрузиться с live-диска и быстро всё исправить).


Вывод 1. Кандидат на вакансию может не иметь опыта работы с конкретным софтом. Важнее иметь хорошие базовые навыки (работа в командной строке, отличать raid от LVM, понимание как проверить загруженность машины, общие сведения о виртуализации и тд) и умение работать с документацией.


Вывод 2. Не так важен дистрибутив, как голова и прямые руки. Умеешь работать в командной строке, понимаешь устройство ОС семейства linux, и адаптация к дистрибутиву перестаёт быть для тебя проблемой. Мне самому приходилось переходить из мира gentoo в мир redhat, что было не сложно. Переход в мир debian/ubuntu также не занял вопросов. Найти почему есть специфические проблемы с прошивке российской разработки тоже не проблема.


Вывод 3. Уметь работать в консоли всё таки надо. Не попадалось мне ещё панели управления или иного gui, имеющих мощность и гибкость командной строки. Да, можно здорово упростить себе жизнь, используя gui (мне нравится, как в zentyal построено управление openvpn). Но консоль никто не отменял.


Gentoo как источник технических навыков


Начну с самого простого чему учится новичек, решивший отвоить Linux с нуля. Как гласит старая шутка:"Инструкцию по gentoo надо ещё склеить".
Большинство дистрибутивов всё же имеют инсталлятор, утрированно: "Да да далее установить". Так вот, Gentoo не имеет инсталлятора. Уже с моменты начала установки есть шанс получить новые знания. Причем базовые, без которых сложно (а может и невозможно) двигаться дальше. Не могу представить себе linux-профи, не умеющего форматировать разделы в командной строке. Или не освоившего chroot.


Итак, вот краткий список навыков, получаемых при установке Gentoo:


  • Общие навыки работы в командной строке копирование, скачивание файлов, навигация по дискам и тд
  • Проверка контрольных сумм
  • Понимание параметров загрузки ядра
  • управление пользователями/группам, специальные группы
  • понимание модулей ядра, их загрузки
  • настройка сети(статика/dhcp/wifi)
  • маршрутизация в сетях
  • работа с разделами дисков
  • создание различных файловых систем(в тч LVM/SWAP/BTRFS)
  • дата/время (вручную и ntp, часовые пояса)
  • работа с переменными (объявление, использование, export)
  • понимание основ сборки пакетов флаги сборки, настройка параметров компилятора
  • монтирование на лету (в тч особых систем вроде /dev /sys /proc)
  • настройка fstab
  • chroot
  • настройка локали
  • конфигурирование и сборка ядра
  • настройка логирования
  • cron
  • настройка удаленного доступа
  • установка/настройка загрузчика( в тч EFI)

По отдельности кажется, что ничего особого. А где вы видели олимпийского чемпиона по бегу, не умеющего ходить? Чтобы научиться писать книги, надо сначала научиться читать по слогам. Кроме того, все эти задачи выполняются только в консоли и на выходе получается система без графического интерфейса. Хочешь графику? Вэлкам в руководства и изучение новых горизонтов в мире gui.


Maquefel:


"Исключения конечно есть, люди которые дальше Ctrl+C, Ctrl+V не пошли и находятся в состоянии "ждем ebuild'ов", но с ростом количества "простых" дистрибутивом, такие уже в большинстве своём исчезли с нашего горизонта."

Согласен с этим высказыванием. Если человек не готов пойти чуть дальше copy/past готовый инструкции, не хочет включать голову то дистрибутив не имеет значения. Ошибка как всегда в прокладке между стулом и клавиатурой.


Замечу, что все этапы подробно расписаны в официальном HANDBOOK с привязкой к архитектуре/языку. В нём не только подробно описаны все команды, но и даётся много справочной информации что, зачем, как устроено.


Результат на выходе получается человек, который попробовал много чего. А ведь он ещё не начал устанавливать прикладной софт и графику. Сделает и это весьма не плохо освоит новые подсистемы ОС.


Замечу, что установка Arch в целом похожа (на мой взгляд), только без компиляции. И даёт примерно такой же набор навыков.


У обоих дистрибутивов отличная документация. Особенно у Arch, субъективно она мне больше нравится.


Психологический аспект


Невероятно, на факт мне часто встречались/встречаются так называемые "linux-специалисты", неуверенно работающие в командной строке. Или вовсе её боящиеся.


Боязнь эта вполне объяснима и понятно нет устойчивых навыков работы в командной строке. Что называется не набита рука! Представьте себе лётчика, который привык летать только на автопилоте. Вы предпочтете лететь с ним или с тем, кто в добавок умеет уверенно управлять вручную? В результате типичные ошибки:


  • Отрицание возможности выполнения задачи, потому что не знают как
  • Ошибки при выполнении команд (использование неверных ключей, неверный порядок операндов)
  • Использование избыточных средств (например, искать/писать скрипт на питоне там, где достаточно grep)

В то же время профи, умеющий работать в консоли (уверенное использование самых простых команд, общее понимание работы в консоли) будет уверенно выполнять нужные действия. При этом в большинстве случаев он будет изучать новые команды, писать скрипты для автоматизации, экономить рабочее время организации и как следствие выполнять больше задач за то же время.


Простой пример "Сервер тормозит". Или ещё хуже не отрабатывает загрузчик. Совсем страшное тормозит сеть на отдачу, отлично работает на приём. Уверяю вас, для решения этих задач потребуется уметь работать в консоли, возможно менять параметры ядра (sysctl это сила). И да, тюнинг в нагруженных системах тоже потребует умения работать в консоли.


Нет, конечно можно потратить много времени, откатиться с бэкапа или установить всё по новой. Можно безконечно перебирать дистриутивы в поисках не тормозящей сети(актуально для сборок узкого направления). А можно загрузиться с live-диска и за пару минут поправить загрузчик. Или решить проблемы с сетью (пример реальный, кстати).


Человек, имеющий устойчивые навыки работы в любой области будет гораздо эффективнее и надёжнее в этой области. В данном случае умеющий работать в консоли и понимающий механизмы работы ОС будет большим плюсом для компании, чем привыкший идти путём 'Да-да-далее' "специалист".
И это всё на уровне инженера, не говоря уж о разработчике.


В моем понимание успешная работа с Gentoo это не только рост профессиональных навыков, но и психологический отбор. С закалкой.


Gentoo vs Enterprise


Снова вернусь к истоку, к Q&A. Прозвучали фразы вроде "не корпоративный дистриб". В целом согласен, сам предпочитал использовать Centos.
Но сейчас будет подрыв.
Так вот, есть ситуации, когда gentoo используется в суровом Enterprise. Пример разработка. Gentoo отличный инструмент для разработчика:


  • Свежие версии пакетов
  • Всегда свежий компилятор (пользователи RH/Centos/Oracle поймут)
  • Возможность собрать свой дистрибутив/прошивку с минимальным и достаточным функционалом
  • Восстановление работы конкретного сервера с live-диска.

Как видно, gentoo может пригодиться в больших компаниях. Скажите, что специфичным образом? А что не специфичный? Почтовик или паблик-шара на samba? :-D


И снова напомню про нештатные/аварийные ситуации. Или про "черные ящики"('мы не знаем что там и как, но без него падает что-то важное'), которыми грешат крупные фирмы с историей.


Почему я предпочту гентушника в коллегах


Исходя из выше описанного, при прочих равных гентушник будет иметь преимущество в моих глазах.
Как минимум он не испугался и освоил не слабый handbook.
Он умеет работать в консоли, что критически важно для linux-администратора/инженера/архитектора.
Он понимает основы компиляции и сборки пакетов, что иногда крайне важно (почитайте про тесты SPEC CPU, например spec 2006).


Да, конечно, бывает оборотная сторона. Желание перекомпилировать пакеты или написать свой огромный скрипт вместо готового отлаженного решения.
Но это больше относится к неофитам. Проходит легко и безболезненно в большинстве случаев.


Кроме того, имея богатый арсенал умений, такой человек лучше справится в нестандартной обстановке. Потому что не растеряется и не будет терять прорву времени на поиск 100% работающего руководства именно под такой уровень. Он в состоянии разобраться, догуглить нужное и решить задачу. Впоследствии написать своё руководство.


"Поколение Ubuntu"


Впервые этот термин был мной услышан в 2012 году в ходе собеседования. Сейчас я им активно пользуюсь.
Maquefel:


Вообще за все надо платить. Линуксоиды, со стажем 15+ лет, а помните, как мы занимались популяризацией GNU/Linux? Вот мы и добились своего, только почему то мы думали, что адепты проникнуться духом и путем Linux/UNIX и все станет лучше, богаче, проще. А нет, адепты просто притащили к нам философию Microsoft, ничего не делать, ждать готового, изобретать громадные комбайны, не читать документацию."

Всё чаще на собеседования приходят странные люди, считающие себя linux-экспертами как минимум. А по факту знание многих слов без малейшей практики и понимания. Немало тех, кто не видел консоль или не понимает, зачем уметь в ней работать(это в linux-то). Часто знают много терминов, но не понимают что за ними стоит:


- Вот выдержка из лога. Выведите список неуспешных попыток входа по SSH

И вот лучшие из ответов(кроме попыток считать вручную):


  • Я бы написал на питоне, там что-то было для работы с SSH (ну да, с текстовым файлом)
  • Может это можно сделать через awk? но я не уверен (тепло, но написать не смог)
  • Есть grep, но я не помню как им пользоваться (оставлю без комментариев)

Ещё пример:


- с Systemd сталкивались?- да,  редкостное...(в общем не нравится)- а что именно не так, что мешает?- ну...так пишут

В данном случае я не агитирую за systemd. У него есть и плюсы, и минусы.


- Вам говорят, что linux сервер тормозит. Что будете делать?- Гляну загрузку проца, оперативки.- Как?- эээ...не знаю/не помню- ок, оперативки много, процессор показывает высокую загрузку (load averange)- Значит процессоров не хватает!- А может тормозят диски?(намёк на  CPU utilization и iowait)- Нет, едва ли. - И всё же, если диски. Как посмотреть?- не знаю.

Замечу, что данные вопросы задавал не только я, но и мне практически на всех собеседованиях, где нужен специалист со знанием linux. Конкретно вот эти результаты получены были при собеседовании на далеко не начальную должность, тесно связанную с linux. С шестизначной зарплатой.


Причем что интересно, гентушники (даже слабые, говорящие что смогли собрать, но работать не захотели) ответили на эти вопросы гораздо лучше. Те, кто уверенно работают на gentoo вообще легко ответили на эти и иные вопросы.


Постскриптум


Для написания статьи использовался следующий инструментарий:


  • Visual studio code (да, в нем достаточно удобна работать с markdown)
  • vim
  • git

Редактировалось много раз, в основном для замены неприличный слов.

Подробнее..

Второе интервью с разработчиком Reiser4 Эдуардом Шишкиным

24.05.2021 22:08:50 | Автор: admin

Недавно со мной связался Эдуард Шишкин и попросил опубликовать интервью (что я с радостью и делаю) в формате вопрос-ответ.
С первым интервью (2010-го года) можно ознакомиться здесь.

Для начала напомни, пожалуйста, читателям, где и кем ты работаешь.

Я работаю в должности Principal Storage Architect в компании Huawei Technologies, German Research Center. В отделе виртуализации занимаюсь разными аспектами хранения данных. Моя деятельность не связана с конкретной операционной системой.

Коммитишь ли ты сейчас в основную ветку ядра?

Очень редко, и только если это требует мой работодатель. Последний раз года три назад я посылал патчи, позволяющие повысить пропускную способность для хранилищ, расшаренных на хостах по протоколу 9p (другое название этого дела - VirtFS). Здесь надо сделать одно важное замечание: хоть я и давно работаю рядом с Линукс, его фанатом я никогда не являлся, то есть, "дышу ровно", как впрочем и ко всему остальному. В частности, если я заметил какой-то изъян, то могу указать на него максимум один раз. А так, чтобы потом за кем-то ходить и уговаривать - такого не будет.

Помнится, в прошлый раз, десять лет назад, ты довольно критически отзывался о стиле разработки ядра. Поменялось ли с твоей (или, возможно, корпоративной) точки зрения что-либо, сообщество стало более отзывчивым или нет? Если нет, кто, по-твоему, в этом виноват?

Я так и не увидел каких-либо сдвигов в лучшую сторону. Основная проблема сообщества - это подмена науки политтехнологиями, персональными отношениями, мнением большинства, популизмом, советами "внутренних голосов", гнилыми компромиссами, всем чем угодно кроме науки. Computer science, как ни крути, прежде всего точная наука. И если кто-то начинает провозглашать для 2x2 своё значение, отличное от 4, под флагом "Linux way", либо под каким другим, то кроме вреда это вряд ли что принесёт. Все беды прежде всего от некомпетентности и необразованности тех, кто принимает решения. Если руководитель некомпетентен, он не способен принять объективное адекватное решение. Если он к тому же и бескультурный, он не способен найти компетентного специалиста, который даст ему правильный совет. С большой вероятностью выбор падёт на мошенника, говорящего "с виду правильные вещи". Вокруг некомпетентных лидеров-одиночек всегда складывается коррумпированное окружение. Причём, история не знает исключений на этот счёт, и сообщество - ярчайшее тому подтверждение.

Как ты оцениваешь прогресс в разработке Btrfs? Эта ФС избавилась от детских болезней? Как ты её для себя позиционируешь как ФС для дома или и для корпоративного применения тоже?

Не избавилась. Всё то, о чём я упоминал 11 лет назад, актуально и поныне. Одна из проблем Btrfs, делающая последнюю непригодной для серьёзных нужд, - это проблема свободного места. Я уж не говорю о том, что пользователю предлагается бежать в магазин за новым диском в ситуациях, когда любая другая ФС показала бы ещё уйму свободного места на разделе. Невозможность завершить операцию над логическим томом по причине нехватки свободного места - это тоже не самое страшное. Хуже всего то, что непривилегированный пользователь почти всегда может за достаточно короткое время в обход любых дисковых квот лишить всех свободного места. Выглядит это так (проверено для ядра Linux 5.12). На свежеинсталлированной системе запускается скрипт, который в цикле создаёт в домашней директории файлы с определенными именами, записывает в них данные по определенным смещениям и затем удаляет эти файлы. Через минуту работы этого скрипта ничего необычного не происходит. Через пять минут порция занятого места на разделе слегка увеличивается. Через два-три часа она достигает 50% (при начальном значении 15%). А после пяти-шести часов работы скрипт валится с ошибкой "нет свободного места на разделе". После этого вы уже не в состоянии записать на ваш раздел даже 4К файл. Происходит интересная ситуация: вы ничего на раздел в итоге не записали, а всё свободное место (порядка 85%) куда-то улетучилось. Анализ раздела, подвергнутого такой атаке, покажет множество узлов дерева, содержащих всего один айтем (объект, снабженный ключом), размером несколько байт. То есть, контент, который ранее занимал 15% дискового пространства оказался равномерно "размазанным" на весь раздел так, что новый файл записать уже некуда, ибо его ключ больше всех существующих, а свободные блоки на разделе закончились. Причем это всё происходит уже на базовой конфигурации Btrfs (безо всяких снапшотов, сабвольюмов и пр.), и не имеет значения то, как вы решили хранить тела файлов в той ФС (как "фрагменты" в дереве, или же как экстенты неформатированных блоков) - конечный результат будет один и тот же.

Подвергнуть такой атаке остальные файловые системы из апстрима вам не удастся (что бы вам там ни говорили). Причину проблемы я уже давно объяснил: это полное извращение в Btrfs понятия B-дерева, что делает возможным его спонтанное или намеренное вырождение. В частности, при некоторых нагрузках ваша ФС в процессе эксплуатации будет непрерывно "разваливаться" и сама, без посторонней помощи. Понятно, что всевозможные "поджимающие" фоновые процессы спасут дело только на индивидуальных десктопах. На коллективных же серверах злоумышленник всегда будет в состоянии их "опередить". Системный администратор даже не сможет определить, кто именно над ним издевался. Быстрее всего исправить эту проблему в Btrfs можно лишь восстановив структуру регулярного B-дерева, т.е. заново перепроектировав дисковый формат и переписав существенную часть кода Btrfs. Это займёт 8-10 лет вместе с отладкой при условии что разработчики чётко следовали оригинальным статьям по соответствующим алгоритмам и структурам данным, а не играли в "испорченный телефон", как это принято (и поощряется) в "Linux way". Сюда ещё надо прибавить время, необходимое разработчикам на то, чтобы понять всё это. Вот тут уже сложнее. Во всяком случае, 10 лет на понимание им не хватило. Ну, а до этого можете не уповать на чудо. Оно не произойдёт в виде опции монтирования "про которую мы с вами не знали", или в виде патча, который приготовить "всего-то делов". Для каждого такого скоропалительного "исправления" я предъявлю новый сценарий вырождения. B-деревья - это одна из моих излюбленных тем, и должен сказать, что вольности в обращении с собой эти структуры не терпят!

Как я для себя позиционирую Btrfs? Как нечто, что именоваться файловой системой категорически не может, не говоря уже об использовании. Ибо по определению ФС - это подсистема ОС, ответственная за эффективное управление ресурсом "дисковое пространство", чего в случае c Btrfs мы не наблюдаем. Ну, представьте, что вы пришли в магазин купить часы, чтобы не опаздывать на работу, а там вместо часов вам впарили электрогриль с таймером на 30 минут максимум. Так вот - с Btrfs ситуация ещё хуже.

Просматривая листы рассылок, я часто сталкиваюсь с утверждением, что эффективно управлять дисковым пространством уже не актуально по причине дешевизны накопителей. Это полнейший бред. Без эффективного менеджера дискового пространства ОС станет уязвимой и непригодной к использованию. Вне зависимости от того, какой ёмкости диски стоят на вашей машине.

Хотелось бы попросить прокомментировать прекращение поддержки Btrfs в RHEL.

Тут особо нечего комментировать, всё предельно ясно. Она же была у них "technology preview". Вот, и не прошла это самое "preview". Не висеть же вечно этому ярлыку! А запустить ущербный by-design продукт с полной поддержкой они не могут. RHEL - это энтерпрайз, то есть прописанные товарно-денежные отношения. Red Hat не может издеваться над пользователями, как это происходит в листе рассылки Btrfs. Просто представьте ситуацию: клиент, заплативший свои кровные за диск и ещё вам за поддержку, хочет понять, куда делось его дисковое пространство, после того, как он ничего не записал. Что вы ему на это ответите? Далее. В число клиентов Red Hat входят известные крупные банки, биржи. Представьте, что будет, если они подвергнутся DoS-атакам, основанным на упомянутой уязвимости в Btrfs. Как вы думаете, кто за это ответит? Тем, кто собрался ткнуть пальцем в строчку лицензии GPL, где написано, что автор ни за что не отвечает, сразу скажу: "спрячьте её подальше!" Ответит Red Hat, причём так, что мало не покажется! Но я знаю, что Red Hat-у такого рода проблемы не грозят, учитывая их особенно сильную команду QA-инженеров, с которыми мне довелось плотно поработать в своё время.

Почему некоторые компании продолжают поддерживать Btrfs в своих энтерпрайз-продуктах?

Заметьте, что приставка "энтерпрайз" в названии продукта мало о чём говорит. Энтерпрайз - это мера ответственности, заложенная в договорные отношения с клиентом. Я знаю только один энтерпрайз, основанный на GNU/Linux - это RHEL. Всё остальное, с моей точки зрения, лишь выдаётся за энтерпрайз, но таковым не является. И, наконец, если на что-либо есть спрос, то всегда будет и предложение (в нашем случае это упомянутая "поддержка"). Спрос же бывает абсолютно на всё, в т.ч. и на непригодный к использованию софт. Как он формируется такой спрос, и кто его подпитывает - это уже другая тема.

Так что, я бы не стал делать какие-то выводы после того, как Facebook по слухам развернул Btrfs на своих серверах. Более того, адреса тех серверов я бы порекомендовал тщательно хранить в тайне по вышеупомянутым причинам.

Почему в последнее время очень много усилий приложено к вылизыванию кода XFS? Ведь изначально это сторонняя ФС, да и ext4 давно стабильна и имеет преемственность от предыдущих таких же стабильных версий. Какой интерес у Red Hat'а к XFS? Есть ли смысл активно разрабатывать две похожие по предназначению ФС ext4 и XFS?

Уже не помню, чем это мотивировалось. Вполне возможно, что инициатива шла от клиентов Red Hat. Я помню, что проводились исследования такого рода: на некоторых файловых системах из апстрима создавалось гигантское количество объектов на хай-енд накопителях нового поколения. По результатам XFS повела себя лучше ext4. Вот её и стали продвигать как наиболее перспективную. В любом случае, я бы не искал тут чего-то сенсационного. По мне, так сменили шило на мыло. Разрабатывать ext4 и XFS нет смысла. Как параллельно, так и любую из них на выбор. Из этого ничего хорошего не получится. Хотя, в природе часто встречаются ситуации, когда потенций для роста много, а объёма куда расти нет. В этом случае возникают разные причудливо-уродливые новообразования, на которые все показывают пальцем ("О, смотри, чего только в этой жизни не увидишь!").

Считаешь ли ты вопрос о layer violation исчерпанным (в негативном смысле) с появлением функций шифрования в ext4, F2FS (не говоря уже о RAID в Btrfs)?

Вообще, введение каких-либо уровней и принятие решения о их ненарушении - это обычно вопрос политики, и я не берусь тут что-либо комментировать. Объективные же аспекты нарушения уровней мало кому интересны, но мы можем рассмотреть некоторые из них на примере нарушения "сверху", а именно, реализацию в ФС функциональности, уже имеющейся на block layer. Такое "нарушение" оправдано всего лишь за редким исключением. Для каждого такого случая вы сначала должны доказать две вещи: что это действительно нужно, и что дизайну системы при этом не будет причинён ущерб. Скажем, зеркалирование, которое традиционно являлось занятием для block layer, имеет смысл реализовать на уровне файловой системы. По разным причинам. Например, на дисковых накопителях имеет место "тихая" порча данных (bit rot). Это когда устройство исправно работает, но данные блока неожиданно повреждаются под воздействием жесткого гамма-кванта, испущенного далёким квазаром и т.п. Хуже всего, если этим блоком оказался системный блок ФС (суперблок, bitmap-блок, узел дерева-хранилища и т.д), ибо это непременно приведёт к kernel panic. Заметьте, что зеркала, предлагаемые block layer (т.н. RAID 1) от этой проблемы не спасут. Ну, действительно: кто-то же должен проверять контрольные суммы и считывать реплику в случае неудачи? Кроме того, имеет смысл зеркалировать не тупо все подряд, а только метаданные. Некоторые важные данные (к примеру, исполняемые файлы критических приложений) можно хранить на правах мета-данных. В этом случае они получают такие же гарантии сохранности. Защиту же остальных данных имеет смысл поручить другим подсистемам (возможно, даже пользовательским приложениям) - мы предоставили для этого все необходимые условия. Такие "экономные" зеркала вполне имеют право на существование и эффективно их организовать можно только на уровне файловой системы. В остальном же layering violation - это захламление подсистемы дублированным кодом ради каких-то микроскопических выгод. Яркий тому пример - это реализация RAID-5 средствами ФС. Такие решения (собственные RAID / LVM в файловой системе) убивает последнюю в архитектурном плане. Здесь ещё нужно отметить, что layering violation "поставлено на поток" разного рода маркетинговыми мошенниками. За отсутствием каких-либо идей, в подсистемы добавляется функциональность давно уже реализованная на соседних уровнях, это выдается за новую черезвычайно полезную фичу и активно проталкивается.

Reiser4 же обвинили в нарушении уровней "снизу". Исходя из того, что файловая система не монолитная, как все остальные, а модульная, было сделано ничем не обоснованное предположение, что она занимается тем, чем должен заниматься уровень выше (VFS).

Можно ли говорить о смерти ReiserFS v3.6 и, например, JFS? Последнее время им в ядре почти не уделяется внимания. Они морально устарели?

Здесь надо определить, что значит смерть программного продукта. С одной стороны, они с успехом используются (их для этого и создавали, в конце концов) - значит живут. С другой стороны, не скажу за JFS (мало знаю), а вот ReiserFS (v3) очень трудно приспособить к новым тенденциям (проверено на практике). Значит, разработчики в дальнейшем будут уделять внимание не ей, а тем, которые легче приспособить. С этой стороны получается, что, увы, мертва в архитектурном плане. Понятием "морально устаревший" я бы вообще не манипулировал. Оно хорошо применимо, например, к гардеробу, но никак не к программным продуктам. Есть понятие уступать и превосходить в чем-либо. Совершенно точно могу сказать, что ReserFS v3 сейчас во всём уступает Reiser4, но на некоторых типах рабочей нагрузки превосходит все остальные ФС из апстрима.

Известно ли тебе о разработке ФС Tux3 и HAMMER/HAMMER2 (ФС для DragonFly BSD)?

Да, известно. В Tux3 меня когда-то заинтересовала технология их снимков (т.н. "версионные указатели"), но в Reiser4 мы, скорее всего, пойдём другим путём. Я давно думаю о поддержке снимков (snapshots) и ещё не определился с тем, как их реализовать для простых Reiser4 томов. Дело в том, что новомодная техника "ленивых" счётчиков ссылок, предложенная Охадом Родехом, работает лишь для B-деревьев. У нас их нет. Для тех структур данных, которые используются в Reiesr4, "ленивые" счётчики не определены - для их введения необходимо решить определённые алгоритмические проблемы, за что пока никто не брался.

По HAMMERу: читал статью от создателя. Не заинтересовало. Опять же, B-деревья. Эта структура данных безнадёжно устарела. Мы отказались от неё ещё в прошлом веке.

Как ты оцениваешь нарастающую востребованность в сетевых кластерных ФС по типу CephFS/GlusterFS/etc? Означает ли эта востребованность сдвиг приоритетов разработчиков в сторону сетевых ФС и недостаточность внимания к локальным ФС?

Да, такой сдвиг приоритетов произошел. Разработка локальных ФС стагнировала. Увы, сделать что-то существенное для локальных томов сейчас довольно сложно и далеко не всем под силу. Вкладываться в их разработку никто не хочет. Это примерно так же, как попросить коммерческую структуру выделить деньги на математические исследования - вас без этнузиазма спросят, а как на новой теореме можно будет заработать. Теперь локальная ФС - это нечто, что магически появляется "из коробки" и "всегда должно работать", а если не работает, то вызывает безадресное ворчание навроде: "да, что они там себе думают!". Отсюда недостаток внимания к локальным ФС, хотя работы в той области ещё невпроворот. И да, все повернулись к распределённым хранилищам, которые строятся на базе уже существующих локальных ФС. Это сейчас очень модно. Словосочетание "Big Data" у многих вызывает выброс адреналина, ассоциируясь с конференциями, воркшопами, большими зарплатами и т.п.

Насколько разумным в принципе выглядит подход, при котором сетевая ФС реализуется в пространстве ядра, а не в пространстве пользователя?

Очень разумный подход, который до сих пор нигде не был реализован. Вообще, вопрос о том, в каком пространстве надо реализовывать сетевую ФС - это "палка о двух концах". Ну, давайте рассмотрим на примере. Клиент записал данные на удалённой машине. Они упали в её page cache в виде грязных страниц. Это работа для "тонкого шлюза" сетевой ФС в пространстве ядра. Дальше операционная система рано или поздно попросит записать те страницы на диск для их освобождения. Тогда в дело включается IO-forwarding (отправляющий) модуть сетевой ФС. Он определяет на какую серверную машину (server node) эти страницы попадут. Потом эстафету принимает сетевой стек (а он, как мы знаем, реализован в пространстве ядра). Далее, server node принимает тот пакет с данными или метаданными и даёт указание backend storage - модулю (т.е. локальной ФС, которая работает в пространстве ядра) записать всё это хозяйство. Итак, мы свели вопрос к тому, где должны работать "отправляющий" и "принимающий" модули. Если какой-либо из тех модулей работают в пространстве пользователя, то это неминуемо приведёт к переключению контекстов (из-за необходимости пользования сервисами ядра). Число таких переключений зависит от деталей реализации. Если таких переключений много, то будет падать пропускная способность хранилища (производительность ввода-вывода). Если ваш backend storage составлен из медленных дисков, то значительного падения вы не заметите. Но если у вас диски быстрые (SSD, NVRAM и т.д), то переключение контекстов уже становится "бутылочным горлышком" и, сэкономив на переключении контекстов, производительность можно увеличить в разы. Стандартный путь такой экономии заключается в переносе модулей в пространство ядра. К примеру, мы выяснили, что перенос 9p-сервера из QEMU в ядро на хост-машине приводит к трёхкратному приросту производительности VirtFS. Это, конечно, не сетевая ФС, но суть вещей вполне отражает. Обратная сторона такой оптимизации - это проблемы с переносимостью. Для кого-то последняя может оказаться критичной. К примеру, GlusterFS вообще не имеет модулей в составе ядра. Благодаря этому она сейчас работает на многих платформах, в том числе и на NetBSD.

Какие концепции локальные ФС могли бы позаимствовать у сетевых и наоборот?

Сейчас сетевые ФС, как правило, есть надстройки над локальными ФС, поэтому я не совсем понимаю, как можно у последних что-то позаимствовать. Ну, действительно, рассмотрим компанию из 4 сотрудников, в которой каждый занимается своим делом: один распределяет, другой отправляет, третий получает, четвертый хранит. И вопрос, а что же компания может позаимствовать от её сотрудника, который хранит, звучит как-то некорректно (то, что можно было от него позаимствовать она уже давно имеет).

А вот локальным ФС есть чему поучиться у сетевых. Во-первых, у них стоит поучиться агрегировать логические тома на высоком уровне. Сейчас т.н. "передовые" локальные ФС агрегируют логические тома исключительно при помощи технологии "виртуальных девайсов", заимствованной из LVM (тот самый заразный layering violation, который сначала был реализован в ZFS). Иначе говоря, происходит трансляция виртуальных адресов (номеров блоков) в реальные и обратно на низком уровне (т.е. после того, как файловая система издала запрос ввода-вывода). Заметьте, что добавление и удаление устройств в логические тома (не зеркала), скомпанованные на block layer ведёт к проблемам, о которых поставщики подобных "фич" скромно умалчивают. Я говорю, о фрагментации на реальных устройствах, которая может достигать чудовищных значений, в то время, как на виртуальном устройстве у вас всё замечательно. Однако виртуальные устройства мало кого интересуют: всем интересно, что у вас происходит на реальных устройствах. Но ZFS-подобные ФС (также как и любые ФС в связках с LVM) работают только с виртуальными дисковыми устройствами (выделяют виртуальные дисковые адреса из числа свободных, дефрагментируют эти виртуальные устройства и т.д.). А что происходит на реальных устройствах они даже понятия не имеют! Теперь представьте, что на виртуальном устройстве у вас фрагментация равна нулю (то есть, у вас там живёт всего один гигантский экстент), вы добавляете диск в ваш логический том, а затем удаляете другой случайный диск из вашего логического тома с последующей перебалансировкой. И так вот много раз. Нетрудно сообразить, что на виртуальном устройстве у вас по-прежнему будет жить тот самый единственный экстент, но вот на реальных устройствах вы ничего хорошего уже не увидите. Хуже всего то, что вы даже не в состоянии исправить эту ситуацию! Единственное, что тут можно сделать - это попросить файловую систему дефрагментировать виртуальное устройство. Но она вам скажет, что у вас там всё замечательно - есть один только экстент, фрагментация равна нулю, а лучшего и быть не может! Итак, логические тома, скомпанованные на блочном уровне не предназначены для многократного добавления/удаления устройств. По-хорошему, нужно только один раз скомпановать логический том на блочном уровне, отдать его файловой системе, и потом ничего уже больше с ним не делать.

Кроме того, связка независимых подсистем ФС+LVM никак не позволяет учитывать разную природу накопителей, из которых агрегируются логические тома. Действительно, предположим, скомпановали вы логический том из НЖМД и твердотельных устройств. Но тогда первые потребуют дефрагментации, а вторые - нет. Для вторых нужно издавать discard-запросы, а для первых - нет, и т.п. Однако в указанной связке такую избирательность проявить достаточно сложно. Заметьте, что после создания в файловой системе своего собственного LVM, ситуация сильно лучше не становится. Более того, этим вы фактически ставите крест на перспективе когда-либо её улучшить в будущем. Это очень плохо. На одной и той же машине могут жить накопители разного типа. И если не файловая система будет их различать, то кто тогда?

Еще одна проблема подстерегает т.н. "Write-Anywhere" файловые системы (сюда же входит и Reiser4, если во время монтирования вы задали соответствующую транзакционную модель). Такие файловые системы должны предъявить беспрецедентные по своей мощи средства дефрагментации. А низкоуровневый менеджер томов тут не помогает, а только мешает. Дело в том, что с таким менеджером ваша ФС будет хранить карту свободных блоков только одного девайса - виртуального. Соответственно, дефрагментировать вы сможете только виртуальный девайс. Это значит, что дефрагментатор ваш будет долго-долго пахать на огромном едином пространстве виртуальных адресов. И если у вас много пользователей, делающих случайные перезаписи, то полезный эффект от такого дефрагментатора сведётся к нулю. Система ваша неминуемо начнёт тормозить, и вам останется лишь сложить руки перед неутешительным диагнозом "broken design". Несколько дефрагментаторов, работающих на одном и том же пространстве адресов будут только мешать друг другу. Совсем другое дело, если для каждого реального устройства вы поддерживаете свою карту свободных блоков. Это позволит эффективно распараллелить процесс дефрагментации. Но сделать это можно, лишь обладая высокоуровневым менеджером логических томов. Локальных ФС с такими менеджерами ранее не существовало (по крайней мере, я о них не знаю). Такими менеджерами обладали только сетевые ФС (например GlusterFS). Другой очень важный пример - утилита проверки целостности тома (fsck). Если для каждого подтома у вас хранится своя независимая карта свободных блоков, то процедуру проверки логического тома можно эффективно распараллелить. Иначе говоря, логические тома с высокоуровневыми менеджерами лучше масштабируются.

Кроме того, с низкоуровневыми менеджерами томов вы не сможете организовать полноценные снимки (snapshots). С LVM и в ZFS-подобных ФС вы можете делать только локальные снимки, но никак не глобальные. Локальные снимки позволяют моментально откатывать только регулярные файловые операции. A операции с логическими томами (добавление / удаление устройств) вам никто там не откатит. Давайте рассмотрим это на примере. В некоторый момент времени, когда у вас имеется логический том из двух устройств А и В, содержащий 100 файлов, вы делаете снимок S системы и затем создаете ещё одну сотню файлов. После этого вы добавляете к вашему тому устройство С, и наконец откатываете вашу систему к снимку S. Вопрос: сколько файлов и устройств содержит ваш логический том после отката к S? Файлов, как вы уже догадались, будет 100, но вот устройств будет 3 - это те же устройства A, B и C, хотя, на момент создания снимка в системе было всего два устройства (A и B). Операция добавления устройства C не откатилась, и если вы сейчас удалите устройство C из компьютера, то это закорраптит ваши данные, так что перед удалением вам вам нужно будет сначала провести дорогостоящую операцию удаления устройства из логического тома с перебалансировкой, которая раскидает все данные с устройства C на устройства A и B. А вот если бы ваша ФС поддерживала глобальные снимки, такая перебалансировка бы не потребовалась, и после моментального отката к S вы бы могли смело удалить устройство C из компьютера. Итак, глобальные снимки хороши тем, что они позволяют избежать дорогостоящего удаления (добавления) устройства из логического тома (в логический том) c большим количеством данных (разумеется, если вы не забыли "сфотографировать" свою систему в нужный момент). Напомню, что создание снимков и откат файловой системы к оным - моментальные операции. Может возникнуть вопрос: а как вообще такое возможно - моментально откатить операцию с логическим томом, которая заняла у вас три дня? А вот возможно! При условии, что ваша ФС правильно спроектирована. Идея таких "3D-снапшотов" у меня возникла три года назад, а в прошлом году я запатентовал эту методику.

Следующее, чему стоит поучиться локальным ФС у сетевых - это хранить метаданные на отдельных устройствах точно так же, как сетевые ФС хранят их на отдельных машинах (так называемые метадата-серверы). Есть приложения, работающие в основном с метаданными, и эти приложения можно значительно ускорить, разместив метаданные на дорогостоящих высокопроизводительных накопителях. Со связкой ФС+LVM проявить такую избирательность вам не удастся: LVM не знает, что там на блоке, который вы ему передали (данные там или же метаданные). От реализации в ФС собственного низкоуровневого LVM большого выигрыша по сравнению со связкой ФС+LVM вы не получите, а вот что у вас получится очень хорошо - так это захламить ФС так, что потом станет невозможно работать с её кодом. ZFS и Btrfs, поспешившие с виртуальными девайсами, - это всё наглядные примеры того, как layering violation убивает систему в архитектурном плане.Итак, к чему я всё это? Да к тому, что не нужно городить в файловой системе свой собственный низкоуровневый LVM. Вместо этого нужно агрегировать устройства в логические тома на высоком уровне, как это делают некоторые сетевые ФС с разными машинами (storage nodes). Правда, делают они это отвратительно по причине применения плохих алгоритмов. Примеры совершенно ужасных алгоритмов - это транслятор DHT в файловой системе GlusterFS и так называемый CRUSH map в файловой системе Ceph. Ни один из тех алгоритмов, что я видел, не устроил меня в плане простоты и хорошей масштабируемости. А потому пришлось вспоминать алгебру и изобретать всё самому. В 2015 году, экспериментируя с расслоениями над хэш-функциями, я придумал и запатентовал то, что меня устраивает. Сейчас могу сказать, что попытка реализовать всё это на практике оказалась успешной. Никаких проблем с масштабируемостью в новом подходе я не вижу. Да, каждый подтом потребует в памяти отдельную структуру типа суперблока. Это что, очень страшно? Вообще, я не знаю, кто собирается "кипятить океан" и создавать логические тома из сотен тысяч и более устройств на одной локальной машине. Если кто-нибудь мне это объяснит, буду очень благодарен. А пока что для меня это маркетинговый булшит.

Как повлияли изменения в подсистеме блочных устройств ядра (например, появление blk-mq) на требования к реализации ФС?

Никак не повлияли. Уж не знаю, что там должно такого случиться на block layer, что пришлось бы проектировать новую ФС. Интерфейс взаимодействия этих подсистем очень беден. Со стороны драйверов на ФС влиять должно только появление накопителей нового типа, к которым будет подстраиваться сначала block layer, а потом уже ФС (для reiser4 это будет означать появление новых плагинов).

Означает ли появление новых типов носителей (например, SMR, или повсеместное распространение SSD) принципиально новые вызовы для проектирования ФС?

Да. И это нормальные стимулы для развития ФС. Вызовы могут быть разные и совершенно неожиданные. К примеру, я слышал о накопителях, где скорость операции ввода-вывода сильно зависит от размера куска данных и его смещения. В Линуксе, где размер ФС-блока не может превышать размер страницы, такой накопитель по умолчанию не проявит свои полные возможности. Однако, если ваша ФС правильно спроектирована, то есть шанс много ещё чего из него "выжать".

Сколько людей сейчас работает с кодом Reiser4 кроме тебя?

Меньше, чем хотелось бы, но и острой нехватки ресурсов я не испытываю. Темпы развития Reiser4 меня более чем устраивают. "Гнать лошадей" я не собираюсь - это не та область. Здесь "тише едешь - дальше будешь!" Современная ФС - это самая сложная подсистема ядра, неправильные решения в дизайне которой могут перечеркнуть последующий многолетний труд людей. Предлагая волонтёрам что-то реализовать, я всегда гарантирую, что усилия непременно приведут к корректному результату, который может быть востребован для серьёзных нужд. Как вы понимаете, таких гарантий не может быть много и сразу. В то же время я терпеть не могу "деятелей", которые бесстыдно пиарят "фичи" заведомо непригодного для использования софта, обманывая сотни пользователей и разработчиков, да при этом сидят и улыбаются на кернел саммитах.

Выражала ли какая-нибудь компания готовность поддержать разработку Reiser4?

Да, такие предложения были, в т.ч. и от крупного вендора. Но, для этого мне надо было переезжать в другую страну. К сожалению, мне уже давно не 30 лет, я не могу сорваться и уехать вот так вот по первому свисту.

Каких функций не хватает в Reiser4 сейчас?

Не хватает функции "растяжения" (resize) для простых томов по аналогии с той, что имеется в ReiserFS(v3). Кроме того, не помешали бы файловые операции с флагом DIRECT_IO. Далее, хотелось бы уметь сегрегировать том на "семантические подтома", которые не имеют фиксированного размера, и которые можно монтировать как самостоятельные тома. Эти задачи хороши для начинающих, желающих попробовать себя в "настоящем деле". И, наконец, хотелось бы иметь сетевые логические тома с простой реализацией и администрированием (современные алгоритмы уже позволяют это сделать). А вот чего в Reiser4 точно никогда не будет - так это RAID-Z, скрабов, кэшей свободного пространства, 128-битных переменных и прочей маркетинговой ерунды, возникшей на фоне дефицита идей у разработчиков некоторых ФС.

Всё ли то, что может понадобиться, может быть реализовано плагинами?

Если говорить только в терминах интерфейсов и плагинов (модулей), которые их реализуют, то не всё. Но если ввести ещё и отношения на этих интерфейсах, то помимо всего прочего у вас возникнут понятия высших полиморфизмов, которыми уже можно обойтись. Представьте, что вы гипотетически заморозили объектно-ориентированную систему времени выполнения, поменяли значение instruction pointer, чтобы он указывал на другой плагин, который реализует тот же интерфейс X, а потом разморозили систему, так чтобы она продолжила выполнение. Если при этом конечный пользователь не заметит такой "подмены", то мы говорим, что система обладает полиморфизмом нулевого порядка в интерфейсе X (или система гетерогенна в интерфейсе X, что то же самое). Если теперь у вас не просто набор интерфейсов, а ещё имеются и отношения на них (граф интерфейсов), то можно ввести полиморфизмы и более высоких порядков, которые будут характеризовать гетерогенность системы уже в "окрестности" какого-либо интерфейса. Такую классификацию я когда-то давно ввёл, но опубликовать, к сожалению, так и не получилось. Так вот, при помощи плагинов и таких вот высших полиморфизмов можно описать любую известную фичу, а также "предсказать" те, которые никогда даже не упоминались. Строго доказать это мне не удалось, но и контрпримера я тоже пока не знаю. Вообще, вопрос этот напомнил мне "Эрлангенскую Программу" Феликса Клейна. Он в своё время пытался представить всю геометрию разделом алгебры (конкретно, теории групп).

Теперь к главному вопросу как обстоят дела с продвижением Reiser4 в основное ядро? Были ли публикации по архитектуре этой ФС, о которых ты говорил в прошлом интервью? Насколько вообще с твоей точки зрения этот вопрос актуален?

Вообще, мы в течение трёх лет просили о включении в основную ветку. Последний комментарий Рейзера в публичном треде, где был сделал запрос на включение так и остался без ответа. Так что все дальнейшие вопросы - не к нам. Я же лично не понимаю, зачем нам "вливаться" в конкретную операционную систему. На Линуксе свет клином не сошёлся. Так что, есть отдельный репозиторий, в котором будут несколько веток-портов под разные ОС. Кому нужно - можете клонировать соответствующий порт и делать с ним всё что заблагорассудится (в рамках лицензии, разумеется). Ну, а если это кому-то не нужно, то это не мои проблемы. На этом вопрос о "продвижении в основное ядро Линукс" я предлагаю считать исчерпанным.

Публикации по архитектуре ФС актуальны, но я пока что находил время только на свои новые результаты, которые считаю более приоритетными. Ещё дело в том, что я математик, а в математике любая публикация - это сводка теорем и их доказательств. Публиковать там что-либо без доказательства - это признак дурного тона. Если же я сторого докажу или опровергну какое-нибудь утверждение по архитектуре ФС, то получатся такие нагромождения, через которые будет довольно сложно продраться. Кому это нужно? Наверно, поэтому всё продолжает оставаться в своём старом виде - исходный код и комментарии к нему.

Что существенно нового появилось в Reiser4 за последние несколько лет?

Материализовалась, наконец, долгожданная стабильность. Одной из последних сдалась ошибка, приводившая к "неудаляемым" директориям. Трудность была в том, что она проявлялась только на фоне коллизий хэшей имён и при определённом расположении директорных записей в узле дерева. Однако, для продакшена я Reiser4 по-прежнему рекомендовать не могу: для этого надо провести определённую работу при активном взаимодействии с администраторами продакшн-систем.

Удалось, наконец, реализовать свою давнюю задумку - разные транзакционные модели. До этого в Reiser4 работала только одна жестковкодированная модель Макдональда-Рейзера. Это создавало проблемы в дизайне. В частности, в такой транзакционной модели невозможны снимки (snapshots) - их будет портить компонента атома под названием "OVERWRITE SET". Сейчас Reiser4 поддерживает три транзакционные модели. В одной из них (Write-Anywhere) в атомную компоненту OVERWRITE SET входят только системные страницы (образы дисковых битовых карт и т.п.), которые "фотографированию" не подлежат (проблема курицы и яйца). Так что снимки теперь можно реализовать в лучшем виде. В другой транзакционной модели все модифицированные страницы попадают только в OVERWRITE SET (то есть, это по сути чистое журналирование). Эта модель для тех, кто жаловался на быструю фрагментацию Reiser4 разделов. Теперь в этой модели ваш раздел будет фрагментироваться не быстрее чем с ReiserFS (v3). Все три существующие модели за некоторыми оговорками гарантируют атомарность операций, однако пригодиться могут также и модели с потерей атомарности и с сохранением лишь целостности раздела. Такие модели могут оказаться полезными для всевозможных приложений (базы данных и т.п.), которые уже взвалили на себя часть подобных функций. Добавить эти модели в Reiser4 очень легко, но я этого не делал, ибо меня никто не просил, а лично мне это не нужно.

Появились контрольные суммы метаданных и недавно я дополнил их "экономичными" зеркалами" (пока нестабильный материал). В случае провала проверки контрольной суммы какого-либо блока Reiser4 немедленно считывает соответствующий блок с девайса-реплики. Заметьте, что ZFS и Btrfs так не могут: не позволяет дизайн. Там вы должны запустить специальный фоновый сканирующий процесс под названием "скраб" и ждать, когда он доберётся до проблемного блока. Такие мероприятия программисты образно называют "костылями".

И, наконец, появились гетерогенные логические тома, предлагающие всё то, чего ZFS, Btrfs, block layer, а также связки FS+LVM в принципе дать не могут - это параллельное масштабирование, O(1)-аллокатор дисковых адресов, прозрачная миграцией данных между подтомами. Для последней также имеется пользовательский интерфейс. Теперь наиболее "горячие" данные вы без труда можете переместить на самый высокопроизводительный накопитель вашего тома. Кроме того, имеется возможность экстренно сбрасывать на такой накопитель любые грязные страницы, и, тем самым, значительно ускорить приложения, часто вызывающие fsync(2). Отмечу, что функциональность block layer, называемая bcache, совершенно не предоставляет такой свободы действий. Новые логические тома основаны на моих алгоритмах (есть соостветствующие патенты). Софт уже достаточно стабилен, вполне можно попробовать, замерить производительность и т.п. Единственное неудобство - пока нужно вручную обновлять конфигурацию тома и где-то ёё хранить.

Реализовать свои задумки мне удалось пока что процентов на 10. Однако, удалось то, что я считал наиболее трудным - это "подружить" логические тома с флаш-процедурой, которая выполняет все отложенные действия в reiser4. Это всё пока в экспериментальной ветке "format41".

Проходит ли Reiser4 тесты xfstests?

По крайней мере, у меня прошла, когда я готовил последний релиз.

Возможно ли в принципе с помощью плагинов сделать Reiser4 сетевой (кластерной) ФС?

Можно, и даже нужно! Если на основе правильно спроектированной локальной ФС создать сетевую, то результат будет очень впечатляющим! В современных сетевых ФС меня не устраивает наличие уровня backend storage, который реализуется при помощи любой локальной ФС. Существование этого уровеня совершенно неоправдано. Сетевая ФС должна нарямую взаимодействовать с block layer, и не просить локальную ФС создать ещё какие-то там служебные файлы! Вообще, деление файловых систем на локальные и сетевые - это от лукавого. Оно возникло от несовершенства алгоритмов, которыми пользовались тридцать лет назад, и вместо которых до сих пор ничего не было предложено. Это же является причиной появления массы ненужных софтверных компонет (различных сервисов и т.д). По-хорошему, должна быть только одна ФС в виде модуля ядра и набора пользовательских утилит, инсталлируемых на каждой машине - узле кластера. Эта ФС одновременно и локальная и сетевая. И ничего лишнего!

Если с Reiser4 в Linux'е так ничего и не получится, хотелось бы предложить ФС для FreeBSD (цитата из прошлого интервью: FreeBSD имеет академические корни А это означает, что с большой долей вероятности мы найдём с разработчиками общий язык)?

Итак, как мы только что выяснили, с Линуксом у нас всё уже прекрасно получилось: под него есть отдельный работающий порт Reiser4 в виде мастер-бранча нашего репозитория. Про FreeBSD я и не забыл! Предлагайте! Готов плотно поработать c теми, кто хорошо знает внутренности FreeBSD. Кстати: что мне очень нравится в их сообществе - там решения принимаются обновляемым советом независимых экспертов, не имеющим ничего общего с губонадувательством одной бессменной персоны.

Как ты оцениваешь сообщество пользователей Linux сегодня? Стало ли оно более попсовым?

Мне по роду своей деятельности оценить такое достаточно трудно. Ко мне в основном приходят пользователи с багрепортами и просьбами починить раздел. Пользователи как пользователи. Кто-то подкован больше, кто-то меньше. Ко всем отношение одинаковое. Ну, а если пользователь игнорирует мои указания, то тут уж извините: игнор будет введён и с моей стороны.

Возможно ли спрогнозировать развитие файловых систем на ближайшие пять-десять лет? Какие, по-твоему, основные вызовы могут стоять перед разработчиками ФС?

Да, сделать такой прогноз несложно. В апстриме давно уже нет никакого развития файловых систем. Создаётся лишь видимость такового. Разработчики локальных ФС упёрлись в проблемы связанные с неудачным дизайном. Здесь нужно сделать оговорку. Т.н "хранение", "вылизывание" и портирование кода я за развитие и разработку не считаю. А недоразумение под названием "Btrfs" к разработкам не причисляю по причинам, которые я уже объяснил. Каждый патч лишь усугубляет её проблемы. Ну. и всегда находятся разного рода "евангелисты", у которых "всё работает". В основном, это школьники и студенты, прогуливающие лекции. Вы только представьте: у него работает, а у профессора нет. Это какой же выброс адреналина! Наибольший же вред с моей точки зрения приносят "умельцы", бросившиеся с энтузиазмом "привинчивать" чудо-фичи Btrfs к всевозможным прослойкам типа systemd, docker, и т.п. - это уже напоминает метастазы.

Давайте теперь попробуем сделать прогноз на пять-десять лет. Что мы будем делать в Reiser4 я вкратце уже перечислил. Основным вызовом для разработчиков локальных ФС из апстрима станет (да, уже стало) невозможность заниматься приличным делом за зарплату. Без каких-либо идей в области хранения данных они будут продолжать пытаться патчить эти несчастные VFS, XFS и ext4. Особенно комично на этом фоне выглядит ситуация с VFS, напоминающая остервенелую модернизацию ресторана в котором поваров нет, и не предвидится. Теперь код VFS безо всяких условий залочивает одновременно несколько страниц памяти и предлагает нижележащей ФС оперировать над ними. Это было введено для повышения производительности Ext4 на операциях удаления, но, как нетрудно понять, такой одновременный лок совершенно несовместим с продвинутыми транзакционными моделями. То есть, добавить поддержку какой-то умной ФС в ядре вы уже просто так не сможете. Я не знаю, как дело обстоит в остальных областях Linux, но что касается файловых систем, какое-либо развитие здесь вряд ли совместимо с политикой, проводимой Торвальдсом на деле (академические проекты изгоняются, а мошенникам, не имеющим понятия, что такое B-дерево, выдаются бесконечные кредиты доверия). Поэтому тут был взят курс на медленное загнивание. Его, конечно же, изо всех сил будут пытаться выдать за "развитие". Далее, "хранители" файловых систем, поняв, что на одном лишь "хранении" много не заработаешь, будут пробовать себя в более прибыльном бизнесе. Это, как правило, распределенные файловые системы и виртуализация. Возможно, куда-то ещё будут портировать модную ZFS там, где её ещё нет. Но она, как и все ФС из апстрима, напоминает новогоднюю ёлку: если сверху ещё что-то из мелочи повесить можно, то глубже уже не подлезешь. Я допускаю, что построить серьёзную энтерпрайз-систему на базе ZFS можно, но поскольку мы сейчас обсуждаем будущее, то мне остаётся с сожалением констатировать, что ZFS в этом плане безнадёжна: своими виртуальными девайсами ребята перекрыли себе и будущим поколениям кислород для дальнейших разработок. ZFS - это вчерашний день. А ext4 и XFS - уже даже не позавчерашний.

Отдельно стоит сказать про нашумевшее понятие "Linux file system of next generation". Это полностью политический и маркетинговый проект, созданный для возможности, так скажем, "столбить будущее файловых систем" в Linux за конкретными персонажами. Дело в том, что это раньше Linux был "just for fun". А сейчас это прежде всего машина для зарабатывания денег. Они делаются на всём, на чём только можно. К примеру, создать хороший программный продукт очень трудно, но смышленые "разработчики" давно уже сообразили, что напрягаться-то вовсе и не нужно: успешно продавать можно и несуществующий софт, анонсированный и распиаренный на всевозможных публичных мероприятиях - главное, чтобы в презентационных слайдах было побольше "фич". Файловые системы подходят для этого как нельзя лучше, ибо на результат можно смело выторговывать лет десять. Ну, а если кто-то потом будет сетовать на отсутствие этого самого результата, то он же в файловых системах просто ничего не смыслит! Это напоминает финансовую пирамиду: на вершине располагаются заварившие эту кашу авантюристы, и те немногие, кому "повезло": они "сняли дивиденды", т.е. получили деньги на разработку, устроились менеджерами на высокооплачиваемую работу, "засветились" на конференциях, и т.п. Далее идут те, кому "не повезло": они будут подсчитывать убытки, расхлебывать последствия развертывания в продакшн непригодного программного продукта ", и т.д. Их гораздо больше. Ну, и в основании пирамиды - огромная масса разработчиков, "пилящих" никчёмный код. Они - в самом большом проигрыше, ибо впустую потраченное время не вернёшь. Такие пирамиды чрезвычайно выгодны Торвальдсу и его приближенным. И чем этих пирамид больше - тем для них лучше. Для подпитки таких пирамид в ядро может быть принято всё что угодно. Разумеется, на публике они утверждают обратное. Но я сужу не по словам а по поступкам.

Так что, "будущее файловых систем в Линукс" - это очередной сильно распиаренный, но мало пригодный к использованию софт. После Btrfs с большой вероятностью место такого "будущего" займёт Bcachefs, представляющая собой ещё одну попытку скрестить Linux block layer с файловой системой (дурной пример заразителен). И что характерно: там те же проблемы, что и в Btrfs. Я давно это подозревал, а потом как-то не удержаляся и заглянул в код - так и есть! Авторы Bcachefs и Btrfs, создавая свои ФС, активно пользовались чужими исходниками, мало что в них понимая. Ситуация очень напоминает детскую игру "испорченный телефон". И я примерно представляю, как будет происходить включение этого кода в ядро. Собственно "грабли" никто не увидит (на них все будут наступать потом). После многочисленных придирок к стилю кода кода, обвинению в несуществующих нарушениях, и пр. будет делаться заключение о "лояльности" автора, о том, насколько он хорошо "взаимодействует" с остальными разработчиками, и как успешно всё это потом можно будет продавать корпорациям. Конечный же результат никого не заинтересует. Лет двадцать назад, может быть, бы и заинтересовал, но сейчас вопросы ставятся по-другому: получится ли это раскрутить так, чтобы ближайший десяток лет определённые люди оказались трудоустроены. А задаваться вопросом о конечном результате, увы, не принято.

А вообще, я бы настоятельно не рекомендовал начинать изобретать свою файловую систему "с нуля". Ибо даже существенных финансовых вложений будет недостаточно, чтобы лет за десять получить что-то конкурентоспособное. Разумеется, я говорю о серьёзных проектах, а не о тех, которые предназначены для "проталкивания" в ядро. Так что, более эффективный способ заявить о себе - это присоединиться к реальным разработкам, например к нам. Сделать это, разумеется, непросто - но так дело обстоит с любым проектом высокого уровня. Сначала нужно будет самостоятельно одолеть задачку, которую я предложу. После чего, убежденный в серьёзности ваших намерений, я начну помогать. Традиционно мы используем только собственные наработки. Исключение составляют алгоритмы компрессии и некоторые хэш-функции. Мы не отправляем разработчиков колесить по конференциям, а потом не сидим и не комбинируем чужие идеи ("авось, чего и получится"), как это принято в большинстве стартапов. Все алгоритмы мы разрабатываем сами. В настоящий момент меня интересуют алгебраические и комбинаторные аспекты науки хранения данных. В частности, конечные поля, асимптотика, доказательство неравенств. Для простых программистов тоже найдётся работа, однако должен сразу предупредить: все предложения "посмотреть на другую ФС и сделать так же" отправляются в игнор. Туда же пойдут патчи, направленные на более тесную интеграцию с Linux по линии VFS. Итак, у нас нет граблей, а есть понимание, куда надо двигаться, и есть уверенность, что это направление правильное. Такое понимание не пришло в виде манны небесной. Я напомню, что позади 29-летний опыт разработок, две файловые системы написанные "с нуля". И столько же утилит восстановления данных. А это очень много!


Подробнее..

Даже не пытайтесь повторить это в GUI

19.05.2021 10:20:52 | Автор: admin


Есть такое понятие, как дружественный пользователю Linux. Возникло оно оно очень давно, возможно через несколько минут после того, как Линус Торвальдс анонсировал свою разработку в листе comp.os.minix. Трудно сказать принесла-ли пользу данная концепция и различные её воплощения на рабочей станции. Понятно одно, что прогресс на этом пути довольно-таки ощутимо не совпадает с ожиданиями этого самого пользователя.

Возможно дело в том, что производители коммерческих дистрибутивов Linux и производители ПК стараются показать, что вполне реалистично использовать на рабочей станции Linux и при этом не использовать консоль в качестве основного инструмента управления ОС. Возможно, в будущем так и будет, но сегодня консоль на домашнем компьютере так же незаменима, как и на сервере и для этого есть веские основания.

Предлагаем рассмотреть несколько сценариев из реальной жизни, где хочешь не хочешь, все необходимо сделать в CLI. Таких примеров очень и очень много, это всего лишь пара кейсов в огромном множестве подобных.

Кейс 1 настройка пользовательского окружения


Большинство дистрибутивов Linux дают возможность графической установки ОС, которая для продвинутого пользователя Windows, или macOS не представляет из себя ничего сложного. Есть свидетельства в пользу того, что Ubuntu работает из коробки для пользователя, который впервые ставит Linux.

Однако дальше сразу возникает необходимость ручками внести правки в /etc/sudoers для того, чтобы пользователь имел права на исполнение sudo команд. Входить под учетной записью root в DE окружение не удастся, по умолчанию большинство Desktop Manager-ов отключают эту опцию. Придется вручную снимать эти ограничения в консольном режиме, вот и уловка 22. Остается visudo /etc/sudoers, или в крайнем случае vim /etc/sudoers из под супер пользователя.

Так выгладит моя правка файла.

|18:42:09|admin@srv:[~]> sudo diff /etc/sudoers /etc/sudoers.orig

85c85

< %wheel ALL=(ALL) NOPASSWD: ALL

---

> # %wheel ALL=(ALL) NOPASSWD: ALL


Нужно всего лишь отключить комментирование в соответствующей строке, после чего достаточно включить пользователя в группу wheel. Странно было бы это делать в графике, если можно запустить всего лишь gpasswd -a admin wheel из под пользователя root.

Но раз уж у нас есть права sudo надо уметь ими пользоваться. Самое первое для чего эти права понадобятся для установки и обновления программ. Можно конечно воспользоваться графическим фронтендом программ из репозитория, в конце концов даже Gentoo имеет GUI для своего portage. Однако ограниченность и второсортность этих средств настолько выпирают, что буквально подталкивают пользователя в сторону CLI.

Вы же не собираетесь вместо простого sudo aptitude update / sudo dnf update запускать графический фронтенд и беспомощно кататься вверх-вниз по списку пакетов. Если вы собираетесь оставаться на Linux всерьез и надолго, то необходимо освоить необходимый минимум консольных команд для вашего пакетного менеджера.

Чуть менее привычным делом является настройка шрифтов. Одной установкой шрифтов семейства Liberation, Noto, Dejavu и Droid дело не ограничивается. Нужно еще избавиться от использования древних шрифтов Microsoft из пакета corefonts. Проще всего их не ставить совсем, однако часто они проникают в систему, как зависимость для Wine, или других пакетов. В таком случае придется создать, или редактировать файл ~/.fonts.conf. Вот директива по избавлению от Arial.

<match target=pattern><test name=family qual=any><string>Arial</string></test><edit name=family binding=same mode=assign><string>Noto Sans</string></edit></match>

Хочу подчеркнуть, что проблема не в том, что используются шрифты Microsoft, а в том, что это очень старые версии их. Соответственно, на экране они выглядят не очень, мягко говоря. Только их и разрешено использовать в дистрибутивах Linux. Можно делать это в графическом редакторе, но файлы с точкой в начале имени придется хитрым способом открывать во встроенном файловом менеджере каждый раз, а это неудобно.

Кроме того, возможно придется шаманить с симлинками в /usr/share/fonts, или в /etc/fonts для того, чтобы избавиться от ШГ. Так что лучше сразу все делать в командной строке. На самом деле тут многие сходят с дистанции, стараясь как можно дольше делать все с помощью графических приложений, через какое-то время ломаются обновления, слетают драйвера и все катится в тартарары.

Этого нельзя допустить, поэтому сразу переходим к следующему этапу необходимости освоить консольный текстовый редактор: vim, emacs, или их клоны. Поверьте не стоит привязываться к простеньким nano, или mcedit, в которых даже undo еще не завезли. Освоив эти редакторы вы спокойно можете редактировать конфигурационные файлы в /etc, $HOME и получить надежный контроль над системой.

Кейс 2 настроить сетевое окружение в офисе


Сейчас с NetworkManager настраивать сети стало намного проще, а раньше для настройки беспроводного соединения обязательно нужно было редактировать файл wpa_supplicant.conf. Однако и сегодня функционал NetworkManager во многом пока еще ограничен. Например в нем нельзя подключиться к vpn по протоколу Juniper Pulse с двухфакторной аутентификацией только CLI.

|18:29:57|admin@srv:[~]> sudo openconnect --protocol=pulse \--authgroup ТOTP -u jsmith https://my.company-gateway.com

В офисе вполне может случится так, что заблокированы многие порты на исходящий и входящий трафик по главной сети, но разблокированы во второстепенной. Если подключится ко второстепенной сети, то тогда многие важные ресурсы главной сети становятся недоступны. С другой стороны утыкаться в межсетевой экран, тоже не хочется. Какой выход?

Берем важные подсети главной сети и прописываем их статистическим маршрутом.

sudo ip route add 110.10.0.0/8 via 110.10.10.1;

После чего подключаемся ко второстепенной сети и остальной трафик направляем через нее. Так как метрика главной сети меньше в абсолютном значении, у нее приоритет выше и при двух параллельных default route трафик все же упорно идет через нее. Так как поменять метрику маршрута нельзя, проще удалить ненужный default route.

sudo ip route delete default dev eth0;

Для одного раза достаточно запустить эти две команды, но каждый день так подключаться неудобно, надо это автоматизировать. Для этого нужно создать скрипт в папке /etc/NetworkManager/dispatcher.d/.

|17:43:17|admin@srv:[~]> ls /etc/NetworkManager/dispatcher.d/10-openrc-status no-wait.d pre-down.d pre-up.d|17:43:22|admin@srv:[~]> cd /etc/NetworkManager/dispatcher.d/pre-up.d|17:43:27|admin@srv:[~]> sudo chmod +x 10-office-netw.sh

Скрипты, которые следует выполнить перед активацией сетевого соединения должны находиться в pre-up.d. Соответственно, скрипты на случай отключения сетевого соединения следует класть в pre-down.d. Названия могут быть произвольные, если скриптов несколько, то они будут выполняться в алфавитном порядке.

#!/bin/bashif [ $1 == eth0 ] && [ $2 == up ]; thenip route add 110.10.0.0/8 via 110.10.10.1ip route delete default dev eth0#более высокая метрика, чтобы быть ниже основного gw в ip routeip route add default dev eth0 metric 700fi

Объективности ради надо сказать, что директивы ip route add можно реализовать из интерфейса NetworkManager в свойствах в закладке соединения IPv4 => Routes.

Кейс 3 поднять Wireguard VPN


Буквально каждый день мы получаем новые доказательство в пользу того, что неплохо бы обзавестись собственным VPN решением. Сегодня под запретом торренты, зарубежные букмекерские сайты, завтра решат ограничить социальные сети и онлайн-библиотеки, а затем и новостные ресурсы кому-то не понравятся. Благо технологии тоже не стоят на месте и при соответствующих навыках можно за 15 минут настроить Wireguard VPN и обходить все нелепые ограничения. Самое главное наличие Linux сервера с внешним, т е не российским IP адресом.

Вся настройка происходит исключительно с использование CLI и текстовых конфигурационных файлов. Это не полноценный гайд по настройке, так как весь процесс состоит из следующих этапов.

  1. Установить пакет утилит Wireguard.
    aptitude install wireguard-tools
  2. Установить kernel-headers для более ранних версий ядра.
    aptitude install linux-headers
  3. Открыть наружу связующий UDP порт (в нашем примере 51820) с управляющей консоли сервиса виртуального сервера.
  4. Создать открытый и закрытый ключи для Wireguard на клиенте и сервере.
    umask 077; wg genkey | tee privatekey | wg pubkey > publickey
  5. Создать конфигурационный файл в /etc/wireguard.
  6. Проверить наличие L2 соединения.
    wg show, если есть нечто вроде transfer: 4.80 MiB received, 1833.04 KiB sent, то это хороший признак.
  7. Подключить IP Forwarding с помощью sysctl -w net.ipv4.ip_forward=1 и прописать в /etc/sysctl.conf, если этого еще не было сделано.
  8. Настроить маршрутизацию трафика и NAT masquerade.

На самом деле в терминологии Wireguard участники сетевого соединения именуются не клиент и сервер, а peers равные. Однако на деле, все же один из узлов выполняет функцию именно сервера, поэтому логичнее их так и именовать для данного кейса.

Конфигурационный файл VPN-сервера содержит собственный закрытый ключ и открытый ключ клиента. Обратное также верно, в конфигурационном файле VPN-клиента прописываем собственный закрытый ключ и открытый ключ сервера.

#client config[Interface]PrivateKey = uJPzgCQ6WNlAUp3s5rabE/EVt1qYh3Ym01sx6oJI0V4Address = 192.168.10.2/24[Peer]PublicKey = qdjdqh2pN3DEMDUDRob8K3bp9BZFJbT59fprBrl99zMAllowedIPs = 0.0.0.0/0Endpoint = 172.105.211.120:51820PersistentKeepalive = 20

Любая неточность в каждом из перечисленных пунктов, кроме проверки OSI L2 соединения приведет к сбою в работе VPN туннеля, но при необходимой сноровке все можно сделать быстро и точно.

#server conifg[Interface]Address = 192.168.10.1/24ListenPort = 51820PrivateKey = eEvqkSJVw/7cGUEcJXmeHiNFDLBGOz8GpScshecvNHUSaveConfig = true[Peer]PublicKey = 2H8vRWKCrddLf8vPwwTLMfZcRhOj10UBdc0j8W7yQAk=AllowedIPs = 192.168.10.2/32

В некоторых примерах AllowedIPs клиента бывает выставлен на внутренний туннельный IP адрес сервера непонятно зачем. Тогда только запросы на этот IP адрес и будут разрешены, если же выставить 0.0.0.0/0 то весь трафик пойдет через Wireguard VPN. Также Endpoint клиента обязательно должен указывать на внешний IP адрес сервера.

Остается настроить NAT masquerade для того, чтобы Wireguard сервер мог осуществлять маршрутизацию трафика в NAT среде.

#IPv4[root@wgsrv ~]$ iptables -A FORWARD -i wg0 -j ACCEPT[root@wgsrv ~]$ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE#IPv6[root@wgsrv ~]$ ip6tables -A FORWARD -i wg0 -j ACCEPT[root@wgsrv ~]$ ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE#NAT[root@wgsrv ~]$ iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j MASQUERADE

После чего надо сохранить правила в базе iptables, или netfilter. Также и сервис wg-quick, заведующий VPN туннелем Wireguard, необходимо добавить в автозагрузку.

[root@wgsrv ~]$ systemctl enable wg-quick@wg0[root@wgsrv ~]$ systemctl netfilter-persistent save[root@wgsrv ~]$ systemctl enable netfilter-persistent

Заключение


Надеюсь приведенных примеров достаточно для того, чтобы показать насколько необходимо умение работать с CLI и насколько проще выполнять основные действия по системному администрированию даже для домашней рабочей станции с Linux OS.



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Перевод Сеть в bitly Linux tc для минимизации издержек и забавы ради

02.06.2021 20:09:34 | Автор: admin

Представьте, что вы, например, bitly то есть очень большой сервис сокращения ссылок. И вот, вы хотите скопировать свои 150 ТБ сжатых данных с одного физического кластера на другой, новый. Чтобы сделать это, вы запускаете distcp из набора инструментов hadoop и рады тому, насколько быстро он работает. Но, несколько позже, вы уже совсем не радуетесь жалобам обычных пользователей веб-сайта и API-клиентов случаются ошибки, задерживаются ответы, а данные их дата-центра только запутывают. К старту курса о DevOps мы перевели материал о том, что делать, если вы, как и bitly, оказались в подобной ситуации.


Мы в bitly очень любим работать с данными и устройствами, а если говорить точнее, то с устройствами для передачи данных. Сегодня расскажем историю о том, как мы пытались перекачать через устройства очень и очень много данных.

Важной частью инфраструктуры bitly и основным инструментом команды Data Science и рабочей группы обслуживания операций и инфраструктуры (Ops/Infra) в течение довольно долгого времени был физический кластер hadoop набор утилит, библиотек и фреймворк для разработки и выполнения распределённых программ, работающих на кластерах из сотен и тысяч узлов. Мы уже давно вынашивали идею подключения нового кластера, копирования и объединения данных старого кластера с данными нового.

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

  • bitly работает с огромными объёмами данных: на момент миграции кластер hadoop занимал более 150 ТБ дискового пространства сжатых потоковых данных, то есть данных, полученных в результате работы наших различных приложений. Объёмы таких данных продолжали увеличиваться в результате дополнительной обработки другими приложениями;

  • физически инфраструктура bitly располагается в центре обработки данных нашего партнёра. Она состоит из трёх физических видов шасси (приложения, хранилище и база данных), расположенных в ряд в смежных стойках. На момент написания этой статьи каждое шасси имело три физических гигабитных Ethernet-канала (каждый логически изолирован посредством организации сети VLAN) прямой канал, обратный канал и схему удалённого управления (для внеполосного управления серверными шасси). Каждое соединение после прохождения через ряд патч-панелей и коммутаторов подключается к нашим главным коммутаторам через стеклянное оптоволокно 10 Gb по звездообразной схеме сети;

  • инфраструктура bitly занимает значительные физические ресурсы (сотни физических серверных шасси), однако управлением такой сетевой инфраструктурой и топологией занимается наш партнёр в центре обработки данных. Другими словами, на большинстве уровней физического сетевого стека мы имеем крайне ограниченную видимость сетевых операций, не говоря уже об их контроле.

Вернёмся к нашей истории.

Для быстрого копирования данных с одного кластера на другой мы воспользовались инструментом distcp, поставляемом в комплекте с кластером hadoop. Говоря просто, инструмент distcp выдаёт задание программному фреймворку mapreduce (используемому для параллельных вычислений над очень большими наборами данных в компьютерных кластерах) на перемещение данных из одного кластера hdfs в другой при копировании узлов в режиме "многие ко многим". Инструмент distcp сработал быстро, и это нас порадовало.

Но тут сломался сервис bitly, и это не привело в восторг команду Ops/Infra.

Пользователи веб-сайта и API-клиенты стали жаловаться на ошибки и задержки в получении ответов. Нам удалось выяснить, что причиной ошибок было получение нашим сервисом ошибок от других сервисов, что приводило к превышению времени ожидания при вызове баз данных и даже сбоям при обработке DNS-запросов внутри нашей собственной сети. Мы выяснили, что копирование данных привело к необъяснимому увеличению нагрузки на сетевые устройства, особенно на устройства, передающие трафик через физические стойки. Мы надеялись решить проблему, получив информацию от нашего партнёра из центра обработки данных, но эта информация не только не помогла, но и ещё больше нас запутала: ни одно соединение и ни одна стойка не проявляли никаких признаков насыщения, перегруженности или ошибок.

Теперь нам нужно было решать сразу две противоречивые задачи: с одной стороны, надо было продолжать миграцию hadoop, а с другой устранить неполадки и понять, в чём же кроется наша сетевая проблема.

Мы ограничили количество диспетчеров mapreduce, которые инструмент distcp использовал для копирования данных между кластерами, что позволило искусственно снизить пропускную способность при копировании и кое-как возобновить миграцию. После завершения копирования нам, наконец, удалось установить новый кластер взамен старого.

В новом кластере было больше узлов, значит, по идее, hadoop должен был работать быстрее.

Мы, кажется, осчастливили команду Data Science.

Но, к сожалению, поскольку кластер hadoop стал крупнее и быстрее, во время работы mapreduce на большее количество узлов стало передаваться большее количество данных, и это привело к непредвиденному эффекту:

Сервис bitly опять сломался, на этот раз очень серьёзно. Команда Ops/Infra была от этого не в восторге.

Первым импульсивным действием было вообще отрубить hadoop.

Но такое решение очень не понравилось команде Data Science.

Отключение кластера hadoop самое плохое из возможных решений (по последствиям может сравниться разве что с поломкой bitly), поэтому мы вернули кластер в состояние 1995 года, заставив все сетевые карты перейти на 100 Мбит/с (с 1 Гбит/с) с помощью команды ethtool -s eth1 speed 100 duplex full autoneg on. Теперь можно было спокойно подключить hadoop, но какой же медленной стала его работа!

Команда Data Science по-прежнему не выказывала признаков восторга.

И действительно, работа кластера была настолько "тормозной", что при вводе данных, выполнении запланированных заданий ETL (извлечения, преобразования и загрузки) и выдаче отчётов стали часто возникать сбои, постоянно срабатывали аварийные сигналы, будившие среди ночи членов команды Ops/Infra.

Надо ли говорить, как это не нравилось команде Ops/Infra!

Поскольку мы были лишены возможности удовлетворительно контролировать состояние сети, работа по поиску и устранению неисправностей вместе с партнёром из центра обработки данных предстояла сложная и длительная. Нужно было что-то сделать, чтобы привести hadoop в пригодное для использования состояние, и одновременно сделать так, чтобы сервис bitly перестал выходить из строя.

Сделаем ещё одно небольшое отступление:

Что нам было доступно в bitly?

  • roles.json : список серверов (app01, app02, userdb01, hadoop01 и т. д.), ролей (userdb, app, web, monitoring, hadoop_node и т.д.), а также сведения об отображении серверов на роли (app01,02 -> app, hadoop01,02 -> hadoop_node и т. д.);

  • $datacenter/jsons/* : каталог, содержащий json-файл для каждого логического сервера с атрибутами, описывающими сервер, IP-адресами, именами, информацией конфигурирования и, что наиболее важно в нашей истории, расположением стоек.;

  • Linux : Linux.

Поскольку мы могли легко определить, какие серверы за что отвечают и где они расположены, мы могли воспользоваться мощными функциями Linux. Проблему, в принципе, можно было решить, и мы приступили к работе.

Но команда Ops/Infra не проявляла признаков радости.

Её не устраивал синтаксис системы контроля сетевого трафика (Traffic Control, tc) в Linux, не говоря уже о совершенно неудобочитаемой документации. После напряжённого периода работы (с многочисленными проклятиями и разбиванием о стену клавиатур) мы смогли, наконец, создать не вызывающие отторжения работающие сценарии в tc. Были открыты ветви, написаны скрипты, выполнены развёртывания, проведены эталонные тестирования, и в результате было создано несколько тестовых узлов с таким кодом:

$ tc class show dev eth1class htb 1:100 root prio 0 rate 204800Kbit ceil 204800Kbit burst 1561b    cburst 1561bclass htb 1:10 root prio 0 rate 819200Kbit ceil 819200Kbit burst 1433b     cburst 1433bclass htb 1:20 root prio 0 rate 204800Kbit ceil 204800Kbit burst 1561b     cburst 1561b$ tc filter show dev eth1filter parent 1: protocol ip pref 49128 u32 filter parent 1: protocol ip pref 49128 u32 fh 818: ht divisor 1 filter parent 1: protocol ip pref 49128 u32 fh 818::800 order 2048 key     ht 818 bkt 0 flowid 1:20     match 7f000001/ffffffff at 16filter parent 1: protocol ip pref 49129 u32 filter parent 1: protocol ip pref 49129 u32 fh 817: ht divisor 1 filter parent 1: protocol ip pref 49129 u32 fh 817::800 order 2048 key     ht 817 bkt 0 flowid 1:10     match 7f000002/ffffffff at 16filter parent 1: protocol ip pref 49130 u32 filter parent 1: protocol ip pref 49130 u32 fh 816: ht divisor 1 filter parent 1: protocol ip pref 49130 u32 fh 816::800 order 2048 key     ht 816 bkt 0 flowid 1:20     match 7f000003/ffffffff at 16<snipped>$ tc qdisc showqdisc mq 0: dev eth2 root qdisc mq 0: dev eth0 root qdisc htb 1: dev eth1 root refcnt 9 r2q 10 default 100     direct_packets_stat 24

Говоря простым языком, есть три класса управления траффиком. Каждый класс это логическая группа, на которую может быть подписан фильтр, например:

class htb 1:100 root prio 0 rate 204800Kbit ceil 204800Kbit burst 1561b cburst 1561b

Каждый класс это потолок пропускной способности исходящего трафика, агрегированного по всем подписанным на этот класс фильтрам.

Каждый фильтр это конкретное правило для конкретного IP (к сожалению, каждый IP выводится в шестнадцатеричном формате), поэтому фильтр:

filter parent 1: protocol ip pref 49128 u32 filter parent 1: protocol ip pref 49128 u32 fh 818: ht divisor 1 filter parent 1: protocol ip pref 49128 u32 fh 818::800 order 2048 key     ht 818 bkt 0 flowid 1:20     match 7f000001/ffffffff at 16

можно интерпретировать как "subscribe hadoop14 to the class 1:20", где "7f000001" можно интерпретировать как IP для hadoop14, а "flowid 1:20" класс для подписки. Затем запускаем команду qdisc, формирующую более или менее активную очередь для устройства eth1. Данная очередь по умолчанию помещает любой хост, не определённый в фильтре класса, в класс 1:100:

qdisc htb 1: dev eth1 root refcnt 9 r2q 10 default 100 direct_packets_stat 24

В такой конфигурации любой хост (hadoop или другой), находящийся в одной стойке с конфигурируемым хостом, получает фильтр, назначенный классу "1:10", разрешающий скорость передачи до ~800 Мбит/с для класса в целом. Аналогичным образом для предопределённого списка ролей, считающихся "ролями приоритетных узлов", создаётся фильтр по тому же правилу "1:100". Такие узлы выполняют довольно важные задачи, например запускают сервисы hadoop namenode или jobtracker, а также наши узлы мониторинга. Любой другой хост hadoop, не находящийся в той же стойке, подключается к классу "1:20", ограниченному более консервативным классом ~200 Мбит/с.

Как было сказано выше, любой хост, не определённый в фильтре, попадает в класс по умолчанию для eth1 qdisc, то есть в класс "1:100". Как это выглядит на практике? Вот хост, подпадающий под действие правила "1:100":

[root@hadoop27 ~]# iperf -t 30 -c NONHADOOPHOST------------------------------------------------------------Client connecting to NONHADOOPHOST, TCP port 5001TCP window size: 23.2 KByte (default)------------------------------------------------------------[  3] local hadoop27 port 35897 connected with NONHADOOPHOST port 5001[ ID] Interval       Transfer     Bandwidth[  3]  0.0-30.1 sec   735 MBytes   205 Mbits/sec

Теперь при подключении к другому хосту, расположенному в той же стойке или подпадающему под правило "1:10":

[root@hadoop27 ~]# iperf -t 30 -c CABINETPEER------------------------------------------------------------Client connecting to CABINETPEER, TCP port 5001TCP window size: 23.2 KByte (default)------------------------------------------------------------[  3] local hadoop27 port 39016 connected with CABINETPEER port 5001[ ID] Interval       Transfer     Bandwidth[  3]  0.0-30.0 sec  2.86 GBytes   820 Mbits/sec

Что произойдёт при подключении к двум серверам, подпадающим под правило "1:10"?

[root@hadoop27 ~]# iperf -t 30 -c CABINETPEER1------------------------------------------------------------Client connecting to CABINETPEER1, TCP port 5001TCP window size: 23.2 KByte (default)------------------------------------------------------------[  3] local hadoop27 port 39648 connected with CABINETPEER1 port 5001[ ID] Interval       Transfer     Bandwidth[  3]  0.0-30.0 sec  1.47 GBytes   421 Mbits/sec[root@hadoop27 ~]# iperf -t 30 -c CABINETPEER2------------------------------------------------------------Client connecting to 10.241.28.160, TCP port 5001TCP window size: 23.2 KByte (default)------------------------------------------------------------[  3] local hadoop27 port 38218 connected with CABINETPEER2 port 5001[ ID] Interval       Transfer     Bandwidth[  3]  0.0-30.0 sec  1.43 GBytes   408 Mbits/sec

Трафик уменьшится вдвое? Похоже на правду. Даже лучше стало относительно проще отслеживать тренды данных, анализируя статистические данные, выводимые на наши сервисы трендов:

$ /sbin/tc -s class show dev eth1 classid 1:100class htb 1:100 root prio 0 rate 204800Kbit ceil 204800Kbit     burst 1561b cburst 1561b Sent 5876292240 bytes 41184081 pkt (dropped 0, overlimits 0 requeues 0) rate 3456bit 2pps backlog 0b 0p requeues 0 lended: 40130273 borrowed: 0 giants: 0tokens: 906 ctokens: 906

После тестирования мы проверили хосты hadoop, подняв их скорости до первоначальных 1Gb после применения ролей traffic control. После всех описанных действий кластер hadoop вновь обрёл достаточно высокую производительность.

Мы осчастливили команду Data Science.

Команда Ops/Infra смогла приступить к устранению неполадок и поиску решений, при этом спокойно спать по ночам, зная, что сервис bitly будет вести себя нормально.

Мы осчастливили и команду Ops/Infra.

Выводы:

  • Попав в трудное положение, помните: ваш набор инструментов для управления средой так же важен, как и сама среда. Поскольку у нас уже имелся набор инструментов для комплексного контроля среды, мы сумели выбраться из ямы почти так же быстро, как попали в неё.

  • Не загоняйте себя в трудные ситуации: изучите все нюансы среды, в которой будете работать. В данном случае нам следовало лучше понять и оценить масштабы миграции hadoop и её возможные последствия.

  • Linux TC это дорогой инструмент, но и отдача от него очень большая. Этот инструмент почти наверняка создавали бородатые люди с самыми мохнатыми свитерами на свете, и для его применения требуются время и терпение. В любом случае это невероятно мощный инструмент он помог нам исправить наши собственные ошибки.

  • Linux: Linux

И последнее

Эта история хорошая иллюстрация "закона Мерфи для девопсов":

Закон Мёрфи для девопсов: "Если что-то может пойти не так, значит, что-то уже идёт не так, просто Nagios ещё не предупредил".

Временные решения, подобные тем, о которых идёт речь в этой статье, позволили нам выиграть время для поиска и устранения неисправностей в сети. Мы разогнали кластер hadoop, перенесли его в собственную выделенную сеть, обошли проблему некачественного сетевого оборудования, укрепив тем самым основную сеть, и сделали много чего ещё. Оставайтесь с нами.

Эта статья подчёркивает, что правильная, эффективная организация инфраструктуры не менее важна, чем данные и программное обеспечение. По мере развития информационных технологий, увеличения нагрузок, специалисты DevOps, возможно, станут называться иначе, а область станет ещё более самостоятельной. Если она вам интересна, вы можете обратить внимание на наш курс о DevOps, после которого будете иметь полное представление об этой профессии и сможете работать в DevOps.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Баги ради науки как Университет Миннесоты внедрял баги в код Linux

16.06.2021 10:13:29 | Автор: admin


Грег Кроа-Хартман, ответственный за сопровождение стабильных релизов ядра, в начале апреля запретил Университету Миннесоты (УМ) вносить изменения в код Linux. Университет Миннесоты по-видимому, всё это время сознательно вносил вредоносные изменения в код проекта. Развязка наступила после патча к механизму авторизации NFSv4, который не прошел проверку двух профильных разработчиков. Так выглядел тот самый патч.

diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.cindex 5f42aa5fc612..eb52eebb3923 100644--- a/net/sunrpc/auth_gss/auth_gss.c+++ b/net/sunrpc/auth_gss/auth_gss.c@@ -848,7 +848,8 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)warn_gssd();gss_release_msg(gss_msg);}-gss_release_msg(gss_msg);+if (gss_msg)+gss_release_msg(gss_msg);}static void gss_pipe_dentry_destroy(struct dentry *dir,--2.25.1

Ирония однако заключается в том, что спорный проект, который привел к потере доверия к Университету Миннесоты, изначально был направлен на повышение безопасности Linux. В университете с августа 2020 г ассистент профессора Kangije Lu и аспирант Qjushi Wu работали над статьёй под названием On the Feasibility of Stealthily Introducing Vulnerabilities in Open-Source Software via Hypocrite Commits. Под термином Open-Source Software имеется в виду ядро Linux.

Авторам Hypocrite Commits удалось внести в код ядра изменения, содержащие в скрытом виде уязвимость вида обращение после освобождения памяти. Статья была принята на 42-м симпозиуме IEEE по безопасности и конфиденциальности, но уже 26-го апреля Qjushi Wu и Kangije Lu отозвали своё участие в мероприятии. В письме они выражают своё сожаление по поводу случившегося и просят прощения комитету за доставленные неудобства.

We now understand that it was inappropriate and hurtful to the community to make it a subject of our research, and to waste its effort reviewing these patches without its knowledge or permission.

Исследование было поддержано со стороны National Science Foundation и содержало гарантии того, что ни один баг не попадет в исходный код Linux. Тем не менее, именно это и произошло в итоге. Однако миннесотовцы не остановились на достигнутом и решили пойти дальше, стремясь в этот раз протащить новую серию уязвимых патчей под видом нового статического анализатора кода. Развязка наступила 6-го апреля, когда Грег Кроа-Хартман назвал вещи своими именами.

Please stop submitting known-invalid patches. Your professor is playing around with the review process in order to achieve a paper in some strange and bizarre way.

This is not ok, it is wasting our time, and we will have to report this, AGAIN, to your university...

В ответ на это сообщение один из участников переписки указал на презентацию статьи исследователей из УМ и заметил, что три патча из четырех за раз создали новые уязвимости в коде ядра.

Излишне напоминать о том, что у разработчиков ядра и так достаточно проблем со спонтанными ошибками, следовательно поэтому патчи с намеренными ошибками явно нежелательны. Кроа-Хартман сказал, что все патчи, поступающие от @umn.edu отныне подлежат отмене, так как то, что они делают, является преднамеренным вредоносным поведением, неприемлемо и совершенно неэтично.

Надо отдать должное сдержанности Грега и Линуса Торвальдса. Лидер проекта не стал доставать боевой топор, высказавшись в том духе, что технический ущерб невелик, но имеет значение подрыв доверия со стороны товарищей. Можно было высказаться намного резче, особенно после такого пассажа.

On Wed, Apr 21, 2021 at 02:56:27AM -0500, Aditya Pakki wrote:

Greg, I respectfully ask you to cease and desist from making wild accusations that are bordering on slander.

These patches were sent as part of a new static analyzer that I wrote and it's sensitivity is obviously not great. I sent patches on the hopes to get feedback. We are not experts in the linux kernel and repeatedly making these statements is disgusting to hear.

Адитья Пакки энергично отрицает злой умысел и требует от Грега прекратить эту ужасные обвинения. Последний, однако, не обращая внимания на весь пафос экспериментатора из УМ, собрал патч для отката тех 190 изменений, где это можно сделать без усилий. Оставшиеся 68 патчей требуют более тонкой работы для отката изменений, так как часть из них уже была отменена, или для них уже были исправления.

В тех же исключительно редких случаях, когда патчи были действительными исправлениями, их оставят как есть. Всё же, патчи от Университета Миннесота в своем подавляющем большинстве были неисправны. К сожалению некоторые из них были приняты и в стабильную ветку ядра Linux, они также подлежат удалению. На это уйдёт некоторое время, которое можно было потратить с большей пользой.

Возникает вопрос: как относиться к этому новому явлению, можно ли квалифицировать подобное, как эксперименты над людьми? Ведь одного факта проведения исследования от имени респектабельного университета мало для того, чтобы оправдать явную эксплуатацию труда и времени сообщества разработчиков, действующих на некоммерческой основе. Как минимум, это неэтичное исследование по мнению участников Linux проекта видевших патчи УМ.

Our community does not appreciate being experimented on, and being tested by submitting known patches that are either do nothing on purpose, or introduce bugs on purpose. If you wish to do work like this, I suggest you find a different community to run your experiments on, you are not welcome here.

Сложно предположить, что Адитье Паккки удастся найти другое сообщество, где ему беспрепятственно позволят в научных целях присылать испорченные патчи. Можно конечно схитрить и присылать патчи с личного адреса электронной почты, но тогда это будет попыткой внедрить бэкдор, так как фиговый листок академического исследования будет утерян. А это уже статья УК и одними порицаниями дело не закончится.

Технический Консультационный Совет Linux Foundation направил сообщение в электронной почте факультету Computer Science с полным списком патчей Университета Миннесоты, начиная с 2018 г., связанных с исследованиями по безопасности в ядре Linux. Авторы сообщения стремились исчерпать конфликтную ситуацию и восстановить доверие между обеими сторонами.

Университет Миннесоты ответил 9 мая, признав предумышленно неисправными лишь четыре патча. Принося извинения за случившееся, представители УМ берут на себя обязательство соблюдать не только высокие академические, но и этические стандарты в своих исследованиях.



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Ограничение попыток входа в ssh с помощью fail2ban (средство от ботов подбирающих пароли через ssh)

18.05.2021 14:13:42 | Автор: admin
image

SSH довольно безопасен, особенно если вы примете разумные меры предосторожности, такие как требование аутентификации на основе пары ключей. Тем не менее, в дикой природе по-прежнему существует множество ботов, которые пытаются найти уязвимые хосты, пытаясь войти в систему с распространенными скомпрометированными именами пользователей и паролями, такими как root / root или admin / admin. Хотя маловероятно, что они добьются успеха, они все равно будут использовать вашу пропускную способность и генерировать огромное количество журналов.
Один из способов минимизировать количество попыток входа в систему методом перебора изменить порт по умолчанию, который прослушивает SSH. Однако это не считается хорошей практикой во-первых, нужно помнить, что каждый раз, когда они подключаются к серверу, следует устанавливать правильный порт, отличный от порта по умолчанию. Более того, это может создать еще одну уязвимость безопасности, если выбранный порт больше 1024. Обычно только root может связываться с номерами портов ниже 1024. Однако, если для SSH используется больший номер порта, при определенных обстоятельствах пользователи без корневого доступа может заменить демон SSH другой, возможно, вредоносной службой.
Лучший способ решить возникшую проблему использовать инструмент, который заблокирует злоумышленнику доступ к SSH-серверу. Одним из таких широко используемых инструментов является fail2ban ( www.fail2ban.org ). Анализируя журналы, fail2ban обнаруживает повторяющиеся неудачные попытки аутентификации и автоматически устанавливает правила брандмауэра для отбрасывания трафика, исходящего с IP-адреса злоумышленника.

Установка fail2ban на Ubuntu

Ручная установка
Установить fail2ban в Ubuntu (и других дистрибутивах на основе Debian) очень просто:

$ sudo apt install fail2ban

Проверяем как это работает

Вы можете проверить, запущена ли служба, с помощью следующей команды:

$ sudo systemctl status fail2ban

Вывод должен быть похож на следующий статус службы должен быть активным:

fail2ban.service Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2021-05-18 12:36:36 ***; ** min ago
Docs: man:fail2ban(1)
Process: 723*** ExecStartPre=/bin/mkdir -p /run/fail2ban (code=exited, status=0/SUCCESS)
Main PID: 723*** (f2b/server)
Tasks: 5 (limit: 38293)
Memory: 18.0M
CGroup: /system.slice/fail2ban.service
723488 /usr/bin/python3 /usr/bin/fail2ban-server -xf start


Посмотрим, как fail2ban изменил правила iptables:

$ sudo iptables -L -n -v

Вы также должны увидеть, что в конфигурации iptables есть новая цепочка f2b-sshd, на которую ссылается правило цепочки INPUT:

Chain INPUT (policy ACCEPT 73411 packets, 6622K bytes)
pkts bytes target prot opt in out source destination
1019 65297 f2b-sshd tcp * * 0.0.0.0/0 0.0.0.0/0 multiport dports 22

Chain f2b-sshd (1 references)
pkts bytes target prot opt in out source destination
8 480 REJECT all * * 94.191.93.46 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all * * 77.50.75.162 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 51.254.143.190 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 46.101.97.5 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 43.129.28.88 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 41.221.168.167 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 35.247.219.12 0.0.0.0/0 reject-with icmp-port-unreachable
12 720 REJECT all * * 220.180.119.192 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 218.75.121.75 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all * * 213.87.101.176 0.0.0.0/0 reject-with icmp-port-unreachable
0 0 REJECT all * * 192.139.15.34 0.0.0.0/0 reject-with icmp-port-unreachable
21 1260 REJECT all * * 187.104.145.210 0.0.0.0/0 reject-with icmp-port-unreachable
8 480 REJECT all * * 177.191.189.13 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 159.89.82.134 0.0.0.0/0 reject-with icmp-port-unreachable
10 600 REJECT all * * 159.75.140.97 0.0.0.0/0 reject-with icmp-port-unreachable
8 480 REJECT all * * 157.92.13.105 0.0.0.0/0 reject-with icmp-port-unreachable
11 660 REJECT all * * 117.80.225.245 0.0.0.0/0 reject-with icmp-port-unreachable
9 540 REJECT all * * 106.53.121.179 0.0.0.0/0 reject-with icmp-port-unreachable
865 56057 RETURN all * * 0.0.0.0/0 0.0.0.0/0


Пакет fail2ban содержит инструмент под названием fail2ban-client. Он позволяет вам проверять статус службы и взаимодействовать с ней (например, позволяет вручную блокировать и разблокировать IP-адреса, включать и отключать тюрьмы и т. Д.)

Посмотрим, какие jails активны:

$ sudo fail2ban-client status

Status
|- Number of jail: 1
`- Jail list: sshd


Есть только один jail sshd которsq отвечает за мониторинг журналов SSH-сервера на предмет неудачного входа в систему и настройку правил брандмауэра для блокировки дальнейших попыток.
Теперь мы можем проверить статистику по sshd jail:

$ sudo fail2ban-client status sshd

Status for the jail: sshd
|- Filter
| |- Currently failed: 8
| |- Total failed: 26
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 22
|- Total banned: 25
`- Banned IP list: 106.53.121.179 117.80.225.245 157.92.13.105 159.75.140.97 159.89.82.134 177.191.189.13 187.104.145.210 213.87.101.176 218.75.121.75 220.180.119.192 35.247.219.12 41.221.168.167 43.129.28.88 46.101.97.5 51.254.143.190 77.50.75.162 94.191.93.46 1.55.165.141 120.53.245.68 104.131.178.145 106.56.102.83 152.32.146.21


Настройка fail2ban

В большинстве случаев конфигурации по умолчанию должно быть достаточно. Тем не менее, полезно понимать, что это за значения по умолчанию и как их можно изменить в соответствии с вашими потребностями.
В стандартной конфигурации fail2ban защитит SSH-сервер и заблокирует злоумышленника на 10 минут после 5 неудачных попыток входа в систему в течение 10 минут. Файл конфигурации по умолчанию можно найти в /etc/fail2ban/jail.conf. Файл хорошо документирован и в основном не требует пояснений. Имейте в виду, что вам не следует вносить какие-либо изменения в этот файл, так как он может быть перезаписан во время обновления fail2ban.

После изменения конфигурации не забудьте перезапустить службу:

$ sudo systemctl restart fail2ban

Должен заметить, что в данном случае я изменил параметры конфигурации, понимая стратегию атаки и получил суточный список атакующих хостов и заблокировал его.
Но об этом позже расскажу в отдельной заметке, если эта будет кому то полезна и интересна.
Подробнее..

Осторожно, snap

25.05.2021 22:23:12 | Автор: admin
Из к/ф "Иван Васильевич меняет профессию"Из к/ф "Иван Васильевич меняет профессию"

Использую KeePassXC уже года три. Жил, работал, радовался и проблем не знал с паролями. Решил, что лучше так, чем забуду или каждый раз вспоминать: какой пароль откуда, и где какие цифровые личности.

Недавно решил попробовать функционал, который подсказали коллеги. Это хранение SSH-ключей во вложениях записей и автоматическая загрузка их в SSH-agent операционной системы.

Создал связку приватный-публичный ключ. Добавил во вложение к записи. А дальше в разделе SSH-agent поставил галки и выбрал ключ во вложении. Пара галок в настройках - ошибиться негде.

Меню Сервис-Параметры-SSH-агентМеню Сервис-Параметры-SSH-агент

Стал проверять загрузку ключа в SSH-agent при открытии базы. Фокус не получился - встретило сухое сообщение: Сбой подключения агента.

Ошибка без описания причин сбояОшибка без описания причин сбоя

Попробовал переопределить SSH_AUTH_SOCK - не помогло. Стал гуглить подобную проблему и решение. Не буду приводить этот мусор здесь. Чаще проблемы попадались старые или с другим функционалом SSH.

Поиски и попытки выяснить, что я делаю не так заняли три дня. В очередной момент, насторожила мысль, что у коллеги на соседней Kubuntu автоматическая загрузка в SSH-агент работает (он с недоумением смотрел над моими мучениями). Закралась мысль: а что, если дело в источнике установки самого KeePassXC?

Спросил у коллеги - и да, он ставил через консоль и подключение репозитория. У меня же ключница из магазина приложений (пользователь, что поделать). Решил перепроверить, зашел в установленные приложения своей Xubuntu и убедился - источник Snap Store.

Удалил приложение и поставил заново, только уже через консоль:

sudo add-apt-repository ppa:phoerious/keepassxcsudo apt-get updatesudo apt install keepassxc

После этого останется открыть файл базы и проверить настройки SSH-агента. Затем, зайти в запись и нажать Добавить в агент. Всё.

Как итог: дьявол в мелочах. Пользователи Linux - будьте внимательны и осторожны.

А какие у вас были приключения со snap-ами? Поделитесь в комментариях =)

Подробнее..
Категории: Настройка linux , Snap , Keepassxc , Ssh-agent

Уютный VPS-сервер для маленьких проектов за минимум денег как настроить

30.05.2021 16:05:01 | Автор: admin

Мне всегда хотелось иметь в сети личное пространство. Место, где все было бы устроено как мне удобно. Наилучшим решением мне виделся недорогой VPS, который я мог бы обустраивать в соответствии со своими потребностями. Долго я не мог подступиться к решению этой задачи, но как-то незаметно подобрался набор инструментов, который позволил организовать именно такую программную среду, как мне хотелось.

Если вам тоже хочется создать в сети свое личное пространство, но вы не знаете, с чего начать, или вам просто интересны такие замечательные программные продукты как Docker, Portainer, Traefik добро пожаловать под кат.

Введение


Признаться честно, я не очень люблю командную строку. То есть, мне нравится сама идея легкого минималистичного универсального интерфейса, практически не требующего ресурсов, но черное окно с мигающим курсором вызывает у меня фрустрацию и чувство беспомощности. И я даже знаю откуда это у меня.

Когда я учился в институте (это то время, когда у меня в городе только появился интернет по карточкам и первое интернет-кафе), мой друг, который к тому времени умудрился устроится лаборантом на кафедру информатики, активно пытался подсадить меня на Линукс. И вот я, считающий себя продвинутым компьютерщиком, имеющий опыт, наверное, сотни установок винды, прихожу домой после института и уверенным движением вставляю в дисковод диск с дистрибутивом линукс И что? И ничего.

Я уже за давностью лет не помню всех подробностей, наверно мне даже удалось его установить, но дальше был провал я просто не понимал, что делать. Привычный способ, который работал в винде рыться в панели управления рандомно включая разные настройки пока все не заработает (или сломается окончательно, но тогда можно было переустановить винду и начать все снова) тут не действовал. Все, что у меня было, это консоль, которая раз за разом отвергала мои попытки что-то настроить. Помню, что у меня была даже книга по линуксу, которая тоже никак не помогала команды, которые там были приведены, в большинстве своем почему-то не работали. В общем, промучившись несколько дней я тогда решил для себя, что линукс, это какая-то ерунда, и с облегчением вернулся к привычной винде.

Были и еще попытки освоить линукс, жизнь раз за разом сводила меня с его фанатами, которые пытались меня перетянуть в свой лагерь, я даже как-то попытался поставить генту (неудачно естественно, хотя у меня была распечатка с пошаговым руководством как это сделать), но проклятье консоли витало надо мной и с каждой неудачей то самое чувство беспомощности все усиливалось. После графического интерфейса винды, где все настройки перед тобой как на ладони, сталкиваясь с консолью я чувствовал себя ослепшим, пытающимся найти дорогу на ощупь в незнакомом месте. Так что я очень долго оставался приверженцем технологического стека Microsoft, программировал на .net, используя VisualStudio (она шикарна, и никакой консоли). Прошу не судить строго, возможно, если бы в то время у меня была возможность поставить линукс на виртуалку, чтобы спокойно с ней разбираться и приличный интернет под рукой, эта история приняла бы совсем другой оборот.

Сейчас все поменялось. С приходом в нашу жизнь веба в его современном виде я пересел на Node.js и волей-неволей стал проникаться идеологией консоли и Linux. Для хостинга своих разработок мне понадобился VPS, и тут оказалось, что VPS с виндой стоят гораздо дороже, так что моя жаба перевесила мои фрустрации относительно консоли, да и наличие скоростного интернета позволило мгновенно находить ответы на возникающие вопросы.

Я научился пользоваться ssh, осознал силу git и стал активно пользоваться dockerом, но мне все также нравится использовать для своих задач графический интерфейс, и в этой статье я хочу вас познакомить с замечательным набором инструментов, которые позволяют решать мне мои повседневные задачи, не обращаясь лишний раз к консоли.

Подготовка


Я создал VPS минимальной конфигурации (vCPU: 1 core, RAM: 1 ГБ, NVMe: 20 ГБ) на macloud. Для установки на VPS я выбрал дистрибутив Debian 10. При установке я сразу добавил через панель управления SSH-ключ, чтобы было удобно заходить в консоль с помощью SSH-клиента. Для дальнейших экспериментов понадобится следующее:

  1. Обновить систему
  2. Установить docker и docker-compose
  3. Включить файл подкачки.

Обновить операционную систему можно следующими командами:

# apt updateОбновит список доступных пакетов# apt upgradeОбновит систему, установив свежие версии ПО.

Для установки docker следуем официальной инструкции https://docs.docker.com/engine/install/debian/

Устанавливаем необходимые зависимости:

# apt-get install apt-transport-https ca-certificates curl gnupg lsb-release

Добавляем официальный GPG ключ Dockerа:

# curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Добавляем репозиторий:

# echo deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable | tee /etc/apt/sources.list.d/docker.list > /dev/null

Теперь осталось обновить список пакетов:

# apt-get update

и установить докер:
# apt-get install docker-ce docker-ce-cli containerd.io

Проверить, что все корректно установилось можно, запустив команду:

# docker run hello-world

Если появилось приветствие, значит все прошло хорошо. У меня на момент написания данной статьи установилась следующая версия:

# docker -vDocker version 20.10.6, build 370c289

Теперь надо установить docker-compose. Для этого воспользуемся официальной инструкцией:

https://docs.docker.com/compose/install/

Для начала нам нужно получить ссылку на свежий релиз docker-compose. Список релизов можно посмотреть тут: https://github.com/docker/compose/releases

На момент написания статьи наиболее свежей была версия 1.29.2. Нам нужна версия для linux, поэтому выбираем файл с названием docker-compose-Linux-x86_64. Скопируем ссылку на него. Теперь надо в консоли ввести следующую команду (вставьте в нужное место ссылку, полученную выше):

# curl -L "<ссылка на релиз>" -o /usr/local/bin/docker-compose

В моем случае получилось:

# curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose

Запустим команду и загрузится бинарный файл docker-compose.

Последний шаг надо установить для него разрешение на исполнение. Это можно сделать следующей командой:

# chmod +x /usr/local/bin/docker-compose

Если всё прошло нормально, то на этом процесс установки окончен, теперь можно проверить версию docker-compose. У меня получилось вот так:

# docker-compose -vdocker-compose version 1.29.2, build 5becea4c

Последний шаг это включение файла подкачки, учитывая малый объем памяти он точно не будет лишним:

Создадим файл размером 4 Гб

# fallocate -l 4G /swapfile

Назначим ему необходимые права:

# chmod 600 /swapfile

Инициализируем его как файл подкачки:

# mkswap /swapfile

Наконец активируем:

# swapon /swapfile

Проверить, появился ли файл подкачки в системе можно командой free. У меня все получилось:

# freetotal    used    free   shared buff/cache  availableMem:    1010900   150500   143788    2892   716612   714916Swap:    4194300      0   4194300

Осталось последнее действие: для того, чтобы файл подкачки оставался активным после перезагрузки надо добавить в файл /etc/fstab следующую строку:

/swapfile swap swap defaults 0 0


Для подобных задач мне нравится использовать файловый менеджер Midnight Commander. Это консольный файловый менеджер с классическим интерфейсом. Если вы сталкивались с Norton Commander/FAR/TotalCommander вы легко поймете, как им пользоваться. Установить его можно следующей командой:

# apt-get install mc


А запустить с помощью команды

# mc

Увидим до боли знакомую картину:


Теперь найдем нужный файл и отредактируем его:


На этом предварительная настройка закончена, можно двигаться дальше.

Portainer


Первый инструмент, с которым мне бы хотелось вас познакомить это Portainer. Portainer это инструмент для управления контейнерами в Docker, Swarm, Kubernetes и Azure ACI. Как написано в документации Portainerа:

Отрицая необходимость для пользователей использовать интерфейс командной строки, писать YAML или понимать манифесты, Portainer делает развертывание приложений и устранение неполадок настолько простым, что любой может это сделать.

Для моих целей Portainer подходит просто идеально. Давайте познакомимся с ним поближе. В соответствии с документацией https://documentation.portainer.io/v2.0/deploy/ceinstalldocker/ проще всего это сделать следующим образом:

Создадим том для хранения данных:

# docker volume create portainer_data

Теперь можно запустить Portainer следующей командой:

# docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

После запуска Portainer будет доступен по адресу http://<ip серевера>:9000/

При первом запуске Portainer попросит вас указать пароль администратора, после чего спросит, каким образом подключится к окружению:


Я выбрал вариант подключится к локальному Dockerу, после чего появился домашний экран Portainerа:


Как видно, доступен локальный эндпоинт, позже можно при необходимости добавить подключения к другим узлам.

Зайдем в него, и увидим удобный дэшбоард со сводной информацией:


Здесь мы можем управлять всеми аспектами работы Dockerа. Давайте посмотрим, что у нас в контейнерах:


Здесь пока один контейнер это сам Portainer. Интерфейс очень удобно позволяет просматривать и управлять контейнерами, а также есть возможность добавлять новые. Можете пройтись по остальным вкладкам и убедится, насколько удобный и продуманный интерфейс имеет Portainer.

Наибольший интерес представляет вкладка Stacks. Стэк это совокупность взаимосвязанных контейнеров, которые запускаются и работают совместно. По сути, это тоже самое, что композиция Docker Compose, конфигурацию которой мы описываем в файле docker-compose.yml и потом запускаем командой docker-compose up.

Portainer дает возможность добавить стек в систему различными путями, начиная с редактирования с помощью встроенного редактора и заканчивая загрузкой с github.


Для определения стека используется формат docker-compose, именно он работает здесь под капотом. Мне при помощи этой функции удобнее всего построить из контейнеров необходимую инфраструктуру.

На этом предлагаю пока отвлечься от изучения возможностей portainerа. Думаю, вы уже увидели, что это серьезный инструмент, благодаря которому можно практически полностью отказаться от использования CLI dockerа. Давайте теперь кратко рассмотрим, что же я хочу получить в результате на этом VPS.

У меня периодически возникает потребность в разработке быстрых проектов. Я так называю проекты, которые не требуют глубокой проработки. Это могут быть эксперименты, небольшие сайты, боты, сборщики информации и т.п. Все то, что не требует выстраивания цепочки CI/CD. Мне было бы крайне удобно разрабатывать и отлаживать такие приложения прямо там, где они будут работать. Также у меня довольно часто бывает ситуация, когда я нахожусь вдали от своего рабочего места с настроенной средой разработки, и, если что-то в этих моих проектах начинает работать не так как надо, хотелось бы иметь возможность быстро разобраться в ситуации и пофиксить проблему, или просто поработать над проектами пока есть свободная минутка с любого компьютера, даже если на нем нет ничего, кроме браузера и доступа в интернет.

Для удобства доступа у меня есть купленное доменное имя, и я хочу настроить для различных инструментов домены третьего уровня, так, чтобы например portainer был доступен по адресу portainer.example.com. Также я хочу, чтобы всё работало через https, и, чтобы не заморачиваться с покупкой сертификатов SSL, воспользоваться Lets Encrypt. Еще одно требование, чтобы всё, к чему не предполагается публичный доступ было закрыто аутентификацией.

Для того, чтобы всё это реализовать, давайте познакомимся со следующим замечательным инструментом. Это

Traefik


Traefik это реверсивный прокси-сервер, который заточен для работы с контейнерами. Для меня важны следующие его возможности:

  • обновление конфигурации на лету
  • поддержка dockerа в качестве провайдера конфигурации
  • работа с Lets encrypt из коробки

Для того, чтобы запустить traefik совместно с portainerом воспользуемся примером docker-compose.yml, приведенном в документации portainer: https://documentation.portainer.io/v2.0/ad/traefik/rp-traefik/

version: 3.9services:traefik:container_name: traefikimage: traefik:latestcontainer_name: traefikcommand:- --entrypoints.web.address=:80- --entrypoints.websecure.address=:443- --providers.docker=true- --providers.docker.exposedbydefault=false- --log.level=ERROR- --certificatesresolvers.leresolver.acme.httpchallenge=true- --certificatesresolvers.leresolver.acme.email=user@mymail.com- --certificatesresolvers.leresolver.acme.storage=./acme.json- --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web- --entrypoints.web.http.redirections.entryPoint.to=websecure- --entrypoints.web.http.redirections.entryPoint.scheme=https- --metrics.prometheus=trueports:- 80:80- 443:443volumes:- /var/run/docker.sock:/var/run/docker.sock:ro- ./acme.json:/acme.jsonnetworks:- intranetlabels:- traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)- traefik.http.routers.http-catchall.entrypoints=web- traefik.http.routers.http-catchall.middlewares=redirect-to-https- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=httpsportainer:image: portainer/portainer-ce:2.5.0-alpinecontainer_name: portainercommand: -H unix:///var/run/docker.sockrestart: alwaysvolumes:- /var/run/docker.sock:/var/run/docker.sock- portainer_data:/datanetworks:- intranetlabels:- traefik.enable=true- traefik.http.routers.frontend.rule=Host(`portainer.example.com`)- traefik.http.routers.frontend.entrypoints=websecure- traefik.http.services.frontend.loadbalancer.server.port=9000- traefik.http.routers.frontend.service=frontend- traefik.http.routers.frontend.tls.certresolver=leresolvervolumes:portainer_data:networks:intranet:name: intranet

Чтобы процесс получения сертификата Lets encrypt прошел успешно, перед запуском надо убедится, что записи DNS корректно настроены. Я для этих целей использую cloudflare, и в нем конфигурация должна выглядеть примерно так:


В графе Content должен находится адрес нашего VPS.

Если теперь скопировать получившийся docker-compose.yml на сервер выполнить следующую команду:

# docker-compose up -d

То после запуска по адресу portainer.example.com будет такая картина:


Причем он уже будет защищен сертификатом Lets Encrypt:


У traefik есть очень удобный дашборд, который помогает понять, правильно ли применились настройки конфигурации. Для того, чтобы его активировать, надо добавить в docker-compose.yml следующие строки:

services:traefik:...command:...- --api.dashboard=truelabels:...- traefik.enable=true- traefik.http.routers.traefik.entrypoints=websecure- traefik.http.routers.traefik.rule=Host(`traefik.example.com`)- traefik.http.routers.traefik.tls=true- traefik.http.routers.traefik.service=api@internal- traefik.http.routers.traefik.tls.certresolver=leresolver- traefik.http.services.traefik.loadbalancer.server.port=8080

Также надо в настройках DNS добавить домен третьего уровня traefik.example.com. Это можно сделать по аналогии с тем, как ранее был добавлен домен для portainer. После применения настроек по адресу traefik.example.com увидим дашборд:


Как видите, Traefik оказался замечательным инструментом. Он работает в тесной связке с dockerом, а его возможности динамической конфигурации позволяют полностью отказаться от использования конфигурационных файлов для настроек роутинга. Вместо этого мы просто прописываем все необходимые параметры в качестве labels для настраиваемого сервиса, так что все настройки оказываются в одном месте.

Теперь предлагаю немного отвлечься от настройки базовых сервисов и добавить в систему среду разработки. Это будет

Visual Studio Code Server


Для меня всегда среда разработки или IDE, была чем-то таким серьезным. Мощный программный пакет, который устанавливается на машине разработчика, занимает много гигабайт на диске и в оперативной памяти. Пример такой IDE, которой я пользовался много лет, и до сих пор считаю, что это лучший выбор, если вы работаете со стеком технологий Microsoft, это Microsoft VisualStudio. Когда я начал изучать Node.js, я открыл для себя VSCode, и, несмотря на родственные названия, это совсем другая IDE, с совершенно иной концепцией и возможностями. Тот факт, что для отображения своего интерфейса VSCode использует движок Chrome, позволяет разнести интерфейс и сам VSCode. Благодаря такой архитектуре возник Visual Studio Code Server, который может работать на VPS, при этом интерфейс VSCode будет доступен через браузер. И нет, это не очередной онлайн редактор кода, это полноценная IDE VSCode, которая обладает всеми ее замечательными возможностями.

Чтобы добавить VSCode Server на свой VPS я в Portainerе создам новый stack, назову его code-server и добавлю туда следующую конфигурацию:

version: 3.9volumes:codeserverdata:codeappdir:networks:intranet:external: trueservices:code-server:image: ghcr.io/linuxserver/code-servercontainer_name: code-serverenvironment:- PUID=1000- PGID=1000- TZ=Europe/London#    PASSWORD=password #optional- SUDO_PASSWORD=password #optional- PROXY_DOMAIN=code.example.comvolumes:- codeserverdata:/config- codeappdir:/appextra_hosts:host.docker.internal: host-gatewayrestart: alwaysnetworks:- intranetlabels:- traefik.enable=true- traefik.http.routers.code.rule=Host(`code.example.com`)- traefik.http.routers.code.tls=true- traefik.http.routers.code.tls.certresolver=leresolver


Также перед запуском надо не забыть добавить в записи DNS домен третьего уровня code.example.com. Теперь осталось нажать кнопку Deploy the stack.

После окончания процесса в portainerе появится новый stack, и, если мы зайдем по адресу code.example.com, то увидим такую картину (я сразу включил dark theme):


Здесь можно вести разработку также как в десктопной версии VSCode. По большому счету, единственное отличие, с которым я столкнулся, это разница в дефолтных биндингах некоторых сочетаний клавиш, но к этому быстро привыкаешь в процессе использования.

Для удобства я подключил к контейнеру code-servera том, который смонтировал в директорию /app, так что создавать или клонировать из репозитория проекты лучше всего там, в таком случае данные не пропадут даже при пересоздании контейнера.

В данном образе уже установлен Node.js, так что мне не пришлось ничего делать, и я могу сразу приступить к работе над своими проектами. Если же вам в работе нужны другие ЯП, то обратите внимание, что создатели данного образа поддерживают каталог модов, которые позволяют добавить поддержку различных платформ. О том, как их использовать, можно почитать в описании образа на Docker Hub, а со списком официальных модов можно ознакомиться здесь: mods.linuxserver.io/?mod=code-server

Обратите также внимание на то, что в настройках контейнера указан пароль sudo. Для демонстрации я оставил его простым, но на практике лучше сделать его сложным, а еще лучше использовать свойство SUDO_PASSWORD_HASH, чтобы не хранить пароль в открытом виде. Как это сделать, можно почитать в описании образа здесь: hub.docker.com/r/linuxserver/code-server.

Как вы наверное уже заметили на данный момент доступ к code-server никак не защищен и сейчас любой, кто зайдет по адресу code.example.com получит доступ. Это очень плохой вариант, и, хотя в настройках образа можно включить доступ по паролю, мне бы хотелось иметь единый логин для доступа ко всем ресурсам, расположенным на сервере. Для этого предлагаю познакомиться со следующим инструментом. Это будет

KeyCloak


KeyCloak это современный инструмент для организации доступа к распределенным системам с использованием технологии единого входа. На самом деле это единственный свободный инструмент с настолько мощной функциональностью, который мне удалось найти. Если знаете достойную альтернативу, обязательно напишите об этом в комментариях.

Для того, чтобы добавить его в мою систему, я создал в portainerе новый стек с названием auth и добавил в него следующую конфигурацию:

version: '3.9'networks:intranet:external: trueservices:keycloak:image: jboss/keycloakcontainer_name: keycloakrestart: alwaysnetworks:- intranetenvironment:KEYCLOAK_PASSWORD: passwordPROXY_ADDRESS_FORWARDING: truelabels:- traefik.enable=true- traefik.http.routers.keycloak.rule=Host(`auth.yourdomain.com`)- traefik.http.routers.keycloak.tls=true- traefik.http.routers.keycloak.tls.certresolver=leresolver


После нажатия кнопки Deploy the stack, KeyCloak будет доступен по адресу auth.example.com. Если мы зайдем туда, нас встретит приветственное окно KeyCloak:



Зайдем в консоль администратора:


Имя пользователя будет admin, а начальный пароль тот, что мы установили в конфигурации стека переменной окружения KEYCLOAK_PASSWORD. После логина попадем в админку KeyCloak:


Рекомендации по первоначальной настройке KeyCloak для dockerа можно подчерпнуть в официальной документации вот здесь www.keycloak.org/getting-started/getting-started-docker.

Вкратце, надо создать новый пользовательский Realm, в нем создать пользователя и добавить нового клиента.

Для клиента необходимо установить Access Type: confidential и добавить в Valid Redirect URIs наши домены, пока это будут https://traefik.example.com/* и code.example.com*:



После установки Access Type: confidential появится вкладка Credentials, в которой можно будет забрать Secret, он нам пригодится далее при настройке.

На этом пока закончим настройку KeyCloak. Теперь нам надо подружить его с Traefikом. Напомню, что мы хотим закрыть от неаутентифицированных пользователей доступ к узлам code.example.com и traefik.example.com. Для этих целей у traefik есть ForwardAuth middleware, которое позволяет организовать авторизацию через внешний сервис. Чтобы обеспечить его взаимодействие KeyCloak нам понадобится промежуточный сервис, я буду использовать github.com/thomseddon/traefik-forward-auth. Он доступен также в качестве образа на Docker Hub, поэтому я просто дополню конфигурацию стека auth в portainerе таким сервисом:

traefik-forward-auth:image: thomseddon/traefik-forward-authcontainer_name: traefik-forward-authenvironment:- DEFAULT_PROVIDER=oidc- PROVIDERS_OIDC_ISSUER_URL=https://auth.example.com/auth/realms/example- PROVIDERS_OIDC_CLIENT_ID=traefik- PROVIDERS_OIDC_CLIENT_SECRET=d7fb86f0-71a9-44f7-ab04-967f086cd89e- SECRET=something-random- LOG_LEVEL=debuglabels:- traefik.enable=true- traefik.http.middlewares.traefik-forward-auth.forwardauth.address=http://traefik-forward-auth:4181- traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User- traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181restart: always

Здесь в переменной PROVIDERS_OIDC_ISSUER_URL должен быть путь к ранее созданному нами Realm в Keycloak, PROVIDERS_OIDC_CLIENT_ID должен содержать имя клиента, созданного мной ранее в данном realmе, а PROVIDERS_OIDC_CLIENT_SECRET надо взять из вкладки Credentials данного клиента. В переменной SECRET надо забить рандомную строку.

Теперь, чтобы закрыть сервис, роутинг в который обеспечивает Traefik достаточно добавить к нему в labels следующую строку:

- "traefik.http.routers.<имя роутера>.middlewares=traefik-forward-auth"


Для начала я решил закрыть аутентификацией code server, для чего зашел в его стек и дополнил конфигурацию его конфигурацию. Получилось следующее (для краткости привожу только секцию labels):

labels:- traefik.enable=true- traefik.http.routers.code.rule=Host(`code.example.com`)- traefik.http.routers.code.tls=true- traefik.http.routers.code.tls.certresolver=leresolver     - traefik.http.routers.code.middlewares=traefik-forward-auth


Нажмем кнопку Update Stack и попробуем зайти по адресу code.example.com. Если все сделано правильно, то появится окно логина:


После ввода корректных имени пользователя и пароля (которые до этого настроил в KeyCloak) я попал в интерфейс Code server. Все работает!

Подобным образом я закрыл от посторонних глаз дашборд traefikа. Для этого пришлось сходить в консоль (Portainer не может вносить изменения в конфигурацию стека, который был создан не им, а Traefik я поднимал из консоли) и аналогичным образом отредактировать docker-compose.yml:

labels:- traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)- traefik.http.routers.http-catchall.entrypoints=web- traefik.http.routers.http-catchall.middlewares=redirect-to-https- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https- traefik.enable=true- traefik.http.routers.traefik.entrypoints=websecure- traefik.http.routers.traefik.rule=Host(`traefik.example.com`)- traefik.http.routers.traefik.tls=true- traefik.http.routers.traefik.service=api@internal- traefik.http.routers.traefik.tls.certresolver=leresolver- traefik.http.routers.traefik.middlewares=traefik-forward-auth- traefik.http.services.traefik.loadbalancer.server.port=8080


Для проверки я зашел по адресу traefik.example.com. Чтобы удостоверится, что все работает как надо, мне пришлось открыть окно браузера в режиме инкогнито, иначе система меня узнавала и не спрашивала пароль, поскольку я ранее уже логинился для доступа к code-server и поэтому KeyCloak аутентифицировал меня автоматически.

Таким образом, при помощи связки KeyCloak и Traefik мне удалось защитить от несанкционированного доступа чувствительные элементы моей системы. Преимущество данного подхода в том, что он позволяет сделать это даже там, где не предусмотрены собственные механизмы аутентификации. Есть конечно и недостатки KeyCloak достаточно тяжелый, занимает много ресурсов, в первую очередь памяти, да и по возможностям это явный overkill, мне скорее всего не потребуется большая часть того, что он умеет. В качестве альтернативы можно бы было использовать облачный сервис, например traefik-forward-auth имеет встроенную поддержку Google OAuth.

Выводы


В этой статье мы познакомились с набором инструментов, совместное использование которых позволяет во многом упростить и автоматизировать широкий спектр задач, которые возникают сегодня в практике многих ИТ-шников. Такиенастройки, как:

  • Подключение доменных имен
  • Настройка TLS
  • Получение и установка сертификатов
  • Аутентификация

делаются в несколько строчек прямо в конфигурации сервиса.

Конечно, при настройке мне пришлось многое делать из консоли, но в дальнейшем, при повседневном использовании построенная система позволит мне сократить ее использование, что для меня несомненно плюс.

Когда я писал эту статью, главной сложностью было собрать всю необходимую информацию воедино. Многие вещи из документации неочевидны и зачастую чтобы добиться того, чтобы система заработала нужным образом приходилось искать решение проблемы по различным issues на github и вопросам со stackoverflow. Поэтому я постарался ряд моментов осветить более подробно, и надеюсь, кому-нибудь мои изыскания помогут лучше разобраться в описываемых продуктах.

Также искренне рассчитываю на конструктивную критику в комментариях, и идеи, как можно сделать то, что я попытался здесь реализовать, лучше. Например, очень хотелось бы найти более легковесную альтернативу KeyCloak, потому как он несколько тяжеловат для такого рода задач.

Для удобства файлы конфигураций я разместил в репозитории вот тут: https://github.com/debagger/vps-docker-workspace

Благодарю за внимание!



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Уютный VPS-сервер для маленьких проектов как настроить

30.05.2021 18:13:46 | Автор: admin

Мне всегда хотелось иметь в сети личное пространство. Место, где все было бы устроено как мне удобно. Наилучшим решением мне виделся недорогой VPS, который я мог бы обустраивать в соответствии со своими потребностями. Долго я не мог подступиться к решению этой задачи, но как-то незаметно подобрался набор инструментов, который позволил организовать именно такую программную среду, как мне хотелось.

Если вам тоже хочется создать в сети свое личное пространство, но вы не знаете, с чего начать, или вам просто интересны такие замечательные программные продукты как Docker, Portainer, Traefik добро пожаловать под кат.

Введение


Признаться честно, я не очень люблю командную строку. То есть, мне нравится сама идея легкого минималистичного универсального интерфейса, практически не требующего ресурсов, но черное окно с мигающим курсором вызывает у меня фрустрацию и чувство беспомощности. И я даже знаю откуда это у меня.

Когда я учился в институте (это то время, когда у меня в городе только появился интернет по карточкам и первое интернет-кафе), мой друг, который к тому времени умудрился устроится лаборантом на кафедру информатики, активно пытался подсадить меня на Линукс. И вот я, считающий себя продвинутым компьютерщиком, имеющий опыт, наверное, сотни установок винды, прихожу домой после института и уверенным движением вставляю в дисковод диск с дистрибутивом линукс И что? И ничего.

Я уже за давностью лет не помню всех подробностей, наверно мне даже удалось его установить, но дальше был провал я просто не понимал, что делать. Привычный способ, который работал в винде рыться в панели управления рандомно включая разные настройки пока все не заработает (или сломается окончательно, но тогда можно было переустановить винду и начать все снова) тут не действовал. Все, что у меня было, это консоль, которая раз за разом отвергала мои попытки что-то настроить. Помню, что у меня была даже книга по линуксу, которая тоже никак не помогала команды, которые там были приведены, в большинстве своем почему-то не работали. В общем, промучившись несколько дней я тогда решил для себя, что линукс, это какая-то ерунда, и с облегчением вернулся к привычной винде.

Были и еще попытки освоить линукс, жизнь раз за разом сводила меня с его фанатами, которые пытались меня перетянуть в свой лагерь, я даже как-то попытался поставить генту (неудачно естественно, хотя у меня была распечатка с пошаговым руководством как это сделать), но проклятье консоли витало надо мной и с каждой неудачей то самое чувство беспомощности все усиливалось. После графического интерфейса винды, где все настройки перед тобой как на ладони, сталкиваясь с консолью я чувствовал себя ослепшим, пытающимся найти дорогу на ощупь в незнакомом месте. Так что я очень долго оставался приверженцем технологического стека Microsoft, программировал на .net, используя VisualStudio (она шикарна, и никакой консоли). Прошу не судить строго, возможно, если бы в то время у меня была возможность поставить линукс на виртуалку, чтобы спокойно с ней разбираться и приличный интернет под рукой, эта история приняла бы совсем другой оборот.

Сейчас все поменялось. С приходом в нашу жизнь веба в его современном виде я пересел на Node.js и волей-неволей стал проникаться идеологией консоли и Linux. Для хостинга своих разработок мне понадобился VPS, и тут оказалось, что VPS с виндой стоят гораздо дороже, так что моя жаба перевесила мои фрустрации относительно консоли, да и наличие скоростного интернета позволило мгновенно находить ответы на возникающие вопросы.

Я научился пользоваться ssh, осознал силу git и стал активно пользоваться dockerом, но мне все также нравится использовать для своих задач графический интерфейс, и в этой статье я хочу вас познакомить с замечательным набором инструментов, которые позволяют решать мне мои повседневные задачи, не обращаясь лишний раз к консоли.

Подготовка


Я создал VPS минимальной конфигурации (vCPU: 1 core, RAM: 1 ГБ, NVMe: 20 ГБ) на macloud. Для установки на VPS я выбрал дистрибутив Debian 10. При установке я сразу добавил через панель управления SSH-ключ, чтобы было удобно заходить в консоль с помощью SSH-клиента. Для дальнейших экспериментов понадобится следующее:

  1. Обновить систему
  2. Установить docker и docker-compose
  3. Включить файл подкачки.

Обновить операционную систему можно следующими командами:

# apt updateОбновит список доступных пакетов# apt upgradeОбновит систему, установив свежие версии ПО.

Для установки docker следуем официальной инструкции https://docs.docker.com/engine/install/debian/

Устанавливаем необходимые зависимости:

# apt-get install apt-transport-https ca-certificates curl gnupg lsb-release

Добавляем официальный GPG ключ Dockerа:

# curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Добавляем репозиторий:

# echo deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable | tee /etc/apt/sources.list.d/docker.list > /dev/null

Теперь осталось обновить список пакетов:

# apt-get update

и установить докер:
# apt-get install docker-ce docker-ce-cli containerd.io

Проверить, что все корректно установилось можно, запустив команду:

# docker run hello-world

Если появилось приветствие, значит все прошло хорошо. У меня на момент написания данной статьи установилась следующая версия:

# docker -vDocker version 20.10.6, build 370c289

Теперь надо установить docker-compose. Для этого воспользуемся официальной инструкцией:

https://docs.docker.com/compose/install/

Для начала нам нужно получить ссылку на свежий релиз docker-compose. Список релизов можно посмотреть тут: https://github.com/docker/compose/releases

На момент написания статьи наиболее свежей была версия 1.29.2. Нам нужна версия для linux, поэтому выбираем файл с названием docker-compose-Linux-x86_64. Скопируем ссылку на него. Теперь надо в консоли ввести следующую команду (вставьте в нужное место ссылку, полученную выше):

# curl -L "<ссылка на релиз>" -o /usr/local/bin/docker-compose

В моем случае получилось:

# curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose

Запустим команду и загрузится бинарный файл docker-compose.

Последний шаг надо установить для него разрешение на исполнение. Это можно сделать следующей командой:

# chmod +x /usr/local/bin/docker-compose

Если всё прошло нормально, то на этом процесс установки окончен, теперь можно проверить версию docker-compose. У меня получилось вот так:

# docker-compose -vdocker-compose version 1.29.2, build 5becea4c

Последний шаг это включение файла подкачки, учитывая малый объем памяти он точно не будет лишним:

Создадим файл размером 4 Гб

# fallocate -l 4G /swapfile

Назначим ему необходимые права:

# chmod 600 /swapfile

Инициализируем его как файл подкачки:

# mkswap /swapfile

Наконец активируем:

# swapon /swapfile

Проверить, появился ли файл подкачки в системе можно командой free. У меня все получилось:

# freetotal    used    free   shared buff/cache  availableMem:    1010900   150500   143788    2892   716612   714916Swap:    4194300      0   4194300

Осталось последнее действие: для того, чтобы файл подкачки оставался активным после перезагрузки надо добавить в файл /etc/fstab следующую строку:

/swapfile swap swap defaults 0 0


Для подобных задач мне нравится использовать файловый менеджер Midnight Commander. Это консольный файловый менеджер с классическим интерфейсом. Если вы сталкивались с Norton Commander/FAR/TotalCommander вы легко поймете, как им пользоваться. Установить его можно следующей командой:

# apt-get install mc


А запустить с помощью команды

# mc

Увидим до боли знакомую картину:


Теперь найдем нужный файл и отредактируем его:


На этом предварительная настройка закончена, можно двигаться дальше.

Portainer


Первый инструмент, с которым мне бы хотелось вас познакомить это Portainer. Portainer это инструмент для управления контейнерами в Docker, Swarm, Kubernetes и Azure ACI. Как написано в документации Portainerа:

Отрицая необходимость для пользователей использовать интерфейс командной строки, писать YAML или понимать манифесты, Portainer делает развертывание приложений и устранение неполадок настолько простым, что любой может это сделать.

Для моих целей Portainer подходит просто идеально. Давайте познакомимся с ним поближе. В соответствии с документацией https://documentation.portainer.io/v2.0/deploy/ceinstalldocker/ проще всего это сделать следующим образом:

Создадим том для хранения данных:

# docker volume create portainer_data

Теперь можно запустить Portainer следующей командой:

# docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

После запуска Portainer будет доступен по адресу http://<ip серевера>:9000/

При первом запуске Portainer попросит вас указать пароль администратора, после чего спросит, каким образом подключится к окружению:


Я выбрал вариант подключится к локальному Dockerу, после чего появился домашний экран Portainerа:


Как видно, доступен локальный эндпоинт, позже можно при необходимости добавить подключения к другим узлам.

Зайдем в него, и увидим удобный дэшбоард со сводной информацией:


Здесь мы можем управлять всеми аспектами работы Dockerа. Давайте посмотрим, что у нас в контейнерах:


Здесь пока один контейнер это сам Portainer. Интерфейс очень удобно позволяет просматривать и управлять контейнерами, а также есть возможность добавлять новые. Можете пройтись по остальным вкладкам и убедится, насколько удобный и продуманный интерфейс имеет Portainer.

Наибольший интерес представляет вкладка Stacks. Стэк это совокупность взаимосвязанных контейнеров, которые запускаются и работают совместно. По сути, это тоже самое, что композиция Docker Compose, конфигурацию которой мы описываем в файле docker-compose.yml и потом запускаем командой docker-compose up.

Portainer дает возможность добавить стек в систему различными путями, начиная с редактирования с помощью встроенного редактора и заканчивая загрузкой с github.


Для определения стека используется формат docker-compose, именно он работает здесь под капотом. Мне при помощи этой функции удобнее всего построить из контейнеров необходимую инфраструктуру.

На этом предлагаю пока отвлечься от изучения возможностей portainerа. Думаю, вы уже увидели, что это серьезный инструмент, благодаря которому можно практически полностью отказаться от использования CLI dockerа. Давайте теперь кратко рассмотрим, что же я хочу получить в результате на этом VPS.

У меня периодически возникает потребность в разработке быстрых проектов. Я так называю проекты, которые не требуют глубокой проработки. Это могут быть эксперименты, небольшие сайты, боты, сборщики информации и т.п. Все то, что не требует выстраивания цепочки CI/CD. Мне было бы крайне удобно разрабатывать и отлаживать такие приложения прямо там, где они будут работать. Также у меня довольно часто бывает ситуация, когда я нахожусь вдали от своего рабочего места с настроенной средой разработки, и, если что-то в этих моих проектах начинает работать не так как надо, хотелось бы иметь возможность быстро разобраться в ситуации и пофиксить проблему, или просто поработать над проектами пока есть свободная минутка с любого компьютера, даже если на нем нет ничего, кроме браузера и доступа в интернет.

Для удобства доступа у меня есть купленное доменное имя, и я хочу настроить для различных инструментов домены третьего уровня, так, чтобы например portainer был доступен по адресу portainer.example.com. Также я хочу, чтобы всё работало через https, и, чтобы не заморачиваться с покупкой сертификатов SSL, воспользоваться Lets Encrypt. Еще одно требование, чтобы всё, к чему не предполагается публичный доступ было закрыто аутентификацией.

Для того, чтобы всё это реализовать, давайте познакомимся со следующим замечательным инструментом. Это

Traefik


Traefik это реверсивный прокси-сервер, который заточен для работы с контейнерами. Для меня важны следующие его возможности:

  • обновление конфигурации на лету
  • поддержка dockerа в качестве провайдера конфигурации
  • работа с Lets encrypt из коробки

Для того, чтобы запустить traefik совместно с portainerом воспользуемся примером docker-compose.yml, приведенном в документации portainer: https://documentation.portainer.io/v2.0/ad/traefik/rp-traefik/

version: 3.9services:traefik:container_name: traefikimage: traefik:latestcontainer_name: traefikcommand:- --entrypoints.web.address=:80- --entrypoints.websecure.address=:443- --providers.docker=true- --providers.docker.exposedbydefault=false- --log.level=ERROR- --certificatesresolvers.leresolver.acme.httpchallenge=true- --certificatesresolvers.leresolver.acme.email=user@mymail.com- --certificatesresolvers.leresolver.acme.storage=./acme.json- --certificatesresolvers.leresolver.acme.httpchallenge.entrypoint=web- --entrypoints.web.http.redirections.entryPoint.to=websecure- --entrypoints.web.http.redirections.entryPoint.scheme=https- --metrics.prometheus=trueports:- 80:80- 443:443volumes:- /var/run/docker.sock:/var/run/docker.sock:ro- ./acme.json:/acme.jsonnetworks:- intranetlabels:- traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)- traefik.http.routers.http-catchall.entrypoints=web- traefik.http.routers.http-catchall.middlewares=redirect-to-https- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=httpsportainer:image: portainer/portainer-ce:2.5.0-alpinecontainer_name: portainercommand: -H unix:///var/run/docker.sockrestart: alwaysvolumes:- /var/run/docker.sock:/var/run/docker.sock- portainer_data:/datanetworks:- intranetlabels:- traefik.enable=true- traefik.http.routers.frontend.rule=Host(`portainer.example.com`)- traefik.http.routers.frontend.entrypoints=websecure- traefik.http.services.frontend.loadbalancer.server.port=9000- traefik.http.routers.frontend.service=frontend- traefik.http.routers.frontend.tls.certresolver=leresolvervolumes:portainer_data:networks:intranet:name: intranet

Чтобы процесс получения сертификата Lets encrypt прошел успешно, перед запуском надо убедится, что записи DNS корректно настроены. Я для этих целей использую cloudflare, и в нем конфигурация должна выглядеть примерно так:


В графе Content должен находится адрес нашего VPS.

Если теперь скопировать получившийся docker-compose.yml на сервер выполнить следующую команду:

# docker-compose up -d

То после запуска по адресу portainer.example.com будет такая картина:


Причем он уже будет защищен сертификатом Lets Encrypt:


У traefik есть очень удобный дашборд, который помогает понять, правильно ли применились настройки конфигурации. Для того, чтобы его активировать, надо добавить в docker-compose.yml следующие строки:

services:traefik:...command:...- --api.dashboard=truelabels:...- traefik.enable=true- traefik.http.routers.traefik.entrypoints=websecure- traefik.http.routers.traefik.rule=Host(`traefik.example.com`)- traefik.http.routers.traefik.tls=true- traefik.http.routers.traefik.service=api@internal- traefik.http.routers.traefik.tls.certresolver=leresolver- traefik.http.services.traefik.loadbalancer.server.port=8080

Также надо в настройках DNS добавить домен третьего уровня traefik.example.com. Это можно сделать по аналогии с тем, как ранее был добавлен домен для portainer. После применения настроек по адресу traefik.example.com увидим дашборд:


Как видите, Traefik оказался замечательным инструментом. Он работает в тесной связке с dockerом, а его возможности динамической конфигурации позволяют полностью отказаться от использования конфигурационных файлов для настроек роутинга. Вместо этого мы просто прописываем все необходимые параметры в качестве labels для настраиваемого сервиса, так что все настройки оказываются в одном месте.

Теперь предлагаю немного отвлечься от настройки базовых сервисов и добавить в систему среду разработки. Это будет

Visual Studio Code Server


Для меня всегда среда разработки или IDE, была чем-то таким серьезным. Мощный программный пакет, который устанавливается на машине разработчика, занимает много гигабайт на диске и в оперативной памяти. Пример такой IDE, которой я пользовался много лет, и до сих пор считаю, что это лучший выбор, если вы работаете со стеком технологий Microsoft, это Microsoft VisualStudio. Когда я начал изучать Node.js, я открыл для себя VSCode, и, несмотря на родственные названия, это совсем другая IDE, с совершенно иной концепцией и возможностями. Тот факт, что для отображения своего интерфейса VSCode использует движок Chrome, позволяет разнести интерфейс и сам VSCode. Благодаря такой архитектуре возник Visual Studio Code Server, который может работать на VPS, при этом интерфейс VSCode будет доступен через браузер. И нет, это не очередной онлайн редактор кода, это полноценная IDE VSCode, которая обладает всеми ее замечательными возможностями.

Чтобы добавить VSCode Server на свой VPS я в Portainerе создам новый stack, назову его code-server и добавлю туда следующую конфигурацию:

version: 3.9volumes:codeserverdata:codeappdir:networks:intranet:external: trueservices:code-server:image: ghcr.io/linuxserver/code-servercontainer_name: code-serverenvironment:- PUID=1000- PGID=1000- TZ=Europe/London#    PASSWORD=password #optional- SUDO_PASSWORD=password #optional- PROXY_DOMAIN=code.example.comvolumes:- codeserverdata:/config- codeappdir:/appextra_hosts:host.docker.internal: host-gatewayrestart: alwaysnetworks:- intranetlabels:- traefik.enable=true- traefik.http.routers.code.rule=Host(`code.example.com`)- traefik.http.routers.code.tls=true- traefik.http.routers.code.tls.certresolver=leresolver


Также перед запуском надо не забыть добавить в записи DNS домен третьего уровня code.example.com. Теперь осталось нажать кнопку Deploy the stack.

После окончания процесса в portainerе появится новый stack, и, если мы зайдем по адресу code.example.com, то увидим такую картину (я сразу включил dark theme):


Здесь можно вести разработку также как в десктопной версии VSCode. По большому счету, единственное отличие, с которым я столкнулся, это разница в дефолтных биндингах некоторых сочетаний клавиш, но к этому быстро привыкаешь в процессе использования.

Для удобства я подключил к контейнеру code-servera том, который смонтировал в директорию /app, так что создавать или клонировать из репозитория проекты лучше всего там, в таком случае данные не пропадут даже при пересоздании контейнера.

В данном образе уже установлен Node.js, так что мне не пришлось ничего делать, и я могу сразу приступить к работе над своими проектами. Если же вам в работе нужны другие ЯП, то обратите внимание, что создатели данного образа поддерживают каталог модов, которые позволяют добавить поддержку различных платформ. О том, как их использовать, можно почитать в описании образа на Docker Hub, а со списком официальных модов можно ознакомиться здесь: mods.linuxserver.io/?mod=code-server

Обратите также внимание на то, что в настройках контейнера указан пароль sudo. Для демонстрации я оставил его простым, но на практике лучше сделать его сложным, а еще лучше использовать свойство SUDO_PASSWORD_HASH, чтобы не хранить пароль в открытом виде. Как это сделать, можно почитать в описании образа здесь: hub.docker.com/r/linuxserver/code-server.

Как вы наверное уже заметили на данный момент доступ к code-server никак не защищен и сейчас любой, кто зайдет по адресу code.example.com получит доступ. Это очень плохой вариант, и, хотя в настройках образа можно включить доступ по паролю, мне бы хотелось иметь единый логин для доступа ко всем ресурсам, расположенным на сервере. Для этого предлагаю познакомиться со следующим инструментом. Это будет

KeyCloak


KeyCloak это современный инструмент для организации доступа к распределенным системам с использованием технологии единого входа. На самом деле это единственный свободный инструмент с настолько мощной функциональностью, который мне удалось найти. Если знаете достойную альтернативу, обязательно напишите об этом в комментариях.

Для того, чтобы добавить его в мою систему, я создал в portainerе новый стек с названием auth и добавил в него следующую конфигурацию:

version: '3.9'networks:intranet:external: trueservices:keycloak:image: jboss/keycloakcontainer_name: keycloakrestart: alwaysnetworks:- intranetenvironment:KEYCLOAK_PASSWORD: passwordPROXY_ADDRESS_FORWARDING: truelabels:- traefik.enable=true- traefik.http.routers.keycloak.rule=Host(`auth.yourdomain.com`)- traefik.http.routers.keycloak.tls=true- traefik.http.routers.keycloak.tls.certresolver=leresolver


После нажатия кнопки Deploy the stack, KeyCloak будет доступен по адресу auth.example.com. Если мы зайдем туда, нас встретит приветственное окно KeyCloak:



Зайдем в консоль администратора:


Имя пользователя будет admin, а начальный пароль тот, что мы установили в конфигурации стека переменной окружения KEYCLOAK_PASSWORD. После логина попадем в админку KeyCloak:


Рекомендации по первоначальной настройке KeyCloak для dockerа можно подчерпнуть в официальной документации вот здесь www.keycloak.org/getting-started/getting-started-docker.

Вкратце, надо создать новый пользовательский Realm, в нем создать пользователя и добавить нового клиента.

Для клиента необходимо установить Access Type: confidential и добавить в Valid Redirect URIs наши домены, пока это будут https://traefik.example.com/* и code.example.com*:



После установки Access Type: confidential появится вкладка Credentials, в которой можно будет забрать Secret, он нам пригодится далее при настройке.

На этом пока закончим настройку KeyCloak. Теперь нам надо подружить его с Traefikом. Напомню, что мы хотим закрыть от неаутентифицированных пользователей доступ к узлам code.example.com и traefik.example.com. Для этих целей у traefik есть ForwardAuth middleware, которое позволяет организовать авторизацию через внешний сервис. Чтобы обеспечить его взаимодействие KeyCloak нам понадобится промежуточный сервис, я буду использовать github.com/thomseddon/traefik-forward-auth. Он доступен также в качестве образа на Docker Hub, поэтому я просто дополню конфигурацию стека auth в portainerе таким сервисом:

traefik-forward-auth:image: thomseddon/traefik-forward-authcontainer_name: traefik-forward-authenvironment:- DEFAULT_PROVIDER=oidc- PROVIDERS_OIDC_ISSUER_URL=https://auth.example.com/auth/realms/example- PROVIDERS_OIDC_CLIENT_ID=traefik- PROVIDERS_OIDC_CLIENT_SECRET=d7fb86f0-71a9-44f7-ab04-967f086cd89e- SECRET=something-random- LOG_LEVEL=debuglabels:- traefik.enable=true- traefik.http.middlewares.traefik-forward-auth.forwardauth.address=http://traefik-forward-auth:4181- traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User- traefik.http.services.traefik-forward-auth.loadbalancer.server.port=4181restart: always

Здесь в переменной PROVIDERS_OIDC_ISSUER_URL должен быть путь к ранее созданному нами Realm в Keycloak, PROVIDERS_OIDC_CLIENT_ID должен содержать имя клиента, созданного мной ранее в данном realmе, а PROVIDERS_OIDC_CLIENT_SECRET надо взять из вкладки Credentials данного клиента. В переменной SECRET надо забить рандомную строку.

Теперь, чтобы закрыть сервис, роутинг в который обеспечивает Traefik достаточно добавить к нему в labels следующую строку:

- "traefik.http.routers.<имя роутера>.middlewares=traefik-forward-auth"


Для начала я решил закрыть аутентификацией code server, для чего зашел в его стек и дополнил конфигурацию его конфигурацию. Получилось следующее (для краткости привожу только секцию labels):

labels:- traefik.enable=true- traefik.http.routers.code.rule=Host(`code.example.com`)- traefik.http.routers.code.tls=true- traefik.http.routers.code.tls.certresolver=leresolver     - traefik.http.routers.code.middlewares=traefik-forward-auth


Нажмем кнопку Update Stack и попробуем зайти по адресу code.example.com. Если все сделано правильно, то появится окно логина:


После ввода корректных имени пользователя и пароля (которые до этого настроил в KeyCloak) я попал в интерфейс Code server. Все работает!

Подобным образом я закрыл от посторонних глаз дашборд traefikа. Для этого пришлось сходить в консоль (Portainer не может вносить изменения в конфигурацию стека, который был создан не им, а Traefik я поднимал из консоли) и аналогичным образом отредактировать docker-compose.yml:

labels:- traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)- traefik.http.routers.http-catchall.entrypoints=web- traefik.http.routers.http-catchall.middlewares=redirect-to-https- traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https- traefik.enable=true- traefik.http.routers.traefik.entrypoints=websecure- traefik.http.routers.traefik.rule=Host(`traefik.example.com`)- traefik.http.routers.traefik.tls=true- traefik.http.routers.traefik.service=api@internal- traefik.http.routers.traefik.tls.certresolver=leresolver- traefik.http.routers.traefik.middlewares=traefik-forward-auth- traefik.http.services.traefik.loadbalancer.server.port=8080


Для проверки я зашел по адресу traefik.example.com. Чтобы удостоверится, что все работает как надо, мне пришлось открыть окно браузера в режиме инкогнито, иначе система меня узнавала и не спрашивала пароль, поскольку я ранее уже логинился для доступа к code-server и поэтому KeyCloak аутентифицировал меня автоматически.

Таким образом, при помощи связки KeyCloak и Traefik мне удалось защитить от несанкционированного доступа чувствительные элементы моей системы. Преимущество данного подхода в том, что он позволяет сделать это даже там, где не предусмотрены собственные механизмы аутентификации. Есть конечно и недостатки KeyCloak достаточно тяжелый, занимает много ресурсов, в первую очередь памяти, да и по возможностям это явный overkill, мне скорее всего не потребуется большая часть того, что он умеет. В качестве альтернативы можно бы было использовать облачный сервис, например traefik-forward-auth имеет встроенную поддержку Google OAuth.

Выводы


В этой статье мы познакомились с набором инструментов, совместное использование которых позволяет во многом упростить и автоматизировать широкий спектр задач, которые возникают сегодня в практике многих ИТ-шников. Такиенастройки, как:

  • Подключение доменных имен
  • Настройка TLS
  • Получение и установка сертификатов
  • Аутентификация

делаются в несколько строчек прямо в конфигурации сервиса.

Конечно, при настройке мне пришлось многое делать из консоли, но в дальнейшем, при повседневном использовании построенная система позволит мне сократить ее использование, что для меня несомненно плюс.

Когда я писал эту статью, главной сложностью было собрать всю необходимую информацию воедино. Многие вещи из документации неочевидны и зачастую чтобы добиться того, чтобы система заработала нужным образом приходилось искать решение проблемы по различным issues на github и вопросам со stackoverflow. Поэтому я постарался ряд моментов осветить более подробно, и надеюсь, кому-нибудь мои изыскания помогут лучше разобраться в описываемых продуктах.

Также искренне рассчитываю на конструктивную критику в комментариях, и идеи, как можно сделать то, что я попытался здесь реализовать, лучше. Например, очень хотелось бы найти более легковесную альтернативу KeyCloak, потому как он несколько тяжеловат для такого рода задач.

Для удобства файлы конфигураций я разместил в репозитории вот тут: https://github.com/debagger/vps-docker-workspace

Благодарю за внимание!



Облачные серверы от Маклауд быстрые и безопасные.

Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru