本記事はCocone Advent Calendar 2022の22日目の記事になります。
はじめに
今回は全リヴリー大図鑑で導入した、静的サイトのOPG画像生成をNode.js、GitLab CI/CDで自動化した話をしたいと思います。
全リヴリー大図鑑はリヴリーアイランドの20周年記念のWEBコンテンツになります。2002年リリースから現在までに登場した全てのリヴリーを紹介しています。
サイトはAmazon S3 を使用した静的ウェブサイトになります。ビルド時にリヴリーデータを取得し、各リヴリーの詳細ページを生成後S3にデプロイしています。
そして、詳細ページは319ページあり(2022年12月現在)、OGP画像も詳細ページ数と同様に、319枚用意しています。
開発前に決めたこと
OGP画像生成ツールの開発にあたり、事前に決めたことは次の4点になります。
- 詳細ページは静的ページとしてAmazon S3に格納するため、AWS Lambdaの動的生成は利用しない。
- Node.jsを利用し、リヴリーデータの取得から、OGP画像の生成までを行う(あわせて減色も行う)。
- ヘッドレスブラウザのPuppeteerを利用し、テンプレートHTMLにデータをはめ込んだページをキャプチャ・OGP画像として保存する。
- GitLab CI/CDを利用し、OGP画像の生成からAmazon S3へのデプロイを、一気通貫で実行できるようにする。
実際のコードとポイント
実際のコードから、主要となるコードとポイントをまとめてみました。
OGP画像の画像生成
Puppeteerを利用し、OGP画像の生成を行うコードになります。
puppeteer
.launch({
ignoreDefaultArgs: ["--disable-extensions"],
args: ["--no-sandbox", "--disable-setuid-sandbox"]
})
.then(async (browser) => {
await Promise.all(
items.map(async (item) => {
return await browser.newPage().then(async (page) => {
// リヴリー情報の取得
const { name, scientific, imageUrl, imageName } = item;
// リヴリー画像データの取得・Base64に変更
const livlyImage = await fetchImage(imageUrl);
const { headers, data } = livlyImage;
const livlyImageBase64 = convertImageToBase64(
headers["content-type"],
data
);
// キャプチャサイズの設定
await page.setViewport({ width: 1200, height: 630 });
// 取得したリヴリー情報をはめ込んだHTMLコードの取得
const html: string = templateHTML(
name,
scientific,
livlyImageBase64,
backgroundImageBase64
);
// HTMLをページに設定
await page.setContent(html);
// キャプチャ・保存
const output = `${dist}/${imageName}`;
await page.screenshot({ path: output });
});
})
);
// ブラウザを閉じる
await browser.close();
});
OGP画像の画像生成コードについて
- 画像データはBase64に変換する必要があります。
- Puppeteerは、次のメソッドを利用しました。
メソッド 機能 puppeteer.launch() puppeteerを起動 browser.newPage() 新規ページを開く page.setViewport() ページのキャプチャサイズの設定(OPG画像の縦横幅になります) page.setContent() ページのHTMLを設定 page.screenshot() スクリーンショットの撮影と保存( { path: test.png}
と指定すると、test.pngで保存)browser.close() ブラウザを閉じる
CI/CDワークフロー
GitLab CI/CDパイプラインの設定コードになります。
stages:
- build
- deploy
default:
image: buildkite/puppeteer #Puppeteerイメージを指定
cache:
key:
files:
- package-lock.json
- package.json
paths:
- .npm/
before_script:
- echo "checking node version"
- node -v
- apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 4EB27DB2A3B88B8B
- apt-get update -yqq
- apt-get install -yqq fonts-noto-cjk --no-install-recommends #日本語フォントのインストール
- npm config set "//registry.npmjs.org/:_authToken=${NPM_AUTH_TOKEN}"
- npm ci --cache .npm/ --prefer-offline --no-audit
interruptible: true
.build:
stage: build
script: API_BASE_URL=$API_BASE_URL npm run build #OPG画像生成プログラムの実行
artifacts:
paths:
- ogp/
expire_in: 1 day
build_staging:
extends: .build
rules:
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"
- if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "master"
variables:
API_BASE_URL: "(省略)"
build_prod:
extends: .build
rules:
- if: $CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_BRANCH == "master"
variables:
API_BASE_URL: "(省略)"
.deploy:
stage: deploy
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
variables:
GIT_STRATEGY: none
cache: {}
before_script: []
script: aws s3 sync "${DIST_DIR}" "${S3_URI}" --delete --acl bucket-owner-full-control
interruptible: false
deploy_staging:
extends: .deploy
needs: [build_staging]
rules:
- if: $CI_COMMIT_BRANCH == "develop"
variables:
DIST_DIR: "./(OGP画像の格納ディレクトリ)"
S3_URI: "(省略)"
environment:
name: staging
url: "(省略)"
deploy_prod:
extends: .deploy
needs: [build_prod]
rules:
- if: $CI_PIPELINE_SOURCE == "web" && $CI_COMMIT_BRANCH == "master"
variables:
DIST_DIR: "./(OGP画像の格納ディレクトリ)"
S3_URI: "(省略)"
environment:
name: prod
url: "https://www.livly.com/picturebook/"
CI/CDワークフローの設定コードについて
- Dockerのイメージは
buildkite/puppeteer
を利用しました。 - OGP画像で日本語を動的に表示する場合、日本語フォントのインストールが必要です。
おわりに
プロジェクトのキックオフMTG時の話では、クオリティを配慮し、デザイン担当者が1枚1枚作成する予定でした。1枚30分で300枚作成した場合でも、大変な制作工数が想定できました。
OPG画像生成を自動化したことで、デザイン担当者の工数の大幅な削減が実現できました。
現在、運用フェーズですが、サイト更新時にパイプラインから Run pipeline
を実行するだけで作業が終わるのも、大きなメリットです。
静的ウェブサイトでOGP画像生成を検討している場合、参考いただければ嬉しいです。