Penpotを「真のエンタープライズ級」へ:S3/R2外部ストレージ化によるスケーラビリティの極致
Penpotは、デザインツールの民主化における革命児だ。しかし、多くのエンジニアが陥る罠がある。標準のDocker Compose構成のまま運用し、永続ボリューム(`./data`)に依存した「単一障害点」を抱えてしまうことだ。
デザインシステムが肥大化し、アセットの数が増えるほど、ローカルディスクへの書き込みはパフォーマンスのボトルネックとなり、スケーラビリティを阻害する。本稿では、PenpotのストレージレイヤーをAWS S3(またはCloudflare R2)へ完全移譲し、ステートレスなマルチインスタンス環境を構築する「本番運用の深淵」を解き明かす。
—
1. なぜ「ローカルストレージ」を捨てるべきなのか
Penpotの内部アーキテクチャにおいて、アセット管理は `penpot-backend` が担っている。デフォルトではこれらがローカルのファイルシステムを叩くが、以下の理由から即刻廃止すべきだ。
- 水平スケーリングの不能: コンテナを複数立てた際、アセットが各ノードに分散し、整合性が崩壊する。
- バックアップの複雑性: ボリュームのSnapshotに依存すると、数TB規模のデータ移行が地獄と化す。
- I/Oオーバーヘッド: ネットワークストレージをマウントするコストよりも、S3互換APIを通じて直接操作する方が、レイテンシとスループットの制御が容易である。
—
2. 実践:S3互換ストレージへの移行アーキテクチャ
Penpotは内部で `MinIO` クライアント相当のライブラリを利用しており、環境変数によってS3互換ストレージへのルーティングが可能だ。
Docker Composeにおける設定の極意
`docker-compose.yml` において、`penpot-backend` コンテナに以下の環境変数を注入する。
services:
penpot-backend:
environment:
# S3ストレージの有効化
- PENPOT_STORAGE_S3_ENABLED=true
# バケット名
- PENPOT_STORAGE_S3_BUCKET=penpot-assets-prod
# エンドポイント(R2を使う場合は https://
- PENPOT_STORAGE_S3_ENDPOINT=https://s3.ap-northeast-1.amazonaws.com
# 認証情報(IAMロール利用時はインスタンスプロファイル経由を推奨)
- PENPOT_STORAGE_S3_ACCESS_KEY=${AWS_ACCESS_KEY_ID}
- PENPOT_STORAGE_S3_SECRET_KEY=${AWS_SECRET_ACCESS_KEY}
- PENPOT_STORAGE_S3_REGION=ap-northeast-1
# パフォーマンス最適化:CDN経由で配信する場合のBase URL
- PENPOT_STORAGE_S3_ASSETS_URL=https://assets.your-domain.com
【Expert Tip】
Cloudflare R2を使用する場合、リージョン指定は `auto` または `us-east-1` とし、`S3_ENDPOINT` にR2のS3 APIエンドポイントを正確に記述せよ。ここで躓くエンジニアが多いが、`rclone` 等で接続検証を事前に行うのが鉄則だ。
—
3. パフォーマンス最適化ハック:キャッシュとパイプライン
アセットの取得をS3直叩きにすると、高トラフィック時にAPI制限とコストに直撃する。これを回避する最強の布陣が「CDNによるエッジキャッシュ」だ。
CloudFront/Cloudflareによるキャッシュ戦略
1. Cache-Controlの設定: PenpotのbackendがS3へ保存する際、`Cache-Control: max-age=31536000, immutable` を付与するように設定(あるいはS3バケットポリシーで強制付与)。
2. 署名付きURLの回避: 公開アセットであればACLをPublicにし、CDNを前面に立てることで、バックエンドの負荷をゼロに近づける。
—
4. 運用自動化:アセットの健全性監視スクリプト
クラウドストレージへ移行すると、「S3上のオブジェクト」と「DB上のメタデータ」の乖離が最大の敵となる。これを防ぐための、Goで書いた監査スクリプトの断片を紹介する。
// 概念コード: DBのassetsテーブルとS3バケットの整合性チェック
func VerifyAssets(db sql.DB, s3Client s3.Client) {
rows, _ := db.Query(“SELECT s3_key FROM assets”)
for rows.Next() {
var key string
rows.Scan(&key)
// S3にオブジェクトが存在するか確認
_, err := s3Client.HeadObject(&s3.HeadObjectInput{Key: &key})
if err != nil {
log.Printf(“Orphaned asset detected: %s”, key)
// 自動修復ロジックをここに実装
}
}
}
このスクリプトをKubernetesのCronJobとして走らせ、週次でレポートをSlackに飛ばす。これが「伝説的運用」の第一歩だ。
—
5. 結論:ツールを「所有」するということ
Penpotを単なる「ブラウザで動くツール」として捉えるか、あるいは「自分たちのデザインシステムを支える堅牢なインフラ」として捉えるか。その境界線は、このストレージ層の設計に宿る。
S3/R2への移行は、単なるコスト削減ではない。それは、あなたのデザインチームがどれだけ巨大なアセットを抱え、どれほど高速にプロトタイピングを回しても、「インフラがボトルネックにならない」という確信を得るための投資だ。
技術は、管理者が設計した通りにしか振る舞わない。Penpotを使いこなすのではない。Penpotを、あなたのアーキテクチャの一部として最適化するのだ。
さあ、次はどのボトルネックを潰そうか?