7839

雑草魂エンジニアブログ

【GitHub Actions】Next.jsのPreview Mode + microCMS(EC2・S3への自動デプロイ設定)

現在、とあるサイトを Next.js + microCMS で運用している。そして、インフラ環境は、CloudFront + Amazon S3 構成としている。今回、エンジニア以外の人がmicroCMSで記事を執筆して、プレビュー画面で見栄えも確認したいのに、気軽に確認できない。という問題にぶつかった。今回は、突貫作業で EC2 にローカル環境と同一のサイトを構築し、プレビューできる環境を用意してみた。誰かの参考になればと思い、備忘録として残しておく。

Next.js の Preview Mode

Next.js 9.3から、ヘッドレスCMS向けに作られた機能として、Preview Modeが追加された。静的サイト生成(SSG)による Jamstack 構成は高パフォーマンス、高セキュリティを実現できるが、プレビューの実現には毎回ビルドする必要があり、少し手間がかかってしまう部分があった。Preview Modeでは、サーバーレス関数を起動できるAPI Routes機能を利用して、サーバーサイドレンダリングSSR)を行い、簡単にプレビューを実現することができる。

詳細の設定方法などは、公式ページを参照して欲しい。

そして、Next.js アプリで SSR(Server-side Rendering)や API ルートなどの機能を使用している場合、Next.js サーバー上で Web サイトをホスティングする必要がある。そして、現状そのような環境をサーバーレスなサービスとして提供しているのは、Vercel のみである。

ただ、現状 Vercel は使っておらず、AWS 環境しか使っていなかったので、今回は、AWS の EC2 でプレビュー用のページを作ることにした。

システム構成概要

f:id:serip39:20220201005330p:plain

EC2には、Node.jsをインストールして、Next.jsを動かせるようにする。そして、開発サーバーを起動しておく。microCMSのAPI設定の画面プレビュー設定のURLを以下のようにしておくことで、プレビュー画面を表示することができる。

http://<-EC2のPublic IP address->/api/preview?slug={CONTENT_ID}&draftKey={DRAFT_KEY}

セキュリティの問題もあるため、プレビュー画面が閲覧できるのは、社内からのみなど、EC2のセキュリティのインバウンドルールを適切に設定しておく。

また、デザイン変更などをした場合に、自動的にプレビュー画面と本番環境に反映させられるように、Github Actionsを設定した。

EC2 環境構築

少人数で利用するNext.jsのプレビュー画面用なので、「t2.micro」で構築した。(Node.jsの環境構築に関しては、今回説明を割愛する。)

今回、next devで開発サーバーを起動しておけばいいのだが、プロセス管理にSupervisorを導入した。導入した理由は、プロセスの管理がしやすいことと、開発サーバーをデーモン化させて、autorestartなどをさせたかったことが挙げられる。Supervisorのインストールなどは、以前の記事を参考にして欲しい。今回のSupervisorの設定ファイルを参考として紹介する。

[program:preview]
directory=/var/www/html/sample-website
command=/bin/bash -c "/opt/nvm/versions/node/v14.17.0/bin/node /var/www/html/sample-website/node_modules/.bin/next dev"
autostart=true
autorestart=true
startsecs=3
user=ec2-user
redirect_stderr=false
stderr_logfile=/var/www/html/log/preview.stderr.log
stdout_logfile=/var/www/html/log/preview.stdout.log

上記の設定をしておくことで、プロセス管理が簡単にできる。

# 起動
$ supervisorctl start preview
# ステータス確認
$ supervisorctl status preview
# 停止
$ supervisorctl stop preview
# 再起動
$ supervisorctl restart preview

開発サーバーが起動している状態で、microCMSの「画面プレビュー」ボタンを押すと、プレビュー画面を閲覧する環境を構築することができた。

IAMユーザー作成

Github Actionsで、AWSにデプロイするために、IAMユーザーを新規に作成しておく。割り当てておくべきアクセス権限のポリシーは、以下の通りである。

  • EC2デプロイ用
    • AmazonVPCFullAccess
    • AmazonEC2ContainerServiceRole
  • S3 + CloudFront用
    • AmazonS3FullAccess
    • CloudFrontFullAccess

アクセスキーを作成して、AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYを取得しておく。

GitHub Actions作成(Previewページ用)

GitHubから、EC2へのデプロイに関しては、以下の方法で行うことにした。

  1. Github Actions の Public IP address を確認する
  2. AWS の Credential を設定する
  3. EC2 の Security Group に Github Actions の Public IP address を追加する
  4. EC2 に Github Actions から SSH アクセスして、デプロイ作業をする
  5. 先ほど追加した、Github Actions の Public IP address を削除する

IPの追加に関しては、AWS CLI の athorize-security-group-ingress を用いた。詳細は、以下の公式ドキュメントを参考にして欲しい。

GitHubのSettings > Security > Secrets から、以下のSecretsを事前に設定しておく必要がある。

  • AWS_ACCESS_KEY_ID:IAMユーザーから取得
  • AWS_SECRET_ACCESS_KEY:IAMユーザーから取得
  • SECURITY_GROUP:EC2(セキュリティグループID)から取得
  • SECRET_KEY:EC2(キーペア)から取得
  • EC2_HOST:EC2(パブリックIPv4)から取得
  • EC2_USER:EC2から取得
name: deploy-to-ec2

# Controls when the workflow will run
on:
  pull_request:
    branches: [ main ]
    types: [closed]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  build:
    runs-on: ubuntu-latest

      - name: Checkout
        uses: actions/checkout@v2

      - name: Public IP
        id: ip
        uses: haythem/public-ip@v1.2

      - name: Print Public IP
        run: |
          echo ${{ steps.ip.outputs.ipv4 }}

      - name: Configure AWS credentials from account
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Open a specific port of the security group
        run: |
          IP_ADDRESS=`echo ${{ steps.ip.outputs.ipv4 }}`
          aws ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP} --protocol tcp --port 22 --cidr "$IP_ADDRESS"/32
        env:
          SECURITY_GROUP: ${{ secrets.SECURITY_GROUP }}

      - name: Deploy
        run: |
          echo "$SECRET_KEY" > secret_key
          chmod 600 secret_key
          ssh -oStrictHostKeyChecking=no -i secret_key ${EC2_USER}@${EC2_HOST} "cd /var/www/html/sample-website &&
          git pull origin main &&
          supervisorctl restart preview"
        env:
          SECRET_KEY: ${{ secrets.SECRET_KEY }}
          EC2_USER: ${{ secrets.EC2_USER }}
          EC2_HOST: ${{ secrets.EC2_HOST }}

      - name: Close a specific port in a security group
        run: |
          IP_ADDRESS=`echo ${{ steps.ip.outputs.ipv4 }}`
          aws ec2 revoke-security-group-ingress --group-id ${SECURITY_GROUP} --protocol tcp --port 22 --cidr "$IP_ADDRESS"/32
        env:
          SECURITY_GROUP: ${{ secrets.SECURITY_GROUP }}

この設定をしておくことで、プルリクがマージされた際に、このワークフローが実行される。

microCMS用に、GitHubトークンを発行する

microCMS の Webhook で使用するトークンを発行する。
GitHubの「Settings > Developer settings > Personal access tokens」で、「Generate new token」を選択する。

  • Note:何目的で発行したトークンか区別できるようにメモを残しておく
  • Expiration:期限を自由に設定する(No expiration:無期限にもできる)
  • Select scopes:「repo」にチェックを入れる

最後に、「Generate token」を押すと、トークンが発行されるので、コピーして保存しておく。

microCMSにGitHub ActionsのWebhookを追加する

設定方法に関しては、公式ドキュメントを参照ください。

「トリガーイベント名」は自由に設定できるが、GitHub Actionsのワークフローの設定と一致させておく必要がある、今回は、「dispatch_cms」とした。

GitHub Actions作成(本番へのデプロイ用)

S3 + CloudFrontの環境構築に関しては、今回割愛して、GitHub Actions を用いた自動デプロイの設定だけを紹介する。

上記の設定に追加して、GitHubのSettings > Security > Secrets から、以下のSecretsを事前に設定しておく必要がある。

name: deploy-to-s3-cloudfront-for-prod

# Controls when the workflow will run
on:
  pull_request:
    branches: [ main ]
    types: [closed]
  repository_dispatch:
    types: [dispatch_cms]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  deploy:
    name: build and deploy to S3
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [14.x]

    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}

      - name: Get yarn cache directory path
        id: yarn-cache
        run: echo "::set-output name=dir::$(yarn cache dir)"

      - uses: actions/cache@v2
        with:
          path: ${{ steps.yarn-cache.outputs.dir }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-
      - name: yarn install
        if: steps.yarn-cache.outputs.cache-hit != 'true'
        run: yarn install --frozen-lockfile

      - name: yarn build:static
        run: yarn build:static

      - name: Configure AWS credentials from account
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Copy Files to S3
        run: |
          aws s3 sync ./out s3://<-BUCKET NAME->
      - name: invalidate cloudfront
        uses: chetan/invalidate-cloudfront-action@master
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          DISTRIBUTION: ${{ secrets.AWS_PROD_DISTRIBUTION_ID }}
          PATHS: '/*'

この設定をしておくことで、以下の条件下でこのワークフローが実行される。

  • プルリクがマージされた場合
  • microCMS からのWebhook通知があった場合

まとめ

突貫作業で作成したが、エンジニア以外の人が簡単にプレビューできる環境を作ることで、お互いに作業効率を向上させることができた。Vercelでは、今回実行したことをボタンひとつで簡単に実行できてしまうので、最高だなーっと改めて思えた。

是非とも、もっといい方法などがあれば、教えてください。よろしくお願いします。