Bash-скрипты, часть 7: sed и обработка текстов

Bash-скрипты: начало
Bash-скрипты, часть 2: циклы
Bash-скрипты, часть 3: параметры и ключи командной строки
Bash-скрипты, часть 4: ввод и вывод
Bash-скрипты, часть 5: сигналы, фоновые задачи, управление сценариями
Bash-скрипты, часть 6: функции и разработка библиотек

В прошлый раз мы говорили о функциях в bash-скриптах, в частности, о том, как вызывать их из командной строки. Наша сегодняшняя тема — весьма полезный инструмент для обработки строковых данных — утилита Linux, которая называется sed. Её часто используют для работы с текстами, имеющими вид лог-файлов, конфигурационных и других файлов.



Если вы, в bash-скриптах, каким-то образом обрабатываете данные, вам не помешает знакомство с инструментами sed и gawk. Тут мы сосредоточимся на sed и на работе с текстами, так как это — очень важный шаг в нашем путешествии по бескрайним просторам разработки bash-скриптов.

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

Основы работы с sed

Утилиту sed называют потоковым текстовым редактором. В интерактивных текстовых редакторах, наподобие nano, с текстами работают, используя клавиатуру, редактируя файлы, добавляя, удаляя или изменяя тексты. Sed позволяет редактировать потоки данных, основываясь на заданных разработчиком наборах правил. Вот как выглядит схема вызова этой команды:

$ sed options file

По умолчанию sed применяет указанные при вызове правила, выраженные в виде набора команд, к STDIN. Это позволяет передавать данные непосредственно sed.

Например, так:

$ echo "This is a test" | sed 's/test/another test/'

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

Простой пример вызова sed

В данном случае sed заменяет слово «test» в строке, переданной для обработки, словами «another test». Для оформления правила обработки текста, заключённого в кавычки, используются прямые слэши. В нашем случае применена команда вида s/pattern1/pattern2/. Буква «s» — это сокращение слова «substitute», то есть — перед нами команда замены. Sed, выполняя эту команду, просмотрит переданный текст и заменит найденные в нём фрагменты (о том — какие именно, поговорим ниже), соответствующие pattern1, на pattern2.

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

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

$ sed 's/test/another test' ./myfile

Текстовый файл и результаты его обработки

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

Sed не меняет данные в обрабатываемом файле. Редактор читает файл, обрабатывает прочитанное, и отправляет то, что получилось, в STDOUT. Для того, чтобы убедиться в том, что исходный файл не изменился, достаточно, после того, как он был передан sed, открыть его. При необходимости вывод sed можно перенаправить в файл, возможно — перезаписать старый файл. Если вы знакомы с одним из предыдущих материалов этой серии, где речь идёт о перенаправлении потоков ввода и вывода, вы вполне сможете это сделать.

Выполнение наборов команд при вызове sed

Для выполнения нескольких действий с данными, используйте ключ -e при вызове sed. Например, вот как организовать замену двух фрагментов текста:

$ sed -e 's/This/That/; s/test/another test/' ./myfile

Использование ключа -e при вызове sed

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

$ sed -e ' > s/This/That/ > s/test/another test/' ./myfile

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

Другой способ работы с sed

Чтение команд из файла

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

Вот содержимое файла mycommands:

s/This/That/ s/test/another test/

Вызовем sed, передав редактору файл с командами и файл для обработки:

$ sed -f mycommands myfile

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

Использование файла с командами при вызове sed

Флаги команды замены

Внимательно посмотрите на следующий пример.

$ sed 's/test/another test/' myfile

Вот что содержится в файле, и что будет получено после его обработки sed.

Исходный файл и результаты его обработки

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

Схема записи команды замены при использовании флагов выглядит так:

s/pattern/replacement/flags

Выполнение этой команды можно модифицировать несколькими способами.

  • При передаче номера учитывается порядковый номер вхождения шаблона в строку, заменено будет именно это вхождение.
  • Флаг g указывает на то, что нужно обработать все вхождения шаблона, имеющиеся в строке.
  • Флаг p указывает на то, что нужно вывести содержимое исходной строки.
  • Флаг вида w file указывает команде на то, что нужно записать результаты обработки текста в файл.

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

$ sed 's/test/another test/2' myfile

Вызов команды замены с указанием позиции заменяемого фрагмента

Тут мы указали, в качестве флага замены, число 2. Это привело к тому, что было заменено лишь второе вхождение искомого шаблона в каждой строке. Теперь опробуем флаг глобальной замены — g:

$ sed 's/test/another test/g' myfile

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

Глобальная замена

Флаг команды замены p позволяет выводить строки, в которых найдены совпадения, при этом ключ -n, указанный при вызове sed, подавляет обычный вывод:

$ sed -n 's/test/another test/p' myfile

Как результат, при запуске sed в такой конфигурации на экран выводятся лишь строки (в нашем случае — одна строка), в которых найден заданный фрагмент текста.

Использование флага команды замены p

Воспользуемся флагом w, который позволяет сохранить результаты обработки текста в файл:

$ sed 's/test/another test/w output' myfile

Сохранение результатов обработки текста в файл

Хорошо видно, что в ходе работы команды данные выводятся в STDOUT, при этом обработанные строки записываются в файл, имя которого указано после w.

Символы-разделители

Представьте, что нужно заменить /bin/bash на /bin/csh в файле /etc/passwd. Задача не такая уж и сложная:

$ sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd

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

К счастью, sed позволяет нам самостоятельно задавать символы-разделители для использования их в команде замены. Разделителем считается первый символ, который будет встречен после s:

$ sed 's!/bin/bash!/bin/csh!' /etc/passwd

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

Выбор фрагментов текста для обработки

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

  • Задать ограничение на номера обрабатываемых строк.
  • Указать фильтр, соответствующие которому строки нужно обработать.

Рассмотрим первый подход. Тут допустимо два варианта. Первый, рассмотренный ниже, предусматривает указание номера одной строки, которую нужно обработать:

$ sed '2s/test/another test/' myfile

Обработка только одной строки, номер который задан при вызове sed

Второй вариант — диапазон строк:

$ sed '2,3s/test/another test/' myfile

Обработка диапазона строк

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

$ sed '2,$s/test/another test/' myfile

Обработка файла начиная со второй строки и до конца

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

$ sed '/likegeeks/s/bash/csh/' /etc/passwd

По аналогии с тем, что было рассмотрено выше, шаблон передаётся перед именем команды s.

Обработка строк, соответствующих фильтру

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

Удаление строк

Утилита sed годится не только для замены одних последовательностей символов в строках на другие. С её помощью, а именно, используя команду d, можно удалять строки из текстового потока.

Вызов команды выглядит так:

$ sed '3d' myfile

Мы хотим, чтобы из текста была удалена третья строка. Обратите внимание на то, что речь не идёт о файле. Файл останется неизменным, удаление отразится лишь на выводе, который сформирует sed.

Удаление третьей строки

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

Вот как применить команду d к диапазону строк:

$ sed '2,3d' myfile

Удаление диапазона строк

А вот как удалить строки, начиная с заданной — и до конца файла:

$ sed '3,$d' myfile

Удаление строк до конца файла

Строки можно удалять и по шаблону:

$ sed '/test/d' myfile

Удаление строк по шаблону

При вызове d можно указывать пару шаблонов — будут удалены строки, в которых встретится шаблон, и те строки, которые находятся между ними:

$ sed '/second/,/fourth/d' myfile

Удаление диапазона строк с использованием шаблонов

Вставка текста в поток

С помощью sed можно вставлять данные в текстовый поток, используя команды i и a:

  • Команда i добавляет новую строку перед заданной.
  • Команда a добавляет новую строку после заданной.

Рассмотрим пример использования команды i:

$ echo "Another test" | sed 'i\First test '

Команда i

Теперь взглянем на команду a:

$ echo "Another test" | sed 'a\First test '

Команда a

Как видно, эти команды добавляют текст до или после данных из потока. Что если надо добавить строку где-нибудь посередине?

Тут нам поможет указание номера опорной строки в потоке, или шаблона. Учтите, что адресация строк в виде диапазона тут не подойдёт. Вызовем команду i, указав номер строки, перед которой надо вставить новую строку:

$ sed '2i\This is the inserted line.' myfile

Команда i с указанием номера опорной строки

Проделаем то же самое с командой a:

$ sed '2a\This is the appended line.' myfile

Команда a с указанием номера опорной строки

Обратите внимание на разницу в работе команд i и a. Первая вставляет новую строку до указанной, вторая — после.

Замена строк

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

$ sed '3c\This is a modified line.' myfile

Замена строки целиком

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

$ sed '/This is/c This is a changed line of text.' myfile

Замена строк по шаблону

Замена символов

Команда y работает с отдельными символами, заменяя их в соответствии с переданными ей при вызове данными:

$ sed 'y/123/567/' myfile

Замена символов

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

Вывод номеров строк

Если вызвать sed, использовав команду =, утилита выведет номера строк в потоке данных:

$ sed '=' myfile

Вывод номеров строк

Потоковый редактор вывел номера строк перед их содержимым.

Если передать этой команде шаблон и воспользоваться ключом sed -n, выведены будут только номера строк, соответствующих шаблону:

$ sed -n '/test/=' myfile

Вывод номеров строк, соответствующих шаблону

Чтение данных для вставки из файла

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

Рассмотрим пример:

$ sed '3r newfile' myfile

Вставка в поток содержимого файла

Тут содержимое файла newfile было вставлено после третьей строки файла myfile.

Вот что произойдёт, если применить при вызове команды r шаблон:

$ sed '/test/r newfile' myfile

Использование шаблона при вызове команды r

Содержимое файла будет вставлено после каждой строки, соответствующей шаблону.

Пример

Представим себе такую задачу. Есть файл, в котором имеется некая последовательность символов, сама по себе бессмысленная, которую надо заменить на данные, взятые из другого файла. А именно, пусть это будет файл newfile, в котором роль указателя места заполнения играет последовательность символов DATA. Данные, которые нужно подставить вместо DATA, хранятся в файле data.

Решить эту задачу можно, воспользовавшись командами r и d потокового редактора sed:

$ Sed '/DATA>/ { r newfile d}' myfile

Замена указателя места заполнения на реальные данные

Как видите, вместо заполнителя DATA sed добавил в выходной поток две строки из файла data.

Итоги

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

На сегодня это всё. В следующий раз поговорим о языке обработки данных awk.

Уважаемые читатели! А вы пользуетесь sed в повседневной работе? Если да — поделитесь пожалуйста опытом.
ссылка на оригинал статьи https://habrahabr.ru/post/327530/

Разработчики Debian публикуют отчет о подготовке «Stretch» и отключают поддержку FTP на своих серверах

На днях команда разработки Debian опубликовала очередной отчет о подготовке и результатах работы над следующей веткой Debian — «Stretch». На сегодняшний день пакетная база Debian 9 находится в «заморозке» уже почти три месяца. Причина — скорый релиз с остановкой переноса пакетов unstable в testing. Что касается нового релиза, то разработчики насчитали 143 критических для его формирования ошибок. К сожалению, пока что точная дата релиза неизвестна. Разработчики говорят о «начале лета», но это тоже неточно.

Для того, чтобы сформировать релиз в сроки (которые, правда, регулярно переносятся), разработчики решили пока вывести обеспечение поддержки UEFI Secure Boot из категории критически важных, блокирующих релиз. Видимо, есть опасения в том, что команда, не успевая подготовить все в срок, может допустить ошибки при разработке, менее тщательно проверяя свою работу. Поэтому, насколько можно судить, у Debian 9 не будет поддержки UEFI Secure Boot на момент релиза. Она будет добавлена чуть позже, когда разработчики будут немного менее загружены.

Кроме того, от разработчиков поступают и другие новости. Например, было решено отказаться от создания полных наборов образов на CD для Debian. В такой набор входит около 80 дисков и по понятным причинам их не слишком охотно разбирают пользователи. Также не будут больше выпускаться ограниченные установочные сборки на CD с разными рабочими столами. Команда решила оставить лишь образ на одном диске с рабочим столом Xfce и сборки для загрузки пакетов по сети. Что касается других форматов, включая DVD, Blu-Ray (BD) и двухслойные Blue-Ray, то такие сборки будут продолжать выпускать.

Плюс ко всему, теперь снова будут формировать Live-сборки, выпуская их каждую неделю. Это касается, в первую очередь, архитектур amd64 и i386. Такие сборки будут включать рабочие столы GNOME, KDE, Cinnamon, Mate, Xfce и LXDE. Стоит отметить, что в новых Live-сборках есть поддержка загрузки на системах с UEFI. Появились также сборки для развертывания облачных систем, пока что OpenStack для ARM64. Плюс есть неофициальные сборки с несвободными прошивками. Они формируются в различных формах, включая Live-сборки, сборки для работы по сети и DVD.

Что касается своей работы, то Debian отказывается от поддержки File Transfer Protocol (FTP) на своих серверах. Причина — мало кто с ними работает, а вот поддержка требует времени и усилий. Так что ftp.debian.org and security.debian.org больше работать не будут. Команда заявила по этому поводу следующее: «FTP не поддерживают кэширование и ускорение», а сам «протокол неэффективен и требует сложных настроек для конфигурирования файерволлов и настройки демонов». Правда, какое-то время будут поддерживаться ftp.upload.debian.org и security-master.debian.org. Но общедоступные сервисы уберут с 1 ноября 2017 года.


ссылка на оригинал статьи https://habrahabr.ru/post/327564/

Не нужно оборачивать все в Promise

Оригинал статьи

Представим что мы разрабатываем библиотеку которая содержит функцию isPi:

function isPi(x) {   return x === 3.14 }

И тут кто-то говорит: "Мы не хотим знать откуда приходят данные (БД, сеть и т.д.), по-этому давайте обернем наш код в Promise":

function isPi(px) {   return px.then(x =>     return x === 3.14   }) }

И будем вызывать функцию так:

const px = grabXfromDb() isPi(px)

– круто, да? – Нет

Проблема в том, что утверждение: "Мы оборачиваем код в Promise, чтобы не парится асинхронный код или синхронный" – не верно. Мы не оборачиаем код, но Promise это "что-то", что может:

  • либо "разрешиться" с значением
  • либо "сорваться" с ошибкой

когда-нибудь в будущем:

px.then(x =>   // в этом месте x содержит уже известное "синхронное" значение   isPi(x) )

А значит мы можем удалить избыточный код:

function isPi(x) {   return x === 3.14 }  const px = grabXfromDb() px.then(isPi)

Вы можете спросить: ну ладно, начальный код чуть избыточен, что в этом плохого?
Проблема возникает когда мы пытаемся добавить обратку ошибок – очень легко закончить с чем-то вроде:

function isPi(px) {   return px.then(x =>     return x === 3.14   })   .catch(fuction(e) {     assert(e instanceof SomeDBError)     // какой-нибудь код на случай ошибки базы данных   }) }

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

function isPi(x) {   return x === 3.14 }  grabXfromDb()   .catch(fuction(e) {     assert(e instanceof SomeDBError)     // some code here    }) .then(isPi)

Несколько простых правил:

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

ссылка на оригинал статьи https://habrahabr.ru/post/327560/

Как позаботиться о сайтах во время праздников

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

Реальная жизнь

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

Праздники: ждем или боимся

Итак, есть задача: сайт/сервер/сервис должен работать непрерывно несколько дней без человеческого вмешательства. Что же может этому помешать?
Во-первых, обычные сбои, которые время от времени случаются везде. Только вот ночной сбой со вторника на среду решается перегрузкой/перезаливом с бэкапа в среду утром. А на выходных нередки сбои «с пятницы на понедельник». Сколько в таком случае может лежать сайт во время праздников, зависит от длительности отпуска ответственных сотрудников.
Во-вторых, у хостера тоже работают люди (пока). А это значит, что они тоже могут уйти в отпуск, оставив на хозяйстве 3,5 случайно подвернувшихся под руку раздолбаев. А кроме того, перед походом в отпуск, они запросто могут запланировать апгрейд оборудования/мегабэкап/кучу обновлений, следуя логике «никто не заметит». Что с этим делать — я писал ранее.
В-третьих, недобросовестные конкуренты или злоумышленники часто планируют свои черные делишки именно на это время, рассчитывая на возросшее время реакции с вашей стороны.

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

Все это приводит к прямым и непрямым потерям денег и клиентов и запросто испортит всем послевкусие после праздников.

Что делать?

Решение можно найти за небольшие деньги или даже бесплатно, в зависимости от потребностей. Я опишу подход с помощью сервиса мониторинга сайтов ХостТрекер. Опережаю вопрос: да, есть Яндекс.Метрика и подобные вещи. Но в чем их отличия и минусы, я подробно объяснил вот здесь. Если кратко — это не их основная функция, и прислать оповещение через несколько часов — это норма. К тому же, далеко не все ошибки попадают в их поле зрения.

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

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

Как и всегда, мы рады всем предложениям и пожеланиям. И хорошего всем отдыха!
ссылка на оригинал статьи https://habrahabr.ru/post/327548/

Конфигурирование Spark на YARN

Хабр, привет!

Вчера на митапе, посвященном Apache Spark, от ребят из Rambler&Co было довольно много вопросов от участников, связанных с конфигурированием этого инструмента. Решили по его следам поделиться своим опытом. Тема непростая — поэтому предлагаем делиться опытом тоже в комментариях, может быть, мы тоже что-то не так понимаем и используем.

Небольшая вводная — как мы используем Spark. У нас есть трёхмесячная программа “Специалист по большим данным”, и весь второй модуль наши участники работают на этом инструменте. Соответственно, наша задача, как организаторов, подготовить кластер под использование в рамках такого кейса.

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

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

Начнём с самого начала. У Spark есть 3 варианта работать на кластере: standalone, с использованием Mesos и с использованием YARN. Мы решили выбрать третий вариант, потому что для нас он был логичен. У нас уже есть hadoop-кластер. Наши участники хорошо уже знакомы с его архитектурой. Давайте юзать YARN.

spark.master=yarn

Далее интереснее. У каждого из этих 3 вариантов развертывания есть 2 варианта деплоя: client и cluster. Исходя из документации и разных ссылок в интернете, можно сделать вывод, что client подходит для интерактивной работы — например, через jupyter notebook, а cluster больше подходит для production-решений. В нашем случае нас интересовала интерактивная работа, поэтому:

spark.deploy-mode=client

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

spark.dynamicAllocation.enabled=true

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

spark.shuffle.service.enabled=true

Зачем он нужен? Когда наш джоб больше не требует такого количества ресурсов, то Spark должен вернуть их в общий пул. Самая трудозатратная стадия почти в любой MapReduce задаче — это стадия Shuffle. Этот параметр позволяет сохранять данные, которые образуются на этой стадии и соответственно освобождать executors. А executor — это процесс, который на воркере всё обсчитывает. У него есть какое-то количество процессорных ядер и какое-то количество памяти.

Добавили этот параметр. Всё вроде бы заработало. Стало заметно, что участникам реально стало выдаваться больше ресурсов, когда им было нужно. Но возникла другая проблема — в какой-то момент другие участники просыпались и тоже хотели использовать Spark, а там всё занято, и они были недовольны. Их можно понять. Стали смотреть в документацию. Там оказалось, что есть еще какое-то количество параметров, при помощи которых можно повлиять на процесс. Например, если executor находится в режиме ожидания — через какое время у него можно забрать ресурсы?

spark.dynamicAllocation.executorIdleTimeout=120s

В нашем случае — если ваши executors ничего не делают в течение двух минут, то, будьте добры, верните их в общий пул. Но и этого параметра не всегда хватало. Было видно, что человек уже давно ничего не делает, а ресурсы не освобождаются. Оказалось, что есть еще специальный параметр — по прошествии какого времени отбирать executors, которые содержат закэшированные данные. По дефолту этот параметр стоял — infinity! Мы его поправили.

spark.dynamicAllocation.cachedExecutorIdleTimeout=600s

То есть если в течение 5 минут ваши executors ничего не делают, отдайте-ка их в общий пул. В таком режиме скорость освобождения и выдачи ресурсов для большого количества пользователей стала достойной. Количество недовольства сократилось. Но мы решили пойти дальше и ограничить максимальное количество executors на один application — по сути на одного участника программы.

spark.dynamicAllocation.maxExecutors=19

Теперь, конечно, появились недовольные с другой стороны — “кластер простаивает, а у меня всего лишь 19 executors”, но что поделать — нужен какой-то правильный баланс. Всех сделать счастливыми не получится.

И еще одна небольшая история, связанная со спецификой нашего кейса. Как-то на практическое занятие опоздали несколько человек, и у них Spark почему-то не стартовал. Мы посмотрели на количество свободных ресурсов — вроде бы есть. Spark должен стартовать. Благо, что к тому моменту документация уже где-то записалась на подкорку, и мы вспомнили, что при запуске Spark ищет себе порт, на котором стартовать. Если первый порт из диапазона занят, то он переходит к следующему по порядку. Если он свободен, то захватывает. И есть параметр, который указывает на максимальное количество попыток для этого. По умолчанию — это 16. Число меньше, чем людей в нашей группе на занятии. Соответственно, после 16 попыток Spark бросал это дело и говорил, что не могу стартануть. Мы поправили этот параметр.

spark.port.maxRetries=50

Дальше расскажу о некоторых настройках, уже не сильно связанных со спецификой нашего кейса.

Для более быстрого старта Spark есть рекомендация папку jars, лежащую в домашней директории SPARK_HOME, заархивировать и положить на HDFS. Тогда он не будет тратить времени на загрузку этих джарников по воркерам.

spark.yarn.archive=hdfs:///tmp/spark-archive.zip

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

spark.serializer=org.apache.spark.serializer.KryoSerializer

И есть еще давняя проблема Spark, что он часто валится по памяти. Часто это происходит в тот момент, когда воркеры всё посчитали и отправляют результат на драйвер. Мы сделали себе этот параметр побольше. По умолчанию, он 1Гб, мы сделали — 3.

spark.driver.maxResultSize=3072

И последнее, в качестве десерта. Как обновить Spark до версии 2.1 на HortonWorks дистрибутиве — HDP 2.5.3.0. Эта версия HDP содержит в себе предустановленную версию 2.0, но мы как-то однажды для себя решили, что Spark довольно активно развивается, и каждая новая версия фиксит какие-то баги плюс дает дополнительные возможности, в том числе и для python API, поэтому решили, что нужно делать апдейт.

Скачали версию с официального сайта под Hadoop 2.7. Разархивировали, закинули в папку с HDP. Поставили симлинки как надо. Запускаем — не стартует. Пишет очень непонятную ошибку.

java.lang.NoClassDefFoundError: com/sun/jersey/api/client/config/ClientConfig

Погуглив, выяснили, что Spark решил не ждать пока Hadoop разродится, и решили использовать новую версию jersey. Они сами там друг с другом ругаются на эту тему в JIRA. Решением было — скачать jersey версии 1.17.1. Закинуть это в папку jars в SPARK_HOME, снова сделать zip и закинуть на HDFS.

Эту ошибку мы обошли, но возникла новая и довольно-таки обтекаемая.

org.apache.spark.SparkException: Yarn application has already ended! It might have been killed or unable to launch application master

При этом пробуем запускать версию 2.0 — всё ок. Попробуй догадайся, в чем дело. Мы залезли в логи этого application и увидели что-то такое:

/usr/hdp/${hdp.version}/hadoop/lib/hadoop-lzo-0.6.0.${hdp.version}.jar

В общем, по каким-то причинам hdp.version не резолвилась. Погуглив, нашли решение. Нужно в Ambari зайти в настройки YARN и добавить там параметр в custom yarn-site:

hdp.version=2.5.3.0-37

Эта магия помогла, и Spark взлетел. Протестили несколько наших jupyter-ноутбуков. Всё работает. К первому занятию по Spark в субботу (уже завтра) мы готовы!
ссылка на оригинал статьи https://habrahabr.ru/post/327556/