DockerでImmichをデプロイ:AI搭載のセルフホスト型Googleフォト代替環境を構築

HomeLab tutorial - IT technology blog
HomeLab tutorial - IT technology blog

アプローチの比較:真のクラウドフォト代替ツールを見極める

ある日曜日の午前2時15分、自宅サーバーのアラートが一斉に鳴り響きました。家族が旅行で撮影した65GBもの4K動画とRAWファイルをNextcloudにまとめてアップロードした直後のことでした。サーバーは瞬く間に悲鳴を上げました。プレビュー生成処理によって16個ものPHP-FPMワーカープロセスが立ち上がり、Redisは2GBのメモリ上限を突破。データベースのロック競合によってWeb UIが1時間以上完全にフリーズしてしまったのです。この障害により、汎用的なファイル同期プラットフォームは高負荷なメディアワークロードには耐えられないという厳しい現実を思い知らされました。

ホームラボ環境でGoogleフォトやiCloudを置き換えようとする場合、主に3つの設計アプローチから選択することになります。

  • 汎用ストレージプラグイン(Nextcloud Memories、FileRunなど): 従来のディレクトリツリー上で動作します。標準的なJPEGギャラリー程度なら安定して動作しますが、スマートフォンから同時に20GB単位のアップロードが行われると、バックグラウンドでのインデックス作成、ファイルロック、顔認識クラスタリング処理がマルチコアCPUをあっという間に占有してしまいます。
  • アーカイブ型インデクサー(PhotoPrism、LibrePhotosなど): 主に静的なNFSマウントや既存のフォルダー構造をスキャンする目的で構築されています。メタデータタグ付けやEXIFのパース性能は極めて堅牢ですが、モバイル端末からのバックグラウンド同期はいまだ洗練されていません。家族2人が同時にバックアップを開始すると、同期が停止してしまうトラブルが頻発します。
  • 専用マイクロサービスエンジン(Immich): 最初からGoogleフォトと同等の体験を提供することを目的に設計されています。Immichは負荷の高い処理を独立したワーカーに分散します。動画のトランスコード、顔認識クラスタリング、CLIPベクトル検索のすべてが、RedisとPythonの機械学習コンテナをバックエンドとする専用の非同期キューで実行されます。

本番運用におけるメリットとデメリット

Immichは商用サービス並みの完成度を誇りますが、家庭内の環境を完全に移行する前に、セルフホストならではの適切なアーキテクチャ設計を行っておく必要があります。

メリット

  • オンデバイスでのセマンティック検索&顔認識検索: PostgreSQLがpgvectorを介して顔の埋め込みベクトルやViT-B-32 CLIPベクトルを保存します。「ビーチでアイスを食べる子供たち」や「雪の中の赤いHonda Civic」といった自然言語で検索すると、9万枚の写真の中から80ミリ秒未満で正確なショットを抽出して表示します。
  • ネイティブなモバイル操作感: iOSおよびAndroid向けのFlutter製モバイルアプリは、カメラロールのバックグラウンドアップロード、複数ユーザーアカウント、パートナーとのライブラリ共有、商用クラウドアプリと見分けがつかないほど滑らかなタイムラインスクロールをサポートしています。
  • リソースの完全分離: Pythonの機械学習サービスは完全に独立して動作します。QuickSyncを備えたIntelのiGPUやNvidia RTXグラフィックカードに推論処理をバインドし、厳格なRAM制限を設定することで、バックグラウンドのAIジョブがホストOSのリソースを枯渇させるのを防ぐことができます。

トレードオフと運用の負担

  • リリースサイクルの速さ: Immichの開発スピードは非常に高速です。データベースマイグレーションが頻繁に実施されるため、スキーマ破損のリスクを避けるには、管理者の確認なしでの自動アップデート放置は避けるべきです。
  • ベースラインとなるメモリ消費量: PostgreSQL、Redis、コアAPIサーバー、MLエンジンを含むスタック全体が起動していると、アイドル時でも約1.8GBのRAMを消費します。初回の写真10万枚のインポート時には、メモリ消費量が5GB〜7GBに達することも珍しくありません。

ハードウェア選定とストレージアーキテクチャ

一時的なアプリケーションデータとメインのメディアストレージは絶対に混在させないでください。高IOPSが要求されるデータベース処理と、書き込み負荷の高いメディアボリュームを分離することで、4台のスマートフォンから同時にギガバイト単位のメディアが流し込まれても、UIの軽快さを維持できます。

以下は、マルチユーザー世帯で実績のあるストレージ構成例です。

  • コンピュートコア: 4コア以上のCPU(x86_64)と最低8GBのRAM。第8世代以降のIntel Core i3-8100以上であれば、QuickSync対応の統合UHD 630 GPUを利用できます。このハードウェアエンジンを活用すれば、4K 60fpsのHEVCスマートフォン動画を1080pのVP9ストリームへ、CPU使用率わずか5%程度でリアルタイムにトランスコード可能です。
  • データベースレイヤー(高速NVMe SSD): PostgreSQLのテーブル、Redisスナップショット、MLモデルのキャッシュはNVMeドライブ上に配置します。これにより、検索インデックス作成やタイムラインのページネーションにおいてミリ秒未満のシークタイムを担保できます。
  • バルクストレージレイヤー(ZFSまたはBtrfsアレイ): HDDプールを /mnt/storage/immich/upload にマウントします。スマートフォンのカメラアップロード、SonyのARWファイル、Apple ProResクリップなどはシーケンシャル書き込みでHDDに直接ストリーミングされるため、データベース用のNVMeに負荷をかけることは一切ありません。

導入ガイド:DockerによるImmichのデプロイ

以下のデプロイ構成では、Docker Composeを使用し、pgvectorによるハードウェアアクセラレーションと明確なストレージボリューム分離を適用します。

ステップ1:ディレクトリの準備と環境変数の設定

ホスト側で専用ディレクトリを作成し、適切な所有権を割り当てます。

sudo mkdir -p /opt/immich/data/pgdata
sudo mkdir -p /opt/immich/data/model-cache
sudo mkdir -p /mnt/storage/immich/upload
sudo chown -R $USER:$USER /opt/immich
cd /opt/immich

ボリュームマッピングとデータベース認証情報を含む .env ファイルを生成します。

cat << 'EOF' > .env
# 写真や動画の元データを保存する大容量ストレージ
UPLOAD_LOCATION=/mnt/storage/immich/upload

# データベースとMLキャッシュ用の高速SSDストレージ
DB_DATA_LOCATION=/opt/immich/data/pgdata
MODEL_CACHE_LOCATION=/opt/immich/data/model-cache

# Immichのバージョンタグ
IMMICH_VERSION=release

# データベースの接続情報と認証情報
DB_HOSTNAME=database
DB_DATABASE_NAME=immich
DB_USERNAME=postgres
DB_PASSWORD=SecureProductionSecretPassphrase987!
EOF

ステップ2:Docker Composeスタックの作成

docker-compose.yml ファイルを作成します。この定義では、ベクトル演算をpgvecto-rsにルーティングし、永続モデルキャッシュを備えた専用のMLコンテナをプロビジョニングします。

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    volumes:
      - ${UPLOAD_LOCATION}:/usr/src/app/upload
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - "2283:2283"
    depends_on:
      - redis
      - database
    restart: always

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - ${MODEL_CACHE_LOCATION}:/cache
    env_file:
      - .env
    restart: always

  redis:
    container_name: immich_redis
    image: docker.io/redis:6.2-alpine@sha256:eaba718fecd119618655c7854aeaa3db8037b467144e49970c94625b9045a43e
    healthcheck:
      test: redis-cli ping || exit 1
    restart: always

  database:
    container_name: immich_postgres
    image: tensorchord/pgvecto-rs:pg14-v0.2.1@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f1
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    healthcheck:
      test: pg_isready --dbname="${DB_DATABASE_NAME}" --username="${DB_USERNAME}" || exit 1;
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

ステップ3:サービスの起動とヘルスチェック

デタッチモードでコンテナを起動します。

docker compose up -d

各マイクロサービスの起動シーケンスを確認します。

docker compose ps
docker compose logs -f immich-server

ログを確認し、データベースのマイグレーションが正常に完了したこと、およびHTTPリスナーがポート 2283 でアクティブになっていることを確かめます。

ステップ4:初期管理者設定とモバイル端末のペアリング

  1. ブラウザで http://<サーバーのIPアドレス>:2283 にアクセスします。
  2. 初期登録画面で必要事項を入力し、メインの管理者アカウントを作成します。
  3. Administration -> Settings -> Machine Learning を開きます。マシンの物理コア数に合わせて同時ワーカースレッド数を調整します。この設定を行うことで、大規模なライブラリの一括インポート時にワーカースレッドがシステム全体を飽和させるのを防止できます。
  4. iOSまたはAndroidにImmichアプリをインストールします。サーバーURLに http://<サーバーのIPアドレス>:2283 を入力してアカウントでログインし、カメラロールの自動バックグラウンドバックアップを有効にします。

ステップ5:データベースの自動バックアップ

コンテナ自体はいつでも破棄・再作成できますが、メタデータや顔認識クラスタリングデータは失うわけにはいきません。毎晩cronジョブを実行してデータベースをダンプしましょう。なお、cron環境で疑似TTYの割り当てエラーを出さずにクリーンに実行させるため、docker exec から -t フラグを除外しています。

cat << 'EOF' > /opt/immich/backup-db.sh
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR="/mnt/storage/backups/immich"
mkdir -p "${BACKUP_DIR}"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")

# cronで問題なく実行できるようTTYを割り当てずにデータベースをダンプ
docker exec immich_postgres pg_dumpall -c -U postgres | gzip > "${BACKUP_DIR}/immich_db_${TIMESTAMP}.sql.gz"
find "${BACKUP_DIR}" -type f -name "*.sql.gz" -mtime +14 -delete
EOF
chmod +x /opt/immich/backup-db.sh

ホスト側のcrontabに crontab -e でスクリプトを登録し、毎日午前3時に実行するように設定します。

0 3 * * * /opt/immich/backup-db.sh >> /var/log/immich-backup.log 2>&1

このパイプラインが稼働していれば、写真はすべて自身の管理下にセルフホストされ、高精度なAI検索機能をいつでも手元で活用できるようになります。

Share: