Как собрать приложение Node.js с помощью Docker

Вступление

Платформа Docker позволяет разработчикам упаковывать и запускать приложения как containers. Контейнер - это изолированный процесс, который выполняется в общей операционной системе, предлагая более легкую альтернативу виртуальным машинам. Хотя контейнеры не являются новыми, они предлагают преимущества, в том числе изоляцию процессов и стандартизацию среды, которые приобретают все большее значение по мере того, как все больше разработчиков используют распределенные архитектуры приложений.

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

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

Предпосылки

Чтобы следовать этому уроку, вам понадобится:

Шаг 1 - Установка зависимостей вашего приложения

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

Сначала создайте каталог для вашего проекта в домашнем каталоге вашего пользователя без полномочий root. Мы назовем наш ++, но вы можете смело заменять это чем-то другим:

mkdir

Перейдите в этот каталог:

cd

Это будет корневой каталог проекта.

Затем создайте файл https://docs.npmjs.com/files/package.json [+ package.json +] с зависимостями вашего проекта и другой идентифицирующей информацией. Откройте файл с помощью + nano + или вашего любимого редактора:

nano package.json

Добавьте следующую информацию о проекте, включая его имя, автора, лицензию, точку входа и зависимости. Обязательно замените информацию об авторе своим именем и контактными данными:

~ / Node_project / package.json

{
 "name": "",
 "version": "1.0.0",
 "description": "nodejs image demo",
 "author": "",
 "license": "MIT",
 "main": "app.js",
 "keywords": [
   "nodejs",
   "bootstrap",
   "express"
 ],
 "dependencies": {
   "express": "^4.16.4"
 }
}

Этот файл содержит имя проекта, автора и лицензию, по которой он является общим. Npm recommend делает имя вашего проекта коротким и наглядным, избегая дубликатов в реестре npm. Мы перечислили MIT license в поле лицензии, разрешив бесплатное использование и распространение кода приложения.

Дополнительно указан файл:

  • " main ": точка входа для приложения, + app.js. Вы создадите этот файл дальше.

  • " dependencies ": зависимости проекта - в данном случае, Express 4.16.4 или выше.

Хотя этот файл не содержит список репозитория, вы можете добавить его, следуя этим рекомендациям на addory репозиторий в ваш файл + package.json +. Это хорошее дополнение, если вы создаете версию своего приложения.

Сохраните и закройте файл, когда вы закончите вносить изменения.

Чтобы установить зависимости вашего проекта, выполните следующую команду:

npm install

Это установит пакеты, которые вы перечислили в вашем файле + package.json + в каталоге вашего проекта.

Теперь мы можем перейти к созданию файлов приложения.

Шаг 2 - Создание файлов приложения

Мы создадим сайт, который предлагает пользователям информацию об акулах. Наше приложение будет иметь основную точку входа + app.js + и каталог + views +, который будет включать статические ресурсы проекта. Целевая страница + index.html + предложит пользователям некоторую предварительную информацию и ссылку на страницу с более подробной информацией об акулах + sharks.html +. В каталоге + views мы создадим как целевую страницу, так и` + sharks.html`.

Сначала откройте + app.js + в главном каталоге проекта, чтобы определить маршруты проекта:

nano app.js

Первая часть файла создаст приложение Express и объекты Router и определит базовый каталог и порт как константы:

~ / Node_project / app.js

const express = require('express');
const app = express();
const router = express.Router();

const path = __dirname + '/views/';
const port = 8080;

Функция + require загружает модуль` + express + , который мы затем используем для создания объектов + app` и + router &. Объект + router + будет выполнять функцию маршрутизации приложения, и когда мы определим маршруты метода HTTP, мы добавим их в этот объект, чтобы определить, как наше приложение будет обрабатывать запросы.

Этот раздел файла также устанавливает пару констант, + path и` + port`:

  • + path +: Определяет базовый каталог, который будет подкаталогом + views + в текущем каталоге проекта.

  • + port +: говорит приложению прослушивать и связываться с портом + 8080 +.

Затем установите маршруты для приложения, используя объект + router +:

~ / Node_project / app.js

...

router.use(function (req,res,next) {
 console.log('/' + req.method);
 next();
});

router.get('/', function(req,res){
 res.sendFile(path + 'index.html');
});

router.get('/sharks', function(req,res){
 res.sendFile(path + 'sharks.html');
});

Функция + router.use + загружает middleware функцию, которая будет регистрировать запросы маршрутизатора и передавать их на маршруты приложения. Они определены в последующих функциях, которые указывают, что GET-запрос к URL базового проекта должен возвращать страницу + index.html +, а GET-запрос к маршруту + / sharks + должен возвращать + sharks.html + ,

Наконец, подключите промежуточное ПО + router + и статические ресурсы приложения и скажите приложению прослушивать порт + 8080 +:

~ / Node_project / app.js

...

app.use(express.static(path));
app.use('/', router);

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

Готовый файл + app.js будет выглядеть так:

~ / Node_project / app.js

const express = require('express');
const app = express();
const router = express.Router();

const path = __dirname + '/views/';
const port = 8080;

router.use(function (req,res,next) {
 console.log('/' + req.method);
 next();
});

router.get('/', function(req,res){
 res.sendFile(path + 'index.html');
});

router.get('/sharks', function(req,res){
 res.sendFile(path + 'sharks.html');
});

app.use(express.static(path));
app.use('/', router);

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

Сохраните и закройте файл, когда вы закончите.

Далее, давайте добавим немного статического контента в приложение. Начните с создания каталога + views +:

mkdir views

Откройте файл целевой страницы + index.html:

nano views/index.html

Добавьте следующий код в файл, который будет импортировать Boostrap и создать компонент jumbotron со ссылкой на более подробную + sharks.html + информационную страницу :

~ / Node_project / просмотров / index.html

<!DOCTYPE html>
<html lang="en">

<head>
   <title>About Sharks</title>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
   <link href="css/styles.css" rel="stylesheet">
   <link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
</head>

<body>
   <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
       <div class="container">
           <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
           </button> <a class="navbar-brand" href="#">Everything Sharks</a>
           <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
               <ul class="nav navbar-nav mr-auto">
                   <li class="active nav-item"><a href="/" class="nav-link">Home</a>
                   </li>
                   <li class="nav-item"><a href="/sharks" class="nav-link">Sharks</a>
                   </li>
               </ul>
           </div>
       </div>
   </nav>
   <div class="jumbotron">
       <div class="container">
           <h1>Want to Learn About Sharks?</h1>
           <p>Are you ready to learn about sharks?</p>
           <br>
           <p><a class="btn btn-primary btn-lg" href="/sharks" role="button">Get Shark Info</a>
           </p>
       </div>
   </div>
   <div class="container">
       <div class="row">
           <div class="col-lg-6">
               <h3>Not all sharks are alike</h3>
               <p>Though some are dangerous, sharks generally do not attack humans. Out of the 500 species known to researchers, only 30 have been known to attack humans.
               </p>
           </div>
           <div class="col-lg-6">
               <h3>Sharks are ancient</h3>
               <p>There is evidence to suggest that sharks lived up to 400 million years ago.
               </p>
           </div>
       </div>
   </div>
</body>

</html>

Https://getbootstrap.com/docs/4.0/components/navbar/[navbar] верхнего уровня позволяет пользователям переключаться между страницами * Home * и * Sharks *. В подкомпоненте + navbar-nav мы используем Bootstrap` + active` для указания текущей страницы пользователю. Мы также указали маршруты к нашим статическим страницам, которые соответствуют маршрутам, которые мы определили в + app.js +:

~ / Node_project / просмотров / index.html

...
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
  <ul class="nav navbar-nav mr-auto">
     <li class="active nav-item"><a href="/" class="nav-link">Home</a>
     </li>
     <li class="nav-item"><a href="/sharks" class="nav-link">Sharks</a>
     </li>
  </ul>
</div>
...

Кроме того, мы создали ссылку на нашу страницу с информацией об акулах в кнопке нашего jumbotron:

~ / Node_project / просмотров / index.html

...
<div class="jumbotron">
  <div class="container">
     <h1>Want to Learn About Sharks?</h1>
     <p>Are you ready to learn about sharks?</p>
     <br>
     <p><a class="btn btn-primary btn-lg" href="/sharks" role="button">Get Shark Info</a>
     </p>
  </div>
</div>
...

В шапке также есть ссылка на пользовательскую таблицу стилей:

~ / Node_project / просмотров / index.html

...
<link href="css/styles.css" rel="stylesheet">
...

Мы создадим эту таблицу стилей в конце этого шага.

Сохраните и закройте файл, когда вы закончите.

Имея целевую страницу приложения, мы можем создать нашу информационную страницу об акулах + sharks.html +, которая будет предлагать заинтересованным пользователям больше информации об акулах.

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

nano views/sharks.html

Добавьте следующий код, который импортирует Bootstrap и пользовательскую таблицу стилей и предлагает пользователям подробную информацию об определенных акулах:

~ / Node_project / просмотров / sharks.html

<!DOCTYPE html>
<html lang="en">

<head>
   <title>About Sharks</title>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
   <link href="css/styles.css" rel="stylesheet">
   <link href="https://fonts.googleapis.com/css?family=Merriweather:400,700" rel="stylesheet" type="text/css">
</head>
<nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md">
   <div class="container">
       <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">Toggle navigation</span>
       </button> <a class="navbar-brand" href="/">Everything Sharks</a>
       <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
           <ul class="nav navbar-nav mr-auto">
               <li class="nav-item"><a href="/" class="nav-link">Home</a>
               </li>
               <li class="active nav-item"><a href="/sharks" class="nav-link">Sharks</a>
               </li>
           </ul>
       </div>
   </div>
</nav>
<div class="jumbotron text-center">
   <h1>Shark Info</h1>
</div>
<div class="container">
   <div class="row">
       <div class="col-lg-6">
           <p>
               <div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
               </div>
               <img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
           </p>
       </div>
       <div class="col-lg-6">
           <p>
               <div class="caption">Other sharks are known to be friendly and welcoming!</div>
               <img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
           </p>
       </div>
   </div>
</div>

</html>

Обратите внимание, что в этом файле мы снова используем класс + active + для обозначения текущей страницы.

Сохраните и закройте файл, когда вы закончите.

Наконец, создайте пользовательскую таблицу стилей CSS, с которой вы связались в + index.html + и + sharks.html +, сначала создав папку + css + в каталоге + views +:

mkdir views/css

Откройте таблицу стилей:

nano views/css/styles.css

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

~ / Node_project / просмотров / CSS / styles.css

.navbar {
   margin-bottom: 0;
}

body {
   background: #020A1B;
   color: #ffffff;
   font-family: 'Merriweather', sans-serif;
}

h1,
h2 {
   font-weight: bold;
}

p {
   font-size: 16px;
   color: #ffffff;
}

.jumbotron {
   background: #0048CD;
   color: white;
   text-align: center;
}

.jumbotron p {
   color: white;
   font-size: 26px;
}

.btn-primary {
   color: #fff;
   text-color: #000000;
   border-color: white;
   margin-bottom: 5px;
}

img,
video,
audio {
   margin-top: 20px;
   max-width: 80%;
}

div.caption: {
   float: left;
   clear: both;
}

В дополнение к настройке шрифта и цвета этот файл также ограничивает размер изображений, указав + max-width + в 80%. Это помешает им занять больше места, чем нам хотелось бы на странице.

Сохраните и закройте файл, когда вы закончите.

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

Если вы выполнили предварительное руководство по настройке сервера в предварительных условиях, у вас будет активный брандмауэр, разрешающий только трафик SSH. Для разрешения трафика на порт + 8080 + выполните:

sudo ufw allow 8080

Чтобы запустить приложение, убедитесь, что вы находитесь в корневом каталоге вашего проекта:

cd ~/

Запустите приложение с помощью + node app.js:

node app.js

Перейдите в браузере по адресу + http: //: 8080 +. Вы увидите следующую целевую страницу:

изображение: https: //assets.digitalocean.com/articles/docker_node_image/landing_page.png [целевая страница приложения]

Нажмите на кнопку * Получить информацию об акуле *. Вы увидите следующую информационную страницу:

изображение: https: //assets.digitalocean.com/articles/docker_node_image/sharks.png [Страница информации об акулах]

Теперь у вас есть приложение и работает. Когда вы будете готовы, выйдите из сервера, набрав + CTRL + C +. Теперь мы можем перейти к созданию Dockerfile, который позволит нам заново создать и масштабировать это приложение по желанию.

Шаг 3 - Написание Dockerfile

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

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

В корневом каталоге вашего проекта создайте Dockerfile:

nano Dockerfile

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

Давайте использовать https://hub.docker.com//node/ [+ узел: + изображение], поскольку на момент написания этой статьи это recom рекомендуется LTS версия Node.js. Изображение + alpine + получено из проекта Alpine Linux и поможет нам уменьшить размер изображения. Для получения дополнительной информации о том, является ли изображение + alpine + правильным выбором для вашего проекта, см. Полное обсуждение в разделе * Варианты изображения * на https://hub.docker.com//node/ [ Страница изображения узла докера-концентратора.

Добавьте следующую инструкцию + FROM +, чтобы установить базовый образ приложения:

~ / Node_project / Dockerfile

FROM node:

Это изображение включает в себя Node.js и npm. Каждый Dockerfile должен начинаться с инструкции + FROM +.

По умолчанию образ Docker Node включает пользователя без полномочий root * node *, которого вы можете использовать, чтобы избежать запуска контейнера приложения с именем * root *. Рекомендуется избегать запуска контейнеров с правами * root * и указывать restrict в контейнере только те, которые требуются для запустить свои процессы. Поэтому мы будем использовать домашний каталог пользователя * node * в качестве рабочего каталога для нашего приложения и назначать его в качестве нашего пользователя внутри контейнера. Дополнительную информацию о передовых практиках при работе с изображением Docker Node см. В этом best практика руководство.

Чтобы точно настроить разрешения для кода нашего приложения в контейнере, давайте создадим подкаталог + node_modules + в + / home / node + вместе с каталогом + app +. Создание этих каталогов гарантирует, что у них есть необходимые разрешения, что будет важно, когда мы создаем модули локальных узлов в контейнере с помощью + npm install +. В дополнение к созданию этих каталогов мы установим право собственности на них для нашего пользователя * node *:

~ / Node_project / Dockerfile

...
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app

Для получения дополнительной информации о полезности консолидации инструкций + RUN + см. Этот https://www.digitalocean.com/community/tutorials/building-optimized-containers-for-kubernetes#managing-container-layers[discussion о том, как управлять слоями контейнера.

Затем установите рабочий каталог приложения в + / home / node / app:

~ / Node_project / Dockerfile

...
WORKDIR /home/node/app

Если + WORKDIR + не установлен, Docker создаст его по умолчанию, поэтому рекомендуется установить его явно.

Затем скопируйте файлы + package.json и` + package-lock.json` (для npm 5+):

~ / Node_project / Dockerfile

...
COPY package*.json ./

Добавление этой инструкции + COPY + перед запуском + npm install + или копирование кода приложения позволяет нам использовать механизм кэширования Docker. На каждом этапе сборки Docker будет проверять наличие кэшированного слоя для этой конкретной инструкции. Если мы изменим + package.json +, этот слой будет перестроен, но если мы этого не сделаем, эта инструкция позволит Docker использовать существующий слой изображения и пропустить переустановку модулей нашего узла.

Чтобы убедиться, что все файлы приложения принадлежат пользователю без полномочий root * node *, включая содержимое каталога + node_modules +, переключите пользователя на * node * перед запуском + npm install +:

~ / Node_project / Dockerfile

...
USER node

После копирования зависимостей проекта и переключения нашего пользователя, мы можем запустить + npm install:

~ / Node_project / Dockerfile

...
RUN npm install

Затем скопируйте код приложения с соответствующими разрешениями в каталог приложения на контейнере:

~ / Node_project / Dockerfile

...
COPY --chown=node:node . .

Это обеспечит принадлежность файлов приложения пользователю без полномочий root * node *.

Наконец, выставьте порт + 8080 + на контейнер и запустите приложение:

~ / Node_project / Dockerfile

...
EXPOSE 8080

CMD [ "node", "app.js" ]

+ EXPOSE + не публикует порт, а вместо этого служит способом документирования того, какие порты в контейнере будут опубликованы во время выполнения. + CMD + запускает команду для запуска приложения - в этом случае +node app.js + ` , Обратите внимание, что в каждом файле Docker должна быть только одна инструкция `+ CMD +

Related