クライアント/サーバーサイドを両方使うAppを、PaaSにデプロイする方法について、概要を紹介します。
実践した環境は下記の通りです。
- Next.js/Phaser/MaterialUI
- Express + Node.js
- GoogleAppEngine
他のサービスでも同様にデプロイが可能なように、仕組みの全体像や大まかな概念について解説します。
【結論】クライアントとサーバーサイドで別々のGAEインスタンスを構築する
こういった構成でアプリを作りたい場合は、それぞれについてGAEインスタンスを構築して、アップロードしなければなりません。
そのため通信を行う際には、CORSの設定が必要ですし、各インスタンスに環境変数を設定する必要があります。
ただこの方法、わかる人にとっては当然なようです。
しかし初学者にとっては、日本語ではっきりと名言している文献は皆無なので、気づくのは厳しいかもしれません。
【POINT1】サーバーサイド側にCORS設定を追加する
クライアント側からのアクセスを受け入れるように、CORS設定をしておきましょう。
const io = new Server(httpServer, {
cors: {
origin: [
'http://localhost:3000',
'https://frontend-bombattacker.an.r.appspot.com',
],
},
});
【POINT2】クライアント側に環境変数を設定する
クライアント側の環境設定として、アクセス先のURLを記述します。
app.yamlに下記を記述することで解決できます。
env_variables:
NODE_URL: "https://~~~~~"
【POINT3】他のPaaSでも同様の仕組みで実装可能
上記の内容は、他のPaaSでも似たような仕組みによって成り立っています。
つまり、1つのサーバーにデプロイするのではなく、クライアントとサーバーサイドをそれぞれデプロイします。
しかしその組み合わせは、今回のように両方ともGAEにデプロイせずとも可能です。
例)
クライアント・・・Vercel
サーバーサイド・・・Heroku
環境変数の設定方法については異なるので、使いたい環境に応じて調べてみてください。
GAEの機能と動作する基本の流れ
GAEは大まかに下記のような処理により動作しています。
app.yamlの準備
このファイルがあるディレクトリ以下のファイルをアップロードします。
自分は下記のように作成しました。
runtime: nodejs16 // バックエンドの環境
automatic_scaling: // 無料枠を超えないための設定
min_idle_instances: automatic
max_idle_instances: 1
min_pending_latency: 3000ms
max_pending_latency: automatic
max_instances: 2
env: standard // スタンダードかフレキシブル
instance_class: F1 // インスタンスの規模
なお、.gcloudignoreというファイルも自動的に生成されます(多分)。
node_modulesや.gitなどをアップロードしないように設定します。
サーバーにファイルをアップロードする
GoogleSDKをインストールすると、gcloudコマンドが使用できるようになります。
これがアップロードの起点になります。
gcloud init // 初期化・インスタンスへの接続
gcloud app deploy // アップロード
Docsでは上記が紹介されていますが、他にもオプションがあるようです。
なお下記にもある通り、アップロードする前にはローカル上でbuildコマンドを使っておく必要があります。
npm installを実行する
アップロードされた後、まず自動的にnode_modulesが展開されます。
ですので同ディレクトリのアップロードは不要です。
npm run startまたはnode server.jsを実行する
起動コマンドを実行します。
ここが第一にハマる点かもしれません(自分はハマった)。
startコマンドの指定がなければルート上にあるserver.jsを探しに行くので、「何度やっても起動しない!」となります。
特にクライアント側を起動する場合、startコマンドが必須です。
過去のアップロードを上書きしてバージョン管理
アップロードしたファイルは常に全て上書きされます。
ダッシュボードでは、下記を行うことができます。
- バージョン管理
- バージョンの削除
- トラフィックの割り当て
- トラフィックの移行
GAEやその他PaaSなどでできないこと
下記についてはできませんでした(IaaSなどサービスによっては可能かもしれません)。
- 1つのインスタンスでクライアントとサーバーサイドの両方を動かすことはできない
- npm installを複数のディレクトリ下で走らせることはできない(最初のみ)
- サーバー上でbuildコマンドを走らせることはできない
- サーバー上でtscコマンドを走らせることはできない
【番外編】デプロイやビルドを便利にするパッケージ
複数のnpmコマンドを同一のターミナルから走らせることができます。
クライアントとサーバーサイドで別々のnode_modulesを使う構成だったので、こちらのパッケージが便利でした。
以下の例だと、npm run devコマンド1つで、クライアントとサーバーサイド両方の起動を行うことができます。
ターミナルも1つあればOK。
"scripts": {
"dev": "run-p dev:*",
"dev:serve": "cd server && npm run dev",
"dev:client": "cd client && npm run dev",
"build": "run-p build:*",
"build:serve": "cd server && tsc",
"build:client": "cd client && npm run build",
"start": "run-p st:*",
"st:serve": "cd server && node dist/index.js",
"st:client": "cd client && npm run start"
},
まとめ・感想
インフラ周りを学習してみて感じたこと。
- PaaSはどれもほとんど同じ仕組みで動いているみたい
- 1つやり方を覚えればある程度は代用が効く
- 2つのインスタンスに別々にデプロイのは発想さえできなかった
- フロント側は静的ファイルだけで動くわけではない(勘違いしてた)
- package.jsonのscriptの便利さを実感
謝辞
今回の学習内容は、Recursionのチーム開発に参加して得られたものです。
貴重な場を提供してくださり、ありがとうございます。
またチームメンバーのKohさん・Hayatoさん。
Kohさんは上記アイディアを指摘してくれて本当に感謝です。
1人では間違いなく、期日内にデプロイまで到達できなかったです。
Hayatoさんには今思えば最後はやや無茶振りでしたが、ぜひ今後に活かしてもらいたいです。
その他、チームVとXの皆さま。
やり取りがとても参考になりました。
改めてお礼申し上げますm(_ _)m
ではまた、これからも良きプログラミングライフを。
※曖昧な点、間違いなどありましたら、ぜひご指摘ください。