StasyakOFF Blog
Номер версии представляется как MAJOR.MINOR.PATCH, где увеличивается:
1. MAJOR версия, когда добавляется несовместимое с предыдущей версией api,
2. MINOR версия, когда ты добавляешь обратно-совместимую функциональность,
3. PATCH версия, когда ты добавляешь обратно-совместимое исправление ошибки.
Допускается добавление дополнительных меток для предварительного релиза и метаданных о сборке, как расширение формата MAJOR.MINOR.PATCH
Введение
В мире управления программным обеспечением есть внушающая ужас вещь, под названием «ад зависимостей» (dependency hell). Чем быстрее растет ваша система и чем больше зависимостей от сторонних библиотек она имеет, тем выше вероятность, что, в один прекрасный момент, вы попадете в эту яму отчаяния.
В системах с большим количество зависимостей выпуск новых версий может быстро превратиться в настоящий кошмар.
Для решения этой проблемы предлагается простой набор правил, определяющий, как должны назначаться номера версий. Эти правила основаны (но не ограничены) на, давно используемых, принципах нумерации проприетарного и свободного программного обеспечения. Для использования семантического версионирования необходимо определить публичный API (в виде документации или набора интерфейсов). Это очень важно, т.к. именно по характеру изменений этого API определяется, какую новую версию получит ваше программное обеспечение.
Правила Семантического Версионирования (СВ)
1. Программное обеспечение использующее СВ должно иметь публичный API (в виде документации или набора интерфейсов).
2. Стандартный номер версии должен иметь вид X.Y.Z — где X,Y и Z целые неотрицательные числа и не содержать ведущих нулей (т.е. не допускаются версии вида, например 1.01.0 — правильно 1.1.0). При внесении изменений в ПО должен изменяться один или несколько компонентов версии X, Y или Z.
3. После выпуска новой версии ПО, его содержимое для этой версии не должно меняться.
4. В начале разработки MAJOR версия равно 0, версия имеет вид 0.Y.Z. Публичное API нестабильно. Все изменения API считаются обратно-совместимым, т.е при любом изменении API увеличивается Y компонент.
5. Версия 1.0.0 присваивается публичному API в стабильном состоянии. Фактически в этот момент происходит первый релиз ПО.
6. Z компонент версии (патч-номер версии) должен быть увеличен, только если внесены обратно-совместимые исправления ошибок. Исправление ошибки — это внутреннее изменение, исправляющее некорректное поведение без изменения публичного API. Также он МОЖЕТ увеличиваться при незначительных функциональных улучшениях кода без изменения публичного API.
7. Y компонент версии (минорный номер версии) должен быть увеличен, если добавлена новая обратно-совместимая функциональность в публичный API. Также Y должен быть увеличен если какие-либо функции публичного API помечены как устаревшие. Компонент Y МОЖЕТ увеличиваться, если внесены значительные функциональные улучшения кода без изменения публичного API, при этом Z компонент версии (патч-номер) должен быть сброшен в 0. Если одновременно с улучшениями производились исправления ошибок, то Z компонент версии можно не менять.
8. X компонент версии должен увеличиваться при внесении обратно-несовместимых изменений в публичный API. Если одновременно с этим были обратно-совместимые изменения API и исправление ошибок не меняющих API, то компоненты Y и Z можно не менять. При изменении X компонента, компоненты Y и Z должны быть сброшены в 0.
9. Предрелизные версии могут быть обозначены путем добавления дефиса и идентификаторов, разделенных точкой, сразу после версии патча. Идентификаторы могут содержать только символы из набора [0-9A-Za-z]. Числовые идентификаторы не должны содержать ведущий ноль. Предрелизная версия указывает на то, что соответствующая версия публичного API еще не стабильна. Примеры нумерации: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92
10. Метаданные сборки могут быть добавлены в конец релизной или предрелизной версии после знака «+». Метаданные сборки — это один или несколько непустых идентификаторов разделенных точкой. Идентификаторы могут содержать только символы из набора [0-9A-Za-z]. При определении старшинства версии метаданные сборки не учитываются, т.е. если 2 версии отличаются только метаданными сборки — эти версии имеют одинаковый приоритет. Пример: 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.
11. Старшинство версий должно рассчитываться путем сравнения X, Y, Z. При определении очередности слева направо сравниваются соответствующие компоненты версий. Например: 1.0.0
Зачем использовать Семантическое Версионирование?
СВ не является новой или революционной идеей и вы, наверняка, использовали что-то похоже. Достаточно ли хорошо это «похожее» работает? Проблема заключается в том, что без формальной спецификации правил, сама идея версионирования, как инструмент управления зависимостями, бесполезна. Определяя четкие правила назначения версий, становится легче доносить до пользователей вашего ПО — как изменилось ПО и что нужно предпринять для эффективного управления зависимостями.
Рассмотрим пример в котором есть приложение «Пожарная машина», которое зависит от библиотеки «Лестница». Библиотека «Лестница» использует семантическое версионирование. Приложение «Пожарная машина» использует библиотеку «Лестница» версии 3.1.0, т.к именно в этой версии были добавлены необходимые приложению функции. Это означает, что мы можем указать для приложения «Пожарная машина» диапазон совместимых версий библиотеки «Лестница» [3.1.0, 4.0.0). Это означает, что если в системе будет библиотека «Лестница» версии 3.1.1 или 3.2.5, то приложение «Пожарная машина» будет работать так же хорошо как и с версией 3.1.0
Однако с версией 4.0.0 и выше приложение может быть уже несовместимо.
Как ответственный разработчик вы хотели бы быть уверены, что в новой версии ПО именно те изменения, которые декларируются версией. Однако реальный мир несовершенен, и вы ничего не можете с этим поделать, поэтому просто будьте бдительны. Что в ваших силах сделать, так это позволить Семантическому Версионированию дать Вам разумный способ выпускать и обновлять ПО без необходимости обновления всех зависимостей, экономя ваши нервы и время.
Если все это выглядит привлекательно — все что вам нужно сделать — это объявить что вы используете Семантическое Версионирование и начать следовать правилам! Делайте ссылку на этот пост или на оригинальный текст в Вашем README, чтобы пользователи Вашего ПО понимали Вас лучше.
FAQ
→ Как менять версии формата 0.Y.Z на стадии разработки?
↑Самое простое — это начать с версии 0.1.0, а затем увеличивать Y при каждом новом выпуске, т.е. использовать 0.Y.0 (если вам не принципиально что вошло в новую версию, улучшения или исправления ошибок).
→ Как определить что пора выпускать версию 1.0.0?
↑Как только от Вашего ПО начинает зависеть стороннее ПО или пользователи — смело ставьте версию 1.0.0
→ Если даже при малейшем изменении обратной совместимости в публичном API надо увеличивать X компонент версии, не получится ли так что он быстро вырастет до больших значений, например, 42.0.0?
↑Это вопрос планирования и предвидения развития Вашего ПО. При изменении публичного API следует помнить, что это влечет изменение зависимого ПО, что может быть очень затратно с экономической точки зрения.
→ Как удалять из публичного API устаревшие части?
↑Устаревание кода является неотъемлемой частью разработки ПО и часто необходимо для успешного развития. Когда вы помечаете часть публичного ПО, как устаревшее, вам необходимо выпустить минимум один минорный релиз, чтобы переход на новый код был плавным, и подробно описать в документации произведенные изменения.
→ Есть ли ограничение на длину строки версии?
↑Данная спецификация не накладывает никаких ограничений. Просто используйте здравый смысл.
Вольный перевод с дополнениями для Оригинал
Семантическое версионирование 2.0
. это язык, в трёх словах которого общаются независимые проекты
Восхитительное чувство, когда один раз взглянув на новую версию сторонней библиотеки ты понимаешь, можно ли её смело обновлять или нужно быть готовым к изменениям в собственном коде. В сухую нумерацию пакетов вносит осмысленность Семантическое Версионирование. У семантического версионирования есть свой сайт и основные посылы я брал оттуда (ссылка в конце статьи).
Основная Идея
Существует основной формат:
MAJOR.MINOR.PATCH
Формат включает в себя три неотрицательные цифры, которые увеличиваются в соответствии со следующими условиями.
MAJOR — увеличение версии говорит об обратно несовместимых изменениях API.
MINOR — увеличение версии говорит о добавлении новой функциональности при сохранении обратной совместимости.
PATCH — увеличение версии говорит об обратно совместимых фиксах.
Какую проблему решаем?
При большом количестве зависимостей в вашем проекте может встать вопрос о потребности в использовании новых версий разных библиотек. Если дать полную свободу в версионировании, то процесс превратится в настоящий ад, т.к. становится абсолютно не очевидно сломает ли всё, например, переход с версии 2.3.4 на версию 2.6.8. Идея не новая, но её формализация позволяет всем использовать и понимать версии одинаково.
Как решаем?
Основная идея была описана выше, а ниже некоторые, на мой взгляд, важные вещи из спецификации SemVer.
- если какие-то изменения сделаны после релиза, то они попадут только в новый релиз;
- публичный API для версии 0.х.х не должен рассматриваться как стабильный, это версия для начальной разработки;
- версия 1.0.0 определяет публичный API;
- если часть API помечена “устаревшей”, то инкрементируем минорную версию, в том числе она может в себя включать фиксы;
- мажорная версия может включать в себя изменения характерные минорной версии и патчу;
- версию можно дополнять указателями на предрелизные выпуски или сборками изменяющими метаданные, но идентификаторы версий только в ASCII.
Всегда ли это подходит?
Нет, не всегда. Если вы разрабатываете программу/веб приложение для конечного пользователя, а не библиотеку или Http API, то скорее всего семантическое версионирование вам не нужно. Прежде всего, посмотрите на цели и статус вашего проекта, возможно он находится на поддержке, всё, что вы делаете, — исправляете ошибки, то есть “новая функциональность” не появляется, это значит, что первые две цифры будут вечно неизменными, тогда какой в этом смысл? С другой стороны, если взглянуть категорично, то так и должно быть, каждый раз закрывая пачку багов, вы обновляете PATCH версию, а при необходимости хот-фиксов просто расширяете её дополнительными идентификаторами.
Для кого это подходит?
Иногда, мы пишем библиотеки, иногда мы пишем модули от которых будет зависеть остальная часть системы, иногда мы пишем микросервисы с API которых будут взаимодействовать другие команды. Всё это отличные примеры того, где семантическое версионирование преобретает смысл. Семантическое версионирование — это язык, в трёх словах которого общаются независимые проекты.
Семантическое Версионирование 2.0.0
Учитывая номер версии МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ, следует увеличивать:
- МАЖОРНУЮ версию, когда сделаны обратно несовместимые изменения API.
- МИНОРНУЮ версию, когда вы добавляете новую функциональность, не нарушая обратной совместимости.
- ПАТЧ-версию, когда вы делаете обратно совместимые исправления.
Дополнительные обозначения для предрелизных и билд-метаданных возможны как дополнения к МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ формату.
Вступление
В мире управления процессом разработки есть понятие «ад зависимостей» (dependency hell). Чем больше растёт ваша система и чем больше библиотек вы интегрируете в ваш проект, тем больше вероятность оказаться в этой ситуации.
В системе с множественными зависимостями выпуск новой версии может быстро превратиться в кошмар. Если спецификации зависимости слишком жесткие, вы находитесь в опасности блокирования выпуска новой версии (невозможность обновить пакет без необходимости выпуска новой версии каждой зависимой библиотеки). Если спецификация зависимостей слишком свободна, вас неизбежно настигнет версионное несоответствие (необоснованное предположение совместимости с будущими версиями).
В качестве решения данной проблемы я предлагаю простой набор правил и требований, которые определяют, как назначаются и увеличиваются номера версий. Для того чтобы эта система работала, вам необходимо определить публичный API. Он может быть описан в документации или определяться самим кодом. Главное, чтобы этот API был ясным и точным. Однажды определив публичный API, вы сообщаете об изменениях в нём особым увеличением номера версий. Рассмотрим формат версий X.Y.Z (мажорная, минорная, патч). Баг-фиксы, не влияющие на API, увеличивают патч-версию, обратно совместимые добавления/изменения API увеличивают минорную версию и обратно несовместимые изменения API увеличивают мажорную версию.
Я называю эту систему «Семантическое Версионирование» (Semantic Versioning). По этой схеме номера версий и то, как они изменяются, передают смысл содержания исходного кода и что было модифицировано от одной версии к другой.
Спецификация Семантического Версионирования (SemVer)
Слова «ДОЛЖЕН» (MUST), «НЕ ДОЛЖЕН» (MUST NOT), «ОБЯЗАТЕЛЬНО» (REQUIRED), «СЛЕДУЕТ» (SHOULD), «НЕ СЛЕДУЕТ» (SHOULD NOT), «РЕКОМЕНДОВАННЫЙ» (RECOMMENDED), «МОЖЕТ» (MAY) и «НЕОБЯЗАТЕЛЬНЫЙ» (OPTIONAL) в этом документе должны быть интерпретированы в соответствии с RFC 2119.
- ПО, использующее Семантическое Версионирование, должно объявить публичный API. Этот API может быть объявлен самим кодом или существовать строго в документации. Как бы ни было это сделано, он должен быть точным и исчерпывающим.
- Обычный номер версии ДОЛЖЕН иметь формат X.Y.Z, где X, Y и Z — неотрицательные целые числа и НЕ ДОЛЖНЫ начинаться с нуля. X — мажорная версия, Y — минорная версия и Z — патч-версия. Каждый элемент ДОЛЖЕН увеличиваться численно. Например: 1.9.0 ->1.10.0 -> 1.11.0.
- После релиза новой версии пакета содержание этой версии НЕ ДОЛЖНО быть модифицировано. Любые изменения ДОЛЖНЫ быть выпущены как новая версия.
- Мажорная версия ноль (0.y.z) предназначена для начальной разработки. Всё может измениться в любой момент. Публичный API не должен рассматриваться как стабильный.
- Версия 1.0.0 определяет публичный API. После этого релиза номера версий увеличиваются в зависимости от того, как изменяется публичный API.
- Патч-версия Z (x.y.Z | x > 0) ДОЛЖНА быть увеличена только если содержит обратно совместимые баг-фиксы. Определение баг-фикс означает внутренние изменения, которые исправляют некорректное поведение.
- Минорная версия (x.Y.z | x > 0) ДОЛЖНА быть увеличена, если в публичном API представлеа новая обратно совместимая функциональность. Она ДОЛЖНА быть увеличена, если какая-либо функциональность публичного API помечена как устаревший (deprecated). Версия МОЖЕТ быть увеличена в случае реализации новой функциональности или существенного усовершенствования в приватном коде. Версия МОЖЕТ включать в себя изменения, характерные для патчей. Патч-версия ДОЛЖНА быть обнулена, когда увеличивается минорная версия.
- Мажорная версия X (X.y.z | X > 0) ДОЛЖНА быть увеличена, если в публичном API представлены какие-либо обратно несовместимые изменения. Она МОЖЕТ включать в себя изменения, характерные для уровня минорных версий и патчей. Когда увеличивается мажорная версия, минорная и патч-версия ДОЛЖНЫ быть обнулены.
- Предрелизная версия МОЖЕТ быть обозначена добавлением дефиса и серией разделённых точкой идентификаторов, следующих сразу за патч-версией. Идентификаторы ДОЛЖНЫ содержать только ASCII буквенно-цифровые символы и дефис [0-9A-Za-z-]. Идентификаторы НЕ ДОЛЖНЫ быть пустыми. Числовые идентификаторы НЕ ДОЛЖНЫ начинаться с нуля. Предрелизные версии имеют более низкий приоритет, чем соответствующая релизная версия. Предрелизная версия указывает на то, что эта версия не стабильна и может не удовлетворять требованиям совместимости, обозначенными соответствующей нормальной версией. Примеры: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.
- Сборочные метаданные МОГУТ быть обозначены добавлением знака плюс и ряда разделённых точкой идентификаторов, следующих сразу за патчем или предрелизной версией. Идентификаторы ДОЛЖНЫ содержать только ASCII буквенно-цифровые символы и дефис [0-9A-Za-z-]. Идентификаторы НЕ ДОЛЖНЫ быть пустыми. Сборочные метаданные СЛЕДУЕТ игнорировать, когда определяется старшинство версий. Поэтому два пакета с одинаковой версией, но разными сборочными метаданными, рассматриваются как одна и та же версия. Примеры: 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.
- Приоритет определяет, как версии соотносятся друг с другом, когда упорядочиваются. Приоритет версий ДОЛЖЕН рассчитываться путём разделения номеров версий на мажорную, минорную, патч и предрелизные идентификаторы. Именно в такой последовательности (сборочные метаданные не фигурируют в расчёте). Приоритет определяется по первому отличию при сравнении каждого из этих идентификаторов слева направо: Мажорная, минорная и патч-версия всегда сравниваются численно. Пример: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. Когда мажорная, минорная и патч-версия равны, предрелизная версия имеет более низкий приоритет, чем нормальная версия. Пример: 1.0.0-alpha < 1.0.0. Приоритет двух предрелизных версий с одинаковыми мажорной, минорной и патч-версией ДОЛЖНЫ быть определены сравнением каждого разделённого точкой идентификатора слева направо до тех пор, пока различие не будет найдено следующим образом: идентификаторы, состоящие только из цифр, сравниваются численно; буквенные идентификаторы или дефисы сравниваются лексически в ASCII-порядке. Численные идентификаторы всегда имеют низший приоритет, чем символьные. Больший набор предрелизных символов имеет больший приоритет, чем меньший набор, если сравниваемые идентификаторы равны. Пример: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.
Зачем использовать семантическое версионирование?
Это не новая или революционная идея. Вероятно, вы уже используете что-то подобное. Проблема в том, что «подобное» — не достаточно хорошо. Без соответствия формальной спецификации, номера версий практически бесполезны для управления зависимостями. Ясно определив и сформулировав идею версионирования, становится легче сообщать о намерениях пользователям вашего ПО. Когда эти намерения ясны, гибки (но не слишком), спецификации зависимостей наконец могут быть созданы.
Простой пример демонстрирует, как Семантическое Версионирование может сделать «ад зависимостей» вещью из прошлого. Представим библиотеку, названную «Firetruck». Она требует Семантически Версионированный пакет под названием «Ladder». Когда Firetruck был создан, Ladder был 3.1.0 версии. Так как Firetruck использует функциональности версии 3.1.0, вы спокойно можете объявить зависимость от Ladder версии 3.1.0, но менее чем 4.0.0. Теперь, когда доступен Ladder 3.1.1 и 3.2.0 версии, вы можете интегрировать его в вашу систему и знать, что он будет совместим с текущей функциональностью.
Как ответственный разработчик, вы, конечно, хотите быть уверены, что все обновления функционируют как заявлено. В реальном мире полный бардак и ничего нельзя с этим поделать. Что вы можете сделать — это дать Семантическому Версионированию предоставить способ выпуска релизов без выпуска новых версий зависимых пакетов и сохранить вам время и нервы.
Если это звучит соблазнительно, всё что вам нужно — это начать использовать Семантическое Версионирование, объявить, что вы его используете, и следовать правилам. Добавьте ссылку на этот сайт в вашем README, тогда пользователи будут знать правила и извлекать из этого пользу.
FAQ
Что я должен делать с ревизиями в 0.y.z на начальной стадии разработки?
Самое простое — начать разработку с 0.1.0 и затем увеличивать минорную версию для каждого последующего релиза.
Как я узнаю, когда пора делать релиз 1.0.0?
Если ваше ПО используется на продакшене, оно, вероятно, уже должно быть версии 1.0.0. Если у вас стабильный API, от которого зависят пользователи, версия должна быть 1.0.0. Если вы беспокоитесь за обратную совместимость, вероятно, версия вашего ПО уже 1.0.0.
Не препятствует ли это быстрой разработке и коротким итерациям?
Мажорная версия 0 как раз и означает быструю разработку. Если вы изменяете API каждый день, вы должны быть на версии 0.y.z или на отдельной ветке разработки работать над следующей главной версией.
Даже если малейшие обратно несовместимые изменения в публичном API требуют выпуска новой главной версии, не закончится ли это тем, что очень скоро версия станет 42.0.0?
Это вопрос ответственной разработки и предвидения. Несовместимые изменения не должны быть представлены как незначительные в ПО, имеющем много зависимого кода. Стоимость обновления может быть велика. Практика увеличения главных версий релизов с обратно несовместимыми изменениями означает, что вам придётся думать о последствиях ваших изменений и учитывать соотношение цена/качество.
Документирование всего API — слишком много работы!
Это ваша ответственность, как профессионального разработчика, правильно документировать ПО, предназначенное для широкого использования. Управление сложностью ПО очень важная часть поддержки высокой эффективности проекта. Это тяжело сделать, если никто не знает, как использовать ваше ПО или какой метод можно вызывать безопасно. В долгосрочной перспективе Семантическое Версионирование и настойчивость в качественном документировании публичного API поможет всем и всему работать слаженно.
Что мне делать, если я случайно зарелизил обратно несовместимые изменения как минорную версию?
Как только вы поняли, что нарушили спецификации Семантического Версионирования, исправьте проблему и выпустите новую минорную версию, которая исправляет проблему и восстанавливает обратную совместимость. Даже в таких обстоятельствах неприемлемо модифицировать уже выпущенные релизы. Если это необходимо, укажите в документации о нарушении обратной совместимости, версионирования и проинформируйте ваших пользователей, чтобы они знали о нарушении порядка версий.
Что я должен делать, если я обновляю свои собственные зависимости без изменения публичного API?
Это можно рассматривать как совместимые изменения, так как они не влияют на публичный API. ПО, которое явно зависит от тех же зависимостей что и ваш пакет, должно иметь собственные спецификации зависимостей и автор будет уведомлен о возможных конфликтах. Являются ли данные изменения уровня патча или минорного уровня, зависит от того, обновили ли вы свои зависимости чтобы исправить баг или реализовать новую функциональность. В последнем случае, как правило, добавляется некоторое количество дополнительного кода и как следствие, увеличивается минорная версия.
Что если я нечаянно изменил публичный API в несоответствии с изменением номера версии (т.е. код содержит обратно несовместимые изменения в патч-релизе)?
На ваше усмотрение. Если у вас огромная аудитория, которая будет поставлена перед фактом возвращения прежнего поведения API, то лучше выпустить новый релиз с увеличением главной версии, даже несмотря на то, что фикс содержит исправления уровня патча. Запомните, в Семантическом Версионировании номера версий изменяются строго следуя спецификации. Если эти изменения важны для ваших пользователей, используйте номер версии, чтобы информировать их.
Что делать с устаревшей функциональностью?
Объявление функциональности устаревшей — это обычное дело в ходе разработки и часто необходимо для продвижения вперёд. Когда вы объявляете устаревшим часть публичного API, вы должны сделать две вещи: (1) обновить вашу документацию, чтобы дать пользователям узнать об этом изменении; (2) выпустить новый релиз с увеличением минорной версии. Прежде чем вы полностью удалите устаревшую функциональность в релизе с увеличением главной версии, должен быть как минимум один минорный релиз, содержащий объявление функциональности устаревшим, чтобы пользователи могли плавно перейти на новый API.
Есть ли в SemVer лимиты на длину строки версии?
Нет, но руководствуйтесь здравым смыслом. 255 символов в строке версии, пожалуй, перебор. Кроме того, определенные системы могут предъявлять свои собственные ограничения на размер строки.
Об авторе
Авторство спецификаций Семантического Версионирования принадлежит Тому Престон-Вернеру, основателю Gravatars и соучредителю GitHub.
Если вы хотите оставить отзыв, пожалуйста, создайте запрос на GitHub.
Вопросы для собеседования Системного администратора или DevOps инженера Linux. Часть 3. Medium Linux Questions — 1
Всем привет! В прошлой статье мы продолжили разбор статьи с GitHub — Linux System Administrator/DevOps Interview Questions.
Сегодня будет пачка вопрос из раздела «Средние» — экзамен на звание Middle Linux administrator. Так как в оригинальной статье этот блок вышел довольно объемным (49 вопросов), я решил разбить его на 2 части (29 и 30 соответственно).
Вопросы среднего уровня сложности / Medium Linux Questions
1. Что делают следующие команды и как вы можете их использовать? (What do the following commands do and how would you use them?): tee, awk, tr, cut, tac, curl, wget, watch, head, tail
- tee — читает со стандартного ввода (stdin) и выводит на стандартный вывод (stdout) и в указанный файл. Очень удобно когда вашему скрипту нужно и лог писать, и интерактивно сообщения на экран отправлять
- awk — потоковый редактор который помогает управлять текстом при выводе. Я например использую его для удобного оперирования многоколоночным текстом.
- tr — утилита для управления символами во входящем потоке текста. Подставлять или удаляет указанные символы
- cut — утилита обработки текста, позволяющая выбирать колонки из текста или поля из строки
- tac — команда, обратная команде cat — выводит файл (или конкатенацию файлов) но задом-наперед
- curl клиентская программа для взаимодействия с серверами, поддерживающими формат url обращений, обычно веб серверами. Лично я применяю ее как консольный клиент для работы с веб серверами- проверить доступность, статус, дернуть api и тд.
- wget — утилита для сетевой загрузки файлов.
- watch — утилита позволяющая отслеживать вывод не интерактивной программы, запуская ее многократно, с указанным интервалом времени. Удобно когда вы хотите посмотреть какой то процесс в динамике — например “watch cat /proc/mdstat”
- head — утилита обработки текста. Вывод указанное число строк с начала файла
- tail утилита обработки текста. Вывод указанное число строк с конца файла. Может работать в режиме постоянного чтения и вывода на экран информации, дописываемой другим процессом в конец файла. Удобно смотреть логи в режиме реального времени
2. Что сделает символ &, введенный сразу после команды? (What does an & after a command do?)
Автоматически отправляет команду работать в виде фонового процесса Вашей текущей сессии. Посмотреть список таких процессов можно командой jobs, вернуть назад при помощи команды fg:
kirill @ xxx : ~ $ ping ya .ru > / dev / null &
kirill @ xxx : ~ $ jobs
[ 1 ] + Запущен ping ya .ru > / dev / null &
kirill @ xxx : ~ $ fg 1
ping ya .ru > / dev / null
^ Ckirill @ xxx : ~ $
3. Что такое пакетный фильтр и как он работает? (What is a packet filter and how does it work?)
Пакетный фильтр — обобщенное название системы фильтрации трафика в linux-based операционных системах. Подсистема ядра, занимающаяся анализом и обработкой всех входящих сетевых пакетов заданным администратором правилам. Трафик либо пропускается, либо отбрасывается, либо каким-то образом маршрутизируется, либо логируется. Так же возможны некоторые комбинации этих действий.
4. Что такое виртуальная память? (What is Virtual Memory?)
Метод управления памятью, позволяющий выделить процессу памяти больше, чем на самом деле это возможно сделать. Программе “выделяется” некий пул страниц памяти, которые в дальнейшем могут быть перемещены на диск, в специализированную область подкачки, либо наоборот, “подняты” из нее в случае необходимости.
5. Что такое swap и для чего он используется? (What is swap and what is it used for?)
Специально выделенная область диска или файл, использующийся для расширения виртуального адресного пространства памяти ( см виртуальная память) за счет места на дисковом устройстве.
6. Что такое A, NS, PTR и CNAME записи? (What is an A record, an NS record, a PTR record, a CNAME record, an MX record?)
Ресурсные записи системы dns:
- A — основная запись, ставящая человеко-читаемое имя в формате fqdn в соответствие ip адресу.
- NS — ресурсная запись, содержащая информацию об ip адресе dns сервера, обслуживающего данный домен
- PTR — т.н. “обратная” ресурсная запись, противоположная А, ставящая в соответствие ip адресу имя в формате fqdn
- CNAME — ресурсная запись- псевдоним, позволяющая создать одноуровневую переадресацию, задавая соответствие имя-имя (например для сервера srv.example.com, функциональный псевдоним mail.example.com)
7. Какие есть еще ресурсные записи и для чего они используются? (Are there any other RRs and what are they used for?)
- MX — ресурсная запись, указывающая на сервер, обслуживающий почту в домене
- TXT — произвольная текстовая запись. Часто используется для различных проверок
- SRV — сервисная ресурсная запись. Указывает на местоположение серверов, обеспечивающих тот или иной сервис. Пример — Active Directory
- SOA — Базовая запись DNS зоны с ее параметрами и прочими ресурсными записями внутри.
- AAAA — то же самое что и A, только для ipv6
8. Что такое расщепление горизонта в терминах dns? (What is a Split-Horizon DNS?)
Прием, используемый для разрешения одного и того же DNS имени в разные ( по смыслу) IP адреса, например mail.example.com изнутри сети направит клиентов непосредственно на внутренний почтовый сервер, а снаружи, пользователи будут направлены на сервер, стоящий перед почтовиком и выполняющий роль антивирусного и спам сканера.
9. Что такое “липкий” бит? (What is the sticky bit?)
Дополнительный атрибут файла в UNIX файловых системах. Изначально он означал что программа, будучи запущена, должна оставаться в оперативной памяти целиком для ускорения работы и повторных обращений. С ростом объема оперативной памяти это стало не актуально и теперь этот бит выполняет роль защитного предохранителя для каталогов — если на каталог установлен “липкий бит”, пользователь, даже имея все необходимые права, сможет удалить только те файлы, владельцем которых он является.
10. Что означает для файла выставленный иммутабельный бит? (What does the immutable bit do to a file?)
Дополнительный атрибут файла в UNIX файловых системах, который будучи установленным для конкретного файла, не позволяет записывать в него изменения, тем самым принудительно устанавливая на него режим “только для чтения” (даже если редактировать его попытается пользователь с привилегиями root).
11. В чем разница между жесткой ссылкой/хардлинком и мягкой ссылкой/симлинком? Что происходит когда вы удаляете хардлинк/симлинк? (What is the difference between hardlinks and symlinks? What happens when you remove the source to a symlink/hardlink?)
Хардлинк — это по сути имя файла, символическое значение, ссылающееся на значение inode в файловой системе. Именно поэтому нельзя создать хардлинк на другой раздел. Хардлинков может быть создано более одного — это будут разные имена одного и того же файла. До тех пор пока существует хотя бы один хардлинк, файл существует.
Софтлинк это файл который просто внутри себя содержит указание на другой файл ( его имя). поэтому софтлинки являются более гибким решением, например могут указывать на файл, хранящийся на другой файловой системе), однако если оригинальный файл удален, симлинк остается и становится не рабочим.
12. Что такое айнода и что хранится в ней? (What is an inode and what fields are stored in an inode?)
Структура данных файловой системы в которой хранится информация о файле ( по одной айноде на файл), такая как:
- Блок данных с которого файл начинается
- Дата создания, изменения
- Права доступа
- Владелец
Имя файла не хранится в айноде — это хардлинк
13. как принудительно заставить систему выполнить проверку файловой системы при следующей загрузке? (How to force/trigger a file system check on next reboot?)
Мне известны два способа:
- Создать в корне пустой файл: touch /forcefsck — его наличие переопределяет все настройки для fsck в файле /etc/fstab и заставляет систему принудительно проверить корневую файловую систему при запуске. После успешной проверки файл удаляется
- Использовать команду tune2fs -c 1 /dev/sdb1 чтобы включить проверку файловой системы на sdb1 при следующей загрузке.
14. Что такое snmp и для чего он используется? (What is SNMP and what is it used for? )
simple network management protocol. переводить я думаю не нужно) Является стандартом дефакто в мире сетевого мониторинга, позволяя снимать различные метрики с хостов, устройств и любых объектов, которые могут быть подключены к сети и на которых производитель реализовал поддержку этого протокола.
15. Что такое уровень запуска и как посмотреть текущий? (What is a runlevel and how to get the current runlevel? )
Нумерованный режим функционирования операционной системы. В зависимости от номера ( уровня) зависит объем задействованных возможностей, например:
- 1 — однопользовательский режим, предназначен для различных административных действий по восстановлению системы; на этом уровне выполнения система полностью сконфигурирована, но не запущен ни один сервис, а из пользователей может работать только один root;
- 2 — многопользовательский режим без поддержки сети
- 3 — многопользовательский режим с поддержкой сети, нормальный режим работы сервера;
- 5 — загрузка в многопользовательском режиме с графическим входом в систему;
Текущий уровень можно посмотреть командой runlevel:
root @ xxx : ~ # runlevel
16. Что такое проброс портов ssh? (What is SSH port forwarding?)
Это жаргонизм, который означает создание посредством ssh (поверх ssh соединения) криптографически защищенного тоннеля, когда на вашей машине открывается локально слушающий порт, куда может быть направлен трафик ( и получен ответ), и который будет доставлен до удаленной машины или сети и направлен на указанный порт ( возможно в комбинации с удаленным ip адресом)
17. В чем разница между локальным и удаленным пробросом портов? (What is the difference between local and remote port forwarding?)
В такой формулировке к сожалению вопрос не очень понятный, я для себя воспринимаю его так:
- мы можем поверх ssh соединения к машине А, пробросить локальный порт со своей машины на некоторый локальный порт машины А. Например у нас есть веб сервер на котором так же установлена СУБД MySQL. Она сконфигурирована так, чтобы принимать соединения только с локальной машины ( в целях безопасности), но мы хотим подключиться к ней удаленно неким клиентским ПО ( стандартной утилитой mysql или графической оболочкой типа MySQL Workbrench). Для этого мы устанавливаем ssh соединения и поверх него настраиваем тоннель, соединяющий наш локальный порт xxx с портом localhost:3360 на удаленном сервере
- Удаленный проброс портов, когда у нас есть возможность соедениться по ssh с машиной в удаленной сети, но по факту нам нужно попасть на другую машину в той же сети, куда прямого доступа нет. Тогда мы можем “пробросить” это соединение через машину, которая нам доступна.
18. Какие действия необходимо выполнить чтобы добавить пользователя без использования команд useradd/adduse ? (What are the steps to add a user to a system without using useradd/adduser?)
- Создать соответствующие записи в файлах /etc/passwd, /etc/shadow, /etc/groups
- Создать домашний каталог с необходимым содержимым
- Сделать нового пользователя владельцем этого каталога
19. Что такое мажорная и минорная версия файла? (What is MAJOR and MINOR numbers of special files?)
Согласно документации semver (https://semver.org/lang/ru/), действуют следующие понятия:
Обычный номер версии ДОЛЖЕН иметь формат X.Y.Z, где X, Y и Z — неотрицательные целые числа и НЕ ДОЛЖНЫ начинаться с нуля. X — мажорная версия, Y — минорная версия и Z — патч-версия.
Минорная версия (x.Y.z | x > 0) ДОЛЖНА быть увеличена, если в публичном API представлена новая обратно совместимая функциональность. Версия ДОЛЖНА быть увеличена, если какая-либо функциональность публичного API помечена как устаревшая (deprecated). Версия МОЖЕТ быть увеличена в случае реализации новой функциональности или существенного усовершенствования в приватном коде. Версия МОЖЕТ включать в себя изменения, характерные для патчей. Патч-версия ДОЛЖНА быть обнулена, когда увеличивается минорная версия.
Мажорная версия X (X.y.z | X > 0) ДОЛЖНА быть увеличена, если в публичном API представлены какие-либо обратно несовместимые изменения. Она МОЖЕТ включать в себя изменения, характерные для уровня минорных версий и патчей. Когда увеличивается мажорная версия, минорная и патч-версия ДОЛЖНЫ быть обнулены.
20. Опишите команду mknod и для чего бы вы ее применили? (Describe the mknod command and when you’d use it)
Команда используется для создания специальных файлов-устройств — символьных, блочных и именованных каналов типа fifo. Первые два типа сейчас создавать нет смысла — системы типа udev сделают это за вас при подключении устройства, а вот создать именованный fifo канал для связи двух программ можно — первая в канал пишет, вторая из него читает.
21. Опишите сценарий, при котором вы получаете сообщение “нет свободного места на файловой системе”, при этом команда “df” показывает что свободное место еще есть? (Describe a scenario when you get a «filesystem is full» error, but ‘df’ shows there is free space.)
Все очень просто — закончились свободные inode на данной файловой системе. То есть свободные блоки на блочном устройстве еще есть, а вот свободные файловые структуры под метаданные файлов в самой файловой системе закончились.
Проверить можно командой:
kirill @ xxx : ~ $ df — i /
Файл . система I нодов I Использовано I Свободно I Использовано % C монтировано в
/ dev / mapper / neon — vg — root 14548992 664833 13884159 5 % /
kirill @ xxx : ~ $
22. Опишите сценарий при удалении файла, когда “df” не показывает что после удаления место освободилось? (Describe a scenario when deleting a file, but ‘df’ not showing the space being freed.)
Опять же, все очень просто — дескриптор удаленного файла остался открыт каким-то приложением, которое тем самым не позволяет освободить место на файловой системе. выход из ситуации — при помощи команды lsof посмотреть список открытых файлов. Удаленные но все еще открытые файлы будут помечены как deleted, но так же вы увидите pid процесса который держит открытый дескриптор. Перезапустите этот процесс ( если это сервис) или просто убейте и вы увидите, что место освободилось!
23. Опишите, как работает утилита “ps”? (Describe how ‘ps’ works.)
В Linux команда ps работает со специальной псевдофайловой системой proc. Каталог /proc/PID содержит большое количество различных файлов, которые предоставляют информацию о процессе с номером PID. Эти файлы и каталоги создаются “на лету” ядром операционной системы
Вы сами можете убедиться в этом, используя утилиту strace при запуске команды ps — вы увидите все системные вызовы и файлы, которые открывает программа.
24. Что происходит, когда дочерний процесс умирает, не имея родителя, который бы дождался окончания его работы и чем это плохо? (What happens to a child process that dies and has no parent process to wait for it and what’s bad about this?)
Если родитель умер раньше завершения дочернего процесса, такой процесс называется “осиротевшим” и его забирает (“усыновляет”) процесс init, который корректно может его завершить. Если же мы имеем дело с ситуацией когда дочерний процесс умер а родитель просто не обработал это (возможно именно это имеется ввиду) — он превращается в процесс-зомби. Фактически ресурсов он не потребляет, остается только запись в таблице процессов, то есть некоторое число открытых файлов. Пока он один это не страшно, но если число зомби процессов растет, мы рискуем попасть в ситуацию когда мы достигнем лимита числа открытых файловых дескрипторов…. В итоге вы не сможете зайти на сервер или уже будучи там, не сможете запустить новый экземпляр bash или любую команду.
25. Кратко объясните каждое из состояний процесса? (Explain briefly each one of the process states.)
Существует всего 3 состояния, в которых может находиться процесс:
- Работающий процесс — в данный момент код процесса выполняется.
- Спящий процесс — в данный момент код процесса не выполняется в ожидании какого либо события (нажатия клавиши на клавиатуре, поступление данных из сети и т.д.)
- Процесс-зомби — сам процесс уже не существует, его код и данные выгружены из оперативной памяти, но запись в таблице процессов остается по тем или иным причинам.
26. Как узнать какой процесс слушает указанный порт? (How to know which process listens on a specific port?)
Есть старый и новый способ.
Старый способ- использовать команду netstat — например мы хотим узнать кто слушает 53/tcp порт:
kirill @ xxx : Загрузки $ sudo netstat — antpl | grep 53 | grep — i listen
tcp 0 0 127.0.0.53 : 53 0.0.0.0 : * LISTEN 870 / systemd — resolve
Новый способ — утилита ss. Посмотрим для того же порта:
kirill @ xxx : Загрузки $ sudo ss — antpl | grep 53
LISTEN 0 128 127.0.0.53 % lo : 53 0.0.0.0 : * users : ( ( «systemd-resolve» , pid = 870 , fd = 13 ) )
27. Что такое зомби-процесс и что может быть его причиной? (What is a zombie process and what could be the cause of it?)
Зомби в операционных системах UNIX называют завершившиеся процессы, код завершения которых не забрал родительский процесс. Зомби не потребляют никаких ресурсов, память и файловые дескрипторы таких процессов уже освобождены. Остается только запись в таблице процессов, которая занимает несколько десятков байт памяти. Так что единичный зомби процесс на систему никак не влияет. НО он явный индикатор того, что у какого то процесса в системе что то пошло не так.
Поиск зомби: ps -axho state,pid,ppid | grep Z | sed ‘s/./ps/’ | sh
Данная команда покажет все зомби процессы и их родителей (тестировалась под linux, под другими *nix возможны другие ключи у команды ps).
Убить зомби можно только перезапуском родительского процесса. kill -9 самого процесса-зомби и чеснок обычно не помогают. Если появление зомби разовое явление, то возможно проще на факт его появления забить
Вариант 2: родительский процесс использует wait, но зомби все равно появляются. Копать в сторону того, какая разновидность wait используется, если waitpid, которая проверяет завершение конкретных потомков, то смотреть откуда она берет проверяемые pid, возможно в процессе работы какие то pid потомков теряются и программа про них забывает. Может программа в какой то момент запрещает обработку сигналов и забывает восстановить обработку, после прохождения критического участка. Опять же — вариантов очень много, но сосредоточены они вокруг обработчика SIGCHLD и функций wait.
Вариант 3: родительский процесс умеет обрабатывать и готов правильно обработать завершение своих потомков. Но зацикливается в другом месте программы или засыпает на системном вызове, например чтения с сетевого диска, который стал недоступен и при этом прерывание по SIGCHLD запрещено. В этом случае надо разбираться с причинами его зависания. Кстати, отсутствие доступа к каким либо ресурсам, типа сетевых дисков (или при выходе из строя физического диска) — довольно частая причина массового появления зомби.
Каких либо специальных логов в системе, где можно было бы увидеть хоть какую то информацию по появляющимся зомби не существует. Следы можно найти только в логах той программы, которая их порождает, при их наличии.
28. Вы запускаете баш скрипт и хотите одновременно видеть его вывод на экран а так же в то же время писать лог скрипта в файл. Как это реализовать? (You run a bash script and you want to see its output on your terminal and save it to a file at the same time. How could you do it?)
Использовать утилиту tee, которая принимая на свой sdtin вывод вашего скрипта, может отдать вывод как вам на stdout так и в указанный файл.
29. Опишите, что делает команда “echo «1» > /proc/sys/net/ipv4/ip_forward”? (Explain what echo «1» > /proc/sys/net/ipv4/ip_forward does.)
Данная команда передает ядру опцию включения механизма маршрутизации. После активации этого механизма, linux-хост может выполнять роль маршрутизатора и/или шлюза.