Tutorial
Контейнеризация приложения Node.js для разработки с использованием Docker Compose
Введение
Если вы активно занимаетесь разработкой приложений, использование Docker может упростить ваш рабочий процесс и процесс развертывания вашего приложения в продакшен. Работа с контейнерами в процессе разработки предоставляет следующие преимущества:
- Среды окружения остаются согласованными, что означает, что вы можете выбирать языки и зависимости, которые вам нужны для вашего проекта, не беспокоясь о системных конфликтах.
- Среды окружения изолированы, что упрощает устранение проблем и включение в работу новых участников групп.
- Среды окружения портативны, что позволяет вам упаковывать свой код и делиться им с другими.
Из этого руководства вы узнаете, как выполнять настройку среды разработки для приложения Node.js с помощью Docker. Вы создадите два контейнера, один для приложения Node, а другой для базы данных MongoDB с помощью Docker Compose. Поскольку это приложение работает с Node и MongoDB, необходимо выполнить следующие настройки:
- Синхронизируйте код приложения на хосте с кодом в контейнере, чтобы облегчить внесение изменений во время разработки.
- Убедитесь, что изменения, вносимые в код приложения, используются без необходимости перезапуска.
- Создайте пользователя и базу данных с защитой паролем для хранения данных приложения.
- Сохраните данные.
После изучения данного руководства у вас будет рабочее приложение с информацией об акулах, работающее на контейнерах Docker:
Предварительные требования
Для данного обучающего модуля вам потребуется следующее:
- Сервер разработки на базе Ubuntu 18.04, а также пользователь без прав root с привилегиями
sudo
и активный брандмауэр. Дополнительную информацию о настройке этих параметров см. в руководстве по первоначальной настройке сервера. - Система Docker, установленная на сервере в соответствии с шагами 1 и 2 руководства Установка и использование Docker в Ubuntu 18.04.
- Docker Compose, установленный на сервере, в соответствии с шагом 1 руководства Установка Docker Compose в Ubuntu 18.04.
Шаг 1 — Клонирование проекта и изменение зависимостей
Первым, что необходимо сделать при настройке системы, является клонирование кода проекта и изменение файла package.json
, который включает зависимости проекта. Мы добавим nodemon
в devDependencies
проекта, чтобы указать, что мы будем использовать его при разработке. Запуск приложения с nodemon
обеспечивает автоматический перезапуск при внесении изменений в ваш код.
Во-первых, клонируйте репозиторий nodejs-mongo-mongoose
из учетной записи DigitalOcean Community на GitHub. Этот репозиторий включает код настройки, описанной в руководстве Интеграция MongoDB с вашим приложением Node, которое содержит описание процесса интеграции базы данных MongoDB с существующим приложением Node с помощью Mongoose.
Клонируйте репозиторий в директорию с именем node_project
:
- git clone https://github.com/do-community/nodejs-mongo-mongoose.git node_project
Перейдите в директорию node_project
:
- cd node_project
Откройте файл проекта package.json
с помощью nano
или вашего любимого редактора:
- nano package.json
После зависимостей проекта и над закрывающей фигурной скобкой создайте новый объект devDependencies
, который включает nodemon
:
...
"dependencies": {
"ejs": "^2.6.1",
"express": "^4.16.4",
"mongoose": "^5.4.10"
},
"devDependencies": {
"nodemon": "^1.18.10"
}
}
Сохраните и закройте файл после завершения редактирования.
После добавления кода проекта и изменения зависимостей вы можете перейти к рефакторингу кода для контейнеризованного рабочего процесса.
Шаг 2 — Настройка вашего приложения для работы с контейнерами
Изменение нашего приложения для контейнеризованного рабочего процесса подразумевает, что код должен быть более модульным. Контейнеры обеспечивают возможность перемещения между рабочими средами, и наш код должен отражать это, оставаясь как можно менее привязанным к лежащей в основе операционной системе. Для этого мы выполним рефакторинг нашего кода, чтобы добиться более эффективного использования свойства Node process.env, которое возвращает объект с информацией о среде пользователя при запуске. Мы можем использовать этот объект в нашем коде для динамического сохранения информации о конфигурации при запуске в переменных среды.
Давайте начнем с app.js
, главной точки входа нашего приложения. Откройте файл:
- nano app.js
Внутри файла вы увидите определение для константы port
, а также функцию listen
, которая использует эту константу для указания порта, который будет прослушивать приложение:
...
const port = 8080;
...
app.listen(port, function () {
console.log('Example app listening on port 8080!');
});
Давайте изменим константу port
, чтобы разрешить динамическое присвоение при запуске, с помощью объекта process.env
. Внесите следующие изменения в определение константы и функцию listen
:
...
const port = process.env.PORT || 8080;
...
app.listen(port, function () {
console.log(`Example app listening on ${port}!`);
});
Наше новое определение константы присваивает port
динамически с помощью значения, передаваемого при запуске, или 8080
. Аналогично мы переписали функцию listen
для использования шаблонного литерала, который будет интерполировать значение порта при прослушивании соединений. Поскольку мы будем выполнять маппинг портов где-либо еще, эти изменения позволят нам избежать необходимости постоянно пересматривать этот файл при внесении изменений в нашу среду.
После завершения редактирования сохраните и закройте файл.
Затем нам нужно изменить информацию о подключении базы данных, чтобы удалить любые учетные данные конфигурации. Откройте файл db.js
, содержащий следующую информацию:
- nano db.js
В настоящее время файл выполняет следующие функции:
- Импортирует Mongoose, Object Document Mapper (ODM), который мы используем для создания схем и моделей данных нашего приложения.
- Устанавливает учетные данные базы данных в качестве констант, включая имя пользователя и пароль.
- Подключается к базе данных с помощью метода
mongoose.connect
.
Дополнительную информацию о файле см. в шаге 3 руководства Интеграция MongoDB с вашим приложением Node.
Наш первый шаг по изменению файла будет состоять в переопределении констант, содержащих чувствительную информацию. В настоящее время эти константы выглядят следующим образом:
...
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
для захвата значений этих констант в момент запуска. Измените блок следующим образом:
...
const {
MONGO_USERNAME,
MONGO_PASSWORD,
MONGO_HOSTNAME,
MONGO_PORT,
MONGO_DB
} = process.env;
...
Сохраните и закройте файл после завершения редактирования.
На данный момент у вас есть измененный файл db.js
для работы с переменными среды вашего приложения, но вам нужно передавать эти переменные в ваше приложение. Давайте создадим файл .env
со значениями, которые вы можете передать вашему приложению при запуске.
Откройте файл:
- nano .env
Этот файл будет содержать информацию, которую вы удалили из db.js
: имя пользователя и пароль для базы данных вашего приложения, а также настройки порта и имя базы данных. Обязательно измените имя пользователя, пароль и имя базы данных, перечисленные здесь, на собственные:
MONGO_USERNAME=sammy
MONGO_PASSWORD=your_password
MONGO_PORT=27017
MONGO_DB=sharkinfo
Обратите внимание, что мы удалили настройки хоста, которые первоначально присутствовали в db.js
. Теперь мы определим наш хост на уровне файла Docker Compose вместе с другой информацией о наших сервисах и контейнерах.
Сохраните и закройте файл после завершения редактирования.
Поскольку ваш файл .env
содержит важную информацию, вы должны быть уверены, что она содержится в файлах .dockerignore
и .gitignore
вашего проекта и не копируется в систему контроля версий или контейнеры.
Откройте ваш файл .dockerignore
:
- nano .dockerignore
Добавьте следующую строку внизу файла:
...
.gitignore
.env
Сохраните и закройте файл после завершения редактирования.
Файл .gitignore
в этом репозитории уже включает .env
, но вы можете убедиться в его наличии:
- nano .gitignore
...
.env
...
На данный момент вы успешно извлекли чувствительную информацию из кода проекта и приняли меры для контроля того, как и куда будет скопирована эта информация. Теперь вы можете добавить дополнительный уровень надежности для кода подключения к базе данных, чтобы оптимизировать его для контейнеризованного рабочего процесса.
Шаг 3 — Изменение настроек подключения к базе данных
Следующим нашим шагом будет повышение надежности работы метода подключения к базе данных с помощью кода, который обрабатывает случаи, когда нашему приложению не удается подключиться к нашей базе данных. Введение данного уровня устойчивости в код приложения является рекомендуемой практикой при работе с контейнерами с помощью Compose.
Откройте db.js
для редактирования:
- nano db.js
Вы увидите добавленный нами ранее код, а также константу url
для URI подключения к Mongo и метод подключения к Mongoose
:
...
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
:
...
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 выглядит следующим образом:
...
mongoose.connect(url, {useNewUrlParser: true});
Удалите существующий метод connect
и замените его следующим кодом, который включает константу options
и promise:
...
mongoose.connect(url, options).then( function() {
console.log('MongoDB is connected');
})
.catch( function(err) {
console.log(err);
});
В случае успешного подключения наша функция сохраняет соответствующее сообщение, в ином случае она перехватывает
и сохраняет ошибку, позволяя нам устранять ошибки.
Итоговый файл будет выглядеть примерно так:
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
:
- nano 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
, добавив в файл следующий код:
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
, добавив следующий код под определением службы приложения:
...
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
.
В качестве завершающего шага добавьте определения тома и сети в конец файла:
...
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
будет выглядеть примерно так:
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
в фоновом режиме:
- 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 Shark Info (Получить информацию об акулах). Вы увидите страницу с формой входа, где вы можете ввести имя акулы и описание общего типа этой акулы:
В этой форме добавьте акулу по вашему выбору. В демонстрационных целях мы добавим Megalodon Shark
в поле Shark Name (Имя акулы) и Ancient
в поле Shark Character (Тип акулы):
Нажмите кнопку Submit (Отправить). Вы увидите страницу с информацией об акуле, отображаемую для вас:
В заключение мы можем проверить, что данные, которые вы только что добавили, будут сохраняться, если вы удалите контейнер базы данных.
Вернитесь в терминал, введите следующую команду для остановки и удаления контейнеров и сети:
- 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 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.