DockerComposeを使用した開発用のNode.jsアプリケーションのコンテナー化

提供:Dev Guides
移動先:案内検索

序章

アプリケーションを積極的に開発している場合は、 Docker を使用すると、ワークフローとアプリケーションを本番環境にデプロイするプロセスを簡素化できます。 開発中のコンテナを使用すると、次の利点があります。

  • 環境は一貫しています。つまり、システムの競合を心配することなく、プロジェクトに必要な言語と依存関係を選択できます。
  • 環境が分離されているため、問題のトラブルシューティングや新しいチームメンバーの参加が容易になります。
  • 環境は移植可能であり、コードをパッケージ化して他の人と共有することができます。

このチュートリアルでは、Dockerを使用してNode.jsアプリケーションの開発環境をセットアップする方法を説明します。 Docker Compose を使用して、2つのコンテナー(1つはNodeアプリケーション用、もう1つは MongoDB データベース用)を作成します。 このアプリケーションはNodeとMongoDBで動作するため、セットアップでは次のようになります。

  • ホスト上のアプリケーションコードをコンテナ内のコードと同期して、開発中の変更を容易にします。
  • アプリケーションコードへの変更が再起動せずに機能することを確認します。
  • アプリケーションのデータ用にユーザーとパスワードで保護されたデータベースを作成します。
  • このデータを永続化します。

このチュートリアルの最後に、Dockerコンテナで実行されている動作するサメ情報アプリケーションがあります。

前提条件

このチュートリアルに従うには、次のものが必要です。

  • Ubuntu 18.04を実行している開発サーバーと、sudo特権とアクティブなファイアウォールを持つ非rootユーザー。 これらの設定方法のガイダンスについては、この初期サーバー設定ガイドを参照してください。
  • Ubuntu18.04にDockerをインストールして使用する方法の手順1と2に従ってサーバーにDockerをインストールします。
  • Ubuntu 18.04にDockerComposeをインストールする方法のステップ1に従って、サーバーにDockerComposeをインストールします。

ステップ1—プロジェクトのクローンを作成して依存関係を変更する

このセットアップを構築する最初のステップは、プロジェクトコードのクローンを作成し、プロジェクトの依存関係を含むpackage.jsonファイルを変更することです。 nodemonをプロジェクトのdevDependenciesに追加し、開発中に使用することを指定します。 nodemonを使用してアプリケーションを実行すると、コードに変更を加えるたびにアプリケーションが自動的に再起動されます。

まず、 DigitalOceanCommunityGitHubアカウントからnodejs-mongo-mongooseリポジトリのクローンを作成します。 このリポジトリには、 MongoDBをノードアプリケーションと統合する方法で説明されているセットアップのコードが含まれています。このコードでは、Mongooseを使用してMongoDBデータベースを既存のノードアプリケーションと統合する方法について説明しています。

リポジトリをnode_projectというディレクトリに複製します。

git clone https://github.com/do-community/nodejs-mongo-mongoose.git node_project

node_projectディレクトリに移動します。

cd node_project

nanoまたはお気に入りのエディターを使用して、プロジェクトのpackage.jsonファイルを開きます。

nano package.json

プロジェクトの依存関係の下で、中括弧の上に、nodemonを含む新しいdevDependenciesオブジェクトを作成します。

〜/ node_project / package.json

...
"dependencies": {
    "ejs": "^2.6.1",
    "express": "^4.16.4",
    "mongoose": "^5.4.10"
  },
  "devDependencies": {
    "nodemon": "^1.18.10"
  }    
}

編集が終了したら、ファイルを保存して閉じます。

プロジェクトコードを配置し、その依存関係を変更したら、コンテナ化されたワークフローのコードのリファクタリングに進むことができます。

ステップ2—コンテナを操作するようにアプリケーションを構成する

コンテナ化されたワークフロー用にアプリケーションを変更することは、コードをよりモジュール化することを意味します。 コンテナは環境間の移植性を提供します。コードは、基盤となるオペレーティングシステムから可能な限り分離されたままにすることで、それを反映する必要があります。 これを実現するために、コードをリファクタリングして、ノードの process.env プロパティをさらに活用します。このプロパティは、実行時にユーザー環境に関する情報を含むオブジェクトを返します。 コードでこのオブジェクトを使用して、実行時に環境変数を使用して構成情報を動的に割り当てることができます。

まず、メインのアプリケーションエントリポイントであるapp.jsから始めましょう。 ファイルを開きます。

nano app.js

内部には、port constant の定義と、この定数を使用してアプリケーションがリッスンするポートを指定するlisten関数が表示されます。

〜/ home / node_project / app.js

...
const port = 8080;
...
app.listen(port, function () {
  console.log('Example app listening on port 8080!');
});

port定数を再定義して、process.envオブジェクトを使用して実行時に動的に割り当てることができるようにします。 定数定義とlisten関数に次の変更を加えます。

〜/ home / node_project / app.js

...
const port = process.env.PORT || 8080;
...
app.listen(port, function () {
  console.log(`Example app listening on ${port}!`);
});

新しい定数定義は、実行時に渡された値または8080を使用して、portを動的に割り当てます。 同様に、listen関数を書き直して、テンプレートリテラルを使用します。これにより、接続をリッスンするときにポート値が補間されます。 ポートを他の場所にマッピングするため、これらのリビジョンにより、環境の変化に応じてこのファイルを継続的にリビジョンする必要がなくなります。

編集が終了したら、ファイルを保存して閉じます。

次に、データベース接続情報を変更して、構成資格情報を削除します。 次の情報を含むdb.jsファイルを開きます。

nano db.js

現在、ファイルは次のことを行います。

  • アプリケーションデータのスキーマとモデルを作成するために使用しているObjectDocument Mapper (ODM)であるMongooseをインポートします。
  • データベースのクレデンシャルを、ユーザー名とパスワードを含む定数として設定します。
  • mongoose.connectメソッドを使用してデータベースに接続します。

ファイルの詳細については、MongoDBをノードアプリケーションと統合する方法ステップ3を参照してください。

ファイルを変更する最初のステップは、機密情報を含む定数を再定義することです。 現在、これらの定数は次のようになっています。

〜/ node_project / db.js

...
const MONGO_USERNAME = 'sammy';
const MONGO_PASSWORD = 'your_password';
const MONGO_HOSTNAME = '127.0.0.1';
const MONGO_PORT = '27017';
const MONGO_DB = 'sharkinfo';
...

この情報をハードコーディングする代わりに、process.envオブジェクトを使用して、これらの定数の実行時の値を取得できます。 次のようにブロックを変更します。

〜/ node_project / db.js

...
const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;
...

編集が終了したら、ファイルを保存して閉じます。

この時点で、アプリケーションの環境変数を操作するようにdb.jsを変更しましたが、これらの変数をアプリケーションに渡す方法が必要です。 実行時にアプリケーションに渡すことができる値を使用して.envファイルを作成しましょう。

ファイルを開きます。

nano .env

このファイルには、db.jsから削除した情報(アプリケーションのデータベースのユーザー名とパスワード、およびポート設定とデータベース名)が含まれます。 ここにリストされているユーザー名、パスワード、およびデータベース名を、自分の情報で更新することを忘れないでください。

〜/ node_project / .env

MONGO_USERNAME=sammy
MONGO_PASSWORD=your_password
MONGO_PORT=27017
MONGO_DB=sharkinfo

db.jsに元々表示されていたホスト設定を削除していることに注意してください。 ここで、Docker Composeファイルのレベルでホストを定義し、サービスとコンテナーに関するその他の情報を示します。

編集が終了したら、このファイルを保存して閉じます。

.envファイルには機密情報が含まれているため、プロジェクトの.dockerignoreおよび.gitignoreファイルに含まれていることを確認して、バージョン管理またはコンテナ。

.dockerignoreファイルを開きます。

nano .dockerignore

ファイルの最後に次の行を追加します。

〜/ node_project / .dockerignore

...
.gitignore
.env

編集が終了したら、ファイルを保存して閉じます。

このリポジトリの.gitignoreファイルにはすでに.envが含まれていますが、そこにあることを確認してください。

nano .gitignore

~~ / node_project / .gitignore

...
.env
...

この時点で、プロジェクトコードから機密情報を正常に抽出し、この情報をコピーする方法と場所を制御するための対策を講じています。 これで、データベース接続コードに堅牢性を追加して、コンテナー化されたワークフロー用に最適化できます。

手順3—データベース接続設定の変更

次のステップは、アプリケーションがデータベースに接続できない場合を処理するコードを追加することにより、データベース接続方法をより堅牢にすることです。 このレベルの復元力をアプリケーションコードに導入することは、Composeを使用してコンテナーを操作する場合の推奨プラクティスです。

db.jsを開いて編集します。

nano db.js

以前に追加したコードと、Mongoの接続URIのurl定数およびMongooseconnectメソッドが表示されます。

〜/ node_project / db.js

...
const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;

const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;

mongoose.connect(url, {useNewUrlParser: true});

現在、connectメソッドは、Mongoの新しいURLパーサーを使用するようにMongooseに指示するオプションを受け入れます。 このメソッドにさらにいくつかのオプションを追加して、再接続を試行するためのパラメーターを定義しましょう。 これを行うには、新しいURLパーサーオプションに加えて、関連情報を含むoptions定数を作成します。 Mongo定数の下に、options定数の次の定義を追加します。

〜/ node_project / db.js

...
const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;

const options = {
  useNewUrlParser: true,
  reconnectTries: Number.MAX_VALUE,
  reconnectInterval: 500, 
  connectTimeoutMS: 10000,
};
...

reconnectTriesオプションは、Mongooseに無期限に接続を試行し続けるように指示し、reconnectIntervalは、接続試行間の期間をミリ秒単位で定義します。 connectTimeoutMSは、Mongoドライバーが接続の試行に失敗する前に待機する期間として10秒を定義します。

これで、Mongoose connectメソッドで新しいoptions定数を使用して、Mongoose接続設定を微調整できます。 また、潜在的な接続エラーを処理するためにpromiseを追加します。

現在、Mongooseconnectメソッドは次のようになっています。

〜/ node_project / db.js

...
mongoose.connect(url, {useNewUrlParser: true});

既存のconnectメソッドを削除し、options定数とpromiseを含む次のコードに置き換えます。

〜/ node_project / db.js

...
mongoose.connect(url, options).then( function() {
  console.log('MongoDB is connected');
})
  .catch( function(err) {
  console.log(err);
});

接続が成功した場合、関数は適切なメッセージをログに記録します。 それ以外の場合は、をキャッチしてエラーをログに記録し、トラブルシューティングを可能にします。

完成したファイルは次のようになります。

〜/ node_project / db.js

const mongoose = require('mongoose');

const {
  MONGO_USERNAME,
  MONGO_PASSWORD,
  MONGO_HOSTNAME,
  MONGO_PORT,
  MONGO_DB
} = process.env;

const options = {
  useNewUrlParser: true,
  reconnectTries: Number.MAX_VALUE,
  reconnectInterval: 500,
  connectTimeoutMS: 10000,
};

const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;

mongoose.connect(url, options).then( function() {
  console.log('MongoDB is connected');
})
  .catch( function(err) {
  console.log(err);
});

編集が終了したら、ファイルを保存して閉じます。

これで、アプリケーションがデータベースに接続できない可能性がある場合に対処するために、アプリケーションコードに復元力が追加されました。 このコードを配置すると、Composeを使用したサービスの定義に進むことができます。

ステップ4—DockerComposeを使用したサービスの定義

コードをリファクタリングすると、サービス定義を使用してdocker-compose.ymlファイルを作成する準備が整います。 Composeのserviceは実行中のコンテナーであり、サービス定義(docker-compose.ymlファイルに含める)には、各コンテナーイメージの実行方法に関する情報が含まれています。 作成ツールを使用すると、複数のサービスを定義して、マルチコンテナーアプリケーションを構築できます。

ただし、サービスを定義する前に、 wait-for というツールをプロジェクトに追加して、データベースの起動タスクが完了した後にのみアプリケーションがデータベースへの接続を試行するようにします。 このラッパースクリプトは、 netcat を使用して、特定のホストとポートがTCP接続を受け入れているかどうかをポーリングします。 これを使用すると、データベースが接続を受け入れる準備ができているかどうかをテストすることにより、アプリケーションのデータベースへの接続の試行を制御できます。

Composeではdepends_onオプションを使用してサービス間の依存関係を指定できますが、この順序は、コンテナーの準備ではなく、コンテナーが実行されているかどうかに基づいています。 depends_onの使用は、admin認証データベースへのユーザーとパスワードの追加など、データベースの起動タスクが完了したときにのみアプリケーションを接続する必要があるため、セットアップには最適ではありません。 wait-forおよびその他のツールを使用して起動順序を制御する方法の詳細については、作成ドキュメントの関連する推奨事項を参照してください。

wait-for.shというファイルを開きます。

nano wait-for.sh

次のコードをファイルに貼り付けて、ポーリング関数を作成します。

〜/ node_project / app / wait-for.sh

#!/bin/sh

# original script: https://github.com/eficode/wait-for/blob/master/wait-for

TIMEOUT=15
QUIET=0

echoerr() {
  if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi
}

usage() {
  exitcode="$1"
  cat << USAGE >&2
Usage:
  $cmdname host:port [-t timeout] [-- command args]
  -q | --quiet                        Do not output any status messages
  -t TIMEOUT | --timeout=timeout      Timeout in seconds, zero for no timeout
  -- COMMAND ARGS                     Execute command with args after the test finishes
USAGE
  exit "$exitcode"
}

wait_for() {
  for i in `seq $TIMEOUT` ; do
    nc -z "$HOST" "$PORT" > /dev/null 2>&1
    
    result=$?
    if [ $result -eq 0 ] ; then
      if [ $# -gt 0 ] ; then
        exec "$@"
      fi
      exit 0
    fi
    sleep 1
  done
  echo "Operation timed out" >&2
  exit 1
}

while [ $# -gt 0 ]
do
  case "$1" in
    *:* )
    HOST=$(printf "%s\n" "$1"| cut -d : -f 1)
    PORT=$(printf "%s\n" "$1"| cut -d : -f 2)
    shift 1
    ;;
    -q | --quiet)
    QUIET=1
    shift 1
    ;;
    -t)
    TIMEOUT="$2"
    if [ "$TIMEOUT" = "" ]; then break; fi
    shift 2
    ;;
    --timeout=*)
    TIMEOUT="${1#*=}"
    shift 1
    ;;
    --)
    shift
    break
    ;;
    --help)
    usage 0
    ;;
    *)
    echoerr "Unknown argument: $1"
    usage 1
    ;;
  esac
done

if [ "$HOST" = "" -o "$PORT" = "" ]; then
  echoerr "Error: you need to provide a host and port to test."
  usage 2
fi

wait_for "$@"

コードの追加が完了したら、ファイルを保存して閉じます。

スクリプトを実行可能にします。

chmod +x wait-for.sh

次に、docker-compose.ymlファイルを開きます。

nano docker-compose.yml

まず、ファイルに次のコードを追加して、nodejsアプリケーションサービスを定義します。

〜/ node_project / docker-compose.yml

version: '3'

services:
  nodejs:
    build:
      context: .
      dockerfile: Dockerfile
    image: nodejs
    container_name: nodejs
    restart: unless-stopped
    env_file: .env
    environment:
      - MONGO_USERNAME=$MONGO_USERNAME
      - MONGO_PASSWORD=$MONGO_PASSWORD
      - MONGO_HOSTNAME=db
      - MONGO_PORT=$MONGO_PORT
      - MONGO_DB=$MONGO_DB 
    ports:
      - "80:8080"
    volumes:
      - .:/home/node/app
      - node_modules:/home/node/app/node_modules
    networks:
      - app-network
    command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js

nodejsサービス定義には、次のオプションが含まれています。

  • build:これは、contextdockerfileなど、Composeがアプリケーションイメージをビルドするときに適用される構成オプションを定義します。 Docker Hub などのレジストリの既存のイメージを使用する場合は、代わりに image命令を使用して、ユーザー名、リポジトリ、イメージタグに関する情報を使用できます。

  • context:これは、イメージビルドのビルドコンテキスト(この場合は現在のプロジェクトディレクトリ)を定義します。

  • dockerfile:これは、Composeがアプリケーションイメージのビルドに使用するファイルとして、現在のプロジェクトディレクトリのDockerfileを指定します。 このファイルの詳細については、Dockerを使用してNode.jsアプリケーションを構築する方法を参照してください。

  • imagecontainer_name:これらは画像とコンテナに名前を適用します。

  • restart:これは再起動ポリシーを定義します。 デフォルトはnoですが、コンテナが停止しない限り再起動するように設定しています。

  • env_file:これは、ビルドコンテキストにある.envというファイルから環境変数を追加することをComposeに通知します。

  • environment:このオプションを使用すると、.envファイルで定義したMongo接続設定を追加できます。 NODE_ENVdevelopmentに設定していないことに注意してください。これは、NODE_ENVが設定されていない場合のExpressのデフォルトの動作です。 本番環境に移行する場合は、これをproductionに設定してビューのキャッシュを有効にし、エラーメッセージの詳細を少なくすることができます。 また、ステップ2 で説明したように、dbデータベースコンテナをホストとして指定していることにも注意してください。

  • ports:これは、ホストのポート80をコンテナーのポート8080にマップします。

  • volumes:ここには2種類のマウントが含まれています:

    • 1つ目は、ホスト上のアプリケーションコードをコンテナの/home/node/appディレクトリにマウントするbindmountです。 これにより、ホストコードに加えた変更がすぐにコンテナに入力されるため、迅速な開発が容易になります。
    • 2つ目は、 volumenode_modulesという名前です。 DockerがアプリケーションDockerfileにリストされているnpm install命令を実行すると、npmは、実行に必要なパッケージを含む新しいnode_modulesディレクトリを作成します。アプリケーション。 ただし、作成したバインドマウントは、この新しく作成されたnode_modulesディレクトリを非表示にします。 ホストのnode_modulesは空であるため、バインドは空のディレクトリをコンテナにマップし、新しいnode_modulesディレクトリを上書きして、アプリケーションを起動できないようにします。 名前付きのnode_modulesボリュームは、/home/node/app/node_modulesディレクトリの内容を永続化し、それをコンテナにマウントしてバインドを非表示にすることで、この問題を解決します。

    このアプローチを使用するときは、次の点に注意してください

    • 名前付きボリュームはDockerによって作成されたため、バインドによってコンテナ上のnode_modulesディレクトリの内容がホストにマウントされ、このディレクトリはrootによって所有されます。
    • ホストに既存のnode_modulesディレクトリがある場合、コンテナに作成されたnode_modulesディレクトリが上書きされます。 このチュートリアルで構築しているセットアップは、に既存のnode_modulesディレクトリを持たず、npmを使用しないことを前提としています。亭主。 これは、実行環境間の依存関係を最小限に抑えるアプリケーション開発への12要素アプローチと一致しています。
  • networks:これは、アプリケーションサービスがapp-networkネットワークに参加することを指定します。これは、ファイルの下部で定義します。

  • command:このオプションを使用すると、Composeがイメージを実行するときに実行するコマンドを設定できます。 これにより、アプリケーションDockerfileで設定したCMD命令が上書きされることに注意してください。 ここでは、wait-forスクリプトを使用してアプリケーションを実行しています。このスクリプトは、ポート27017dbサービスをポーリングして、データベースサービスの準備ができているかどうかをテストします。 準備テストが成功すると、スクリプトは設定したコマンド/home/node/app/node_modules/.bin/nodemon app.jsを実行して、nodemonでアプリケーションを起動します。 これにより、アプリケーションを再起動しなくても、コードに加えた将来の変更が確実に再ロードされます。

次に、アプリケーションサービス定義の下に次のコードを追加して、dbサービスを作成します。

〜/ node_project / docker-compose.yml

...
  db:
    image: mongo:4.1.8-xenial
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME
      - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD
    volumes:  
      - dbdata:/data/db   
    networks:
      - app-network  

nodejsサービスに対して定義した設定の一部は同じままですが、imageenvironment、およびvolumesにも次の変更を加えました。 ]定義:

  • image:このサービスを作成するために、ComposeはDockerHubから4.1.8-xenialMongoイメージをプルします。 Mongoイメージが変更されたときに発生する可能性のある将来の競合を回避するために、特定のバージョンを固定しています。 バージョンの固定の詳細については、Dockerfileのベストプラクティスに関するDockerのドキュメントを参照してください。
  • MONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORDmongoイメージにより、これらの環境変数が使用可能になり、データベースインスタンスの初期化を変更できるようになります。 MONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORDを一緒に使用して、admin認証データベースにrootユーザーを作成し、コンテナーの起動時に認証が有効になっていることを確認します。 .envファイルの値を使用してMONGO_INITDB_ROOT_USERNAMEMONGO_INITDB_ROOT_PASSWORDを設定し、env_fileオプションを使用してdbサービスに渡します。 。 これを行うことは、sammyアプリケーションユーザーがデータベースインスタンスのrootユーザーになり、その役割のすべての管理および操作特権にアクセスできることを意味します。 本番環境で作業する場合は、適切なスコープの特権を持つ専用のアプリケーションユーザーを作成する必要があります。 <$>[注] ノート: 既存のデータディレクトリを配置してコンテナを起動した場合、これらの変数は有効にならないことに注意してください。 <$>
  • dbdata:/data/db:指定されたボリュームdbdataは、Mongoのデフォルトデータディレクトリ/data/dbに保存されているデータを保持します。 これにより、コンテナを停止または削除した場合にデータが失われることがなくなります。

また、dbサービスをapp-networkネットワークにnetworksオプションで追加しました。

最後のステップとして、ボリュームとネットワークの定義をファイルの最後に追加します。

〜/ node_project / docker-compose.yml

...
networks:
  app-network:
    driver: bridge

volumes:
  dbdata:
  node_modules:  

ユーザー定義のブリッジネットワークapp-networkは、コンテナーが同じDockerデーモンホスト上にあるため、コンテナー間の通信を可能にします。 これにより、アプリケーション内のトラフィックと通信が合理化されます。これは、同じブリッジネットワーク上のコンテナ間のすべてのポートを開き、ポートを外部に公開しないためです。 したがって、dbコンテナとnodejsコンテナは相互に通信でき、アプリケーションへのフロントエンドアクセス用にポート80を公開するだけで済みます。

トップレベルのvolumesキーは、ボリュームdbdataおよびnode_modulesを定義します。 Dockerがボリュームを作成すると、ボリュームのコンテンツは、Dockerによって管理されるホストファイルシステム/var/lib/docker/volumes/の一部に保存されます。 各ボリュームの内容は、/var/lib/docker/volumes/の下のディレクトリに保存され、ボリュームを使用する任意のコンテナにマウントされます。 このように、ユーザーが作成するサメ情報データは、dbコンテナーを削除して再作成しても、dbdataボリュームに保持されます。

完成したdocker-compose.ymlファイルは次のようになります。

〜/ node_project / docker-compose.yml

version: '3'

services:
  nodejs:
    build:
      context: .
      dockerfile: Dockerfile
    image: nodejs
    container_name: nodejs
    restart: unless-stopped
    env_file: .env
    environment:
      - MONGO_USERNAME=$MONGO_USERNAME
      - MONGO_PASSWORD=$MONGO_PASSWORD
      - MONGO_HOSTNAME=db
      - MONGO_PORT=$MONGO_PORT
      - MONGO_DB=$MONGO_DB
    ports:
      - "80:8080"
    volumes:
      - .:/home/node/app
      - node_modules:/home/node/app/node_modules
    networks:
      - app-network
    command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js 

  db:
    image: mongo:4.1.8-xenial
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME
      - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD
    volumes:     
      - dbdata:/data/db
    networks:
      - app-network  

networks:
  app-network:
    driver: bridge

volumes:
  dbdata:
  node_modules:  

編集が終了したら、ファイルを保存して閉じます。

サービス定義が整ったら、アプリケーションを開始する準備が整います。

ステップ5—アプリケーションのテスト

docker-compose.ymlファイルを配置したら、 docker-composeupコマンドを使用してサービスを作成できます。 docker-compose down を使用してコンテナーを停止および削除することにより、データが保持されることをテストすることもできます。

まず、コンテナイメージをビルドし、docker-compose up-dフラグで実行してサービスを作成します。これにより、nodejsおよびdbコンテナがバックグラウンドで実行されます。 :

docker-compose up -d

サービスが作成されたことを確認する出力が表示されます。

Output...
Creating db ... done
Creating nodejs ... done

また、サービスからのログ出力を表示することにより、起動プロセスに関するより詳細な情報を取得できます。

docker-compose logs 

すべてが正しく開始されている場合は、次のように表示されます。

Output...
nodejs    | [nodemon] starting `node app.js`
nodejs    | Example app listening on 8080!
nodejs    | MongoDB is connected
...
db        | 2019-02-22T17:26:27.329+0000 I ACCESS   [conn2] Successfully authenticated as principal sammy on admin

docker-compose ps を使用して、コンテナのステータスを確認することもできます。

docker-compose ps

コンテナが実行されていることを示す出力が表示されます。

Output Name               Command               State          Ports        
----------------------------------------------------------------------
db       docker-entrypoint.sh mongod      Up      27017/tcp           
nodejs   ./wait-for.sh db:27017 --  ...   Up      0.0.0.0:80->8080/tcp

サービスを実行している状態で、ブラウザでhttp://your_server_ipにアクセスできます。 次のようなランディングページが表示されます。

Get SharkInfoボタンをクリックします。 サメの名前とそのサメの一般的な性格の説明を入力できる入力フォームのあるページが表示されます。

フォームに、選択したサメを追加します。 このデモンストレーションの目的で、Megalodon SharkShark Name フィールドに追加し、AncientSharkCharacterフィールドに追加します。

送信ボタンをクリックします。 このサメの情報が表示されたページが表示されます。

最後のステップとして、データベースコンテナを削除しても、入力したデータが保持されることをテストできます。

ターミナルに戻り、次のコマンドを入力して、コンテナとネットワークを停止および削除します。

docker-compose down

--volumesオプションを含むではないことに注意してください。 したがって、dbdataボリュームは削除されません。

次の出力は、コンテナとネットワークが削除されたことを確認します。

OutputStopping nodejs ... done
Stopping db     ... done
Removing nodejs ... done
Removing db     ... done
Removing network node_project_app-network

コンテナを再作成します。

docker-compose up -d

次に、サメ情報フォームに戻ります。

お好みの新しいサメを入力してください。 Whale SharkLargeを使用します。

送信をクリックすると、入力済みのデータを失うことなく、新しいサメがデータベースのサメコレクションに追加されたことがわかります。

これで、アプリケーションは、データの永続性とコードの同期が有効になっているDockerコンテナで実行されます。

結論

このチュートリアルに従うことで、Dockerコンテナーを使用してNodeアプリケーションの開発セットアップを作成しました。 機密情報を抽出し、アプリケーションの状態をアプリケーションコードから切り離すことで、プロジェクトをよりモジュール化して移植性の高いものにしました。 また、開発のニーズや要件の変化に応じて修正できる定型文docker-compose.ymlファイルを構成しました。

開発するにつれて、コンテナ化された CloudNativeワークフロー用のアプリケーションの設計についてさらに学ぶことに興味があるかもしれません。 これらのトピックの詳細については、Kubernetes用アプリケーションのアーキテクチャおよびKubernetes用アプリケーションの最新化を参照してください。

このチュートリアルで使用されるコードの詳細については、Dockerを使用してNode.jsアプリケーションを構築する方法およびMongoDBをノードアプリケーションと統合する方法を参照してください。 コンテナーを使用してNginxリバースプロキシを使用してNodeアプリケーションをデプロイする方法については、 Nginx、Let's Encrypt、およびDockerComposeを使用してコンテナー化されたNode.jsアプリケーションを保護する方法を参照してください。