Как собрать и развернуть приложение Node.js в DigitalOcean Kubernetes, используя непрерывную интеграцию и доставку семафоров

_Автор выбрал Open Internet / Free Speech fund для получения пожертвования в рамках https://do.co/w4do-cta [Писать для DOnations] программа.

Вступление

https://kubernetes.io [Kubernetes] позволяет пользователям создавать отказоустойчивые и масштабируемые сервисы с помощью одной команды. Как и все, что звучит слишком хорошо, чтобы быть правдой, оно имеет подвох: вы должны сначала подготовить подходящее изображение https://www.docker.com [Docker] и тщательно протестировать его.

Continuous Integration (CI) - это практика тестирования приложения при каждом обновлении. Выполнение этого вручную утомительно и подвержено ошибкам, но платформа CI запускает тесты для вас, рано обнаруживает ошибки и находит точку, в которой были введены ошибки. Процедуры выпуска и развертывания часто сложны, занимают много времени и требуют надежной среды сборки. С помощью Continuous Delivery (CD) вы можете создавать и развертывать свое приложение в каждом обновлении без вмешательства человека. ,

Для автоматизации всего процесса вы будете использовать https://semaphoreci.com [Семафор], платформу непрерывной интеграции и доставки (CI / CD).

В этом руководстве вы создадите службу API адресной книги с помощью https://nodejs.org [Node.js]. API предоставляет простой интерфейс RESTful API для создания, удаления и поиска людей в базе данных. Вы будете использовать https://git-scm.com [Git], чтобы отправить код на https://github.com [GitHub]. Затем вы будете использовать Semaphore для тестирования приложения, создания образа Docker и развертывания его в кластере DigitalOcean Kubernetes. Для базы данных вы создадите кластер PostgreSQL, используя DigitalOcean Managed Databases.

Предпосылки

Прежде чем читать, убедитесь, что у вас есть следующее:

  • Учетная запись DigitalOcean и токен личного доступа. Следуйте Create личный токен доступа, чтобы настроить его для своей учетной записи.

  • Счет Docker Hub.

  • Учетная запись https://github.com [GitHub].

  • Учетная запись https://semaphoreci.com [Семафор]; Вы можете зарегистрироваться с помощью своей учетной записи GitHub.

  • Новый репозиторий GitHub для проекта называется + addressbook +. При создании хранилища установите флажок * Initialize the repository README * и выберите * Node * в меню * Add .gitignore *. Следуйте Create a Repo справочной странице для получения более подробной информации.

  • https://git-scm.com [Git] установлен на вашем локальном компьютере и set up для работы с вашей учетной записью GitHub. Если вы незнакомы или нуждаетесь в переподготовке, почитайте справочное руководство How to Git.

  • curl установлен на вашем локальном компьютере.

  • https://nodejs.org [Node.js] установлен на вашем локальном компьютере. В этом руководстве вы будете использовать Node.js версии + 10.16.0 +.

Шаг 1 - Создание базы данных и кластера Kubernetes

Начните с предоставления услуг, которые приведут в действие приложение: кластер баз данных DigitalOcean и кластер DigitalOcean Kubernetes.

Войдите в свою учетную запись DigitalOcean и create проект. Проект позволяет вам организовать все ресурсы, из которых состоит приложение. Назовите проект + адресная книга +.

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

Как только служба PostgreSQL будет готова, create база данных и пользователь. Установите имя базы данных в + addessbook_db + и установите имя пользователя в + addressbook_user +. Запомните пароль, сгенерированный для вашего нового пользователя. Базы данных - это способ организации данных в PostgreSQL. Обычно каждое приложение имеет свою собственную базу данных, хотя жестких правил по этому поводу нет. Приложение будет использовать имя пользователя и пароль, чтобы получить доступ к базе данных, чтобы оно могло сохранять и извлекать свои данные.

Наконец, создайте кластер Kubernetes. Выберите тот же регион, в котором работает база данных. Назовите кластер + адресная книга-сервер и установите количество узлов в` + 3 + `.

Пока узлы инициализируются, вы можете приступить к созданию приложения.

Шаг 2 - Написание приложения

Давайте создадим приложение адресной книги, которое вы собираетесь развернуть. Для начала клонируйте репозиторий GitHub, который вы создали в предварительных условиях, чтобы у вас была локальная копия файла GitHub + .gitignore +, созданного для вас, и вы сможете быстро зафиксировать код приложения без необходимости вручную создавать репозиторий. , Откройте браузер и перейдите в новый репозиторий GitHub. Нажмите кнопку * Клонировать или загрузить * и скопируйте предоставленный URL-адрес. Используйте Git для клонирования пустого репозитория на ваш компьютер:

git clone https://github.com//addressbook

Введите каталог проекта:

cd addressbook

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

Хотя это и не является строго обязательным, рекомендуется проверять код во время его написания, поэтому вы также создадите модуль тестирования. Это запланированный макет для приложения:

  • + database.js +: модуль базы данных. Он обрабатывает операции с базой данных.

  • + app.js +: модуль конечного пользователя и основное приложение. Он предоставляет HTTP-сервис пользователям для подключения.

  • + database.test.js +: тесты для модуля базы данных.

Кроме того, для вашего проекта вам понадобится файл package.json, в котором будет описан проект и его необходимые зависимости. Вы можете создать его вручную в редакторе или в интерактивном режиме, используя npm. Запустите команду + npm init + для интерактивного создания файла:

npm init

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

npm outputpackage name: (addressbook)
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
keywords:
author: "
license: (ISC)
About to write to package.json:

{
 "name": "addressbook",
 "version": "1.0.0",
 "description": "Addressbook API and database",
 "main": "app.js",
 "scripts": {
   "test": "echo \"Error: no test specified\" && exit 1"
 },
 "author": "",
 "license": "ISC"
}


Is this OK? (yes)

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

Вам не нужно кодировать все биты приложения; Node.js имеет большую библиотеку повторно используемых модулей. Например, вам не нужно писать какие-либо SQL-запросы, если у вас есть модуль Sequelize ORM в проекте. Этот модуль предоставляет интерфейс, который обрабатывает базы данных как объекты и методы JavaScript. Он также может создавать таблицы в вашей базе данных. Sequelize необходим модуль pg для работы с PostgreSQL.

Установите модули с помощью команды + npm install с опцией` + - save + , которая говорит + npm + сохранить модуль в + package.json`. Выполните эту команду, чтобы установить + sequelize + и + pg +:

npm install --save sequelize pg

Создайте новый файл JavaScript для хранения кода базы данных:

nano database.js

Импортируйте модуль + sequelize +, добавив эту строку в файл:

database.js

const Sequelize = require('sequelize');

. . .

Затем под этой строкой инициализируйте объект + sequelize + с параметрами соединения с базой данных, которые вы получите из системной среды. Благодаря этому учетные данные не попадают в ваш код, поэтому вы не можете случайно поделиться своими учетными данными при отправке кода в GitHub. Вы можете использовать + process.env + для доступа к переменным среды и оператор JavaScripts + || + для установки значений по умолчанию для неопределенных переменных:

database.js

. . .

const sequelize = new Sequelize(process.env.DB_SCHEMA || 'postgres',
                               process.env.DB_USER || 'postgres',
                               process.env.DB_PASSWORD || '',
                               {
                                   host: process.env.DB_HOST || 'localhost',
                                   port: process.env.DB_PORT || 5432,
                                   dialect: 'postgres',
                                   dialectOptions: {
                                       ssl: process.env.DB_SSL == "true"
                                   }
                               });

. . .

Теперь определите модель + Person +. Чтобы пример не стал слишком сложным, вы создадите только два поля: + firstName + и + lastName +, оба из которых хранят строковые значения. Добавьте следующий код для определения модели:

database.js

. . .

const Person = sequelize.define('Person', {
   firstName: {
       type: Sequelize.STRING,
       allowNull: false
   },
   lastName: {
       type: Sequelize.STRING,
       allowNull: true
   },
});

. . .

Это определяет два поля, делая + firstName + обязательным с + allowNull: false +. В документации modelручной документации Sequelize показаны доступные типы данных и параметры.

Наконец, экспортируйте объект + sequelize + и модель + Person +, чтобы другие модули могли использовать их:

database.js

. . .

module.exports = {
   sequelize: sequelize,
   Person: Person
};

Удобно иметь сценарий создания таблицы в отдельном файле, который можно вызывать в любое время в процессе разработки. Эти типы файлов называются migrations. Создайте новый файл для хранения этого кода:

nano migrate.js

Добавьте эти строки в файл, чтобы импортировать определенную вами модель базы данных, и вызовите функцию + sync () +, чтобы инициализировать базу данных, которая создаст таблицу для вашей модели:

migrate.js

var db = require('./database.js');
db.sequelize.sync();

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

nano .env

Добавьте следующие объявления переменных в файл. Убедитесь, что вы установили + DB_HOST +, + DB_PORT + и + DB_PASSWORD + для тех, которые связаны с вашим кластером DigitalOcean PostgreSQL:

env
export DB_SCHEMA=
export DB_USER=
export DB_PASSWORD=
export DB_HOST=
export DB_PORT=
export DB_SSL=true
export PORT=3000

Сохраните файл.

Вы готовы инициализировать базу данных. Импортируйте файл среды и запустите + migrate.js:

source ./.env
node migrate.js

Это создает таблицу базы данных:

Output
Executing (default): CREATE TABLE IF NOT EXISTS "People" ("id"   SERIAL , "firstName" VARCHAR(255) NOT NULL, "lastName" VARCHAR(255), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'People' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;

Выходные данные показывают две команды. Первый создает таблицу + People + согласно вашему определению. Вторая команда проверяет, что таблица действительно была создана, просматривая ее в каталоге PostgreSQL.

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

Среда тестирования jest отлично подходит для написания тестов для приложений Node.js. Jest сканирует файлы в проекте на наличие тестовых файлов и выполняет их один раз. Установите Jest с опцией + - save-dev +, которая сообщает + npm +, что модуль не требуется для запуска программы, но это зависимость для разработки приложения:

npm install --save-dev jest

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

Создайте файл + database.test.js +:

nano database.test.js

Добавьте следующий контент. Начните с импорта кода базы данных:

database.test.js

const db = require('./database');

. . .

Чтобы убедиться, что база данных готова к использованию, вызовите + sync () + внутри функции + beforeAll +:

database.test.js

. . .

beforeAll(async () => {
   await db.sequelize.sync();
});

. . .

Первый тест создает запись о человеке в базе данных. Библиотека + sequelize + выполняет все запросы асинхронно, что означает, что она не ожидает результатов запроса. Чтобы тест ждал результатов, чтобы вы могли их проверить, вы должны использовать ключевые слова + async + и + await +. Этот тест вызывает метод + create () + для вставки новой строки в базу данных. Используйте + ожидаем + для сравнения столбца + person.id + с + 1 +. Тест не пройдёт, если вы получите другое значение:

database.test.js

. . .

test('create person', async () => {
   expect.assertions(1);
   const person = await db.Person.create({
       id: 1,
       firstName: 'Sammy',
       lastName: 'Davis Jr.',
       email: '[email protected]'
   });
   expect(person.id).toEqual(1);
});

. . .

В следующем тесте используйте метод + findByPk () +, чтобы получить строку с + id = 1 +. Затем проверьте значения + firstName + и + lastName +. Еще раз, используйте + async + и + await +:

database.test.js

. . .

test('get person', async () => {
   expect.assertions(2);
   const person = await db.Person.findByPk(1);
   expect(person.firstName).toEqual('Sammy');
   expect(person.lastName).toEqual('Davis Jr.');
});

. . .

Наконец, проверьте удаление человека из базы данных. Метод + destroy () + удаляет человека с + id = 1 +. Чтобы убедиться, что это сработало, попробуйте получить человека во второй раз и проверить, что возвращаемое значение равно + null +:

database.test.js

. . .

test('delete person', async () => {
   expect.assertions(1);
   await db.Person.destroy({
       where: {
           id: 1
       }
   });
   const person = await db.Person.findByPk(1);
   expect(person).toBeNull();
});

. . .

Наконец, добавьте этот код, чтобы закрыть соединение с базой данных с помощью + close () + после завершения всех тестов:

app.js

. . .

afterAll(async () => {
   await db.sequelize.close();
});

Сохраните файл.

Команда + jest + запускает набор тестов для вашей программы, но вы также можете сохранять команды в + package.json +. Откройте этот файл в вашем редакторе:

nano package.json

Найдите ключевое слово + scripts + и замените существующую строку + test + (которая была просто заполнителем). Тестовая команда + jest +:

. . .

 "scripts": {
   "test": "jest"
 },

. . .

Теперь вы можете вызвать + npm run test, чтобы вызвать набор тестов. Это может быть более длинная команда, но если вам потребуется изменить команду + jest + позже, внешние службы не должны будут меняться; они могут продолжать вызывать + npm run test +.

Запустите тесты:

npm run test

Затем проверьте результаты:

Output  console.log node_modules/sequelize/lib/sequelize.js:1176
   Executing (default): CREATE TABLE IF NOT EXISTS "People" ("id"   SERIAL , "firstName" VARCHAR(255) NOT NULL, "lastName" VARCHAR(255), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id"));

 console.log node_modules/sequelize/lib/sequelize.js:1176
   Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'People' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;

 console.log node_modules/sequelize/lib/sequelize.js:1176
   Executing (default): INSERT INTO "People" ("id","firstName","lastName","createdAt","updatedAt") VALUES ($1,$2,$3,$4,$5) RETURNING *;

 console.log node_modules/sequelize/lib/sequelize.js:1176
   Executing (default): SELECT "id", "firstName", "lastName", "createdAt", "updatedAt" FROM "People" AS "Person" WHERE "Person"."id" = 1;

 console.log node_modules/sequelize/lib/sequelize.js:1176
   Executing (default): DELETE FROM "People" WHERE "id" = 1

 console.log node_modules/sequelize/lib/sequelize.js:1176
   Executing (default): SELECT "id", "firstName", "lastName", "createdAt", "updatedAt" FROM "People" AS "Person" WHERE "Person"."id" = 1;

PASS  ./database.test.js
 ✓ create person (344ms)
 ✓ get person (173ms)
 ✓ delete person (323ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        5.315s
Ran all test suites.

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

Для обслуживания HTTP-запросов вы будете использовать веб-платформу https://expressjs.com [Express]. Установите Express и сохраните его как зависимость, используя + npm install:

npm install --save express

Вам также потребуется модуль https://www.npmjs.com/package/body-parser [+ body-parser +], который вы будете использовать для доступа к телу HTTP-запроса. Установите это как зависимость также:

npm install --save body-parser

Создайте основной файл приложения + app.js:

nano app.js

Импортируйте модули + express,` + body-parser` и + в. Затем создайте экземпляр модуля + express + под названием + app + для управления и настройки службы. Вы используете + app.use () + для добавления таких функций, как промежуточное ПО. Используйте это, чтобы добавить модуль + body-parser +, чтобы приложение могло читать строки url-encoded:

app.js

var express = require('express');
var bodyParser = require('body-parser');
var db = require('./database');
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));

. . .

Далее добавьте маршруты в приложение. Маршруты похожи на кнопки в приложении или на сайте; они запускают некоторые действия в вашем приложении. Маршруты связывают уникальные URL-адреса с действиями в приложении. Каждый маршрут будет обслуживать определенный путь и поддерживать разные операции.

Первый маршрут, который вы определите, обрабатывает запросы + GET + для пути + / person / $ ID +, который будет отображать запись базы данных для человека с указанным идентификатором. Express автоматически устанавливает значение запрошенного + $ ID + в переменной + req.params.id +.

Приложение должно ответить с данными человека, закодированными в виде строки JSON. Как и в тестах базы данных, используйте метод + findByPk () +, чтобы получить человека по идентификатору и ответить на запрос со статусом HTTP + 200 + (ОК) и отправить запись человека в виде JSON. Добавьте следующий код:

app.js

. . .

app.get("/person/:id", function(req, res) {
   db.Person.findByPk(req.params.id)
       .then( person => {
           res.status(200).send(JSON.stringify(person));
       })
       .catch( err => {
           res.status(500).send(JSON.stringify(err));
       });
});

. . .

Ошибки приводят к выполнению кода в + catch () +. Например, если база данных не работает, соединение не будет установлено, и вместо этого будет выполнено. В случае возникновения проблем установите статус HTTP на «+ 500 +» (Внутренняя ошибка сервера) и отправьте сообщение об ошибке обратно пользователю:

Добавьте другой маршрут, чтобы создать человека в базе данных. Этот маршрут будет обрабатывать запросы + PUT и получать доступ к данным человека из` + req.body`. Используйте метод + create () + для вставки строки в базу данных:

app.js

. . .

app.put("/person", function(req, res) {
   db.Person.create({
       firstName: req.body.firstName,
       lastName: req.body.lastName,
       id: req.body.id
   })
       .then( person => {
           res.status(200).send(JSON.stringify(person));
       })
       .catch( err => {
           res.status(500).send(JSON.stringify(err));
       });
});

. . .

Добавьте еще один маршрут для обработки запросов + DELETE +, который удалит записи из адресной книги. Сначала используйте идентификатор, чтобы найти запись, а затем используйте метод + destroy +, чтобы удалить ее:

app.js

. . .

app.delete("/person/:id", function(req, res) {
   db.Person.destroy({
       where: {
           id: req.params.id
       }
   })
       .then( () => {
           res.status(200).send();
       })
       .catch( err => {
           res.status(500).send(JSON.stringify(err));
       });
});

. . .

А для удобства добавьте маршрут, который извлекает всех людей из базы данных, используя путь + / all +:

app.js

. . .

app.get("/all", function(req, res) {
   db.Person.findAll()
       .then( persons => {
           res.status(200).send(JSON.stringify(persons));
       })
       .catch( err => {
           res.status(500).send(JSON.stringify(err));
       });
});

. . .

Остался последний маршрут. Если запрос не соответствует ни одному из предыдущих маршрутов, отправьте код состояния + 404 + (Не найдено):

app.js

. . .

app.use(function(req, res) {
   res.status(404).send("404 - Not Found");
});

. . .

Наконец, добавьте метод + listen () +, который запускает службу. Если переменная окружения + PORT + определена, то служба прослушивает этот порт; в противном случае по умолчанию используется порт + 3000 +:

app.js

. . .

var server = app.listen(process.env.PORT || 3000, function() {
   console.log("app is running on port", server.address().port);
});

Как вы узнали, файл + package.json позволяет вам определять различные команды для запуска тестов, запуска ваших приложений и других задач, что часто позволяет вам выполнять общие команды с гораздо меньшим набором текста. Добавьте новую команду в + package.json, чтобы запустить приложение. Отредактируйте файл:

nano package.json

Добавьте команду + start +, чтобы она выглядела следующим образом:

package.json

. . .

 "scripts": {
   "test": "jest"

 },

. . .

Не забудьте добавить запятую в предыдущую строку, так как раздел + scripts + требует, чтобы его записи разделялись запятыми.

Сохраните файл и запустите приложение в первый раз. Сначала загрузите файл окружения с помощью + source +; это импортирует переменные в сеанс и делает их доступными для приложения. Затем запустите приложение с помощью + npm run start +:

source ./.env
npm run start

Приложение запускается по порту + 3000 +:

Outputapp is running on port 3000

Откройте браузер и перейдите к + http: // localhost: 3000 / all +. Вы увидите страницу, показывающую + [] +.

Вернитесь к своему терминалу и нажмите + CTRL-C +, чтобы остановить приложение.

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

Установите средство jshint, JavaScript-линтер, как зависимость для разработки:

npm install --save-dev jshint

За прошедшие годы JavaScript получил обновления, функции и изменения синтаксиса. Язык стандартизирован http://ecma-international.org [ECMA International] под названием «ECMAScript». Примерно раз в год ECMA выпускает новую версию ECMAScript с новыми функциями.

По умолчанию + jshint + предполагает, что ваш код совместим с ES6 (ECMAScript версии 6), и выдаст ошибку, если найдет какие-либо ключевые слова, не поддерживаемые в этой версии. Вы хотите найти версию, которая совместима с вашим кодом. Если вы посмотрите на feature table для всех последних версий, вы обнаружите, что ключевые слова + async / await + не были введены до ES8. Вы использовали оба ключевых слова в тестовом коде базы данных, поэтому минимальная совместимая версия устанавливается на ES8.

Чтобы сообщить + jshint + версии, которую вы используете, создайте файл с именем + .jshintrc +:

nano .jshintrc

В файле укажите + esversion +. Файл + jshintrc + использует JSON, поэтому создайте в нем новый объект JSON:

jshintrc
{ "esversion": 8 }

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

Добавьте команду для запуска + jshint +. Отредактируйте + package.json:

nano package.json

Добавьте команду + lint в свой проект в разделе` + scripts + ` + package.json. Команда вызывает инструмент lint для всех файлов JavaScript, которые вы создали:

package.json

. . .

 "scripts": {
   "test": "jest",
   "start": "node app.js"

 },

. . .

Теперь вы можете запустить линтер, чтобы найти любые проблемы:

npm run lint

Там не должно быть никаких сообщений об ошибках:

Output> jshint app.js database*.js migrate.js

Если есть какие-либо ошибки, + jshint + покажет строку, в которой возникла проблема.

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

git add *.js
git add package*.json
git add .jshintrc
git commit -m 'initial commit'
git push origin master

Теперь вы можете настроить Semaphore для тестирования, сборки и развертывания приложения, начиная с настройки Semaphore с вашим токеном DigitalOcean Personal Access Token и учетными данными базы данных.

Шаг 3 - Создание секретов в семафоре

Есть некоторая информация, которой нет в репозитории GitHub. Пароли и токены API являются хорошими примерами этого. Вы сохранили эти конфиденциальные данные в отдельном файле и загрузили их в свою среду. При использовании семафора вы можете использовать секреты для хранения конфиденциальных данных.

В проекте есть три вида секретов:

  • Docker Hub: имя пользователя и пароль вашей учетной записи Docker Hub.

  • Персональный токен DigitalOcean: для развертывания приложения в вашем кластере Kubernetes.

  • Переменные среды: для параметров подключения имени пользователя и пароля базы данных.

Чтобы создать первый секрет, откройте браузер и войдите на веб-сайт https://semaphoreci.com [Семафор]. В левом меню навигации нажмите * Секреты * под заголовком * КОНФИГУРАЦИЯ *. Нажмите кнопку * Создать новый секрет *.

Для * Name of Secret * введите + dockerhub +. Затем в разделе * Переменные среды * создайте две переменные среды:

  • + DOCKER_USERNAME +: ваше имя пользователя DockerHub.

  • + DOCKER_PASSWORD +: ваш пароль DockerHub.

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/jnOY5HR.png [Docker Hub Secret]

Нажмите * Сохранить изменения *.

Создайте второй секрет для своего токена личного доступа DigitalOcean. Еще раз, нажмите * Секреты * в левом меню навигации, затем на * Создать новый секрет *. Вызовите этот секретный ключ + do-access-token + и создайте значение среды с именем + DO_ACCESS_TOKEN + со значением, установленным в свой токен личного доступа:

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/VDj90jI.png [DigitalOcean Token Secret]

Сохрани секрет

Для следующего секрета вместо прямой установки переменных среды вы загрузите файл + .env + из корня проекта.

Создайте новый секрет с именем + env-production. В разделе * Files * нажмите ссылку * Upload file *, чтобы найти и загрузить свой файл + .env +, и скажите Semaphore, чтобы он поместил его в + / home / semaphore / env-production +.

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/IsIDPD2.png [Environment Secret]

Переменные среды все настроены. Теперь вы можете начать настройку непрерывной интеграции.

Шаг 4 - Добавление вашего проекта в семафор

На этом шаге вы добавите свой проект в семафор и запустите конвейер Continuous Integration (CI).

Сначала свяжите ваш репозиторий GitHub с семафором:

  1. Войдите в свою учетную запись https://semaphoreci.com [Семафор].

  2. Нажмите значок * + * рядом с * ПРОЕКТЫ *.

  3. Нажмите кнопку * Добавить репозиторий * рядом с вашим репозиторием.

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/eSZWvM9.png [Добавить репозиторий в семафор]

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

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

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

node -v
Outputv10.16.0

Вы можете указать Semaphore, какую версию Node.js использовать, создав в своем хранилище файл с именем + .nvmrc +. Внутри Semaphore использует node менеджер версий для переключения между версиями Node.js. Создайте файл + .nvmrc + и установите версию + 10.16.0 +:

echo '10.16.0' > .nvmrc

Семафорные конвейеры идут в каталог + .semaphore +. Создайте каталог:

mkdir .semaphore

Создайте новый файл конвейера. Начальный конвейер всегда называется + semaphore.yml +. В этом файле вы определите все шаги, необходимые для сборки и тестирования приложения.

nano .semaphore/semaphore.yml

В первой строке должна быть указана версия файла семафора; текущая стабильная версия + v1.0 +. Кроме того, конвейеру нужно имя. Добавьте эти строки в ваш файл:

semaphore/semaphore.yml
version:
name:

. . .

Семафор автоматически предоставляет виртуальные машины для выполнения задач. Есть various машины на выбор. Для заданий по интеграции используйте + e1-standard-2 + (2 ЦП, 4 ГБ ОЗУ) вместе с ОС Ubuntu 18.04. Добавьте эти строки в файл:

semaphore/semaphore.yml
. . .

agent:
 machine:
   type: e1-standard-2
   os_image: ubuntu1804

. . .

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

Начнем с определения первого блока, который устанавливает все зависимости JavaScript для тестирования и запуска приложения:

semaphore/semaphore.yml
. . .

blocks:
 - name: Install dependencies
   task:

. . .

Вы можете определить переменные среды, которые являются общими для всех заданий, например, установив + NODE_ENV в` + test sso. Node.js знает, что это тестовая среда. Добавьте этот код после + task +:

semaphore/semaphore.yml
. . .
   task:




. . .

Команды в разделе prologue выполняются перед каждым заданием в блоке. Это удобное место для определения задач настройки. Вы можете использовать checkout, чтобы клонировать репозиторий GitHub. Затем + nvm use + активирует соответствующую версию Node.js, которую вы указали в + .nvmrc +. Добавьте раздел + prologue +:

semaphore/semaphore.yml
   task:
. . .

     prologue:
       commands:
         - checkout
         - nvm use

. . .

Затем добавьте этот код, чтобы установить зависимости проекта. Для ускорения работы Semaphore предоставляет инструмент cache. Вы можете запустить + cache store +, чтобы сохранить каталог + node_modules в кеше семафоров. + cache + автоматически определяет, какие файлы и каталоги следует хранить. Во второй раз, когда задание выполняется, + cache restore + восстанавливает каталог.

semaphore/semaphore.yml
. . .

     jobs:
       - name: npm install and cache
         commands:
           - cache restore
           - npm install
           - cache store

. . .

Добавьте еще один блок, который будет запускать два задания. Один для запуска теста lint, а другой для запуска набора тестов приложения.

semaphore/semaphore.yml
. . .

 - name: Tests
   task:
     env_vars:
       - name: NODE_ENV
         value: test
     prologue:
       commands:
         - checkout
         - nvm use
         - cache restore

. . .

+ Prologue повторяет те же команды, что и в предыдущем блоке, и восстанавливает` + node_modules` из кеша. Так как этот блок будет запускать тесты, вы устанавливаете переменную окружения + NODE_ENV в` + test`.

Теперь добавьте рабочие места. Первое задание выполняет проверку качества кода с помощью jshint:

semaphore/semaphore.yml
. . .

     jobs:
       - name: Static test
         commands:
           - npm run lint

. . .

Следующее задание выполняет модульные тесты. Вам потребуется база данных для их запуска, так как вы не хотите использовать производственную базу данных. Https://docs.semaphoreci.com/article/132-sem-service-managing-databases-and-services-on-linux[sem-service] Semaphore может запустить локальную базу данных PostgreSQL в тестовой среде, которая полностью изолирована. База данных уничтожается, когда работа заканчивается. Запустите этот сервис и запустите тесты:

semaphore/semaphore.yml
. . .

       - name: Unit test
         commands:
           - sem-service start postgres
           - npm run test

Сохраните файл + .semaphore / semaphore.yml +.

Теперь добавьте и зафиксируйте изменения в репозитории GitHub:

git add .nvmrc
git add .semaphore/semaphore.yml
git commit -m "continuous integration pipeline"
git push origin master

Как только код загружен в GitHub, Semaphore запускает конвейер CI:

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/g7gd8f1.png [Запуск рабочего процесса]

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

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/WIFWWIu.png [Интеграционный конвейер]

Далее вы создадите новый конвейер, который создает образ Docker для приложения.

Шаг 5 - Создание образов Docker для приложения

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

На этом шаге вы добавите новый конвейер для создания собственного образа Docker для своего приложения и отправите его в Docker Hub.

Чтобы создать собственное изображение, создайте + Dockerfile:

nano Dockerfile

+ Dockerfile + - это рецепт для создания изображения. Вы можете использовать official Node.js в качестве отправной точки вместо того, чтобы начинать с нуля. Добавьте это в ваш + Dockerfile:

Dockerfile

FROM node:10.16.0-alpine

. . .

Затем добавьте команду, которая копирует + package.json и` + package-lock.json`, а затем установите модули узлов внутри образа:

Dockerfile

. . .

COPY package*.json ./
RUN npm install

. . .

Первая установка зависимостей ускорит последующие сборки, так как Docker будет кэшировать этот шаг.

Теперь добавьте эту команду, которая копирует все файлы приложения в корне проекта в образ:

Dockerfile

. . .

COPY *.js ./

. . .

Наконец, + EXPOSE + указывает, что контейнер прослушивает соединения через порт + 3000 +, где приложение прослушивает, а + CMD + устанавливает команду, которая должна выполняться при запуске контейнера. Добавьте эти строки в ваш файл:

Dockerfile

. . .

EXPOSE 3000
CMD [ "npm", "run", "start" ]

Сохраните файл.

Завершив Dockerfile, вы можете создать новый конвейер, чтобы Semaphore мог создать для вас образ, когда вы отправляете свой код в GitHub. Создайте новый файл с именем + docker-build.yml +:

nano .semaphore/docker-build.yml

Запустите конвейер с тем же шаблоном, что и конвейер CI, но с именем + Docker build:

semaphore/docker-build.yml
version: v1.0
name:
agent:
 machine:
   type: e1-standard-2
   os_image: ubuntu1804

. . .

Этот конвейер будет иметь только один блок и одну работу. На шаге 3 вы создали секрет с именем + dockerhub +, используя имя пользователя и пароль Docker Hub. Здесь вы импортируете эти значения, используя ключевое слово + secrets +. Добавьте этот код:

semaphore/docker-build.yml
. . .

blocks:
 - name: Build
   task:
     secrets:
       - name: dockerhub

. . .

Изображения Docker хранятся в репозиториях. Мы будем использовать официальный Docker Hub, который позволяет неограниченное количество общедоступных изображений. Добавьте эти строки, чтобы получить код из GitHub, и используйте команду + docker login для аутентификации в Docker Hub.

semaphore/docker-build.yml
   task:
. . .

     prologue:
       commands:
         - checkout
         - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin

. . .

Каждое изображение Docker полностью идентифицируется комбинацией имени и тега. Название обычно соответствует продукту или программному обеспечению, а метка соответствует конкретной версии программного обеспечения. Например, + node.10.16.0 +. Когда тег не указан, по умолчанию Docker использует специальный тег + latest +. Следовательно, рекомендуется использовать тег + latest + для ссылки на самое последнее изображение.

Добавьте следующий код для создания образа и отправьте его в Docker Hub:

semaphore/docker-build.yml
. . .

     jobs:
     - name: Docker build
       commands:
         - docker pull "${DOCKER_USERNAME}/addressbook:latest" || true
         - docker build --cache-from "${DOCKER_USERNAME}/addressbook:latest" -t "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID" .
         - docker push "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID"

Когда Docker создает образ, он использует части существующих образов для ускорения процесса. Первая команда пытается получить образ + latest + из Docker Hub, чтобы его можно было использовать повторно. Семафор останавливает конвейер, если какая-либо из команд возвращает код состояния, отличный от нуля. Например, если в хранилище нет изображения + latest +, как это будет с первой попытки, конвейер остановится. Вы можете заставить семафор игнорировать неудачные команды, добавив `+ || true + `к команде.

Вторая команда строит образ. Чтобы ссылаться на это конкретное изображение позже, вы можете пометить его уникальной строкой. Семафор предоставляет несколько environment переменных для заданий. Один из них, + $ SEMAPHORE_WORKFLOW_ID +, является уникальным и используется всеми конвейерами в рабочем процессе. Это удобно для ссылки на этот образ позже при развертывании.

Третья команда отправляет изображение в Docker Hub.

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

Отредактируйте файл основного конвейера + .semaphore / semaphore.yml +:

nano .semaphore/semaphore.yml

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

semaphore/semaphore.yml
. . .

promotions:
 - name: Dockerize
   pipeline_file: docker-build.yml
   auto_promote_on:
     - result: passed

+ auto_promote_on + определяет условие для запуска конвейера + docker build. В этом случае он запускается, когда все задания, определенные в файле + semaphore.yml +, пройдены.

Чтобы протестировать новый конвейер, вам нужно добавить, зафиксировать и отправить все измененные файлы в GitHub:

git add Dockerfile
git add .semaphore/docker-build.yml
git add .semaphore/semaphore.yml
git commit -m "docker build pipeline"
git push origin master

После завершения конвейера CI запускается конвейер сборки Docker.

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/XQfOwvK.png [Построить конвейер]

Когда он закончится, вы увидите свое новое изображение в своем Docker Hub репозитории.

Вы прошли тестирование процесса сборки и создания образа. Теперь вы создадите окончательный конвейер для развертывания приложения в своем кластере Kubernetes.

Шаг 6 - Настройка непрерывного развертывания в Kubernetes

Строительным блоком развертывания в Kubernetes является pod. Модуль - это группа контейнеров, которые управляются как единое целое. Контейнеры внутри модуля запускаются и останавливаются в унисон и всегда работают на одной машине, совместно используя свои ресурсы. Каждый модуль имеет IP-адрес. В этом случае у контейнеров будет только один контейнер.

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

Вы можете управлять модулями напрямую, но лучше позволить Kubernetes справиться с этим, используя deployment. В этом разделе вы создадите декларативный манифест, который описывает конечное желаемое состояние для вашего кластера. Манифест имеет два ресурса:

  • Развертывание: запускает модули в узлах кластера по мере необходимости и отслеживает их состояние. Поскольку в этом руководстве мы используем кластер из 3 узлов, мы развернем 3 модуля.

  • Сервис: действует как точка входа для наших пользователей. Прослушивает трафик через порт + 80 + (HTTP) и перенаправляет соединение на модули.

Создайте файл манифеста с именем + deploy.yml +:

nano deployment.yml

Запустите манифест с ресурсом + Deployment +. Добавьте следующее содержимое в новый файл, чтобы определить развертывание:

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
 name: addressbook
spec:
 replicas: 3
 selector:
   matchLabels:
     app: addressbook
 template:
   metadata:
     labels:
       app: addressbook
   spec:
     containers:
       - name: addressbook
         image: ${DOCKER_USERNAME}/addressbook:${SEMAPHORE_WORKFLOW_ID}
         env:
           - name: NODE_ENV
             value: "production"
           - name: PORT
             value: "$PORT"
           - name: DB_SCHEMA
             value: "$DB_SCHEMA"
           - name: DB_USER
             value: "$DB_USER"
           - name: DB_PASSWORD
             value: "$DB_PASSWORD"
           - name: DB_HOST
             value: "$DB_HOST"
           - name: DB_PORT
             value: "$DB_PORT"
           - name: DB_SSL
             value: "$DB_SSL"


. . .

Для каждого ресурса в манифесте вам нужно установить + apiVersion +. Для развертываний используйте + apiVersion: apps / v1 +, стабильную версию. Затем скажите Kubernetes, что этот ресурс представляет собой Deployment с + kind: Deployment +. Каждое определение должно иметь имя, определенное в + metadata.name +.

В разделе + spec + вы сообщаете Kubernetes, каково желаемое конечное состояние. Это определение требует, чтобы Kubernetes создал 3 модуля с + репликами: 3 +.

Labels - это пары ключ-значение, используемые для организации и перекрестных ссылок на ресурсы Kubernetes. Вы определяете метки с помощью + metadata.labels +, и вы можете искать соответствующие метки с помощью + selector.matchLabels +. Вот как вы соединяете элементы вместе.

Ключ + spec.template + определяет модель, которую Kubernetes будет использовать для создания каждого модуля. Внутри + spec.template.metadata.labels + вы устанавливаете одну метку для модулей: + app: addressbook +.

С помощью + spec.selector.matchLabels + вы заставляете развертывание управлять любыми модулями с меткой + app: addressbook +. В этом случае вы делаете это развертывание ответственным за все модули.

Наконец, вы определяете изображение, которое запускается в модулях. В + spec.template.spec.containers + вы задаете имя изображения. Kubernetes будет извлекать изображение из реестра по мере необходимости. В этом случае он будет вытягивать из Docker Hub). Вы также можете установить переменные среды для контейнеров, что является удачным, потому что вам нужно предоставить несколько значений для соединения с базой данных.

Чтобы манифест развертывания был гибким, вы будете полагаться на переменные. Однако формат YAML не позволяет использовать переменные, поэтому файл еще не является допустимым. Вы решите эту проблему, когда определите конвейер развертывания для семафора.

Вот и все для развертывания. Но это только определяет стручки. Вам по-прежнему нужен сервис, который позволит трафику перетекать на ваши модули. Вы можете добавить другой ресурс Kubernetes в тот же файл, если вы используете три дефиса (+ --- +) в качестве разделителя.

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

deployment.yml

. . .

---

apiVersion: v1
kind: Service
metadata:
 name: addressbook-lb
spec:
 selector:
   app: addressbook
 type: LoadBalancer
 ports:
   - port: 80
     targetPort: 3000

Балансировщик нагрузки будет получать соединения через порт «+ 80 +» и перенаправлять их на порт «pods» + 3000 +, где приложение прослушивает.

Сохраните файл.

Теперь создайте конвейер развертывания для Semaphore, который будет развертывать приложение с использованием манифеста. Создайте новый файл в каталоге + .semaphore +:

nano .semaphore/deploy-k8s.yml

Начните конвейер как обычно, указав версию, имя и изображение:

semaphore/deploy-k8s.yml
version: v1.0
name: Deploy to Kubernetes
agent:
 machine:
   type: e1-standard-2
   os_image: ubuntu1804

. . .

Этот трубопровод будет иметь два блока. Первый блок развертывает приложение в кластере Kubernetes.

Определите блок и импортируйте все секреты:

semaphore/deploy-k8s.yml
. . .

blocks:
 - name: Deploy to Kubernetes
   task:
     secrets:
       - name: dockerhub
       - name: do-access-token
       - name: env-production

. . .

Сохраните имя кластера DigitalOcean Kubernetes в переменной среды, чтобы вы могли ссылаться на него позже:

semaphore/deploy-k8s.yml
. . .

     env_vars:
       - name: CLUSTER_NAME
         value:

. . .

Кластеры DigitalOcean Kubernetes управляются с помощью комбинации двух программ: + kubectl + и + doctl +. Первый уже включен в образ Семафора, а второй нет, поэтому вам необходимо установить его. Вы можете использовать раздел + prologue +, чтобы сделать это.

Добавьте этот раздел пролога:

semaphore/deploy-k8s.yml
. . .

     prologue:
       commands:
         - wget https://github.com/digitalocean/doctl/releases/download/v1.20.0/doctl-1.20.0-linux-amd64.tar.gz
         - tar xf doctl-1.20.0-linux-amd64.tar.gz
         - sudo cp doctl /usr/local/bin
         - doctl auth init --access-token $DO_ACCESS_TOKEN
         - doctl kubernetes cluster kubeconfig save "${CLUSTER_NAME}"
         - checkout

. . .

Первая команда загружает официальный каталог + doctl + release с помощью + wget +. Вторая команда распаковывает его с помощью + tar + и копирует его в локальный путь. После установки + doctl + его можно использовать для аутентификации с помощью API DigitalOcean и запроса файла конфигурации Kubernetes для нашего кластера. После проверки нашего кода мы закончили с + prologue +:

Далее следует последний кусок нашего конвейера: развертывание в кластере.

Помните, что в + deploy.yml + были некоторые переменные окружения, и YAML этого не допускает. В результате + deploy.yml + в его текущем состоянии не будет работать. Чтобы обойти это, + source файл среды для загрузки переменных, затем используйте команду` + envsubst`, чтобы развернуть переменные на месте фактическими значениями. Результат, файл с именем + deploy.yml +, является полностью допустимым YAML со вставленными значениями. Имея файл на месте, вы можете начать развертывание с помощью + kubectl apply +:

semaphore/deploy-k8s.yml
. . .

     jobs:
     - name: Deploy
       commands:
         - source $HOME/env-production
         - envsubst < deployment.yml | tee deploy.yml
         - kubectl apply -f deploy.yml

. . .

Второй блок добавляет тег + latest + к изображению в Docker Hub, чтобы указать, что это самая последняя развернутая версия. Повторите шаги входа в Docker, затем потяните, повторно пометите и нажмите на Docker Hub:

semaphore/deploy-k8s.yml
. . .

 - name: Tag latest release
   task:
     secrets:
       - name: dockerhub
     prologue:
       commands:
         - checkout
         - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
         - checkout
     jobs:
     - name: docker tag latest
       commands:
         - docker pull "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID"
         - docker tag "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID" "${DOCKER_USERNAME}/addressbook:latest"
         - docker push "${DOCKER_USERNAME}/addressbook:latest"

Сохраните файл.

Этот конвейер выполняет развертывание, но он может запускаться только в том случае, если образ Docker был успешно создан и отправлен в Docker Hub. В результате вы должны соединить конвейеры сборки и развертывания с повышением. Отредактируйте конвейер сборки Docker, добавив его:

nano .semaphore/docker-build.yml

Добавьте продвижение в конец файла:

semaphore/docker-build.yml
. . .

promotions:
 - name: Deploy to Kubernetes
   pipeline_file: deploy-k8s.yml
   auto_promote_on:
     - result: passed

Вы закончили настройку рабочего процесса CI / CD.

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

git add .semaphore/deploy-k8s.yml
git add .semaphore/docker-build.yml
git add deployment.yml
git commit -m "kubernetes deploy pipeline"
git push origin master

Для завершения развертывания потребуется несколько минут.

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/Ee7VRw7.png [развернуть конвейер]

Давайте протестируем приложение дальше.

Шаг 7 - Тестирование приложения

На данный момент приложение запущено и работает. На этом этапе вы будете использовать + curl + для проверки конечной точки API.

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

  1. Войдите в свою учетную запись DigitalOcean.

  2. Выберите проект адресной книги

  3. Перейти к * Сеть *.

  4. Нажмите * Балансировщики нагрузки *.

  5. Отображается * IP-адрес *. Скопируйте IP-адрес.

изображение: https: //assets.digitalocean.com/articles/semaphore_doks/S6nLLmg.png [Load Balancer IP]

Давайте проверим маршрут + / all, используя` + curl`:

curl -w "\n" /all

Вы можете использовать опцию + -w" \ n "+, чтобы + curl + печатал все строки:

Поскольку в базе данных еще нет записей, в результате вы получите пустой массив JSON:

Output[]

Создайте новую запись о человеке, сделав запрос + PUT + к конечной точке + / person +:

curl -w "\n" -X PUT \
 -d "firstName=Sammy&lastName=the Shark" /person

API возвращает объект JSON для человека:

Output{
   "id": 1,
   "firstName": "Sammy",
   "lastName": "the Shark",
   "updatedAt": "2019-07-04T23:51:00.548Z",
   "createdAt": "2019-07-04T23:51:00.548Z"
}

Создайте второго человека:

curl -w "\n" -X PUT \
 -d "firstName=Tommy&lastName=the Octopus" /person

Вывод указывает, что был создан второй человек:

Output{
   "id": 2,
   "firstName": "Tommy",
   "lastName": "the Octopus",
   "updatedAt": "2019-07-04T23:52:08.724Z",
   "createdAt": "2019-07-04T23:52:08.724Z"
}

Теперь сделайте запрос + GET +, чтобы получить человека с + id + из + 2 +:

curl -w "\n" /person/2

Сервер отвечает запрошенными вами данными:

Output{
   "id": 2,
   "firstName": "Tommy",
   "lastName": "the Octopus",
   "createdAt": "2019-07-04T23:52:08.724Z",
   "updatedAt": "2019-07-04T23:52:08.724Z"
}

Чтобы удалить человека, отправьте запрос + DELETE:

curl -w "\n" -X DELETE /person/2

Эта команда не возвращает никаких результатов.

У вас должен быть только один человек в вашей базе данных, тот с + id + из + 1 +. Попробуйте снова получить + / all +:

curl -w "\n" /all

Сервер отвечает массивом лиц, содержащим только одну запись:

Output[
   {
       "id": 1,
       "firstName": "Sammy",
       "lastName": "the Shark",
       "createdAt": "2019-07-04T23:51:00.548Z",
       "updatedAt": "2019-07-04T23:51:00.548Z"
   }
]

На данный момент в базе данных остался только один человек.

Это завершает тесты для всех конечных точек в нашем приложении и отмечает конец учебника.

Заключение

В этом руководстве вы с нуля написали полное приложение Node.js, в котором использовалась управляемая служба баз данных PostgreSQL от DigitalOcean. Затем вы использовали конвейеры CI / CD Semaphore для полной автоматизации рабочего процесса, который тестировал и создавал образ контейнера, загружал его в Docker Hub и развертывал его в DigitalOcean Kubernetes.

Чтобы узнать больше о Kubernetes, вы можете прочитать An Введение в Kubernetes и остальные https://www.digitalocean.com DigitalOcean. / community / tags / kubernetes [Учебные пособия по Kubernetes].

Теперь, когда ваше приложение развернуто, вы можете рассмотреть adding имя домена, https://www.digitalocean.com/ документы / базы данных / инструкции / кластеры / защищенные кластеры [защита кластера базы данных] или настройка https://www.digitalocean.com/docs/databases/how-to/clusters/set-up-alerts[alerts для вашей базы данных.

Related