GNU parallelによるワークロードのオーケストレーション
GNU parallel、ジョブスロット、結果の順序制御を使い、大規模な入力データをコア間に分散します。
「GNU parallelによるワークロードのオーケストレーション」はCoddyKit上の無料DevOps Bootcampレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDevOps Bootcamp学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DevOps Bootcampコースには全4レッスンが含まれています。
GNU parallelとは何か、なぜ使うのか
GNU parallel は、1台または複数台のマシンでジョブを並列実行できるシェルツールです。大きな項目リストを for ループで1つずつ処理する代わりに、parallel は利用可能なすべてのCPUコアに処理を同時に分散します。
- 速度:逐次実行で8分かかるタスクが、8コアのマシンでは約1分で完了することがあります
- 簡単さ:標準入力、ファイル、引数リストから入力を受け取れるため、プロセスを手動で管理する必要がありません
- 安全性:異なるジョブの出力は分離され、結果が混ざることはありません
Debian/Ubuntuでは sudo apt install parallel、macOSでは brew install parallel でインストールします。parallel --version で確認できます。
最初のparallelコマンド
parallel の最も簡単な形式では、標準入力から項目を読み取り、それぞれに対してコマンドを実行します。プレースホルダー {} は現在の入力項目を表します。
以下の例では、gzip を使って5つのログファイルを同時に圧縮します。parallel がなければ、各ファイルは1つずつ順番に圧縮されます。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コアごとに1つのジョブを実行します。-j(または --jobs)フラグでこの設定を変更できます。
-j 4— 常に4つのジョブを実行します-j 0— 入力の数だけジョブを実行します(慎重に使用してください)-j 200%— CPUコア数の2倍のジョブを実行します(I/Oバウンドの処理に便利です)-j 50%— 利用可能なコアの半分だけを使います
CPUバウンドのタスクでは、-j $(nproc) が最適なことが多いです。ネットワークやディスクI/Oのタスクでは、ジョブの大半が待機に費やされるため、コア数を超えても問題ありません。
#!/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で出力順を維持する
ジョブの終了時間が異なると、標準出力は完了した順に表示されます。そのため、ログが読みにくくなったり、後続処理での解析が不安定になったりします。
出力順は2つのフラグで制御できます。
--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 {}"'出力をまとめてインターリーブを防ぐ
出力順を維持していても、1つのジョブが複数行を出力すると、その行が同時に実行されている別のジョブの行とインターリーブすることがあります。parallelは、各ジョブの標準出力と標準エラー出力をすべてバッファーに保存し、ジョブの完了後に1つのアトミックなブロックとして出力することで、この問題を自動的に解決します。
この動作はデフォルトで有効です。ライブストリーミング出力(進行状況バーを表示する長時間実行ジョブなど)が必要な場合は、--ungroupで無効にできます。ただし、その場合は再び出力がインターリーブする可能性があります。
- デフォルト: ジョブごとに出力がまとめられるため、安全に解析できます。
--ungroup: 出力をライブでストリーミングします。対話的な監視に適しています。--line-buffer: 折衷案です。行が分割されることはありませんが、行の境界でジョブがインターリーブする可能性があります。
シェル関数内で引数を渡す
並列化したい処理が、単一のコマンドではなく複数の手順からなるシェル関数である場合があります。export -fとenv_parallelを組み合わせるか、bash -cを直接呼び出すことで、関数をparallelに渡せます。
複雑なジョブに対して、安全で移植性の高い方法は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によるスロットリングとリトライ
外部サービス(API、リモートサーバー、データベース)に対して並列にアクセスする場合、レート制限と障害への耐性が必要になることがよくあります。
--delay N— 新しいジョブを開始する間隔としてN秒待機します(0.5のような小数値も使用できます)。サービスへの過負荷を防ぎます。--retries N— ジョブがゼロ以外のステータスで終了した場合、諦める前に最大N回再試行します。再試行のたびに新しいジョブスロットが消費されます。--timeout N— N秒を超えて実行しているジョブを強制終了します。--retriesと組み合わせると、ハングしたジョブにも適切に対処できます。
例: 最大4接続で50個のURLをダウンロードし、開始間隔を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— ファイルからホストの一覧を読み込みます(1行につき1ホスト)。ローカルマシンも使用するには、特殊なエントリとして:を指定します。--transfer— 処理前に入力ファイルをリモートホストへコピーします。--return {}— ジョブの完了後に結果ファイルをコピーして戻します。--cleanup— 取得後に、リモートホスト上の転送ファイルを削除します。
リモートホストにはparallelがインストールされ、SSH鍵ベースの認証(パスワード入力を求められない設定)が構成されている必要があります。
進行状況の表示とロギング
長時間実行される処理では、進行状況を監視し、後から失敗の原因を特定できるようにすることが重要です。
--progress— 実行中、完了済み、残りのジョブ数を示す概要行をライブで表示します。--eta— これまでのジョブの平均実行時間に基づいて、完了までの時間を推定します。--joblog results.log— 完了したジョブごとに1行ずつ、終了コード、実行時間、実行したコマンドを含むタブ区切りのログファイルを書き込みます。失敗の監査に非常に役立ちます。--resume --joblog results.log— ログファイルに終了コード0で記録済みのジョブをスキップします。バッチ実行が中断されても、成功済みの処理をやり直さずに再開できます。
--joblogと--resumeの組み合わせは、堅牢な本番パイプラインを構築するためのGNU parallelの最も強力な機能の1つです。
#!/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によるワークロードのオーケストレーション
GNU parallelを使って、大量の入力データをCPUコアに分散するための基本的なツール群を学びました。重要な点をまとめます。
- 基本的な使い方: リストを
parallel command {}にパイプすると、{}が各入力項目に置き換えられます。 - ジョブスロット(
-j): 並行実行数を正確に制御します。CPUバウンドの処理にはコア数を、I/Oバウンドの処理にはより高い割合を使用します。 - プレースホルダー(
{.}、{/}、{//}、{/.})を使うと、追加のコマンドなしでパスの各要素を簡単に取り出せます。 - 出力制御:
-kは入力順を維持し、デフォルトのグループ化は行のインターリーブを防ぎます。--ungroupを使うとライブストリーミングになります。 - 耐障害性:
--retries、--timeout、--delayにより、不安定なジョブやレート制限がある場合でもparallelパイプラインを堅牢にできます。 - 監査性:
--joblogはすべてのジョブの結果を記録し、--resumeを使うと中断後に続きから再開できます。 - スケールアウト:
--sshloginfileは、クラスタ管理のオーバーヘッドなしにSSH経由でリモートマシンへジョブを分散します。
これらのオプションを使いこなすと、parallelはシェルに組み込まれた本番運用レベルのワークロードオーケストレーターになります。
よくある質問
「GNU parallelによるワークロードのオーケストレーション」レッスンは無料ですか?
はい。「GNU parallelによるワークロードのオーケストレーション」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DevOps Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DevOps Bootcampコースには全4レッスンが含まれています。
「GNU parallelによるワークロードのオーケストレーション」で何を学びますか?
GNU parallel、ジョブスロット、結果の順序制御を使い、大規模な入力データをコア間に分散します。 ブラウザで直接実行するハンズオンコードでDevOps Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
DevOps Bootcampを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのDevOps Bootcampは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「GNU parallelによるワークロードのオーケストレーション」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このDevOps Bootcampレッスンでコードを書いて実行できますか?
はい。すべてのDevOps Bootcampレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- スクリプトのプロファイリングと不要なサブシェルの回避
- xargs -Pとバックグラウンドジョブによる並列処理
- GNU parallelによるワークロードのオーケストレーション
- スループット向上のためのストリーミングパイプラインと名前付きパイプ