JA EN

CI/CD パイプラインでの画像最適化自動化 - GitHub Actions と Sharp による実践構成

· 約 9 分で読めます

なぜ CI/CD で画像最適化するのか - 手動運用の限界と自動化の利点

画像最適化を開発者の手動作業に依存すると、品質のばらつきや最適化漏れが必ず発生します。CI/CD パイプラインに組み込むことで、すべての画像が一貫した基準で最適化され、人的ミスを排除できます。

手動運用の問題点:

CI/CD 自動化の利点:

導入効果の実例: ある EC サイトでは CI/CD に画像最適化を導入した結果、画像の平均ファイルサイズが 340KB から 89KB に削減 (74% 減)、LCP が 2.8 秒から 1.4 秒に改善、月間の CDN 転送量が 2.1TB から 0.6TB に減少しました。初期構築に 2 日、以降のメンテナンスはほぼゼロです。

GitHub Actions による画像最適化ワークフロー - 基本構成

GitHub Actions で画像最適化パイプラインを構築する基本的なワークフロー定義を紹介します。プルリクエスト時に変更された画像を検出し、自動最適化を実行します。

ワークフロー定義:

name: Image Optimization
on:
pull_request:
paths: ['src/images/**']
jobs:
optimize:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: node scripts/optimize-images.js
- uses: actions/upload-artifact@v4
with:
name: optimized-images
path: dist/images/

変更画像の検出: git diff --name-only HEAD~1 で直近のコミットで変更された画像ファイルを特定し、変更分のみを処理対象にします。全画像を毎回処理すると実行時間が長くなるため、差分検出は必須です。

キャッシュの活用: actions/cachenode_modules と最適化済み画像のキャッシュを保持します。Sharp のネイティブバイナリは OS ごとに異なるため、キャッシュキーに runner.os を含めてください。キャッシュヒット時はビルド時間が 60-70% 短縮されます。

並列処理: 画像数が多い場合は matrix 戦略でジョブを分割するか、Node.js の Promise.all() で並列処理します。GitHub Actions の ubuntu-latest ランナーは 2 コアのため、並列度 2-4 が最適です。

Sharp による画像変換スクリプト - WebP と AVIF の自動生成

Sharp は Node.js の高性能画像処理ライブラリで、libvips をバックエンドに使用しています。CI/CD 環境での画像変換に最適で、ImageMagick の 4-5 倍高速に動作します。

基本的な変換スクリプト:

const sharp = require('sharp');
const glob = require('fast-glob');
const path = require('path');

async function optimizeImage(inputPath) {
const image = sharp(inputPath);
const metadata = await image.metadata();
const outputDir = 'dist/images';
const name = path.basename(inputPath, path.extname(inputPath));

// 元フォーマットの最適化
await image.jpeg({ quality: 80, mozjpeg: true })
.toFile(path.join(outputDir, name + '.jpg'));

// WebP 生成
await image.webp({ quality: 75, effort: 6 })
.toFile(path.join(outputDir, name + '.webp'));

// AVIF 生成
await image.avif({ quality: 65, effort: 4 })
.toFile(path.join(outputDir, name + '.avif'));
}

品質設定の指針:

リサイズの組み込み: レスポンシブ画像用に複数サイズを生成する場合、sharp.resize() を組み合わせます。一般的には 640, 960, 1280, 1920 ピクセル幅の 4 サイズを生成し、srcset で使い分けます。

ファイルサイズの閾値チェックとレポート生成 - 品質ゲートの実装

CI/CD パイプラインに品質ゲートを設けることで、基準を満たさない画像がデプロイされるのを防ぎます。ファイルサイズの上限チェックと、最適化効果のレポート生成を自動化します。

閾値チェックスクリプト:

const MAX_SIZE = {
hero: 200 * 1024, // ヒーロー画像: 200KB
thumbnail: 50 * 1024, // サムネイル: 50KB
icon: 10 * 1024, // アイコン: 10KB
default: 150 * 1024 // その他: 150KB
};

function checkFileSize(filePath, category) {
const stats = fs.statSync(filePath);
const limit = MAX_SIZE[category] || MAX_SIZE.default;
if (stats.size > limit) {
return { pass: false, size: stats.size, limit };
}
return { pass: true, size: stats.size, limit };
}

PR コメントへのレポート出力: GitHub Actions の github-script アクションを使い、最適化結果をプルリクエストのコメントとして自動投稿します。各画像の元サイズ、最適化後サイズ、削減率を表形式で表示し、閾値超過があれば警告アイコンを付与します。

視覚品質の自動検証: ファイルサイズだけでなく、SSIM (Structural Similarity Index) を計測して視覚品質の劣化を検知します。SSIM が 0.95 未満の場合は品質設定を見直す必要があります。Sharp では直接 SSIM を計算できませんが、sharp-ssim パッケージや ImageMagick の compare コマンドで計測可能です。

失敗時の対応: 閾値チェックに失敗した場合、ワークフローを失敗させてマージをブロックするか、警告のみ出力してマージは許可するかを選択できます。初期導入時は警告モードで運用し、チームが慣れてからブロックモードに切り替えることを推奨します。

キャッシュ戦略とビルド時間の最適化 - 大規模プロジェクトへの対応

画像数が数百〜数千に達する大規模プロジェクトでは、毎回全画像を処理するとビルド時間が数十分に膨れ上がります。効率的なキャッシュ戦略とインクリメンタルビルドが不可欠です。

コンテンツハッシュによるスキップ:

const crypto = require('crypto');

function getFileHash(filePath) {
const content = fs.readFileSync(filePath);
return crypto.createHash('md5').update(content).digest('hex');
}

// マニフェストファイルで処理済みハッシュを管理
const manifest = JSON.parse(fs.readFileSync('.image-manifest.json'));
const currentHash = getFileHash(inputPath);
if (manifest[inputPath] === currentHash) {
console.log('Skip (unchanged):', inputPath);
return;
}

マニフェストファイルに各画像のハッシュを記録し、変更がない画像の再処理をスキップします。これにより、1000 枚の画像があっても変更された 5 枚だけを処理できます。

GitHub Actions のキャッシュ設定:

AVIF エンコードの高速化: AVIF のエンコードは CPU 負荷が高く、1 枚あたり 2-5 秒かかります。effort パラメータを 4 (デフォルト) から 2 に下げると速度が 2 倍になりますが、圧縮率は 5-10% 低下します。CI 環境では速度を優先し、effort: 2-3 を推奨します。

実践的な構成パターンと運用のコツ - チーム開発での導入

CI/CD 画像最適化をチームに導入する際の実践的なパターンと、運用で注意すべきポイントを紹介します。

構成パターン 1: PR 時の自動最適化 + コミット

プルリクエストが作成されると、最適化スクリプトが実行され、結果を同じ PR に自動コミットします。開発者は元画像をコミットするだけで、最適化版が自動的に追加されます。stefanzweifel/git-auto-commit-action を使えば、最適化結果の自動コミットが簡単に実装できます。

構成パターン 2: ビルド時に動的生成

最適化画像をリポジトリに含めず、ビルドパイプラインで動的に生成する方式です。リポジトリサイズを抑えられますが、ビルド時間が長くなります。Next.js の Image Optimization や Gatsby の gatsby-plugin-sharp がこのパターンを採用しています。

構成パターン 3: 専用の画像パイプライン

画像の追加・変更を検知する専用ワークフローを用意し、最適化結果を S3 や CDN に直接アップロードする方式です。アプリケーションのビルドと画像処理を分離でき、大規模プロジェクトに適しています。

運用のコツ:

関連記事

Web 用画像のファイルサイズ最適化戦略 - 品質を保ちながら軽量化する技術

Web パフォーマンスを最大化するための画像ファイルサイズ最適化手法を、フォーマット選択からメタデータ除去まで体系的に解説します。

大量画像の一括処理ワークフロー - 効率的なバッチ処理の設計と実装

数百〜数千枚の画像を効率的に一括処理するワークフローの設計方法を、コマンドラインツールとスクリプトの実例で解説します。

画像最適化ツール比較 2024 - Squoosh, Sharp, ImageMagick の性能と使い分け

主要な画像最適化ツールを圧縮率、処理速度、対応フォーマット、導入コストの観点で徹底比較。プロジェクト規模に応じた最適なツール選定の指針を提供します。

画像処理の自動テスト手法 - Visual Regression Testing の実践ガイド

Visual Regression Testing による画像処理パイプラインの品質保証手法を解説。Playwright、Percy、reg-suit を使った自動テストの構築方法と CI/CD 統合を紹介します。

写真ワークフロー自動化 - スクリプトで大量画像を効率処理する方法

数百〜数万枚の写真を効率的に処理するワークフロー自動化を解説。ImageMagick、sharp、ExifTool を使ったバッチ処理の実践テクニックを紹介します。

WebP から AVIF への移行判断 - コスト対効果と実装戦略

WebP 導入済みサイトが AVIF に移行すべきかの判断基準を解説。追加の圧縮効果、移行コスト、段階的な実装戦略を具体的なデータとともに紹介します。

関連用語