目的
- いろいろソフト入れて環境構築しているとわけがわからなくなってくる。
- 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
- ローカルにPython実行環境とMariaDBのクライアントをインストールする。
- MariaDBを初期設定の後、Pythonを使ってデータを流し込む。
- Nodeの初期設定はイメージ作成後にnpm installで実行する。
- 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
ブラウザでの動作確認
とりあえずReactのコンポーネントはちゃんとビルドされてNginxも機能しているようだ。
が、データベースとの接続は一筋縄ではいかなかった。
TODO
- なんでもNode.jsはデータベースだけを扱ってwabpackの高度なビルドはしないとかだといいが、全部やろうとするとサーバーサイドレンダリングとかでダークサイドに落ちてしまう。
- 軽く触った感じでは深入りせずにエンジニアはeslintやmocha、sassの書き方に注力したほうがいい。
- Reactの世界とデータベースの間は薄いけど闇が深い。
- ここをつなぐのはRailsかDjangoか…いや、もっと薄い皮でいいのでREST APIを試してみよう。