あらすじ
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 して動作確認で終了です。