10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CoconeAdvent Calendar 2022

Day 22

静的サイトのOPG画像生成をNode.js、GitLab CI/CDで自動化した話

Last updated at Posted at 2022-12-21

本記事はCocone Advent Calendar 2022の22日目の記事になります。

はじめに

今回は全リヴリー大図鑑で導入した、静的サイトのOPG画像生成をNode.js、GitLab CI/CDで自動化した話をしたいと思います。

全リヴリー大図鑑はリヴリーアイランドの20周年記念のWEBコンテンツになります。2002年リリースから現在までに登場した全てのリヴリーを紹介しています。

サイトはAmazon S3 を使用した静的ウェブサイトになります。ビルド時にリヴリーデータを取得し、各リヴリーの詳細ページを生成後S3にデプロイしています。

そして、詳細ページは319ページあり(2022年12月現在)、OGP画像も詳細ページ数と同様に、319枚用意しています。

OPG画像サンプル

開発前に決めたこと

OGP画像生成ツールの開発にあたり、事前に決めたことは次の4点になります。

  • 詳細ページは静的ページとしてAmazon S3に格納するため、AWS Lambdaの動的生成は利用しない。
  • Node.jsを利用し、リヴリーデータの取得から、OGP画像の生成までを行う(あわせて減色も行う)。
  • ヘッドレスブラウザのPuppeteerを利用し、テンプレートHTMLにデータをはめ込んだページをキャプチャ・OGP画像として保存する。
  • GitLab CI/CDを利用し、OGP画像の生成からAmazon S3へのデプロイを、一気通貫で実行できるようにする。

生成の流れ

実際のコードとポイント

実際のコードから、主要となるコードとポイントをまとめてみました。

OGP画像の画像生成

Puppeteerを利用し、OGP画像の生成を行うコードになります。

generate.ts
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パイプラインの設定コードになります。

.gitlab-ci.yml
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画像生成を検討している場合、参考いただければ嬉しいです。

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?