gazou-compressor.jp

画像最適化パイプライン自動化:CI/CD + ワーカー + キャッシュ設計 実務パターン

画像は“増える前提”で仕組み化しないと管理負債になります。手動ドラッグ圧縮から抜け出す第一歩は 差分判定 + 自動再生成。ここでは最小パターンを提示し、段階的な高度化ポイントを示します。

当サイトの一部ツール(/batch-optimize など)と同じ WASM / ブラウザ最適化ロジックをワーカーへ移植する構成も可能です。

TL;DR
  • 差分のみ再エンコード
  • content-hash + immutable 配信
  • 遅い生成 (AVIF) は非同期・WebP 先行

1. 最小アーキテクチャ

  1. 開発者が PR に raw/ へ元画像追加(命名規約 enforced)
  2. CI がフォーマット変換 (AVIF/WebP/PNG) + メタ最適化(EXIF除去)
  3. 出力を dist/images/ へ配置しサイズ差分コメント
  4. main マージで CDN へ incremental upload(content-hash)
  5. 利用側はパス規約 + <picture> テンプレで自動解決

2. 差分判定スクリプトひな型

// pseudo build script
import fs from 'node:fs/promises';
import { optimize } from './lib/opt';
import ArticleNextPrev from '@/components/ArticleNextPrev';

const SRC = 'raw';
const OUT = 'dist/images';

async function run(){
  const files = (await fs.readdir(SRC)).filter(f=>/.(png|jpe?g)$/i.test(f));
  await fs.mkdir(OUT,{recursive:true});
  for (const f of files){
    const src = SRC + '/' + f;
    const base = f.replace(/.(png|jpe?g)$/i,'');
    const buf = await fs.readFile(src);
    const variants = await optimize(buf,{ avif:true, webp:true, png:true });
    for (const v of variants){
      const out = OUT + '/' + base + '.' + v.ext;
      const prev = await fs.stat(out).catch(()=>null);
      if (!prev || prev.size !== v.data.length){
        await fs.writeFile(out,v.data);
        console.log('updated', out, (v.data.length/1024).toFixed(1)+'KB');
      }
    }
  }
}
run();

3. キャッシュ & 配信

生成成果物は content-hash 含みパス(例: logo.abcd1234.avif)で immutable 配信。既存 URL 互換を守る必要がある場合はマニフェストJSONを生成しアプリ起動時にマップをロード。

4. 高度化ロードマップ

5. CI ワークフロー構成例

name: images
on: [pull_request]
jobs:
  optimize:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci --no-audit --no-fund
      - name: Build variants
        run: node scripts/build-images.js --changed > sizes.json
      - name: Comment diff
        if: always()
        run: node scripts/comment-size-diff.js sizes.json
  deploy:
    needs: optimize
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: node scripts/upload-changed.js --dir dist/images --bucket CDN_BUCKET

PR 上で差分見える化 → main マージ時のみアップロード。

6. メトリクス & 閾値

// metrics-log.ts (抜粋)
export interface MetricRow { name:string; bytes:number; bytesOrig:number; ssim:number; deltaE:number; ms:number; }
// 集計: 中央値/最大/失敗率 など計算し CI で閾値超過なら失敗。

7. スケール & コスト試算

1日追加 2,000 枚 / 平均 1.2MB → AVIF/WebP/PNG 生成後 平均 420KB 配信想定。AVIF effort を 4 で固定し CPU 秒を抑制、遅延影響を避ける大物 (≥4K) はキュー投入し WebP を先行。ストレージは original + hashed variants で 3.2x 膨らむ想定 → 90 日アクセスなしをライフサイクルで低冗長ストレージへ移行。

8. トラブルシュート

9. 次の拡張

Version: 2025-09-08 初版 + 詳細節追加。以後は差分判定/視覚評価自動化を追記予定。

gazou-compressor.jp 編集部

画像圧縮・変換・背景除去などの実践テクニックと、Webで“速く・軽く・崩さない”ためのノウハウを発信しています。

関連記事

トピック/更新日の近いコンテンツ