リモート Mac・静的サイト:
Sharp / libvips / ImageMagick 意思決定マトリクス
静的サイト生成(SSG)でサムネイルやレスポンシブ派生を大量に吐くと、選んだエンジン一つでメモリ曲線が変わります。本稿は Sharp(libvips バインディング)、ネイティブ libvips(CLI/他言語)、ImageMagick の三層を、フォーマット適性・並列用環境変数・一時領域・OOM リスク・CI キャッシュで横並びにし、リモート Mac 上で再現しやすいパラメータ例に落とし込みます。全体のビルド並列やヒープはSSR/フレームワークのメモリ対照と、バンドラ側のVite/Webpack キャッシュ最適化とあわせて読むと筋が通ります。
package.json 断片はそのまま貼り替え可能です。
01 シナリオ:SSG で大量サムネイルを量産するとき
ブログ記事や商品カードごとに複数幅の WebP/AVIF を書き出すと、変換フェーズがビルド全体のボトルネックになります。ローカルでは成功しても、リモート Mac の共有ホストでは同時ビルドと入出力が重なり、画像ワーカーだけが限界突破することがあります。対策の軸は「同時実行の上限」「一時ファイルの置き場」「ネイティブライブラリのスレッド数」「Node ヒープ」の四つに集約できます。スタイル管線のメモリ調整はTailwind v4/PostCSS の対照表も参照し、CSS と画像のピークが重ならないようスロットをずらすと安定します。
02 Sharp・libvips・ImageMagick:三方式パラメータ対照表
以下は静的サイト向けの実務目安です。実バージョンや入力解像度で変動するため、受入れ時は同じ入力セットでピーク RSS を記録してください。
| 方式 | 主な適用フォーマット | 並列・スレッド(環境変数の例) | 一時ディレクトリ | OOM リスクの典型 | CI キャッシュのコツ |
|---|---|---|---|---|---|
| Sharp(Node) | JPEG/PNG/WebP/AVIF/TIFF/GIF 入力 ほか | libvips 側 VIPS_CONCURRENCY=vCPU 前後。アプリ側は同時ジョブ数を別途キューで制限 |
TMPDIR(大画像はディスクスプールに乗ることあり) |
Node ヒープ不足+無制限 Promise.all |
node_modules の再現性重視ならロックファイルと OS をキーに。ビルドキャッシュに .sharp 由来の再取得ログを残す |
| libvips(CLI/バインディング) | 上記に近い。パイプライン操作が明示的 | VIPS_CONCURRENCY。VIPS_DISC_THRESHOLD でメモリ/ディスク切替の閾値(バイト) |
TMPDIR |
巨大タイル読み込みや閾値未設定での同時パイプライン過多 | 変換コマンドと vips バージョンをキャッシュキーに含める。中間ファイルパスをワークスペース内に固定 |
| ImageMagick | 非常に広い。ポリシーでフォーマット制限推奨 | MAGICK_THREAD_LIMIT。メモリ系 MAGICK_MEMORY_LIMIT/MAGICK_MAP_LIMIT/MAGICK_DISK_LIMIT(policy でも可) |
MAGICK_TEMPORARY_PATH または TMPDIR |
デフォルト上限なしに近い構成での一括変換ピーク | policy.xml のハッシュをキャッシュキーに。中間生成物を artifacts で保存し二回目以降を短縮 |
メモ:共有 Mac では「スレッドを張り切る」より「同時プロセス本数を抑える」方が全体の予測可能性が高いことが多いです。
03 npm/pnpm スクリプトと NODE_OPTIONS の例
画像スクリプトを単独フェーズに分けると、失敗時の再実行とキャッシュ境界が明確になります。
{
"scripts": {
"images:thumbs": "NODE_OPTIONS='--max-old-space-size=6144' node ./scripts/generate-thumbnails.mjs",
"build": "pnpm run images:thumbs && NODE_OPTIONS='--max-old-space-size=8192' pnpm exec vite build",
"images:ci": "cross-env TMPDIR=$PWD/.tmp VIPS_CONCURRENCY=4 NODE_OPTIONS='--max-old-space-size=4096' node ./scripts/generate-thumbnails.mjs"
}
}
- npm:
npm run images:thumbs前にexport NODE_OPTIONS=--max-old-space-size=6144を CI の一ブロックにまとめると二重設定を避けられます。 - pnpm:
pnpm run build内でだけヒープを上げたい場合は、上記のようにサブスクリプト側にNODE_OPTIONSを直書きするか、pnpm exec env NODE_OPTIONS=...で局所化します。 - ImageMagick 併用:同じジョブ内で Node と
magickを起動するなら、シェルでMAGICK_THREAD_LIMIT=2などを先に export し、CPU と磁気ディスクの競合を抑えます。
04 リモート Mac での受入れ三歩
- ベースライン計測:代表ブランチでフルビルドを一度走らせ、画像フェーズの wall time・最大 RSS・
TMPDIR配下のピーク使用量を記録する(他テナント負荷の低い時間帯を推奨)。 - パラメータ固定:
VIPS_CONCURRENCY/MAGICK_THREAD_LIMIT/同時ジョブ数を文書化し、CI とローカルのドキュメントを一枚に揃える。 - キャッシュ検証:キャッシュヒット時に画像フェーズがスキップまたは増分のみになることをログで確認し、キーにロックファイル・ランタイム OS・アーキテクチャを含めて無効化漏れを防ぐ。
05 FAQ
Q. Sharp だけで十分なのに libvips CLI を併記する意味は?
Sharp は内部で libvips を利用するため、トラブルシュート時は VIPS_CONCURRENCY やディスクスプールの挙動が共通の語彙になります。バッチを Node 以外(Makefile/Go など)に分離する場合は CLI 側の知識がそのまま効きます。
Q. AVIF まで含めると遅いが、品質は落としたくない。
解像度段階を設け、最終段だけ高コストコーデックにする、または本番用とプレビュー用の二層キャッシュを分けると、PR ビルドの待ち時間を抑えられます。
Q. OOM のログが Node でなく kern で出ることがある。
ネイティブ側の確保失敗です。ヒープだけでなく ulimit、ホストの残メモリ、スワップ、同時ビルドスロットを疑ってください。