コメント_2019-09-16_191519

秒.workのシェア画像を生成する仕組みとは……?

こんにちは、うなすけです。先日解散が発表された株式会社バンクにて、業務委託として働いています。

匿名で仕事探しができる秒.work ですが、これは元バンクの同僚であるillyが開発したものです。

実は僕も少し手伝った部分があるので、この記事ではその中から「シェア画像生成」について書きます。

シェア画像を「何で」生成するか

さて、「画像に文字を重ねる」ということをしたいときに皆さんがまず思いつく方法はなんでしょうか。僕は ImageMagick でした。

単純に文字を重ねるだけなら、ImageMagick は手軽で便利なツールではありますが、長さが変化する文字列の中央寄せ、改行の扱い、絵文字対応、フォントをどうするか、などを考慮すると、単純に「面倒だな……」という気持ちになったことは事実です。

そこで、「長さが変化する文字列の中央寄せ、改行の扱い、絵文字対応、フォントをどうするか、など」について僕が楽をできる仕組みについて考えると、HTML/JavaScript/CSSで画像を組み立てるのが一番実装が素早くできることに気付きました。そして、それによって生成したレイアウトを画像にするには、Puppeteerが使用できますね。

第一段階、ローカルでPuppeteerを動かす


まずは、Railsの lib 以下に、Puppeteer を実行して画像を生成するスクリプトを作りました。具体的には……

1. 画像生成用クラスにタイトルと説明を渡す
2. 画像生成用クラスは、受け取った文言をERBテンプレートに埋め込み、HTMLを生成する
3. それを Puppeteer に渡し、画像を tmp 以下に出力する
4. 画像生成用クラスは、その画像の path を返り値として出力する

ということを行うようにしました。

これは手元での検証はうまくいったので、よし本番に出すぞ!と意気込んだものの、本番では上手くいきませんでした。

原因は「メモリ不足」です。Rails 自体も割とギリギリで動作しているのか、Puppeteer のプロセスを起動させてもすぐに落ちてしまっていました。秒.workはillyのサイドプロジェクトであり、豪華なインスタンスで動いている訳ではありません。インスタンスを強くすると、その分費用もかさんでしまいます。

第二段階、Cloud FunctionsでPuppeteerを動かす


Puppeteerを外部に逃がすことを考えていると、次の記事を見付けました。

https://web.archive.org/web/20190729170550/https://cloud.google.com/blog/products/gcp/introducing-headless-chrome-support-in-cloud-functions-and-app-engine

(今はオリジナルが消えてしまっているのでweb archiveを貼っておきます)

そこで、このような仕組みを構築しました。

1. 求職投稿それぞれに、シェア用画像の元となるHTMLを返すだけのエンドポイントを作成する
2. Cloud Functions には、その URL を request body に入れて POST request をする
3. Cloud Functions の response body として生成した画像をそのまま入れて返す
4. Rails 側はその画像を GCS にアップロードする

そのために作成した Cloud Functions がこれです。

この仕組みはうまくいき、現在も皆さんの秒.workへの投稿をトリガーにして、この Cloud Functions が元気に動いています。

秒.workは上記のように Google Cloud Platform を活用したサービスとなっています。そこで、Googleの SokoP さんこと浦底さんにアプローチしてみたところ、見事 Google Cloud Startup Program に採択されました。SokoP さん、ありがとうございます。

ということですので、皆さん、秒.work を使ってみてください。


投げ銭していただけるととても嬉しいです