0Pricing
DevOps Bootcamp · Урок

Оркестрация рабочих нагрузок с GNU parallel

Распределяйте большие наборы входных данных по ядрам с помощью GNU parallel, слотов заданий и упорядочивания результатов

«Оркестрация рабочих нагрузок с GNU parallel» — бесплатный урок DevOps Bootcamp на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения DevOps Bootcamp, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс DevOps Bootcamp содержит 4 уроков всего.

Что такое GNU parallel и зачем он нужен

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

  • Скорость: задача, выполняемая последовательно 8 минут, может завершиться примерно за 1 минуту на машине с 8 ядрами.
  • Простота: инструмент принимает данные из стандартного ввода, файлов или списков аргументов — ручное управление процессами не требуется.
  • Безопасность: вывод разных заданий хранится раздельно; результаты никогда не перемешиваются.

Установите инструмент с помощью sudo apt install parallel (Debian/Ubuntu) или brew install parallel (macOS). Проверьте установку с помощью parallel --version.

Ваша первая команда parallel

В простейшей форме parallel считывает элементы из стандартного ввода и запускает для каждого из них команду. Заполнитель {} обозначает текущий входной элемент.

В приведённом ниже примере пять файлов журналов одновременно сжимаются с помощью gzip. Без parallel файлы сжимались бы один за другим. С ним одновременно сжимается до N файлов, где N — количество ядер CPU.

#!/usr/bin/env bash
# Create sample files first
for i in 1 2 3 4 5; do
  dd if=/dev/urandom bs=1M count=2 of="log_${i}.txt" 2>/dev/null
done

# Compress all of them in parallel
ls log_*.txt | parallel gzip {}

echo "Done. Compressed files:"
ls log_*.txt.gz

Управление слотами заданий с помощью -j

По умолчанию parallel запускает по одному заданию на каждое ядро CPU. Переопределить это поведение можно с помощью флага -j (или --jobs).

  • -j 4 — одновременно выполнять ровно 4 задания
  • -j 0 — запускать столько заданий, сколько имеется входных данных (используйте осторожно!)
  • -j 200% — запускать вдвое больше заданий, чем имеется ядер CPU (полезно для задач, ограниченных вводом-выводом)
  • -j 50% — использовать только половину доступных ядер

Для задач, ограниченных CPU, значение -j $(nproc) часто является оптимальным. Для задач, связанных с сетевым вводом-выводом или диском, можно безопасно превысить количество ядер, поскольку задания большую часть времени ожидают.

#!/usr/bin/env bash
# Show how many cores are available
echo "CPU cores: $(nproc)"

# Run 8 sleep jobs but limit to 3 at a time
# -j 3 means at most 3 jobs run simultaneously
seq 1 8 | parallel -j 3 'echo "Starting job {}"; sleep 1; echo "Done job {}"'

echo "All jobs finished."

Чтение входных данных из файлов и аргументов

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

  • Из файла: parallel -a urls.txt wget {}
  • Встроенный список аргументов: parallel echo ::: apple banana cherry
  • Несколько источников аргументов (декартово произведение): parallel echo {1}-{2} ::: a b c ::: 1 2 — создаёт a-1, a-2, b-1, b-2, c-1, c-2
  • Явно из стандартного ввода: cat list.txt | parallel -j4 process {}

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

#!/usr/bin/env bash
# Inline list with :::
parallel echo 'Hello from {}' ::: Alice Bob Carol Dave

echo '---'

# Cartesian product: combine two lists
# Generates: dev-v1, dev-v2, prod-v1, prod-v2
parallel echo 'Deploy {1} to env {2}' ::: v1 v2 ::: dev prod

Заполнители: обработка входных элементов

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

  • {} — полный входной элемент
  • {.} — входной элемент без расширения файла (report.csv → report)
  • {/} — только базовое имя (путь к каталогу удаляется)
  • {//} — только путь к каталогу
  • {/.} — базовое имя без расширения

Благодаря этому вызовы basename / dirname внутри команды задания не нужны, а конвейеры становятся понятнее и быстрее.

#!/usr/bin/env bash
# Demonstrate placeholder substitutions
parallel --dry-run 'convert {} -resize 800x600 {.}_thumb.jpg' \
  ::: /photos/vacation/beach.png /photos/work/team.png

# {.}  strips extension: /photos/vacation/beach
# Result command shown (--dry-run does NOT execute):
#  convert /photos/vacation/beach.png -resize 800x600 /photos/vacation/beach_thumb.jpg
#  convert /photos/work/team.png      -resize 800x600 /photos/work/team_thumb.jpg
echo 'No files were changed (dry run)'

Сохранение порядка вывода с помощью --keep-order

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

Порядок вывода управляется двумя флагами:

  • --keep-order (-k) — выводит результат каждого задания в том же порядке, что и входные данные, даже если более позднее задание завершилось первым. Вывод буферизуется до завершения предыдущих заданий.
  • --line-buffer — промежуточный вариант: выводит полностью сформированные строки по мере их поступления, не дожидаясь завершения задания, но никогда не перемешивает частично записанные строки.

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

#!/usr/bin/env bash
# Without -k: output order is unpredictable
echo '--- Without --keep-order ---'
seq 5 1 1 | parallel 'sleep 0.$((RANDOM % 5)); echo "Result for {}"'

echo

# With -k: output always appears as 5, 4, 3, 2, 1
echo '--- With --keep-order (-k) ---'
seq 5 1 1 | parallel -k 'sleep 0.$((RANDOM % 5)); echo "Result for {}"'

Группировка вывода для предотвращения перемешивания

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

Это поведение включено по умолчанию. Его можно отключить с помощью --ungroup, если Вам нужен вывод в реальном времени (например, для длительных задач с индикаторами выполнения), но тогда перемешивание снова станет возможным.

  • По умолчанию: вывод сгруппирован по задачам — безопасен для разбора.
  • --ungroup: вывод передаётся в реальном времени — удобно для интерактивного мониторинга.
  • --line-buffer: компромиссный вариант — строки никогда не разделяются, но задачи могут перемешиваться на границах строк.

Передача аргументов внутри функций оболочки

Иногда работу, которую Вы хотите выполнить параллельно, нельзя свести к одной команде — это многошаговая функция оболочки. Вы можете передать функцию в parallel, используя export -f вместе с env_parallel, либо напрямую вызвав bash -c.

Самый безопасный переносимый подход для сложных задач — шаблон bash -c '...'. Заполнитель {} передаётся как $1, если завершить команду фрагментом _ {}.

#!/usr/bin/env bash
# Define a multi-step processing function
process_item() {
  local item="$1"
  echo "[START] $item"
  # Simulate two steps
  sleep 0.2
  local result=$(echo "$item" | tr '[:lower:]' '[:upper:]')
  echo "[END]   $item -> $result"
}

export -f process_item

# Run the function in parallel for each input
echo 'alpha beta gamma delta epsilon' | tr ' ' '\n' \
  | parallel -j 3 process_item {}

Ограничение частоты и повторные попытки с --delay и --retries

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

  • --delay N — ждать N секунд между запусками очередных задач (допускаются дробные значения, например 0.5). Предотвращает перегрузку службы.
  • --retries N — если задача завершается с ненулевым статусом, повторить её до N раз, прежде чем отказаться от выполнения. Каждая повторная попытка занимает новый слот задачи.
  • --timeout N — завершить задачу, если она выполняется дольше N секунд. В сочетании с --retries это позволяет корректно обрабатывать зависшие задачи.

Пример: загрузка 50 адресов с не более чем 4 одновременными соединениями, задержкой 0,5 секунды между запусками и 3 повторными попытками при сбое.

#!/usr/bin/env bash
# Simulate downloading URLs with throttling and retries
# (using echo instead of curl so this is self-contained)

download_url() {
  local url="$1"
  # Randomly fail ~30% of the time to demo --retries
  if (( RANDOM % 10 < 3 )); then
    echo "FAIL: $url" >&2
    return 1
  fi
  echo "OK:   $url downloaded"
}

export -f download_url

printf 'https://example.com/file%d\n' $(seq 1 10) \
  | parallel -j 4 --delay 0.2 --retries 3 download_url {}

echo 'All downloads attempted.'

Распределение работы между удалёнными узлами с помощью --sshloginfile

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

  • --sshlogin user@host — выполнять задачи на определённом удалённом узле.
  • --sshloginfile machines.txt — считывать список узлов из файла (по одному на строку). Используйте : как специальную запись, чтобы также задействовать локальную машину.
  • --transfer — копировать входной файл на удалённый узел перед обработкой.
  • --return {} — копировать файл с результатом обратно после завершения задачи.
  • --cleanup — удалять переданные файлы с удалённого узла после их получения.

На удалённом узле должны быть установлены parallel и настроена аутентификация по ключу SSH (без запросов пароля).

Отображение хода выполнения и ведение журнала

Для длительных вычислений крайне важно отслеживать ход выполнения и впоследствии диагностировать сбои.

  • --progress — выводить в реальном времени сводную строку с количеством выполняющихся, завершённых и оставшихся задач.
  • --eta — оценивать время до завершения на основе средней продолжительности задач на текущий момент.
  • --joblog results.log — записывать журнал с разделителями-табуляторами, содержащий по одной строке для каждой завершённой задачи, включая код завершения, длительность выполнения и запущенную команду. Незаменимо для проверки сбоев.
  • --resume --joblog results.log — пропускать задачи, которые уже присутствуют в журнале (с кодом завершения 0). Если пакетное выполнение было прервано, его можно продолжить, не повторяя успешно выполненную работу.

Сочетание --joblog + --resume — одна из самых мощных возможностей GNU parallel для надёжных рабочих конвейеров в производственной среде.

#!/usr/bin/env bash
LOGFILE="/tmp/parallel_demo_$$.log"

# Run jobs and record results to a log
seq 1 12 | parallel \
  --jobs 4 \
  --progress \
  --joblog "$LOGFILE" \
  'sleep 0.1; echo "Processed item {}"'

echo
echo '=== Job Log (first 5 entries) ==='
head -6 "$LOGFILE"

# Show only failed jobs (exit value != 0)
echo '=== Failed jobs ==='
awk 'NR>1 && $7 != 0 { print $0 }' "$LOGFILE" || echo '(none)'

rm -f "$LOGFILE"

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

Проверьте, насколько Вы понимаете, как parallel управляет одновременным выполнением.

Итоги урока: оркестрация рабочих нагрузок с GNU parallel

Вы изучили основные инструменты для распределения больших наборов входных данных между ядрами CPU с помощью GNU parallel. Главное:

  • Базовое использование: передайте список через конвейер в parallel command {} — {} заменяется каждым входным элементом.
  • Слоты задач (-j): точно управляют одновременным выполнением — используйте число ядер для задач, ограниченных производительностью CPU, и более высокие значения в процентах для задач, ограниченных вводом-выводом.
  • Заполнители ({.}, {/}, {//}, {/.}) позволяют без дополнительных команд удобно извлекать компоненты путей.
  • Управление выводом: -k сохраняет порядок входных данных; группировка по умолчанию предотвращает перемешивание строк; --ungroup обеспечивает вывод в реальном времени.
  • Устойчивость: --retries, --timeout и --delay делают параллельные конвейеры устойчивыми к нестабильным задачам и ограничениям частоты.
  • Аудит: --joblog записывает результат каждой задачи; --resume позволяет продолжить работу с места остановки после прерывания.
  • Масштабирование: --sshloginfile распределяет задачи между удалёнными машинами через SSH без дополнительных кластерных накладных расходов.

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

Часто задаваемые вопросы

Урок «Оркестрация рабочих нагрузок с GNU parallel» бесплатный?

Да — полный текст урока «Оркестрация рабочих нагрузок с GNU parallel» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс DevOps Bootcamp, подпишись на CoddyKit PRO. Курс DevOps Bootcamp содержит 4 уроков всего.

Чему я научусь в уроке «Оркестрация рабочих нагрузок с GNU parallel»?

Распределяйте большие наборы входных данных по ядрам с помощью GNU parallel, слотов заданий и упорядочивания результатов Ты практикуешь DevOps Bootcamp с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать DevOps Bootcamp?

Предыдущий опыт не требуется. DevOps Bootcamp на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Оркестрация рабочих нагрузок с GNU parallel»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке DevOps Bootcamp?

Да. Каждый урок DevOps Bootcamp включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Профилирование скриптов и отказ от ненужных подоболочек
  2. Параллелизм с xargs -P и фоновыми заданиями
  3. Оркестрация рабочих нагрузок с GNU parallel
  4. Потоковые конвейеры и именованные каналы для пропускной способности
← Назад к DevOps Bootcamp