dely Tech Blog

クラシル・TRILLを運営するdely株式会社の開発ブログです

Cloud Runで手軽にサーバーレス・SSR(サーバーサイドレンダリング)

f:id:yamanoi-y:20201211115524p:plain こんにちはdelyでサーバーサイドエンジニアをしているyamanoiです

この記事は「dely #2 Advent Calendar 2020」の12日目の記事です。

adventar.org

adventar.org

昨日は@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を実現することができます。

その他

ドメイン周り

独自ドメインを設定したい場合はコンソールから設定することができます。 カスタムドメインを管理

f:id:yamanoi-y:20201208220553p:plain

マッピングを追加

f:id:yamanoi-y:20201208220558p:plain

設定するドメインは予め所有権の検証を済ませておく必要があります。

assets周り

そのままでは画像や分割されたjsのようなassets類もCloud Run経由で配信されてしまいます。 assets類はCloud Runを経由する必要がないですし、余計なリソースも必要となってしまうため、Cloud Storageから取得するように変更します

Cloud Storageにassets-nuxt-cloud-run-sampleという名前でバケットを作成
バケットの中身が外部からアクセスできるように公開設定をしておきます

f:id:yamanoi-y:20201208221034p:plain

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を用いたデプロイが可能になったため、そちらも今度試してみたいと思います。

aws.amazon.com

明日は@gomesuitさんの「技術だけではもう足りない?エンジニアとしての成長のために避けては通れない4つの領域とは!」です、お楽しみに!

また、delyではエンジニア・デザイナーを絶賛募集中です。

ご興味がある方はこちらのリンクからお気軽にエントリーください!

join-us.dely.jp

また定期的にTechTalkというイベントも開催しているので、delyについて詳しく知りたい方は是非参加してみてください!

bethesun.connpass.com