あらすじ
Nginx + Node.js + React.js + Mongodb で Web Application の構築手順を説明する必要があるのですが、Dockerfile をドキュメントとして扱う事になりました。
% docker --version Docker version 17.06.2-ce, build cec0b72 % node -v v8.1.0 % create-react-app --version 1.4.0 % mongo --version | head -n 1 MongoDB shell version v3.4.9
準備
一応 Todos アプリのような感じの Web API を backend で用意して React がそのデータを描画する、までの簡単なサンプルアプリを作成しつつ Docker について説明します。
先ずは以下のコマンドを実行して雛形を作っておきます。
cd mkdir practice-docker-provision && cd $_ mkdir backend mongodb mongo_seed nginx create-react-app front touch docker-compose.yml
mongodb
create docker-dompose.yml
version: "3"
services:
mongodb:
image: mongo:latest
environment:
- MONGO_DATA_DIR=/data/db
- MONGO_LOG_DIR=/dev/null
ports:
- 27017:27017
command:
- mongod
docker-compose run --build を実行して mongod を起動します。起動したらアクセスできる事を確認します。
% mongo --quiet > show dbs admin 0.000GB local 0.000GB > exit
mongo_seed
次に mongod を起動する度に database へ初期データを import するようにします。
mongod を起動した後、mongod へ初期データをインサートします。
mkdir mongo_seed/json touch mongo_seed/Dockerfile mongo_seed/json/todos.json
create mongo_seed/json/todos.json
[ { title: "todo 1", done: false}, { title: "todo 2", done: false}, { title: "todo 3", done: false} ]
create mongo_seed/Dockerfile
FROM mongo:latest COPY json json CMD mongoimport --host mongodb --db myapp --collection todos --drop --jsonArray --file ./json/todos.json
edit docker-compose.yml
command:
- mongod
+
+ mongo_seed:
+ build: mongo_seed
+ links:
+ - mongodb
+ depends_on:
+ - mongodb
docker-compose up --build を実行してから動作確認をします。
% mongo --quiet
> show dbs
admin 0.000GB
local 0.000GB
myapp 0.000GB
> use myapp
switched to db myapp
> db.todos.find()
{ "_id" : ObjectId("59d1fd6e73e8aa5ae594a04c"), "title" : "todo 1", "done" : false }
{ "_id" : ObjectId("59d1fd6e73e8aa5ae594a04d"), "title" : "todo 3", "done" : false }
{ "_id" : ObjectId("59d1fd6e73e8aa5ae594a04e"), "title" : "todo 2", "done" : false }
> exit
%
Node.js
cd ~/practice-docker-provision/backend yarn init -y yarn add express mongoose --save touch app.js models.js
create app.js
var express = require('express'); var app = express(); var mongoose = require('mongoose'); var databaseUrl = process.env.MONGO_DATABASE || "mongodb://localhost/myapp" var Todo = require('./models').Todo; mongoose.connect(databaseUrl, {useMongoClient: true}); app.get('/api/todos', function(req, res) { Todo.find().exec((err, todos) => { if (err) { res.send(err) return } res.json(todos) }) }); app.listen(3000);
edit models.js
var mongoose = require('mongoose'); const Todo = mongoose.model('Todo', { title: { type: String, default: "", }, done: { type: Boolean, default: false } }); module.exports = { Todo: Todo }
edit backend/Dockerfile
FROM node:8 WORKDIR /usr/src/app COPY package.json . RUN yarn install COPY . .
edit package.json
{ "name": "backend", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "start": "node app.js" }, "dependencies": { "express": "^4.16.1", "mongoose": "^4.11.14" } }
edit docker-compose.yml
version: "3"
services:
mongodb:
image: mongo:latest
environment:
- MONGO_DATA_DIR=/data/db
- MONGO_LOG_DIR=/dev/null
ports:
- 27017:27017
command:
- mongod
mongo_seed:
build: mongo_seed
links:
- mongodb
depends_on:
- mongodb
backend:
build: "backend"
environment:
- NODE_ENV=production
- MONGO_DATABASE=mongodb://mongodb/myapp
ports:
- "3000:3000"
links:
- mongodb
depends_on:
- mongodb
docker-compose up --build を実行してから動作確認をします。
% curl -s http://localhost:3000/api/todos | jq . [
{
"_id": "59d201da7e61015c1651b11f",
"done": false,
"title": "todo 1"
},
{
"_id": "59d201da7e61015c1651b120",
"done": false,
"title": "todo 3"
},
{
"_id": "59d201da7e61015c1651b121",
"done": false,
"title": "todo 2"
}
]
React.js + Nginx
React.js を build します。
cd practice-docker-provision/front yarn run build
front/build にファイルが生成されている事を確認して下さい。
次に Nginx の config と Dockerfile を準備します。
create nginx/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 80;
# server_name localhost;
gzip on;
gzip_types *;
location /api/ {
proxy_pass http://backend:3000/api/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection ‘upgrade’;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location / {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
}
}
}
create nginx/Dockerfile
FROM nginx:1.13.0 RUN mkdir /app COPY ./nginx.conf /etc/nginx/nginx.conf CMD ["nginx", "-g", "daemon off;"]
edit docker-compose.yml
- mongodb
depends_on:
- mongodb
+
+ nginx:
+ build: "nginx"
+ ports:
+ - "8080:80"
+ volumes:
+ - ./front/build:/app:ro
React.js から backend の API への通信をする
すでに Docker の設定は完了していますが、最後に React.js を修正します
cd ~/practice-docker-provision/front yarn add axios --save
edit src/App.js
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import axios from "axios"; class App extends Component { constructor() { super(); this.state = { todos: [] }; } async componentDidMount() { const res = await axios.get("/api/todos") this.setState({todos: res.data || []}) } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Welcome to React</h1> </header> <p className="App-intro"> To get started, edit <code>src/App.js</code> and save to reload. </p> <ul> {this.state.todos.map((todo, index) => ( <li key={index}>{todo.title}</li> ))} </ul> </div> ); } } export default App;
再度 yarn build して動作確認で終了です。