Edited at
AidemyDay 19

Dockerで簡易版Aidemyをつくってみよう (その4) ~mongodbによるデータベース~


概要

今回は、Dockerで簡易版Aidemyをつくってみようシリーズのその4です。

前回までにフロントエンド・バックエンドを作成したので、今回はデータベースを作成します。

ちなみに、前回までのリンクは以下です。

Dockerで簡易版Aidemyをつくってみよう (その1) ~全体構成とDockerのセットアップ~

https://qiita.com/km42428/items/4e4653aa0e813282986b

Dockerで簡易版Aidemyをつくってみよう (その2) ~React.jsによるフロントエンド~

https://qiita.com/km42428/items/989dac5e7450501c452d

Dockerで簡易版Aidemyをつくってみよう (その3) ~Expressによるバックエンド~

https://qiita.com/km42428/items/f5f7e8f15546b0673d95


今回の目標

今回は mondodb というデータベースを作成し、エクササイズの情報を格納することと、それをバックエンドから呼び出せるようにすることを目標にします。こちらも、今までと同様に docker-compose で起動することを考えます。


mongodb について

mongodbとは、オープンソースのデータベースで、SQLとは異なりドキュメントという形式でデータを保存しています。

公式ドキュメントはこちらです。

https://docs.mongodb.com/

ちなみにドキュメントはJSONないしBSON(Binary JSON)という形式で保存されています。

https://www.mongodb.com/json-and-bson

(ドキュメントという言葉がたくさんでてややこしい。。)


mongodb のDocker Image

mongodbの公式イメージはDocker Hub で公開されています。

https://hub.docker.com/_/mongo/

今回記述していくDockerfileは、こちらのイメージをベースに作成していきます。


docker-compose の更新

以下のように docker-compose.yml ファイルを更新します。


version: "3"
services:
aidemy-mongodb:
image: mongo:4.0.3
ports:
- "28001:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: admin-user
MONGO_INITDB_ROOT_PASSWORD: admin-password
MONGO_INITDB_DATABASE: admin
volumes:
- ./db/:/docker-entrypoint-initdb.d/
aidemy-frontend:
build: ./aidemy-frontend
image: aidemy-frontend
container_name: aidemy-frontend
environment:
- ENV
ports:
- 5000:5000
aidemy-backend:
build: ./aidemy-backend
image: aidemy-backend
container_name: aidemy-backend
environment:
- ENV
ports:
- 4000:4000
links:
- aidemy-mongodb


データの準備

admin というデータベースにexercise情報を入れていきます。


aidemy-mongodb コンテナーの立ち上げ

$ docker-compose build

$ docker-compose up


mongodbの中に入る

admin データベースには権限が必要なので、認証情報をふくめてmongodbにログインする。

参考: https://medium.com/@itseranga/enable-mongodb-authentication-with-docker-1b9f7d405a94


$ mongo -u admin-user -p admin-password --authenticationDatabase admin --port 28001

MongoDB shell version v4.0.4

connecting to: mongodb://127.0.0.1:28001/
Implicit session: session { "id" : UUID("1cdc7c13-25c1-4802-9f52-2a71a55f17cf") }
MongoDB server version: 4.0.3
Server has startup warnings:
2018-12-24T08:04:43.407+0000 I STORAGE [initandlisten]
2018-12-24T08:04:43.407+0000 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2018-12-24T08:04:43.407+0000 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
2018-12-24T08:04:44.050+0000 W CONTROL [initandlisten]
2018-12-24T08:04:44.050+0000 W CONTROL [initandlisten]
2018-12-24T08:04:44.050+0000 I CONTROL [initandlisten]
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
>


exercisesデータを入れる

// データベース一覧確認

> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB

// adminデータベース選択
> use admin
switched to db admin

// exercises コレクション作成
> db.createCollection('exercises')
{ "ok" : 1 }

// exercise コレクションにexercise情報を挿入
> db.exercises.insertMany([
{
exerciseId: "exercise1",
title: "1. Hello world",
script: "# Hello worldを出力しましょう\n",
answer: "# Hello worldを出力しましょう\nprint('Hello world')"
},
{
exerciseId: "exercise2",
title: "2. コメントの入力",
script: "# 3 + 5 の結果を出力しましょう\n",
answer: "# 3 + 5 の結果を出力しましょう\nprint(3 + 5)"
}
])
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5c20943891054e51384ed90d"),
ObjectId("5c20943891054e51384ed90e")
]
}

// 挿入されたか確認
> db.exercises.find()
{ "_id" : ObjectId("5c20943891054e51384ed90d"), "exerciseId" : "exercise1", "title" : "1. Hello world", "script" : "# Hello worldを出力しましょう\n", "answer" : "# Hello worldを出力しましょう\nprint('Hello world')" }
{ "_id" : ObjectId("5c20943891054e51384ed90e"), "exerciseId" : "exercise2", "title" : "2. コメントの入力", "script" : "# 3 + 5 の結果を出力しましょう\n", "answer" : "# 3 + 5 の結果を出力しましょう\nprint(3 + 5)" }
>

※ もし、exercise情報挿入で間違ってしまった場合は、exercisesコレクションを削除する

// コレクションの削除

> db.exercises.drop()
true


aidemy-backendからmongodbへアクセス。

aidemy-backend/index.js を以下を追加する。


app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
res.header(
"Access-Control-Allow-Methods",
"GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS"
);
next();
});

+ const mongodb = require("mongodb");
+ const MongoClient = mongodb.MongoClient;
+
+ // mongodbへの接続文字列
+ var url = "mongodb://admin-user:admin-password@aidemy-mongodb:27017/admin";
+ var exercises;
+ // mongodbの準備ができたらexercisesを初期化する
+ (async function() {
+ console.log("init");
+ for (let i = 0; i < 10; i++) {
+ console.log(i);
+ try {
+ const client = await MongoClient.connect(url);
+ const db = client.db("admin");
+ db.collection("exercises", (err, collection) => {
+ collection.find().toArray((err, docs) => {
+ if (docs.length > 0) {
+ exercises = docs;
+ console.log(docs);
+ }
+ });
+ });
+ break;
+ } catch (err) {
+ console.log(err);
+ await new Promise(resolve => {
+ setTimeout(() => {
+ resolve();
+ }, 1000);
+ });
+ continue;
+ }
+ }
+ })();

- const exercises = [
- {
- exerciseId: "exercise1",
- title: "1. Hello world",
- script: "# Hello worldを出力しましょう\n",
- answer: "# Hello worldを出力しましょう\nprint('Hello world')"
- },
- {
- exerciseId: "exercise2",
- title: "2. コメントの入力",
- script: "# 3 + 5 の結果を出力しましょう\n",
- answer: "# 3 + 5 の結果を出力しましょう\nprint(3 + 5)"
- }
- ];

function getExercise(exerciseId) {
return exercises.find(el => {
return el.exerciseId === exerciseId;
});
}


再度起動する

$ docker-compose build

$ docker-compose up

今回は以上になります。

次回はpythonを実行するサーバを作成します!