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