こんにちはdelyでサーバーサイドエンジニアをしているyamanoiです
この記事は「dely #2 Advent Calendar 2020」の12日目の記事です。
昨日は@yochidrosさんの「KMMでiOS・Android を共通化しよう」でした。
みなさんwebサイトを作成する時にSPAを利用していますか?
SPAはユーザーに対してメリットが大きいですが、SEO観点やOGPタグのレンダリング等で SSRが避けられない場面に出くわすことがあると思います。
SSRが不要であればビルドして生成された成果物をs3等でホスティングするだけなのでデプロイや、運用が楽なのですが、 SSRをするとなるとNode jsの実行環境必要になります。
ある程度大きなプロジェクトであればECSやGKE, GAEに載せてガッチリと運用すべきだと思いますが、 個人開発のや検証段階のプロダクトのような規模の小さいプロジェクトに対して、自前でサーバーを用意したりECSやGKEに載せるとなると運用コストが増すので、できればサーバーレスで実行したいですよね
SPAをサーバーレスで実現する方法はいくつかあり、AWS LambdaやCloud Functionsを使用するのがよくある方法かなと思います。
ここからは筆者が触ったことのあるNuxt.jsについて話していきたいと思います。
Nuxt.jsでAWS LambdaやCloud Functionsを利用したSSRを行うには以下のことを考慮する必要があります
- Node.jsのバージョンが対応しているものからしか選択することができない
- サーバー用のコードを追加で書く必要がある
- 追加のpackageを読み込む必要がある(aws-serverless-express)
そこでCloud Runの出番です。
Cloud Runは簡単に言うとコンテナをサーバーレスで動かすことのできるサービスになります。
Cloud Runはプラットフォームがいくつかあるのですが、普通に使用する場合はフルマネージド版を選択すれば大丈夫です。
フルマネージドでは、デプロイしたいDockerfileを用意するだけアプリケーションを構築することができます。
独自ドメインの設定もでき、httpsも証明書の管理不要で使用することができます。
またコンテナなので、Node.jsのバージョンもプラットフォームに制限されることなく柔軟に扱うことができます。
構築手順
簡単に構築手順を解説します。
*gcpプロジェクトの作成、各apiの有効化・認証、gcloudコマンドのセットアップは省略します。
1 Nuxt.jsプロジェクトの作成
Universalモードを選択してNuxtプロジェクトを作成します
yarn create nuxt-app nuxt-cloud-run-sample
2 Dockerifleの作成
Nuxt.jsを実行するためのDockerfileを作成します
FROM node:10 ARG asset_path ENV ASSET_PATH=$asset_path WORKDIR /src ENV PORT 8080 ENV HOST 0.0.0.0 COPY ./package.json . COPY ./yarn.lock . RUN yarn install --only=production COPY . . RUN yarn build CMD ["yarn", "start"]
3 docker imageをGCR(Google Container Registry)にpush
1で作成したDockerfileをビルドし、GCRにpushします。
docker build -t nuxt-cloud-run-sample .
docker tag nuxt-cloud-run-sample gcr.io/project_id/nuxt-cloud-run-sample
docker push gcr.io/project_id/nuxt-cloud-run-sample
4 デプロイ実行
gcloudコマンドでCloud Runにデプロイします
gcloud run deploy nuxt-cloud-run-sample --image gcr.io/project_id/nuxt-cloud-run-sample --platform managed --region asia-northeast1 --allow-unauthenticated
デプロイが完了するとターミナルにURLが出力されるので、そのURLにアクセスするとNuxt.jsのデフォルトのビューが表示されると思います。
たった4ステップ(所要時間約10分)でSSRを実現することができます。
その他
ドメイン周り
独自ドメインを設定したい場合はコンソールから設定することができます。 カスタムドメインを管理
マッピングを追加
設定するドメインは予め所有権の検証を済ませておく必要があります。
assets周り
そのままでは画像や分割されたjsのようなassets類もCloud Run経由で配信されてしまいます。 assets類はCloud Runを経由する必要がないですし、余計なリソースも必要となってしまうため、Cloud Storageから取得するように変更します
Cloud Storageにassets-nuxt-cloud-run-sampleという名前でバケットを作成
バケットの中身が外部からアクセスできるように公開設定をしておきます
Cloud Storageにビルドしたassetsをアップロード
id=$(docker create gcr.io/project_id/nuxt-cloud-run-sample:latest) docker cp $id:/src/.nuxt/dist/client ./.assets gsutil cp -r ./.assets gs://assets-nuxt-cloud-run-sample/
nuxt.config.jsを変更
ASSET_PATHという環境変数でCloud StorageのURLを受け取れるようにしておきます。
... build: { /* ** You can extend webpack config here */ publicPath: process.env.ASSET_PATH, extend(_, __) {} }, ...
構築手順2のDockerfile内でasset_pathを受け取れるようにしてあるため、dockerrビルド時に指定することで、publicPathを切り替えます。
docker build -t nuxt-cloud-run-sample --build-arg asset_path=https://storage.googleapis.com/assets-nuxt-cloud-run-sample/.assets .
CI, CD
GCPではCI, CDにCloud Buildが使えます。
以下は上に書いてある構築手順を各ステップに定義したymlのサンプルになります。参考にしてみてください
steps: - name: 'gcr.io/cloud-builders/docker' id: Pull Cache entrypoint: 'bash' args: - '-c' - | docker pull gcr.io/project_id/nuxt-cloud-run-sample:latest || exit 0 - name: 'gcr.io/cloud-builders/docker' id: Build App args: - 'build' - '-t' - 'gcr.io/project_id/nuxt-cloud-run-sample:$SHORT_SHA' - '-t' - 'gcr.io/project_id/nuxt-cloud-run-sample:latest' - '--cache-from' - 'gcr.io/project_id/nuxt-cloud-run-sample:latest' - '--build-arg' - 'asset_path=https://storage.googleapis.com/assets-nuxt-cloud-run-sample/.assets' - '.' - name: 'gcr.io/cloud-builders/docker' id: Push Image entrypoint: 'bash' args: - '-c' - | docker push gcr.io/project_id/nuxt-cloud-run-sample:$SHORT_SHA docker push gcr.io/project_id/nuxt-cloud-run-sample:latest - name: 'gcr.io/cloud-builders/docker' id: Copy assets entrypoint: 'bash' args: - '-c' - | id=$(docker create gcr.io/project_id/nuxt-cloud-run-sample:$SHORT_SHA) docker cp $id:/src/.nuxt/dist/client ./.assets - name: 'gcr.io/cloud-builders/gsutil' id: Upload assets args: - 'cp' - '-r' - './.assets' - 'gs://assets-nuxt-cloud-run-sample/' - name: 'gcr.io/cloud-builders/gcloud' args: - 'beta' - 'run' - 'deploy' - 'nuxt-cloud-run-sample' - '--image' - 'gcr.io/project_id/nuxt-cloud-run-sample:$SHORT_SHA' - '--region' - 'asia-northeast1' - '--platform' - 'managed' - '--allow-unauthenticated'
最後に
Cloud Runは小さなプロジェクトをサクッと立ち上げたい時には重宝するサービスだと思います。
他にもPub/Subから起動することができたり、IAMでアクセス制御できたりと便利な機能が沢山あります。
これを書いている途中でAWS Lambdaでもdocker imageを用いたデプロイが可能になったため、そちらも今度試してみたいと思います。
明日は@gomesuitさんの「技術だけではもう足りない?エンジニアとしての成長のために避けては通れない4つの領域とは!」です、お楽しみに!
また、delyではエンジニア・デザイナーを絶賛募集中です。
ご興味がある方はこちらのリンクからお気軽にエントリーください!
また定期的にTechTalkというイベントも開催しているので、delyについて詳しく知りたい方は是非参加してみてください!