Seiichi Yonezawa

Create React Appをデプロイする

Reactを習得してからはRailsで大変だった部分が楽になりました。 しかし、楽になった分だけ遠回りになることもあります。 そのひとつがデプロイです。

ReactやVue.jsなどのフレームワークを利用しているときはFirebaseを利用していました。 Firebaseなどのサービスを利用するとデプロイ周りの面倒ごとが解消できます。 とはいえもともとはバックエンドとの連携がしたくてReactを習得したので、いつまでもFirebaseにばかり頼るわけにもいきません。 そこで今回は同様のSurgeやHerokuのようなサービスは利用しません。

通常RailsのデプロイではCapistranoを使えるようにサーバーに環境を用意する必要がありました。 それからDockerが登場して、VPSのセットアップもずいぶん楽になりました。 今回バックエンドはDocker等ですでに用意できている前提とします。

CIでの設定

さて、そこで問題になってくるのがReactのデプロイです。 ビルドコマンドでHTMLまでは簡単に生成できますが、Node.jsにもCapistranoのようなデプロイに特化したライブラリがあるものだと思っていました。 残念ながら今回そのようなライブラリを見つけることができませんでした。 しかしデプロイの本質はFTPで指定されたディレクトリにファイルをアップロードすることから変わりません。

そこで登場するのがrsyncです。 SCPでもよいのですが、rsyncには--delete-afterというオプションが用意されていて、手元にないファイルやディレクトリを消去してくれます。 それらのオプションを組み合わせてCIで使うことを前提に、おなじみ.gitlab-ymlにコマンドを書いてみました:

deploy:
  stage: deploy
  script:
    - npm install
    - npm run build
    - rsync -glpPrtvz --delete-after ./build/ "$DEPLOY_SERVER_DEST"

$DEPLOY_SERVER_DESTは保存するディレクトリが当てはまります。 例えば[email protected]:/var/www/htmlなら/var/www/htmlに生成したHTMLをアップロードします。 もちろん.gitlab-yml上に書いてもよいのですが、環境変数はGitLab内で管理するとよいでしょう。

ただし、CI上では当然上記の[email protected]には単純にアクセスすることはできません。 そこでSSH秘密鍵をCI側で追加してあげる必要があります。 以下のようにしました:

before_script:
  - apt-get update -y && apt-get install rsync -y
  - "which ssh-agent || (apt-get update -y && apt-get install openssh-client -y)"
  - eval $(ssh-agent -s)
  - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
  - mkdir -p ~/.ssh
  - chmod 700 ~/.ssh
  - ssh-keyscan "$DEPLOY_SERVER_URL" > ~/.ssh/known_hosts

上記コマンドはGitLabのUsing SSH keys with GitLab CI/CDを参考にしました。 これでCI側の設定は完了です。 あとはGitLab側でPipelineを実行して、デプロイがうまくできるか確認しましょう。

Nginxの設定

続いて公開サーバの設定をします。 私の場合はGitLabにバンドルされているNginxを利用していますが、基本的にはパッケージ管理で入れられるNginxと変わりありません。 それでは/etc/nginx/sites-availableに適当なファイル名で以下のファイルを追加します:

server {
  listen 80;
  server_name example.com;
  root /var/www/html;
  index index.html;
  # Other config you desire (logging, etc)...
  location / {
    try_files $uri /index.html;
  }
}

上記ファイルはStackOverflowの回答を参考にしました。 try_filesは通常リクエストが見つからなかった場合にエラーを返すページを設定しますが、常にindex.htmlを返すことでReact Routerが使えるようになるというわけです。 Vue Routerでも同様の設定ができそうですね。

バックエンドサーバとの連携

https://stackoverflow.com/questions/45911067/create-react-app-proxy-in-production-build

Create React Appにはproxyという設定があるのですが、公開サーバでproxyの設定を使うことはできません。

Proxy in development is just a productivity feature. It’s useful if you serve the single-page app from your API server in production, but want to use the development server provided by CRA while you work on the app. However proxy is just that: a development feature. It is not meant for production.

このように、あくまで開発環境で使うことが想定されている機能のようです。 しかし、先ほどのNginxの設定ファイルをよく見てみましょう。 NginxはHTTPサーバだけではなくロードバランサーとしても利用することができます。

+upstream backend-server {
+  server api.example.com;
+}

 server {
   listen 80;
   server_name example.com;
   root /var/www/html;
   index index.html;

+  location /api/ {
+    proxy_pass http://backend-server;
+  }

   # Other config you desire (logging, etc)...
   location / {
     try_files $uri /index.html;
   }
 }

このようにすればバックエンド側にCORSの設定を追加しなくても/api/では同一のサーバのように振る舞うことができます。 つまり、この例ではexample.com/api/のアクセスがapi.example.com/api/として扱われます。 同一のlocationではなく/api/に分離することで、バックエンドのアクセスとして明示的に扱うことができると思います。

まとめ

これで無事バックエンドもReactもすべてサーバで公開できるようになりました。 現在Reactを利用していくつかプロジェクトを進めているのですが、進捗があればこのようにブログとして残していく予定です。 次回は今回触れなかった認証まわりについてまとめたいと思っています。

投稿一覧