docker
reactjs
webpack
docker-compose

Docker+webpackを用いたReact.js開発環境の構築

2018-10-26追記
本記事は2017-05-03に投稿したものです。
Create React Appが出る前にwebpack環境構築しようと試行錯誤した記録なので、今となっては参考になる要素が少ないかもしれませんのでご留意ください。


React.jsの学習を行う環境をDockerで構築します。

  • ホストPCでコーディングする
  • webpackがコードを監視し、babel-loaderでビルドする
  • ビルドしたバンドルデータをnginxが公開する

環境について

環境 バージョン
CentOS Atomic Host CentOS Linux release 7.3.1611
Docker Docker version 1.12.6, build 96d83a5/1.12.6
docker-compose docker-compose version 1.11.2, build dfed245

docker-composeはCentOS Atomic Hostに入っていません。以下のコマンドでインストールします。

$ curl -L https://github.com/docker/compose/releases/download/1.11.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose --version
docker-compose version 1.11.2, build dfed245

ディレクトリ構成

作業ホームを~/reactjsとして以下の構成をゴールとします。

~/reactjs
|- app
|  |- dist
|  |  |- bundle.js
|  |  `- index.html
|  `- src
|     `- app.js
|- docker-compose.yml
|- nginx
|  |- Dockerfile
|  `- nginx.conf
`- webpack
   |- Dockerfile
   `- webpack.config.js

ファイルの作成

webpackコンテナ

webpack/webpack.config.js

webpackの設定ファイルを作成します。

webpack.config.js
const path = require('path');

module.exports = {
  entry: {
    bundle: './src/app.js'
  },
  output: {
    path: path.join(__dirname, 'dist'),
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        loader: 'babel-loader',
        exclude: /node_modules/,
        test: /\.js[x]?$/,
        query: {
          cacheDirectory: true,
          presets: ['react', 'es2015']
        }
      }
    ]
  }
};

webpackのコンテナを作成するDockerfileを定義します。

  • Dockerイメージのベースをnode:7.9.0とします。
  • 作業ディレクトリを/appとします。
  • ホストPCに配置したwebpack.config.jsをコンテナ側の作業ディレクトリ/appに配置します。
  • npmを初期化し、webpackをはじめとする今回必要なパッケージをインストールします。
  • コンテナが作られた際にwebpack -d --watchを実行するように定義します。

webpack/Dockerfile

Dockerfile
FROM node:7.9.0

WORKDIR /app
COPY ./webpack.config.js /app/webpack.config.js

RUN npm init -y
RUN npm install -g webpack
RUN npm install --save react react-dom
RUN npm install --save-dev babel-loader babel-core babel-preset-es2015 babel-preset-react

CMD ["webpack", "-d", "--watch"]

app/src/app.js

React.jsのコードです。id="app"のDOMエレメントに対して<h1>Hello, world!</h1>を設定します。

app.js
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('app')
);

app/dist/index.html

ブラウザで表示されるHTMLファイルです。app.jsでid="app"に対してレンダリングを行うよう記述しましたので、divエレメントを定義します。また、バンドルデータとなるbuild.jsを読み込む記述をしています。

index.html
<!DOCTYPE html>
<html lang="ja">
  <meta charset="UTF-8">
  <title>Reactjs Tutorial</title>
<head>
</head>
<body>
  <div id="app"></div>
  <script src="bundle.js"></script>
</body>
</html>

nginxコンテナ

nginx/nginx.conf

nginxの設定ファイルを作成します。
PASTEBINのsample nginx.confを用います。

# curl -o nginx.conf https://pastebin.com/raw/7gbdCRDk

ダウンロードしたファイルの内容は以下のようになっています。

nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;

    server {
        listen 8080;
        root /wwwroot;
    }
}

nginx/Dockerfile

nginxのコンテナを作成するDockerfileを定義します。

  • Dockerイメージのベースをnginx:1.13.0とします。
  • nginx.confで定義されているドキュメントルート/wwwrootディレクトリを作成します。
  • ホストPCに配置したnginx.confをコンテナ側の/etc/nginx/nginx.confにコピーします。
  • コマンドservice nginx startを実行するようにします。
Dockerfile
FROM nginx:1.13.0

RUN mkdir /wwwroot
COPY ./nginx.conf /etc/nginx/nginx.conf

RUN service nginx start

docker-compose

webpack, nginxのDockerfileをdocker-composeでまとめます。

webpackコンテナのwebpack.config.jsで/app/src/app.jsを監視し、バンドルを/app/distに出力するように定義しましたので、volumesでホスト側のapp/srcapp/distにマウントします。srcは読み取り専用rodistはwebpackが書き出すのでzオプションを付けています。

nginxコンテナではドキュメントルートを/wwwrootとしており、webpackが生成するバンドルはapp/distへマウントしましたので、nginxコンテナでもマウントします。この時nginx側では読むだけですのでroオプションを付けています。また、nginxコンテナ側でnginxを8080ポートとして実行するように設定しました。ホスト側では80番ポートとなるようポートフォワード設定を行います。

Note
zオプションを付けることでボリュームラベルを付与することができます。ボリュームラベルを付与しないとSELinuxがファイル更新を妨ぎます。
詳しくはDocker-docs-ja 1.9beta > コンテナでデータを管理する > データ・ボリューム > ボリューム・ラベルをご参照ください。

docker-compose.yml
version: '2'
services:
  nginx:
    build:
      context: ./nginx
    image: reactjs_nginx
    container_name: reactjs_nginx_container
    ports:
      - "80:8080"
    volumes:
      - ./app/dist:/wwwroot:ro
  webpack:
    build:
      context: ./webpack
    image: reactjs_webpack
    container_name: reactjs_webpack_container
    volumes:
      - ./app/src:/app/src:ro
      - ./app/dist:/app/dist:z

Docker起動

docker-compose.ymlがあるディレクトリで以下のコマンドを実行します。

# docker-compose up -d

ブラウザからhttp://<ホストPCのIP>/でアクセスするとHello, world!という文字列が表示されます。

以上でホストPCにあるapp/src/app.jsを編集していくことにより自動的にバンドルデータが生成されブラウザで確認することができます。

Conclusion

React.jsの学習のみであればCodepenを用いるだけで充分ですし、Dockerを使うにしてもnodeをベースとしたコンテナにwebpack-dev-serverを用いる方法もあります。

今回は開発~本番環境のデプロイメントまでを学習するため、開発環境として利便性のあるwebpack-dev-serverの選択は控えました。

React.jsの学習を進めていき、より開発効率の良い構成を模索していきます。