あのちっく小屋

VercelでOG画像を動的生成する仕組みを作る

2022.10.08
あのちっく

ユーザーがブログ記事的なものを書いたときに、そのページのOG画像を自動生成する仕組みを作りたい事があると思う。

こういう感じのやつ。

問題はどうやってレンダリングするかなのだけれど、思いつく限り3種類ある。

A. ユーザーのブラウザでレンダリングする

すべてのユーザーはWebクライアントを使っているという前提であれば、ユーザーが利用しているブラウザ上でレンダリングし、CSS+HTMLでhtml2canvas的なソリューションで画像化し、それをアップロードするという方法が使えそうだ。

サーバーサイドでレンダリングするのはコストだと感じるのであればこれもあり。

ただし、私が実装を試したところ、以下のような問題があるので注意が必要

  • ユーザーのブラウザの違いについて対応する必要がある
  • CJK系WebFontsを利用している要素にhtml2canvasを使うとものすごく動作が遅い
  • 後から描画ロジックを変えても既に記事が公開されているものに関しては新しいものが適用されない

特にブラウザの差異対応が結構たいへんだった。

B. ヘッドレスChromiumを使う

おそらく一番簡単な方法。

Vercel公式のサンプルコードもあるぐらい。

これでいいじゃん!って思うかもしれないけれど、最新のchromium+puppeteer構成は、デプロイ可能ファイルサイズの50MBを超えるので、デプロイが出来ない。

Vercel のFunctionsはAWS Lambdaを利用しており、これの制限がそのままVercelの制限にもなっている様子。

AWS Lambdaを直接使う場合であればAWS Layersという仕組みを使うことでこの問題を回避できそうだが、Vercelでは今のところAWS Layersを利用する方法がないため、どうしようもない。

chromium+puppeteerで利用するライブラリやchromiumのバージョンを下げればどうにか50MB未満に抑えることは出来るが、レンダリングに利用する日本語フォントファイルを同梱させるとすぐに50MBをオーバーしてしまい厳しい。

C. node-canvasでレンダリングする

node-canvasはCanvasAPIのようなものをサーバサイドでも利用できるライブラリ。 ただし、Canvasは角丸めとか、指定領域内でいい感じにテキストを描画するのような便利な機能はないので、Fabric.jsのような、canvasを便利に扱えるライブラリを使うと良い。

ちなみに、Vercelでnode-canvasを使う場合は、ライブラリを追加でインストールしてあげる必要がある。

まとめ

とりあえず3種類の方法を示した。

どうしてもVercelだけで完結させたい理由があるのであれば[C]で実装するのがよさそうだが、やはりChromiumを使って描画するのは楽なので、chromium+puppeteer構成のサーバーをcloud runとかで提供する形にするのが簡単かも。 [A]は多くの環境で正しく動くことまで求めると結構面倒なのでおすすめはしない。