Closed6
個人開発で静的画像を配信するためにCDNを使うか悩む
要件
- 個人開発でウェブサイトを作っている
- フロントエンドはReactとSSG(非next.js) でビルド時にすべてのデータが吐き出される
- バックエンドはない
- ビルド時にデータソースからフロントエンドが参照するためのjsやjsonが吐かれ、それに必要なデータがすべて含まれている
- フロントエンドのホスティングコストはCloudflare PagesやVercelを使えば無料
- 静的なので無料で置く手段は無限にあるはず
- サイトで使うアセットは別ドメインで管理している
- 画像が主。現在2000ファイル、500MBほど
- これから数MB級の動画も追加されるかも
- 運営してくと増える見込み。たぶん10GB未満くらいで収まるはず
- UGCではない。動的に増えることはない。手動の更新タイミングで増えたり変更されたりする
- これらをどのようにホストするか
- 画像が主。現在2000ファイル、500MBほど
- このサイトでマネタイズする気は無い
- できる限り維持費は抑えたい
- 現状: 無料
- 「個人開発だと時間が取れず放置してしまうことがあるのはしょうがないが、お金がかかると閉じるという選択肢が出てくるのでよくない。運営に『少しお金がかかる』と『完全無料』は大きく差がある」という主張をどこかで見た
現在の運用
- アセットを管理する専用のGitリポジトリを作成
- png画像をcommit
- Romeolight WebPconvでpng画像のwebp版を作成し、同じ場所にcommit
- GitHubにpush
- Cloudflare Pagesで展開
- サイトから直リン
- pngを参照するかwebpを参照するかはフロントエンドのコンポーネントで切り替えている
いいところ
- 完全無料
- データの保存も、CDNで世界各地へのデプロイも無料
- まあまあ楽
- 慣れてるツールでファイル追加しcommit, pushするだけ
わるいところ
- Gitの使い方を間違えている
- Gitは本来ソースコードなどのテキストファイルを管理するもので、バイナリを管理するものではない
- こんな使い方をしているとcommitやpushなどの操作にやたら時間がかかる
- Gitでバイナリを扱う手段としてGit LFSを使う手があるが、GitHubのPrivateリポジトリでGit LFSを使うと入れた分だけお金がかかる
- .gitが膨らんでいく
- 上記と同じ
- これは一応コミット履歴を捨てることで解消可能
- 上記と同じ
- Gitは本来ソースコードなどのテキストファイルを管理するもので、バイナリを管理するものではない
- GitHubの使い方を間違えている
- GitHubのリポジトリはバイナリ置き場ではない
- GitHubの制限にかかる可能性がある
- 今のところ1リポジトリあたりの容量制限はないみたい
- GitHub Hacking ~GitHubを容量無制限のクラウドストレージとして使用する試み~ - Qiita
- 1ファイルは100MBまでの制限がある
- 画像がメインなので今のところ1ファイルで100MBを超えるものを配信するつもりはない
- なお配信に使っているCloudflare Pagesの1ファイルあたりの制限が25 MiB
現在の悩み
- webpを作るのがめんどくさい
- CLIで良い感じにできるツールが見つからなかったのでGUIで手動で作っている
- せめてなんらかのワークフローで自動化したい
- imagemagickをがんばるべき?
- https://www.npmjs.com/package/sharp とか?
- リポジトリが膨らむ
- フォーマット変換以外にも、「そんなに大事じゃないところで使うので画質を落としたやつ」とかも作ったり管理したりするのがめんどい
- 後述の様なCDNでの動的変換を使えばこれはいらなくなる
- せめてなんらかのワークフローで自動化したい
- CLIで良い感じにできるツールが見つからなかったのでGUIで手動で作っている
- 小さい表示枠(40p)に大きい画像(256p)を入れてたらLighthouseに怒られた
- 確かに無駄にでかい画像を入れるのは気持ち悪いし、ピクセルパーフェクトな画像を入れたい
- しかし表示枠に合わせて40p版、256p版などの画像を作ってコミットしておくのはめんどくさいしリポジトリも膨らんでしまう
CDNを使って動的変換すると表示が早くなったり、Lighthouseスコアが上がるのか?
14日間無料で試せるBunny.netを使って動的にリサイズや変換してみる
取得元は先の通りCloudflare Pagesで展開していたURL
結果
表示速度やLighthouseスコアはほぼかわらなかった
左がbeforeで、右がafter
適切なサイズの画像~の警告が消えているが、そもそもこの警告がある時点でも表示にそんな時間はかかってない
いいところ
- 転送量は減った
- 40pxの枠で画像が200件ほどの表示されるページだと
- before (予め変換した256pのwebp): 3.1MB
- after (動的にリサイズした40pのwebp): 720kB
- かなり小さくなるのは良い
- png, webpの判別をしなくて良くなった
-
.png
のURLにアクセスしてもブラウザのacceptヘッダーを見てwebpで返す機能がある - つまり全部
.png
を渡せばいい
-
わるいところ
- 使い続けるにはお金がかかる
- CDN Pricing | Affordable Pay As You Go CDN | bunny.net
- $0.03 /GB (Asia)
- 加えて webpの自動変換など(optimizer)が $9.5 /month
- お金を払うこと自体は別にいいんだが、前述の通りマネタイズしない個人開発という観点だとマイナス点
- srcsetを書くのはちょっとめんどくさい
- こんな感じになる
-
<img src={url + "?class=icon"} srcSet={[`${url}?class=icon 1x`, `${url}?class=icon2x 2x`].join(",")} />
- classはBunny側で設定したプリセットである
お気もちのまとめ
- 手動でwebpとか作るのめんどくさい。良いツールも見つからない
- でもそれだけのために維持費かかるCDN使うのもだるい
- しかもそれで劇的に早くなったりするわけでもない
で悩んでいる
ご意見お待ちしています
とりあえずバイナリ置き場はGitHubではなくS3を使うべきな気がする
sharpでpngからwebpを作成する
import sharp from "sharp";
import globOriginal from "glob";
import { promisify } from "util";
const glob = promisify(globOriginal);
const convertWebp = async (file: string) => {
console.log(`Converting ${file}`);
return await sharp(file)
.webp({
lossless: true,
})
.toFile(file.replace(".png", ".webp"));
};
const listFiles = async (pattern: string) => {
const files = await glob(pattern);
return files;
};
(async () => {
const dirBase = `${__dirname}/../`;
const pattern = `${dirBase}**/*.png`;
const files = await listFiles(pattern);
await Promise.all(files.map(convertWebp));
})();
コストが掛からない方がいいと思ったので使わないようにした
sharpでのwebp変換は結構早くてよかった
このスクラップは2022/08/25にクローズされました