Help us understand the problem. What is going on with this article?

React アプリを小さなサイズの Docker イメージにして運用

要約

  • React アプリを Dockerize するなら、Docker のマルチステージビルドを利用して nginx イメージに載せることで、イメージサイズを 1GB -> 22MB にできる。

はじめに

React アプリを作ってみようと思い立ち、ローカルの開発環境では動かせるようになった。だが、これを本稼働させるにはどうするのがスマートなのか調べてみた。

React の公式ドキュメントでは、第一の方法として、次のように serve を使うのが簡単だよと書いてあるが、これだと実行環境にも Node.js が必要になる。

npm install -g serve
npm run build
serve -s build

Dockerize

運用しているほかの Web アプリは Docker 上で稼働させているので、React アプリも Docker イメージ化(Dockerize)して稼働させてみることにした。

まずは、DockerHub の node.js イメージから公式ドキュメントをたどってみたが、実践的な Dockerfile の書き方については情報なし。

そんな中、まさに的中の次の記事を見つけた。

Dockerizing a React App - Michael Herman

方法

React アプリを Docker イメージ化して稼働させる方法として次の3つが考えられる。

  1. 開発デバッグ用と同様に npm start で手っ取り早く稼働させる。
  2. React の公式ドキュメントの通り、serve を使って稼働させる。
  3. 先の記事にある Production のように、nginx イメージにビルド結果を載せて稼働させる。

1.の方法では、js ファイルの minify もされないため、開発デバッグ用ならともかく、本番環境用とは言い難い。

2.の方法では、本番環境と同様にパッケージ化されて稼働するが、その Docker イメージには Node.js の実行環境に加えて、packages.json の dependencies に指定されているパッケージ(node_modulesフォルダ)も含まれてしまう。そのため、イメージサイズが大きい。

3.の方法ならイメージサイズもコンパクトになると期待できる。

実装

  • React アプリのルートフォルダに下記 Dockerfile と docker-compose.yml を用意し、docker-compose up -d コマンドを実行することでアプリ稼働。

  • 方法3 のポイントは Docker のマルチステージビルド。詳細は次のドキュメント参照。

    https://docs.docker.com/develop/develop-images/multistage-build/

  • Dockerfile では、npm installnpm run build を別々に実行してレイヤを分けることで、依存パッケージに変更なければイメージビルド時のキャッシュが働き、ビルド時間を短縮。

Dockerfile

Dockerfile
ARG NODE_TAG=12
ARG NGINX_TAG=alpine
ARG APP_HOME=/home/node/app

# build stage
FROM node:${NODE_TAG} as build
ARG NODE_TAG
ARG APP_HOME

WORKDIR ${APP_HOME}
COPY package*.json ${APP_HOME}/
RUN echo -n 'node '; node -v; \
    echo -n 'npm '; npm -v ; \
    npm install --production

COPY src ${APP_HOME}/src/
COPY public ${APP_HOME}/public/
RUN npm run build

# deploy stage
FROM nginx:${NGINX_TAG}
ARG NGINX_TAG
ARG APP_HOME
RUN echo "nginx:${NGINX_TAG}" > /docker-image-tag && cat /docker-image-tag
COPY --from=build ${APP_HOME}/build /usr/share/nginx/html

WORKDIR /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

docker-compose.yml

docker-compose.yml
version: '3'

services:
    app:
        container_name: reactapp
        image: reactapp:latest
        build:
            context: .
            args:
              - NODE_TAG=12.8
              - NGINX_TAG=alpine
        environment:
            - NODE_ENV=production
        ports:
            - 8080:80
        restart: always

試行・比較

(方法1) 1.03 GB

https://github.com/tajimak/reactapp-dockerized/tree/start-app/npm-start

$ docker-compose build
$ docker images | grep reactapp
reactapp    latest    f951bf73549b    About a minute ago    1.03GB

(方法2) 1.03 GB

https://github.com/tajimak/reactapp-dockerized/tree/start-app/node-serve

$ docker-compose build
$ docker images | grep reactapp
reactapp    latest    8abace7aab5c    About a minute ago    1.03GB

(方法3) 22 MB

https://github.com/tajimak/reactapp-dockerized/tree/start-app/on-nginx

$ docker-compose build
$ docker images | grep reactapp
reactapp    latest    d07dcfbb1e54    About a minute ago   21.7MB

SSL/TLS 対応

  • 上記の Dockerfile では React アプリのポートを直接公開してしまっているが、実際には、SSL/TLS 対応のリバースプロキシ配下に配置するなどして、セキュリティへの考慮も必要。

  • リバーズプロキシ配下で Context Path(Relative Path)付き URL とする場合は、このドキュメントに従って packages.json に "homepage": ".", を追加しておく。

{
  "name": "reactapp-dockerized",
  "homepage": ".",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
  ...
}

参考:試行環境

項目 バージョン
node.js 12.8.1
npm 6.10.2
Docker 19.03.1
docker-compose 1.24.0
Docker Host OS Debian 9.9
k1tajima
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした