Tutorial

Контейнеризация приложения Node.js для разработки с использованием Docker Compose

Published on January 24, 2020
Русский
Контейнеризация приложения Node.js для разработки с использованием Docker Compose

Введение

Если вы активно занимаетесь разработкой приложений, использование Docker может упростить ваш рабочий процесс и процесс развертывания вашего приложения в продакшен. Работа с контейнерами в процессе разработки предоставляет следующие преимущества:

  • Среды окружения остаются согласованными, что означает, что вы можете выбирать языки и зависимости, которые вам нужны для вашего проекта, не беспокоясь о системных конфликтах.
  • Среды окружения изолированы, что упрощает устранение проблем и включение в работу новых участников групп.
  • Среды окружения портативны, что позволяет вам упаковывать свой код и делиться им с другими.

Из этого руководства вы узнаете, как выполнять настройку среды разработки для приложения Node.js с помощью Docker. Вы создадите два контейнера, один для приложения Node, а другой для базы данных MongoDB с помощью Docker Compose. Поскольку это приложение работает с Node и MongoDB, необходимо выполнить следующие настройки:

  • Синхронизируйте код приложения на хосте с кодом в контейнере, чтобы облегчить внесение изменений во время разработки.
  • Убедитесь, что изменения, вносимые в код приложения, используются без необходимости перезапуска.
  • Создайте пользователя и базу данных с защитой паролем для хранения данных приложения.
  • Сохраните данные.

После изучения данного руководства у вас будет рабочее приложение с информацией об акулах, работающее на контейнерах Docker:

Полное собрание акул

Предварительные требования

Для данного обучающего модуля вам потребуется следующее:

Шаг 1 — Клонирование проекта и изменение зависимостей

Первым, что необходимо сделать при настройке системы, является клонирование кода проекта и изменение файла package.json, который включает зависимости проекта. Мы добавим nodemon в devDependencies проекта, чтобы указать, что мы будем использовать его при разработке. Запуск приложения с nodemon обеспечивает автоматический перезапуск при внесении изменений в ваш код.

Во-первых, клонируйте репозиторий nodejs-mongo-mongoose из учетной записи DigitalOcean Community на GitHub. Этот репозиторий включает код настройки, описанной в руководстве Интеграция MongoDB с вашим приложением Node, которое содержит описание процесса интеграции базы данных MongoDB с существующим приложением Node с помощью Mongoose.

Клонируйте репозиторий в директорию с именем node_project:

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

Перейдите в директорию node_project:

  1. cd node_project

Откройте файл проекта package.json с помощью nano или вашего любимого редактора:

  1. nano package.json

После зависимостей проекта и над закрывающей фигурной скобкой создайте новый объект devDependencies, который включает nodemon:

~/node_project/package.json
...
"dependencies": {
    "ejs": "^2.6.1",
    "express": "^4.16.4",
    "mongoose": "^5.4.10"
  },
  "devDependencies": {
    "nodemon": "^1.18.10"
  }    
}

Сохраните и закройте файл после завершения редактирования.

После добавления кода проекта и изменения зависимостей вы можете перейти к рефакторингу кода для контейнеризованного рабочего процесса.

Шаг 2 — Настройка вашего приложения для работы с контейнерами

Изменение нашего приложения для контейнеризованного рабочего процесса подразумевает, что код должен быть более модульным. Контейнеры обеспечивают возможность перемещения между рабочими средами, и наш код должен отражать это, оставаясь как можно менее привязанным к лежащей в основе операционной системе. Для этого мы выполним рефакторинг нашего кода, чтобы добиться более эффективного использования свойства Node process.env, которое возвращает объект с информацией о среде пользователя при запуске. Мы можем использовать этот объект в нашем коде для динамического сохранения информации о конфигурации при запуске в переменных среды.

Давайте начнем с app.js, главной точки входа нашего приложения. Откройте файл:

  1. nano app.js

Внутри файла вы увидите определение для константы port, а также функцию 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}!`);
});

Наше новое определение константы присваивает port динамически с помощью значения, передаваемого при запуске, или 8080. Аналогично мы переписали функцию listen для использования шаблонного литерала, который будет интерполировать значение порта при прослушивании соединений. Поскольку мы будем выполнять маппинг портов где-либо еще, эти изменения позволят нам избежать необходимости постоянно пересматривать этот файл при внесении изменений в нашу среду.

После завершения редактирования сохраните и закройте файл.

Затем нам нужно изменить информацию о подключении базы данных, чтобы удалить любые учетные данные конфигурации. Откройте файл db.js, содержащий следующую информацию:

  1. nano db.js

В настоящее время файл выполняет следующие функции:

  • Импортирует Mongoose, Object Document Mapper (ODM), который мы используем для создания схем и моделей данных нашего приложения.
  • Устанавливает учетные данные базы данных в качестве констант, включая имя пользователя и пароль.
  • Подключается к базе данных с помощью метода mongoose.connect.

Дополнительную информацию о файле см. в шаге 3 руководства Интеграция MongoDB с вашим приложением Node.

Наш первый шаг по изменению файла будет состоять в переопределении констант, содержащих чувствительную информацию. В настоящее время эти константы выглядят следующим образом:

~/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 со значениями, которые вы можете передать вашему приложению при запуске.

Откройте файл:

  1. 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:

  1. nano .dockerignore

Добавьте следующую строку внизу файла:

~/node_project/.dockerignore
...
.gitignore
.env

Сохраните и закройте файл после завершения редактирования.

Файл .gitignore в этом репозитории уже включает .env, но вы можете убедиться в его наличии:

  1. nano .gitignore
~~/node_project/.gitignore
...
.env
...

На данный момент вы успешно извлекли чувствительную информацию из кода проекта и приняли меры для контроля того, как и куда будет скопирована эта информация. Теперь вы можете добавить дополнительный уровень надежности для кода подключения к базе данных, чтобы оптимизировать его для контейнеризованного рабочего процесса.

Шаг 3 — Изменение настроек подключения к базе данных

Следующим нашим шагом будет повышение надежности работы метода подключения к базе данных с помощью кода, который обрабатывает случаи, когда нашему приложению не удается подключиться к нашей базе данных. Введение данного уровня устойчивости в код приложения является рекомендуемой практикой при работе с контейнерами с помощью Compose.

Откройте db.js для редактирования:

  1. nano db.js

Вы увидите добавленный нами ранее код, а также константу url для URI подключения к Mongo и метод подключения к Mongoose:

~/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 поддерживает параметр, который указывает Mongoose использовать новый парсер URL в Mongo. Давайте добавим дополнительные параметры в этот метод, чтобы определить параметры для попыток восстановления соединения. Мы можем сделать это, создав константу options, которая содержит соответствующую информацию, в дополнение к новой опции парсера URL. Под константами 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 указывает 10 секунд в качестве периода, которое драйвер Mongo будет ждать, прежде чем попытаться снова установить соединение.

Теперь мы можем использовать новую константу options в методе connect Mongoose для тонкой настройки подключения Mongoose. Также мы добавим promise для обработки потенциальных ошибок подключения.

В настоящее время метод connect Mongoose выглядит следующим образом:

~/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 — Настройка служб с помощью Docker Compose

После выполнения рефакторинга кода вы можете начинать добавлять в файл docker-compose.yml определения служб. Служба в Compose — это запущенный контейнер, а определения служб, которые вы будете добавлять в ваш файл docker-compose.yml, содержат информацию о том, как будет запускаться образ каждого контейнера. Compose позволяет вам определять различные службы для создания приложений с несколькими контейнерами.

Прежде чем определять службы, мы добавим в наш проект инструмент с именем wait-for, чтобы гарантировать, что наше приложение будет пытаться подключиться к нашей базе данных только после завершения всех задач, связанных с запуском базы данных. Этот скрипт обертки использует netcat для опроса, поддерживает или нет конкретный хост и порт подключение по протоколу TCP. Благодаря этому вы можете контролировать попытки подключения вашего приложения к базе данных, проверяя, готова ли база данных к подключению.

Хотя Compose позволяет вам указывать зависимости между службами с помощью параметра depends_on, данный порядок определяется тем, запущен контейнер или нет, а не его готовностью. Использование depends_on при настройке не является оптимальным решением, поскольку мы хотим, чтобы наше приложение подключилось только после того, как все задачи запуска базы данных, включая добавление пользователя и пароля в базу данных аутентификации admin, будут выполнены. Дополнительную информацию об использовании wait-for и других инструментов для контроля порядка запуска можно найти в соответствующих разделах с рекомендациями в документации Compose.

Откройте файл с именем wait-for.sh:

  1. 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 "$@"

Сохраните и закройте файл после добавления кода.

Создайте исполняемый скрипт:

  1. chmod +x wait-for.sh

Затем откройте файл docker-compose.yml:

  1. 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: это определение параметров конфигурации, включая context и dockerfile, которые будут применяться при создании образа приложения Compose. Если вы хотите использовать существующий образ из реестра, например, из Docker Hub, вы можете использовать инструкцию image с информацией об имени пользователя, репозитория и теге образа.

  • context: это определение контекста сборки для сборки образа, в этом случае текущая директория проекта.

  • dockerfile: данный параметр определяет Dockerfile в текущей директории проекта в качестве файла, который Compose будет использоваться для сборки образа приложения. Дополнительную информацию об этом файле см. в руководстве Создание приложения Node.js с помощью Docker.

  • image, container_name: эти параметры присваивают имена для образа и контейнера.

  • restart: данный параметр определяет политику перезапуска. По умолчанию установлено значение no, но мы задали значение, согласно которому контейнер будет перезапускаться, пока не будет закрыт.

  • env_file: этот параметр указывает Compose, что мы хотим добавить переменные среды из файла с именем .env, расположенного в контексте сборки.

  • environment: с помощью этого параметра вы можете добавить настройки подключения к Mongo, которые вы определили в файле .env. Обратите внимание, что мы не задаем значение development для NODE_ENV, так как это поведение Express по умолчанию, если значение NODE_ENV не задано. При переходе в продакшен вы можете установить значение production, чтобы активировать кэширование вида и получать более короткие сообщения об ошибках. Также необходимо отметить, что мы указали в качестве хоста контейнер базы данных db, как описано в шаге 2.

  • ports: данный параметр назначает порт 80 хоста для порта 8080 в контейнере.

  • volumes: мы используем два типа монтирования:

    • Первый тип — это связанное монтирование, которое подразумевает монтирование кода приложения на хост в директорию /home/node/app в контейнере. Это упрощает быструю разработку, поскольку любые изменения, которые вы вносите в код хоста, будут немедленно добавлены в контейнер.
    • Второй тип — это том, node_modules. Когда Docker запускает инструкцию npm install, присутствующую в Dockerfile приложения, npm будет создавать новую директорию node_modules в контейнере, которая включает пакеты, необходимые для запуска приложения. Связанное монтирование, которое мы только что создали, будет скрывать эту созданную нами директорию node_modules. Поскольку директория node_modules в хосте пустая, связывание будет назначать пустой каталог для контейнера, переопределяя новую директорию node_modules и не позволяя запускать наше приложение. Том node_modules решает эту проблему, сохраняя содержимое директории /home/node/app/node_modules и монтируя его в контейнер, скрывая связку.

    При использовании этого подхода следует учитывать следующие моменты:

    • Ваша связка будет монтировать содержимое директории node_modules в контейнере на хост, а эта директория будет принадлежать к root, поскольку том с данным названием был создан Docker.
    • Если на хосте уже существует директория node_modules, она будет перезаписана директорией node_modules, созданной в контейнере. Настройка, которую мы создаем в рамках данного руководства, полагает, что у вас нет существующей директории node_modules и вы не будете работать с npm на хосте. Это соответствует подходу к разработке приложений с учетом 12 факторов, который минимизирует зависимости между средами исполнения.
  • networks: данный параметр указывает, что служба приложения будет подключаться к сети app-network, которую мы определим внизу файла.

  • command: данный параметр позволяет вам задавать команду, которая должна быть выполнена при запуске Compose образа. Обратите внимание, что этот параметр переопределяет инструкцию CMD, заданную нами в Dockerfile нашего приложения. Итак, мы запускаем приложение с помощью скрипта wait-for, который будет опрашивать службу db на порте 27017, чтобы проверить, готова ли служба базы данных. После успешной проверки готовности скрипт будет выполнять заданную нами команду /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, остаются прежними, но мы также должны внести следующие изменения в определения image, environment и volumes:

  • image: для создания этой службы Compose будет запрашивать образ 4.1.8-xenial​​​ Mongo из Docker Hub. Мы закрепляем конкретную версию, чтобы избежать возможных будущих конфликтов при изменении образа Mongo. Дополнительную информацию о закреплении версии см. в документации Docker Рекомендации по работе с Dockerfile.
  • MONGO_INITDB_ROOT_USERNAME, MONGO_INITDB_ROOT_PASSWORD: образ mongo делает эти переменные среды доступными, чтобы вы могли изменять порядок инициализации экземпляра вашей базы данных. MONGO_INITDB_ROOT_USERNAME и MONGO_INITDB_ROOT_PASSWORD вместе создают root-пользователя в базе данных аутентификации admin и гарантируют, что аутентификацию будет активирована при запуске контейнера. Мы задали MONGO_INITDB_ROOT_USERNAME и MONGO_INITDB_ROOT_PASSWORD, используя значения из нашего файла .env, который мы передаем службе db, используя параметр env_file. Подобные действия означают, что пользователь нашего приложения sammy будет root-пользователем в экземпляре базы данных с доступом ко всем административным и оперативным правам этой роли. При работе в продакшене вы можете захотеть создать специального пользователя приложения с соответствующим набором привилегий. <$>​​​​​​[note] Примечание: необходимо помнить, что эти переменные не будут применяться, если вы запускаете контейнер, используя существующую директорию данных. <$>
  • 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 создает тома, содержимое тома сохраняется в части файловой системы хоста, /var/lib/docker/volumes/, а данным процессом управляет Docker. Содержимое каждого тома сохраняется в директории /var/lib/docker/volumes/ и монтируются в любой контейнер, который использует том. Таким образом, данные информации об акулах, которые будут добавлять наши пользователи, будут сохраняться в томе dbdata даже при удалении и последующем восстановлении контейнера db.

Итоговый файл 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-compose up. Также вы можете проверить, что ваши данные будут сохраняться, останавливая работу контейнеров и удаляя их с помощью docker-compose down.

Во-первых, необходимо выполнить сборку образов и создать службы, запустив docker-compose с флагом -d, который будет запускать контейнеры nodejs и db в фоновом режиме:

  1. docker-compose up -d

Вы увидите вывод, подтверждающий, что ваши службы были успешно созданы:

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

Также вы можете получить более подробную информацию о процессах запуска, отобразив вывод журнала из служб:

  1. 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:

  1. 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 Shark Info (Получить информацию об акулах). Вы увидите страницу с формой входа, где вы можете ввести имя акулы и описание общего типа этой акулы:

Форма информации об акуле

В этой форме добавьте акулу по вашему выбору. В демонстрационных целях мы добавим Megalodon Shark в поле Shark Name (Имя акулы) и Ancient в поле Shark Character (Тип акулы):

Заполненная форма акулы

Нажмите кнопку Submit (Отправить). Вы увидите страницу с информацией об акуле, отображаемую для вас:

Вывод акулы

В заключение мы можем проверить, что данные, которые вы только что добавили, будут сохраняться, если вы удалите контейнер базы данных.

Вернитесь в терминал, введите следующую команду для остановки и удаления контейнеров и сети:

  1. docker-compose down

Обратите внимание, что мы не включаем параметр --volumes, и поэтому наш том dbdata не удаляется.

Следующая информация подтверждает, что ваши контейнеры и сеть были удалены:

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

Повторно создайте контейнеры:

  1. docker-compose up -d

Затем вернитесь к форме информации об акуле:

Форма информации об акуле

Введите новую акулу по вашему выбору. Мы будем использовать значение Whale Shark и Large:

Ввод новой акулы

После нажатия кнопки **Submit **(Отправить) вы увидите, что новая акула добавлена в коллекцию акул в вашей базе данных без потери ранее введенных данных:

Полное собрание акул

Ваше приложение сейчас запущено в контейнерах Docker с сохранением данных и активацией синхронизации кода.

Заключение

Выполняя указания данного руководства, вы создали настройку разработки для вашего приложения Node с помощью контейнеров Docker. Вы сделали ваш проект более модульным и портативным с помощью извлечения чувствительной информации и отделив состояние вашего приложения от кода приложения. Вы также настроили шаблонный файл docker-compose.yml, который вы можете изменять в зависимости от потребностей разработки и изменений требований.

В процессе разработки вы можете захотеть узнать больше о проектировании приложений для контейнеризованных рабочих процессов и процессов Cloud Native. Дополнительную информацию по этим темам вы можете посмотреть в статьях Разработка архитектуры приложений для Kubernetes и Модернизация приложений для Kubernetes.

Дополнительную информацию о коде, используемом в этом руководстве, см. в статьях Сборка приложения Node.js с помощью Docker и Интеграция MongoDB с вашим приложением Node. Дополнительную информацию о развертывании приложения Node с обратным прокси-сервером Nginx с помощью контейнеров см. в статье Обеспечение безопасности контейнеризованного приложения Node.js с Nginx, Let’s Encrypt и Docker Compose.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
1 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Thank you so much.

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel