DockerでMEAN Stackの稼働環境を簡単実現


# _DockerでMEAN Stackの稼働環境を簡単実現_  

by **_Shuichi Ohtsu_** 


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_47.png" width= "640" >


ビデオ解説(日本語）
<https://youtu.be/RJsJ6jcJDGM>

ビデオ解説(英語）
<https://youtu.be/mjKe-FF4gMc>

Full Source Code
<https://github.com/Ohtsu/mean_stack_on_docker>

--------------------
## 動作環境

今回は、WSL上のDockerを利用することにより、MEAN Stackの環境を簡単に作成する例をご紹介いたします。

MEAN Stackとは、世界中に普及している、JavaScriptベースのフリーソフトの頭文字をとって名付けたものです。

Mは、データベースのMongoDB、Eは、Webサーバを簡単に構築できるミドルウェアのExpress、AはGoogle社がフリーで提供しているクライアント用フレームワークであるAngularを表しており、NはJavaScriptをベースとして、クライアント及びサーバの共通基盤を提供するNode.jsを表しています。

最も重要な点は、JavaScriptでサーバからクライアントまでの環境を構築できる点です。

すなわち、どのようなデバイスであろうと、ブラウザが内装されているのであれば、稼働できてしまいます。

従来開発者を悩ましていた、稼働環境の違いをすべてクリアしてしまうのです。

Windows環境であろうと、Linux環境であろうと、あるいはMac環境であろうと、問題なく稼働できてしまいます。しかも、サーバ環境も含めてです。

さらに受け手であるデバイス環境も問いません。すなわち、デスクトップ・マシンであろうが、スマートフォンであろうが、問題なく稼働します。

この表示については、特にAngular Materialを利用することにより、デバイス環境を意識しないで済むことになります。

今回は、Angular7ベースのAngular Materialを利用することにより、この機能を実現しています。

##### デスクトップ表示
  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_48.png" width= "640" >

##### スマートフォン表示
  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_49.png" width= "300" >


--------------------

## docker-composeのインストール

まず、WSL上のUbuntuを開きます。

そして、`sudo apt update`と入力して、更新情報を取得します。

次に、`sudo apt upgrade`と入力して、この更新情報に基づいて、アップグレードを行います。

次に、目的の _docker-compose_をインストールします。

`sudo apt install docker-compose`と入力します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_00.png" width= "640" >



## クライアント、Rest API、サーバソフトのインストール


次に今回利用する、クライアント、ミドルウェア並びにサーバソフトのソースをダウンロードします。

ブラウザで、GitHubのサイトを開きます。

そのＵＲＬは、<https://github.com/Ohtsu/mean_stack_on_docker>です。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_01.png" width= "640" >


ページが表示されましたら、右側の _Clone or download_をクリックし、そのダウンロード・アドレスをコピーします。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_02.png" width= "640" >


まず、ダウンロード用のディレクトリを作成します。

ここでは、`/mnt/c/__myprg` 内に `__dummy` ディレクトリを作成しました。

そして、このディレクトリ内で、

`git clone https://gitgub.com/Ohtsu/mean_stack_on_docker.git`

と入力して、ソースをダウンロードします。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_03.png" width= "640" >


次に、このディレクトリ内で、`code .`と入力して、Visual Studio Codeを起動します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_04.png" width= "640" >


--------------------

## docker-composeファイルの確認

まず、docker-composeファイルの内容を確認します。

このファイルでは、クライアント、サーバソフトなどの連携関係を記述しています。

たとえば、クライアント・ソフトであるAngular関連のソフトは、MongoDBに依存しています。

```
version: "3.3"
services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "8080:80"
    networks:
      - webnet
    depends_on:
      - mongo
      - backend
    links:
      - backend

  mongo:
    image: mongo
    restart: always
    volumes:
      - mongodb-data:/data/db
    environment:
      MONGO_INITDB_ROOT_USERNAME: username
      MONGO_INITDB_ROOT_PASSWORD: password
    ports:
      - 27017:27017
    networks:
      - webnet

  mongo-express:
    container_name: mongo-express
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    networks:
      - webnet
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: username
      ME_CONFIG_MONGODB_ADMINPASSWORD: password
      ME_CONFIG_BASICAUTH_UERNAME: username
      ME_CONFIG_BASICAUTH_PASSWORD: password
    depends_on:
      - mongo
    links:
      - mongo

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    restart: always
    ports:
      - "5000:5000"
    networks:
      - webnet
    depends_on:
      - mongo
    links:
      - mongo
  
networks:
  webnet:

volumes:
  mongodb-data:

```

また、ミドルウェアである、Express関連のソフトもMongoDBに依存しています。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_06.png" width= "640" >


これらは、`depends_on`及び`links`のコマンドで指定しています・


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_07.png" width= "640" >


また、MongoDB関連では、初期設定用の管理者ユーザ名及びそのパスワードも指定しています。

これらのユーザ名やパスワードは、次にご紹介する、MongoDB管理ツールで使用しますので、銘記しておいください。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_08.png" width= "640" >



## Dockerfileファイルの確認

まず、Express関連のDockerfileについてですが、カレントディレクトリにあるファイル群をコンテナ内にコピーし、`npm install`を実行しています。

```

# stage 1
FROM node:alpine as node
WORKDIR /app
COPY . .
RUN npm install 
# RUN npm run build 
EXPOSE 5000
ENTRYPOINT [ "node", "server.js" ]

```

また、ポート番号5000を公開しています。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_09.png" width= "640" >



次に、クライアント関連のAngularファイルについてですが、まずDockerfileを開いてみます。

```

# stage 1
FROM node:10.12 as node
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build --prod

# # stage 2
FROM nginx:alpine
COPY --from=node /app/dist/frontend /usr/share/nginx/html

```


このファイルの指定は２段階になっています。

すなわち、まず同一の環境をコンテナ内に構築し、プロダクションモードでコンパイルします。

その生成されたJavaScript圧縮ファイルを、軽量のAlpineLinux上のnginxのホームにコピーします。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_10.png" width= "640" >

なお、このクライアントソフトは、Markdownの編集ソフトという形になっていますので、MarkdownからＨＴＭＬへの変換ツールである`markdown-it`もインストールしていることに注意してください。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_15.png" width= "640" >

まず、`docker-compose up`と入力し、関連のDockerイメージを作成します。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_16.png" width= "640" >

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_17.png" width= "640" >

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_18.png" width= "640" >

次に、`docker-compose down`と入力し、一旦すべての関連コンテナを削除します。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_19.png" width= "640" >


まず、クライアントでの表示用に、MongoDBにデータを登録する必要があります。

これは、コンテナ・イメージを先に生成するための手続きです。

というのは、`docker-compose`において各ソフトウェアの起動順序を`depends_on`などによって、指定してもMongoDBなどのデータベースの起動に時間がかかる場合は、イメージを作成中に連携エラーとなってしまうからです。


## MongoDBデータの入力

次に、MongoDBにサンプルデータを入力することにします。

まず、一旦、`docker-compose down`と入力して、すべてのコンテナを削除し、再度`docker-compose up mongo`と入力し、MongoDBだけを起動します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_20.png" width= "640" >


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_21.png" width= "640" >


ここでは、MongoDBデータの入力用に、_Studio 3T_というツールを利用しています。

試用期間中はフリーですのでMongoDBユーザにとっては大変便利なツールです。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_22.png" width= "640" >

まず、MongoDBに接続する必要があります。

_Studio 3T_を起動後、コネクション・アイコンをクリックします。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_23.png" width= "640" >


そして新規コネクションを選択し、サーバ名を適当に入力した後、`Authentications`タブを選択します。

そしてさらに、`Authentication Mode`において`Basic`を選択し、`docker-compose`ファイルで管理者用に設定している、ユーザ名(username）とパスワード(password）をここにも同様に設定します。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_24.png" width= "640" >

そして、左下の`Test Connection`ボタンをクリックして、`OK`であれば、接続完了です。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_25.png" width= "640" >


`Save`ボタンをクリックし、この設定を保存します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_26.png" width= "640" >


さらに、`Connect`ボタンをクリックし、接続します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_27.png" width= "640" >


次に、`IntelliShell`ボタンをクリックし、MongoDBシェルウィンドウを開き、`use blog`と入力し、blogデータベースを作成します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_28.png" width= "640" >


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_29.png" width= "640" >


再度、`IntelliShell`ボタンをクリックし、データを登録します。これはGitHubに登録してありますので、そのファイルを貼り付けて登録します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_30.png" width= "640" >


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_31.png" width= "640" >


最後に、参照ユーザ（sample）のアクセス権限を登録します。

これもGitHubに登録してあるものをそのまま利用します。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_32.png" width= "640" >

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_33.png" width= "640" >

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_34.png" width= "640" >



## サーバ及びクライアントソフトの起動

まず、起動中のMongoDBを停止します。

`docker-compose down`と入力します。

  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_35.png" width= "640" >


次に、すべてのソフトウェアを起動します。

`docker-compose up`と入力します。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_36.png" width= "640" >


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_37.png" width= "640" >



## クライアントソフトの確認

データベース、Express並びにAngularがうまく連携していれば、`http:localhost:8080`にブラウザ上でアクセスすれば、スマホにも対応した_Markdown Editor_が起動できます。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_38.png" width= "640" >


このソフトは、Angular Materialの機能を利用しており、デスクトップマシンやスマートフォンなどの画面解像度に応じて、スタイルが設定されているようになっています。

また、ページング機能、各列ごとのソート機能なども有効になっています。
表示行数も変更可能です。



  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_39.png" width= "640" >


各列のヘッダの矢印をクリックすると、その列をキーとしてソートが行われます。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_40.png" width= "300" >


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_41.png" width= "640" >


右下のアイコンをクリックすると、次ページが表示されます。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_43.png" width= "640" >


各列の`Edit`ボタンをクリックすると、修正用のページが表示されます。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_44.png" width= "640" >



各列の`View`ボタンをクリックすると、Markdownが認識されたページが表示されます。


  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_46.png" width= "640" >



##### デスクトップ表示
  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_48.png" width= "640" >

##### スマートフォン表示
  <img src="https://raw.githubusercontent.com/Ohtsu/images/master/docker/docker_mean_49.png" width= "300" >


## 最後に

このシステムの作成については、簡単にできると考えていましたが、意外に時間がかかってしまいました。

その原因は、Docker-Composeファイルにおける`depends_on`や`links`の使い方がよくわかっていなかったからによります。Web上の情報では、`links`はいずれ廃止されるとのことであり、また他の実装例でも`links`は記述されていなかったので（たぶんRedisをベースとしていたことが影響していると思います）、当初から使用していませんでした。

ところが何度繰り返してもうまく稼働しません。そうこうしているうちに、下記参考文献の"Cannot connect to MongoDB via node.js in Docker"のページに出会い、試しに`links`を使用してみました。

すると簡単にMongoDBに接続することができました。これには、各コンテナ間の連携に、接続のタイミングが影響していることが伺えます。

幸い、下記参考文献上の『プログラマのためのDocker教科書 第2版 インフラの基礎知識&コードによる環境構築の自動化』の231ページにある「ここで注意していただきたいのは、depends_onがコンテナの開始の順序を制御するだけで、コンテナ上のアプリケーションが利用可能になるまで待つという制御を行わない点です。つまり、依存関係にあるデータベースサーバの準備が整うまで待つわけではないため、アプリケーション側で対策する必要があります。」との記述をかすかに覚えていました。

そこで、時間のかかるDockerイメージの作成を行い、そして一旦Docker-Composeを中断し、再起動するという方法をとることにしました。

これにより、ローカル上の既存のDockerイメージを利用できるので、各コンテナの接続がうまくいくことになりました。

皆さんもぜひこの点に注意してください。

　  
## Reference

### MEAN Stack

- "Angular 6 - MEAN Stack Crash Course - Part 1: Front-end Project Setup And Routing", 
<https://www.youtube.com/watch?v=x2_bcCZg8vQ>


- "Angular 6 - MEAN Stack Crash Course - Part 2: Implementing The Back-end", 
<https://www.youtube.com/watch?v=a30flH_q5-A&t=774s>


- "Angular 6 - MEAN Stack Crash Course - Part 3: Connecting Front-end To Back-end", 
<https://www.youtube.com/watch?v=HTqghYMRrtA>

- "ngular 6 - MEAN Stack Crash Course - Part 4: Completing The User Interface", 
<https://www.youtube.com/watch?v=PhIhNU5MLqo>

- "Web Application Example of MEAN Stack", 
<https://github.com/hi1280/mean-example>

- "Integrating Angular with Node.js RESTful Services", 
<https://github.com/DanWahlin/Angular-NodeJS-MongoDB-CustomersService>

- "Node.js+express+MongoDB+Mongooseで簡単なjsonサーバを構築するメモ", 
<https://qiita.com/tdomen/items/4ecb15f25bf9c3652f59>

- "Mean stack の構築", 
<https://qiita.com/baster/items/5b6a2e49030067b6e55c>

- "MEAN Stack Angular 6 CRUD Web Application", 
<https://www.djamware.com/post/5b00bb9180aca726dee1fd6d/mean-stack-angular-6-crud-web-application>



### Express

- "サルでも分かるExpressでのjsonAPIサーバーの作り方", 
<https://qiita.com/leafia78/items/73cc7160d002a4989416>


- "Node.js, Express, sequelize, React で始めるモダン WEB アプリケーション入門（Express/sequelize編）", 
<https://qiita.com/tatsurou313/items/2ba0387806b07f442b8c>


- "[Express] Expressの開発環境構築～デバッグ環境構築", 
<https://qiita.com/ksh-fthr/items/c22dedbc0952bfdcc808>



### MongoDB

- "MongoDB + mongo-expressをDocker Composeでお手軽構築", 
<https://qiita.com/gldn/items/2a8486c4d7a42d7a0f1f>

- "Cannot connect to MongoDB via node.js in Docker", 
<https://stackoverflow.com/questions/44938344/cannot-connect-to-mongodb-via-node-js-in-docker>


### Docker

- "Docker Community Edition for Windows", 
<https://store.docker.com/editions/community/docker-ce-desktop-windows>

- "Docker/Kubernetes 実践コンテナ開発入門", 
<http://amazon.co.jp/o/ASIN/4297100339/>

- "プログラマのためのDocker教科書 第2版 インフラの基礎知識&コードによる環境構築の自動化", 
<http://amazon.co.jp/o/ASIN/4798153222/>


- "Cannot link to a running container started by docker-compose", 
<https://stackoverflow.com/questions/36489696/cannot-link-to-a-running-container-started-by-docker-compose>

### Node.js

- "Node.js超入門[第2版] ", 
<http://amazon.co.jp/o/ASIN/4798055220/>


### Angular

- "Containerizing Angular with Docker - Dan Wahlin", 
<https://www.youtube.com/watch?v=cLT7eUWKZpg&t=1140s>


- "Deploy Angular 5 app in Docker Container in under 10 mins - For local development", 
<https://www.youtube.com/watch?v=L2UkQ2CND68&t=178s>


- "Angular5, Angular6, Angular7 Custom Library: Step-by-step guide", 
<https://www.udemy.com/angular5-custom-library-the-definitive-step-by-step-guide/>

- "Angular5, Angular6, Angular7用 カスタムライブラリの作成: 完全ステップ・バイ・ステップ・ガイド", 
<https://www.udemy.com/angular5-l/>