10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DockerでReactとMariaDB使ってWebサービスの雛形を作る

Last updated at Posted at 2018-03-19

目的

  • いろいろソフト入れて環境構築しているとわけがわからなくなってくる。
    • Dockerに設定全部書いて簡単に構築できるようにする。
  • Pythonであれこれ処理したものをデータベースに保存したい。
    • PandasでMariaDBに接続しよう。
  • フロントエンドはReactを使いたい。
    • Webpack + Babelを導入する。
  • Webサーバーはどうする?
    • 最近流行のNginxを使おう。

Dockerの導入まで

  • GCPでCentOS7のインスタンスを作成する。
  • dockerとdocker-composeをインストールする。
  • dockerをサービスに登録し、sudoなしでdockerコマンドを実行できるようにする。
sudo yum install -y yum-utils device-mapper-persistent-data lvm2 && \
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo && \
sudo yum makecache fast && \
sudo yum install -y docker-ce && \
sudo usermod -aG docker $USER && \
sudo systemctl enable docker

sudo curl -L https://github.com/docker/compose/releases/download/1.20.0/docker-compose-`uname \
-s`-`uname -m` -o /usr/local/bin/docker-compose && \
sudo chmod +x /usr/local/bin/docker-compose
# バージョン確認して再起動
docker-compose --version
sudo reboot

コンテナの構成

以下の3つのコンテナを作る。

  • MariaDB
  • Node
  • Nginx
  1. ローカルにPython実行環境とMariaDBのクライアントをインストールする。
  2. MariaDBを初期設定の後、Pythonを使ってデータを流し込む。
  3. Nodeの初期設定はイメージ作成後にnpm installで実行する。
  4. Nginxの設定書いてからDocker-compose upで起動。

Python3のインストール

sudo yum install -y https://centos7.iuscommunity.org/ius-release.rpm && \
yum search python36 && \
sudo yum install -y python36u python36u-libs python36u-devel python36u-pip && \
sudo ln -s /usr/bin/python3.6 /usr/bin/python3 && \
sudo ln -s /usr/bin/pip3.6 /usr/bin/pip3 && \
python3 --version && pip3 --version
sudo pip3 install pip --upgrade && \
sudo pip3 install sqlalchemy && \
sudo pip3 install PyMySQL && \
sudo pip3 install pandas

MariaDBクライアントのインストール

sudo yum install -y mariadb

MariaDBコンテナの作成

mkdir docker && cd docker
mkdir container && mkdir container/mariadb && mkdir container/mariadb/init

cat << EOF > container/mariadb/multibyte.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
EOF

# Dockerfileの作成とDocker-composeに設定を追記
cat << EOF > container/mariadb/Dockerfile
FROM mariadb

EXPOSE 3306
EOF

cat << EOF > docker-compose.yaml
version: '3'
services:
  mariadb:
    build:
      context: ./container/mariadb
    container_name: mariadb
    image: mariadb
    volumes:
      - ./mariadb_data:/var/lib/mysql:z
      - ./container/mariadb/multibyte.cnf:/etc/mysql/conf.d/multibyte.cnf
      - ./container/mariadb/init:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
      - MYSQL_DATABASE=app
      - MYSQL_USER=username
      - MYSQL_PASSWORD=secret
EOF

docker-compose up

MariaDBの動作確認

別のターミナルから……

cd ~/docker

# テストデータの準備
cat << EOF > test_data.csv
ID,Name,Birthdate,Sex,Occupation,Salary
ID-0001,Abe,1985/1/1,M,Engineer,8422213
ID-0002,Saito,1970/2/11,F,Professor,8222588
ID-0003,Yamada,1975/3/21,M,Doctor,9845288
ID-0004,田中,1980/4/22,F,Sales,8505218
ID-0005,Okamoto,1995/5/25,M,Student,218103
EOF

cat << EOF > test_data.py
import pandas as pd
import sqlalchemy as sa

df = pd.read_csv("test_data.csv")
url = 'mysql+pymysql://username:secret@127.0.0.1/app?charset=utf8'
engine = sa.create_engine(url, echo=True)
df.to_sql('table1', engine, index=False, if_exists='replace')
EOF

python3 test_data.py test_data.csv && rm test_data.csv && rm test_data.py
mysql -u root -prootpass -h 127.0.0.1
> use app
> SELECT * FROM table1;
# UTF8出力チェック
> exit

一度docker-composeを止める

nodeコンテナの作成

mkdir container/node && mkdir app && mkdir app/src && mkdir app/dist && mkdir app/node_modules

cat << EOF > container/node/Dockerfile
FROM node:9

WORKDIR app
RUN npm install -g npm
EOF

cat << EOF >> docker-compose.yaml
  node:
    build:
      context: ./container/node
    container_name: my-node
    image: my-node
    volumes:
      - ./app:/app:z
    ports:
      - "80:8080"
EOF

docker-compose build

npm install

完成package.json用意してのnpm updateは重くて挙動が怪しいので少しずつ入れる。
dev系はサーバーでは不要なものもあるがローカルと設定を共通にするために入れておく。

docker-compose run node npm init -y
docker-compose run node npm i -D webpack webpack-cli webpack-dev-server babel-core babel-loader babel-preset-env babel-preset-react babel-polyfill
docker-compose run node npm i -D uglifyjs-webpack-plugin sass-loader node-sass style-loader css-loader style-loader css-loader url-loader
docker-compose run node npm i -S react react-dom redux react-redux
docker-compose run node npm i -D redux-devtools react-hot-loader
docker-compose run node npm i -D eslint eslint-loader eslint-plugin-node eslint-plugin-react babel-eslint
docker-compose run node npm i -D node-mariadb

設定ファイルの準備

webpack4で設定が簡単になったというが、十分複雑だ。

cat << EOF > update_package_json.py
import json
import collections

decoder = json.JSONDecoder(object_pairs_hook=collections.OrderedDict)
with open('app/package.json') as read_file:
    data = decoder.decode(read_file.read())
    data["name"] = "react-test"
    data["version"] = "0.1.0"
    data["author"] = "tibigame"
    data["description"] = "react-test"
    data["scripts"]["build"] = "webpack"
    data["scripts"]["start"] = "babel-node ./src/server.js"
    data["scripts"]["dev"] = "webpack-dev-server --hot"
    data["scripts"]["watch"] = "webpack -d --watch --progress"
    fw = open('app/package2.json','w')
    json.dump(data, fw, indent=4)
EOF

python3 update_package_json.py && rm -rf app/package.json && mv app/package2.json app/package.json

cat << EOF > app/webpack.config.js
module.exports = {
  mode: 'production',
  entry: [
    'babel-polyfill',
    './src/index.js',
  ],
  output: {
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: [
          {
            loader: 'babel-loader'
          }
        ],
        exclude: /node_modules/
      },
      {
        test: /\.scss/,
        use: [
          'style-loader',
          {loader: 'css-loader', options: {url: false}},
        ],
      }
    ]
  }
};
EOF

cat << EOF > app/.babelrc
{
  "presets": [
    "react",
    ["env", {
      "targets": {
        "browsers": ["last 2 versions"]
      },
      "modules": false
      }
    ]
  ],
  "plugins": [
    "transform-class-properties",
    "transform-object-rest-spread",
    "react-hot-loader/babel"
  ]
}
EOF

ReactとSCSSの確認ファイルの準備

cat << EOF > app/dist/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8"/>
  <style>
    body {
      background: #eee;
    }
    #app {
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
    }
  </style>
  <script defer src="main.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
EOF

cat << EOF > app/src/style.scss
$red: #FF3333;
h1 {
  color: $red;
}
EOF

cat << EOF > app/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import "./style.scss"

import {TestComponent} from './test_component';

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello React!</h1>
        <TestComponent name="My Counter for Babel" />
      </div>
    );
  }
}
console.log('テスト');
ReactDOM.render(<App/>, document.querySelector('#app'));
EOF

cat << EOF > app/src/test_component.js
import React from 'react';

export class TestComponent extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };
  }

  handleClick() {
    console.log('クリックされました');
    this.setState({
      count: this.state.count + 1
    });
  }

  render() {
    return (
      <div>
        <h2>{this.props.name}</h2>
        <div>{this.state.count}</div>
        <button onClick={this.handleClick.bind(this)}>Add +1</button>
      </div>
    );
  }
}
EOF

Nginxコンテナの作成

ここの設定はよく理解していないが、制限かけておけば開発中でも問題ないだろう。

mkdir container/nginx
sudo yum install -y httpd-tools
htpasswd -c ./container/nginx/.htpasswd http_user
password

cat << EOF > container/nginx/Dockerfile
FROM nginx
EOF

vim container/nginx/default.conf
--------
server {
  listen 8080;
  server_name localhost;
  location /api/ {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
  location / {
    root /www/app;
  }
  # IPアドレスによる制限
  allow xxx.xxx.xxx.xxx;
  deny all;
  # BASIC認証による制限
  auth_basic "Restricted";
  auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
}
--------

cat << EOF > docker-compose.yaml
version: '3'
services:
  node:
    build:
      context: ./container/node
    container_name: my-node
    image: my-node
    command: npm run build
    volumes:
      - ./app:/app:z

  mariadb:
    build:
      context: ./container/mariadb
    container_name: mariadb
    image: mariadb
    volumes:
      - ./mariadb_data:/var/lib/mysql:z
      - ./container/mariadb/multibyte.cnf:/etc/mysql/conf.d/multibyte.cnf
      - ./container/mariadb/init:/docker-entrypoint-initdb.d
    expose:
      - 3306
    environment:
      - MYSQL_ROOT_PASSWORD=rootpass
      - MYSQL_DATABASE=app
      - MYSQL_USER=username
      - MYSQL_PASSWORD=secret

  nginx:
    build:
      context: ./container/nginx
    image: nginx
    container_name: nginx
    ports:
      - '80:8080'
    volumes:
      - ./container/nginx:/etc/nginx/conf.d:ro
      - ./app/dist:/www/app:ro
    links:
      - 'node'
      - 'mariadb'
EOF

docker-compose run node npm run build
docker-compose up

ブラウザでの動作確認

a.jpg

とりあえずReactのコンポーネントはちゃんとビルドされてNginxも機能しているようだ。
が、データベースとの接続は一筋縄ではいかなかった。

TODO

  • なんでもNode.jsはデータベースだけを扱ってwabpackの高度なビルドはしないとかだといいが、全部やろうとするとサーバーサイドレンダリングとかでダークサイドに落ちてしまう。
  • 軽く触った感じでは深入りせずにエンジニアはeslintやmocha、sassの書き方に注力したほうがいい。
  • Reactの世界とデータベースの間は薄いけど闇が深い。
  • ここをつなぐのはRailsかDjangoか…いや、もっと薄い皮でいいのでREST APIを試してみよう。
10
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?