従来のクライアントだけで動くReactアプリはビルドされたJavaScript/HTML/CSSファイルをWebサーバー上にデプロイすれば動きましたが、React Server ComponentではServer Componentを実行する環境をサーバー上で動かす必要があります。
今まで、このブログではRSC(React Server Component)を開発環境で動かしてきましたが、開発環境では(例えば)Next.jsがPC/Mac等の上で動いているので問題無くServer Componentも実行できていました。
Bing Image Creatorが生成した画像を使っています
今回の本番環境
今回動かすのは、いつものジャンケンアプリです。このブログで説明したコードを整理したものを、Next.js(React)は最新の安定版で動かしています(Next.jsは14.2.3、Reactは18.3.1です)。
- アプリの実行環境はNext.jsで動きます
- JavaScriptの実行環境が必要なのでNode.jsが必要ですね
- Next.jsの前段にはReverse proxyとしてnginxを入れます
- 今回はサブドメイン
react.xxxx.yyy
からNext.jsのlocalhost:3030
への変換を行っています - またLet’s Encryptを使ってhttpsのサポートをしています
- 今回はサブドメイン
- データベースはMySQLを作っています
- このブログではsqlite3を使っていました
今回起きた問題たち
今までに行った事がないことを試すと、必ずトラブルが発生します。😅
1. Node.js v20がインストールしようとしたら
Next.jsのSystem RequirementsにはNode.js v18.17以上が必要と書いてあります。サーバーにはnvmがインストールされているので、v20.13.1(現在のTLS)をインストールしようとしたのですが、以下のエラーが発生しインストールできませんでした。
node: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found (required by node)
実はサーバーはUbuntu v18だったのです、諦めUbuntu v20にアップデートしました。これで無事にNode.js v20がインストール出来ました。
2. アプリをDeploy起動
アプリのデプロイは以下の手順でデプロイを行いました。ブラウザーからアクセスしたところ問題無くうごきました。
$ git clone git@github.com:XXXXX/next-rsc-jyanken2.git
$ cd next-rsc-jyanken2
$ npm install
$ npx migrate deploy
$ npm run dev -p 3030
その後、Next.jsのDeploy - Self-Hosting - Node.js Serverにあるように、
$ npx migrate deploy
$ npm run build
$ npm start -p 3030
で無事に本番サーバーが動作しました。
3. Reverse proxy nginxを設定
nginxは他のサーバーで動くアプリでも使っているので設定ファイルを追加し、サブドメインreact.xxxx.yyy
で動くように設定したところ、https://react.xxxx.yyy
でジャンケン画面は表示されましたが、ジャンケン・ボタンを押しても反応がありません! Next.jsのログを見ると以下のようなエラー発せしていました。
`x-forwarded-host` header with value `localhost:3030` does not match `origin` header with value `react.xxxx.yyy` from a forwarded Server Actions request. Aborting the action.
Error: Invalid Server Actions request.
at rP (/home/yuumi3/works/next-rsc-jyanken2/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:15:7099)
・・・省略・・・
at nk.render (/home/yuumi3/works/next-rsc-jyanken2/node_modules/next/dist/compiled/next-server/app-page.runtime.prod.js:19:4540) {
digest: '2710703285'
}
検索したところこのページに解答がありました、next.config.mjs
ファイルにallowedOrigins
を設定するれば良いようです(ひょっとしたらnginxの設定でも可能かもしれません?)。
next.config.mjs
ファイルを変更したところ無事にジャンケン・ボタンも動作するようになりました。
next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
serverActions: {
allowedOrigins: ["react.xxxx.yyy", "localhost:3030"]
}
}
};
export default nextConfig;
- nginxの設定
注: 本番運用用には適切ではないかも知れませんので、参考程度に見てください。
server {
listen 80;
server_name react.xxxx.yyy;
server_tokens off;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name react.xxxx.yyy;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/react.xxxx.yyy/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/react.xxxx.yyy/privkey.pem;
ssl_protocols TLSv1.3 TLSv1.2;
location / {
proxy_pass http://localhost:3030;
proxy_redirect off;
}
}
4. データベースをMySQLに変更
ここまでアプリはsqlite3で動いていました、実はPrismaを本気で使った事がなく、 ここで苦労しました。😅
4a. Prismaは開発環境と本番環境で異なる種類のRDBは使えないのかも
この部分は、十分に調べきれてないので憶測も含むので注意してください。
RDBをMySQLに切り替えるには、schema.prisma
ファイルのdatasource db
を以下のようにproviderをmysqlにし、接続情報をurlに指定します。以下では環境変数DATABASE_URL
から取得しています。
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
datasource db
を書き変え、このアプリ用のデータベース、アカウント・権限等をMySQLに作成してnpx prisma migrate deploy
しましたが、エラーになってしまいました。
たしかに、prisma/migrations/migration_lock.toml
ファイルにはprovider = "sqlite"
と書かれています。これをmysql
に変更してもマイグレーションファイル内のSQLが文法エラーYou have an error in your SQL synta
になっていました、sqlite3
依存のSQL文のようです。
ここで既存のマイグレーションを削除し、新たにマイグレーションを開始しましたが、P3014エラーが発生してしまいました。
4b. prismaのshadow databasとは
エラー原因を調べる段階でPrismaについて色々と学びました。PrismaのマイグレーションはRuby on Railsのマイグレーションより高能力で、Alter Table
SQL文では出来ないような変更も行えます。
このような高度な変更を行う際に、Prismaはshadow databaseという一時的なデータベースを作り、そこにデータを一時的に待避し、テーブル変更後にデータを読み込んだりしています。
そのためにMySQLのアカウントにはShadow database user permissionsに書かれているように、CREATE, ALTER, DROP, REFERENCES ON *.*
権限が要ると書かれています。しかし、この強力な権限を与えるのには心理的に抵抗があります(このMySQLは他アプリでも使っているので)。
さらに調べていたらCloud-hosted shadow databases must be created manuallyを見つけました。schema.prisma
ファイルのdatasource db
にshadow databaseを指定できるようです。早速MySQLにshadow database用のデータベースを作成し設定したところマイグレーションが成功しました。
- schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
model Scores {
id Int @id @default(autoincrement())
human Int
computer Int
judgment Int
}
- MySQLのデータベース、ユーザー、権限作成
CREATE DATABASE react CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE DATABASE react_shadow CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;
CREATE USER ‘react’@‘localhost' IDENTIFIED BY ‘react’;
GRANT ALL PRIVILEGES ON react.* TO 'react'@'localhost';
GRANT ALL PRIVILEGES ON react_shadow.* TO 'react'@'localhost';
まとめ
React Server Component(RSC)自体を番環境で動かすこと自体は難しくない事が判りました。ただし本番環境でRSCを運用するには、いろいろな技術を正しく知る必要がありますね。
今後を考えるとPrismaを、もう少し学ばなくてはと思いました。😃