Rails7でViteを使う - 本番ビルド編
概要
以下の続き
目的
- 前編でHMRは実現できたが、Viteは開発環境ではesbuildというgo製のビルドツールを、本番環境などではrollupというJavaScript製のバンドラーを利用しているので各々の環境で確認するのがベター
- 前項の理由で、ここでは本番環境でのビルドがうまくかを確認する
TODO:
- 加えて、本番環境でよく用いられる CDN によるアセットの配信も実現する
目標
- Terraform でインフラをAWS上に構築し、そこに Rails アプリケーションをデプロイできること
TODO:
- アセットはS3にアップロードすること
- S3の前段にCloudFrontを配置し、アセットを高速に読み込めるようにすること
環境
- デプロイ先は AWS
- インフラは IaC (Terraform) で管理
ビルドのトラブルシューティング
CommonJS や AMD Module などを利用したコードを含むプロジェクトでのビルド
Aside: select2 や jQuery など古いパッケージを利用している場合
当方が参画している案件では select2 などの古いパッケージが利用されていた
これは、esbuild では以下のような書き方でワークしていた
import select2 from "select2"
select2()
しかし、テスト実行時のビルドでは app/frontend/entrypoints/application.js (10:7): "default" is not exported by "node_modules/select2/dist/js/select2.js", imported by "app/frontend/entrypoints/application.js".
のように実行時エラーになってしまった
これはrollupでのCommonJSでの扱いが関係していると考えられる
この問題に対応するため、 @rollup/plugin-commonjs
を利用できる
以下でインストールを実行する
docker compose exec app npm install --save-dev @rollup/plugin-commonjs
続いて、 vite.config.ts
を以下のように編集する
+import commonjs from '@rollup/plugin-commonjs'
export default defineConfig({
plugins: [
RubyPlugin(),
+ commonjs(),
],
+ build: {
+ commonjsOptions: { include: [] },
+ }
})
正直詳しいことはわかっていないがこれでビルドは通るようにはなった
この件に関しては、以下を参考にした
本番環境でのビルドとコンパイルされたアセットの参照
冒頭の目標の章で少し触れたが、Viteは開発環境ではesbuildというgo製のビルドツールを、本番環境ではrollupというJavaScript製のバンドラーを利用している
そのため、開発環境ではうまく動いていたがビルドが失敗するなどの問題も起こりうる
ここではAWS上のサービス群を本番環境のデプロイ先として、コンパイルされたアセットを参照できるかを確認する
インフラ構成
簡便のため以下のようにする
- 権限は本来であれば最小で設定すべきだが動作確認後すぐに消すことを前提にざっくり設定する
- SSLは導入しない
- なので、払い出されたDNS名をブラウザのアドレスバーに入力した際にプロトコルが
https
になったらアクセスできないので注意
- なので、払い出されたDNS名をブラウザのアドレスバーに入力した際にプロトコルが
- ALBをリバースプロキシとして機能させ、このバックエンドにRailsのアプリケーションサーバーを配置
- 通常、Nginxなどを置くが今回は配置しない
- リモートバックエンドは構築しない
以下に Terraform による実装を用意した
AWSアカウントやプロファイルを用意しこれを terraform apply
すればデプロイに必要なインフラ基盤ができる
⚠️ 用が済んだら terrafom destroy
を忘れずに
デプロイ用のワークフローの実装
コンテナのもととなるイメージは実行環境以外にもソースコードもワンパッケージとなったものであるため、一度作成したイメージを使い回すというよりは改修毎に新しいリビジョンができることになる。
一般的にはブランチへのマージなどを契機にイメージを作るのがいいが、今回は便宜上任意のタイミングでECSのコンテナにデプロイできるような以下のワークフローを作成した
name: Deploy to ECS
permissions:
contents: read
id-token: write
actions: read
on:
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.PROJECT_NAME }}-github-actions-role
aws-region: ${{ secrets.AWS_REGION }}
- name: Login to AWS ECR
id: ecr-login
uses: aws-actions/amazon-ecr-login@v2
- name: Build and push Docker image
id: build-image
uses: docker/build-push-action@v4
with:
context: ./
file: ./deploy/Dockerfile
push: true
tags: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/${{ secrets.PROJECT_NAME }}-ecr-repo:latest
- name: Render task definition from template
id: render-task-def
run: envsubst < deploy/taskdef-template.json > taskdef.json
env:
IMAGE_URI: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/${{ secrets.PROJECT_NAME }}-ecr-repo:latest
PROJECT_NAME: ${{ secrets.PROJECT_NAME }}
AWS_REGION: ${{ secrets.AWS_REGION }}
SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }}
- name: Register ECS task definition
id: register-task-def
run: |
TASK_DEF_ARN=$(aws ecs register-task-definition \
--family ${{ secrets.PROJECT_NAME }}-ecs-task-rails \
--execution-role-arn arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.PROJECT_NAME }}-ecs-task-execution-role \
--cli-input-json file://taskdef.json \
--query 'taskDefinition.taskDefinitionArn' --output text)
echo "TASK_DEF_ARN=$TASK_DEF_ARN" >> $GITHUB_ENV
- name: Update ECS service
run: |
aws ecs update-service \
--cluster ${{ secrets.PROJECT_NAME }}-ecs-cluster \
--service ${{ secrets.PROJECT_NAME }}-ecs-service-rails \
--task-definition ${{ env.TASK_DEF_ARN }} \
--force-new-deployment
以下のシークレットを Github Actions 用にセットし、ワークフローを実行すると先程作成したAWSのインフラにデプロイが可能になる
- AWS_ACCOUNT_ID
- AWS_REGION
- PROJECT_NAME
- SECRET_KEY_BASE
-
docker compose exec app bin/rails secret
で出せる
-
デプロイ時のトラブルシューティング
Cannot find module @rollup/rollup-linux-x64-gnu. npm has a bug related to optional dependencies
#16 3.195 Error: Cannot find module @rollup/rollup-linux-x64-gnu. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try npm i again after removing both package-lock.json and node_modules directory.
上記のようなエラーがデプロイ時に発生した場合は、 package.json
に optionalDependencies
として @rollup/rollup-linux-x64-gnu
を追加することで、依存関係の解決ができる
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "4.6.1"
}
Discussion