Повседневная жизнь разработчика наполнена однообразными и повторяющимися задачами. К счастью, мы живем в эпоху, предшествующую искусственному интеллекту, а это значит, что компьютеры отлично справляются со скучными делами, и они почти никогда не жалуются на это! Итак, давайте настроим некоторую автоматизацию, чтобы сделать нашу ежедневную работу немного менее мягкой.
Тестирование и развертывание являются двумя неотъемлемыми элементами веб-разработки. С некоторой автоматизацией они становятся решениями, обычно называемыми «непрерывной интеграцией» (CI) и «непрерывным развертыванием» (CD). «Непрерывный» аспект этих решений означает, что ваши проекты будут автоматически протестированы и развернуты, что позволит вам больше сосредоточиться на написании кода и меньше на его переносе на серверы.
В этом уроке мы настроим популярный сервер непрерывной интеграции под названием Jenkins и синхронизируем его с GitHub, чтобы он запускал тесты каждый раз, когда вводится новый код. После этого мы создадим решение для автоматической отправки этого кода на наш сервер приложений, что избавит нас от необходимости развертывания вручную.
Мы будем использовать DigitalOcean для быстрого и простого создания облачных виртуальных частных серверов (VPS) для размещения нашего приложения и Jenkins.
Примечание. В этом руководстве предполагается, что у вас есть базовые знания по работе в командной строке и что на вашем компьютере установлены и Git, и Node.js.
Наше супер образец приложения
Прежде чем мы сможем протестировать или развернуть что-либо, нам нужно что-то протестировать и развернуть. Позвольте мне познакомить вас с нашим дружественным учебным тестовым приложением, которое называется «Привет, Дженкинс».
Мы напишем простое приложение Node.js для наших целей. Это не намного больше, чем отображение строки текста в браузере, но этого достаточно для обеспечения правильной настройки непрерывной интеграции и непрерывного развертывания.
Git Up на GitHub
Поскольку мы будем хранить наш проект на GitHub, давайте начнем с него. Войдите (или создайте) свою учетную запись GitHub и создайте новый репозиторий. Назовите его «hello-jenkins» и дайте ему следующее описание:
1
|
My super sample app to test out Jenkins.
|
Для простоты давайте сохраним репо Public . Продолжите и проверьте Инициализировать этот репозиторий с опцией README и выберите опцию Node из выпадающего списка Add .gitignore .
Нажмите кнопку Создать репозиторий , и наше репо будет готово.
Теперь давайте клонируем наш новый репозиторий на наш локальный компьютер и перейдем к нему:
1
2
|
git clone [email protected]:<you>/hello-jenkins.git
cd hello-jenkins
|
Наше приложение Node
Вот какова будет окончательная структура нашего приложения:
1
2
3
4
5
6
7
8
9
|
├── .gitignore
├── app.js
├── package.json
├── README.md
├── script
│ ├── deploy
│ └── test
└── test
└── test.js
|
Давайте займемся этим один за другим. Первым шагом к созданию любого приложения Node.js является создание файла package.json
. Вот наши:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
{
«name»: «hello-jenkins»,
«description»: «hello jenkins test app»,
«version»: «0.0.1»,
«private»: true,
«dependencies»: {
«express»: «3.12.0»
},
«devDependencies»: {
«mocha»: «1.20.1»,
«supertest»: «0.13.0»
}
}
|
В dependencies
мы добавили express
, который мы будем использовать для создания нашего приложения Node.js. В devDependencies
мы добавили supertest
и supertest
, которые помогут нам написать наши тесты.
Теперь, когда наш package.json
определен, установите зависимости нашего приложения, выполнив:
1
|
npm install
|
Пришло время написать код нашего приложения. Создайте файл с именем app.js
и добавьте в него следующее:
01
02
03
04
05
06
07
08
09
10
11
|
var express = require(‘express’);
var app = express();
app.get(‘/’, function (req, res) {
res.send(‘hello world’);
});
app.listen(process.env.PORT || 5000);
module.exports = app;
|
Давайте разберем наше простое приложение Node.js:
- Сначала мы импортируем
express
библиотеку, указанную вpackage.json
. - Мы используем
express
для создания новогоapp.
- Мы говорим нашему
app
отвечать на все запросы, попадающие в корневой каталог нашего сайта (/
), с текстом «Привет, мир». - Далее мы сообщаем нашему
app
на каком порту прослушивать запросы (process.env.PORT
ссылается на переменную среды, называемую «PORT», и, если она не существует, вместо этого по умолчанию используется порт 5000). - Наконец, мы делаем наше
app
доступным для других модулей Node.js черезmodule.exports
(это пригодится позже, когда мы добавим тесты).
Это оно! Наше приложение готово — давайте запустим его:
1
|
node app.js
|
Откройте ваш любимый браузер и перейдите по http://localhost:5000
, и вы увидите привет мир, сидящий во всей своей славной простоте.
Это не самое захватывающее тестовое приложение, но оно работает! Выключите наше приложение Node.js с помощью Ctrl-C , и давайте двигаться дальше.
Некоторое тестирование в порядке
Пришло время написать тест для нашего приложения — в конце концов, если нам нечего тестировать, то Дженкинс не будет ничего делать!
Создайте папку с именем test
и в ней создайте файл с именем test.js
Добавьте следующий код в test/test.js
:
1
2
3
4
5
6
7
8
|
var request = require(‘supertest’);
var app = require(‘../app.js’);
describe(‘GET /’, function() {
it(‘respond with hello world’, function(done) {
request(app).get(‘/’).expect(‘hello world’, done);
});
});
|
Как работает наш тест? Во-первых, мы импортируем как supertest
и наше app
. Затем мы добавляем один тест, описывающий, что должно происходить, когда запрос GET
попадает в корень нашего сайта. Мы говорим нашему тесту, что ожидаем, что ответом будет «привет мир», и если это так, тест пройден.
Для запуска теста мы будем использовать библиотеку Mocha . Мы установили Mocha как часть наших devDependencies
, поэтому мы просто запустим команду, которая передает наш тестовый файл в Mocha, и Mocha запустит наши тесты:
1
|
./node_modules/.bin/mocha ./test/test.js
|
Когда закончите, вы должны увидеть зеленую точку вместе с информацией о том, что один тест пройден. Это значит, что наш тест прошел успешно! Но ввод этой команды снова и снова скоро вызовет судороги пальцев и подергивание глаз, поэтому давайте сделаем вспомогательный скрипт, который сделает это за нас (помните, что компьютерам не скучно!).
Создайте новый каталог с именем script
и создайте в нем файл с именем test
(обратите внимание, что расширение отсутствует). Добавьте следующее в script/test
:
1
2
3
|
#!/bin/sh
./node_modules/.bin/mocha ./test/test.js
|
Там — теперь у нас есть сценарий оболочки для выполнения этой сложной строки для нас. Но прежде чем мы сможем использовать его, мы должны предоставить ему права на выполнение:
1
|
chmod +x script/test
|
Давайте проверим это! Бегать:
1
|
./script/test
|
… и вы должны увидеть тот же проходной тест, что и раньше.
Время нажать
Хорошо, у нас есть работающее приложение и работающий тест, поэтому давайте отправим наш новый код в GitHub:
1
2
3
|
git add .
git commit -m ‘Add node app’
git push origin master
|
И это все — наше приложение готово и на GitHub!
Наше приложение получает обслуживание
У нас есть увлекательное и увлекательное приложение («Привет, мир» имеет в своем роде поэзию, не правда ли?), Но никто не может его увидеть! Давайте изменим это и запустим наше приложение на сервере.
Для наших нужд хостинга мы обратимся к DigitalOcean. DigitalOcean предоставляет быстрый и простой способ ускорения работы облачных экземпляров VPS, что делает его идеальным хостом для нашей игровой площадки CI / CD.
Первая капля
Войдите в систему (или зарегистрируйтесь ) в DigitalOcean и нажмите кнопку « Создать каплю» . Для имени хоста, назовите его «привет-Дженкинс». Экземпляр самого низкого размера ( 512 МБ / 1/20 ГБ ) подойдет для наших потребностей и выберет ближайший к вам географический регион. Далее нам нужно выбрать изображение, используемое для создания капли. DigitalOcean предоставляет широкий выбор операционных систем на выбор, но что действительно приятно, так это то, что они также предоставляют изображения, специально предназначенные для определенных типов приложений.
Перейдите на вкладку « Приложения » и выберите параметр node-v0.10.29 в Ubuntu 14.04 — это создаст сервер, который будет прекрасно загружен для нашего приложения Node.js.
Теперь нажмите Create Droplet , и DigitalOcean начнет инициализацию нашего сервера.
Настройте сервер
В течение минуты наш новый сервер должен быть готов, и вы должны были получить электронное письмо с корневыми данными вашего сервера. Давайте использовать эту информацию для входа в систему:
1
|
Вам будет предложено ввести пароль, указанный в электронном письме, а затем немедленно будет вынужден создать новый пароль (сделать его очень надежным и сохранить его в безопасном месте, например, в базе данных KeePass ).
Прямо сейчас мы вошли в систему как root
, который является всемогущим полубогом Linux-земли. Но тяжела голова, которая носит корону, и работать как root
— вообще плохая идея. Итак, первое, что мы хотим сделать, это создать нового пользователя — назовем его «приложение»:
1
|
adduser app
|
Вам нужно будет ввести пароль ( другой надежный пароль, надежно хранится), и тогда он задаст ряд дополнительных вопросов.
Мы хотим переключиться на пользователя нашего app
, но прежде чем мы выйдем из системы, нам нужно предоставить привилегии sudo
нашего нового пользователя, чтобы у него была возможность выполнять административные действия:
1
|
usermod -a -G sudo app
|
Теперь закройте соединение с exit
, а затем подключитесь как app
:
1
|
Вам будет предложено ввести пароль пользователя app
, а затем вы должны войти в систему и готово к работе.
Установите наше приложение
Давайте загрузим наше приложение на компьютер. Благодаря образам приложений DigitalOcean наша машина поставляется с предустановленными Node.js и npm, но нам все еще нужно установить Git:
1
|
sudo apt-get install git
|
Вам будет предложено ввести пароль (поскольку вы используете sudo
), и вам придется подтвердить установку с помощью Y. После установки Git мы можем использовать его для получения нашего приложения от GitHub.
Скопируйте URL-адрес клонирования HTTPS со страницы проекта GitHub, а затем клонируйте репозиторий в свою домашнюю папку на сервере:
1
2
|
cd
git clone https://github.com/<you>/hello-jenkins.git
|
Теперь наше приложение находится на нашем сервере, в папке с именем «hello-jenkins». Давайте перейдем к этому:
1
|
cd hello-jenkins
|
Первое, что нам нужно сделать, это установить зависимости приложения:
1
|
npm install —production
|
Как только это будет сделано, мы можем запустить наше приложение! Раскрути это:
1
|
node app.js
|
… и перейдите к IP-адресу вашего сервера в браузере.
Но подождите, это не работает! В чем дело?
Хорошо, давайте вспомним эту строку кода в нашем app.js
:
1
|
app.listen(process.env.PORT || 5000);
|
Прямо сейчас у нас нет установленной переменной среды PORT
, поэтому наше приложение по умолчанию использует порт 5000, и вам нужно добавить порт к IP-адресу в браузере ( http://YOUR.SERVER.IP.ADDRESS:5000
).
Так как же заставить наше приложение работать так, как ожидается, без указания порта? Что ж, когда браузер отправляет HTTP-запрос, по умолчанию используется порт 80. Поэтому нам просто нужно установить переменную среды PORT
80
.
Мы установим наши переменные окружения в /etc/environment
на сервере — этот файл загружается при входе в систему, и набор переменных будет доступен глобально для всех приложений. Откройте файл:
1
|
sudo nano /etc/environment
|
Вы увидите, что сейчас в этом файле устанавливается PATH
. Добавьте следующую строку после него:
1
|
PORT=80
|
Затем нажмите Ctrl-X , Y и Enter, чтобы сохранить и выйти. Выход сервера ( exit
) и SSH снова (это загрузит новую переменную среды).
Еще одна маленькая рутина — запуск приложения на порту 80 требует привилегий root, но выполнение sudo node app.js
не сохранит переменные среды, которые мы настроили. Чтобы обойти это, мы дадим node
возможность работать на порту 80 без sudo
:
1
|
sudo setcap cap_net_bind_service=+ep /usr/local/bin/node
|
Это должно сделать это. Теперь запустите:
1
|
node app.js
|
Перейдите по http://YOUR.SERVER.IP.ADDRESS
, и вы увидите привет мир !
Держите это работает
Прямо сейчас наше приложение работает только во время выполнения процесса — если мы закроем его, наш сайт больше не будет доступен. Нам нужен способ поддерживать приложение Node.js в фоновом режиме. Для этого мы будем использовать вечно . Первый шаг — установить его глобально:
1
|
sudo npm install -g forever
|
Теперь вместо запуска нашего приложения с node app.js
мы будем использовать:
1
|
forever start app.js
|
Обратите внимание, что вместо процесса, зависшего при выполнении, он немедленно завершается и возвращает вам контроль. Это потому, что сервер Node.js работает в фоновом режиме. Теперь нам не нужно беспокоиться о закрытии нашего сервера при выходе из системы. forever
даже автоматически перезапустит наше приложение, если произойдет сбой!
Чтобы остановить наше приложение, мы можем запустить:
1
|
forever stopall
|
А пока давайте продолжим и перейдем к Дженкинсу.
Время для тестирования
Мы будем размещать наш сервер Jenkins на отдельной капле DigitalOcean. Давайте раскрутим это сейчас.
Вторая капля
Создайте новую каплю с именем хоста «jenkins-box». Снова выберите 512MB / 1 / 20GB , с тем же расположением и тем же типом приложения ( node-v0.10.29 в Ubuntu 14.04 ), что и с предыдущей каплей.
Нажмите Create Droplet и после завершения используйте учетные данные, отправленные вам по электронной почте, для входа через SSH (вам нужно будет установить новый пароль, как и раньше).
Как и прежде, мы должны создать нового пользователя, прежде чем делать что-либо еще. На этот раз давайте назовем это admin
:
1
2
|
adduser admin
usermod -a -G sudo admin
|
Выйдите из системы как root
и войдите как вновь созданный admin
.
Поскольку целью Jenkins является получение нашего проекта и запуск его тестов, на нашей машине должны быть установлены все зависимости проекта. Мы расширили этот экземпляр с помощью приложения DigitalOcean Node.js, поэтому Node.js и npm уже установлены. Но нам все еще нужно установить Git:
1
|
sudo apt-get install git
|
Нанять дворецкого
Далее идет Дженкинс. Установка Jenkins довольно проста — мы сделаем все возможное, чтобы apt-get
выполнил всю тяжелую работу. Единственный улов в том, что нам нужно добавить новый репозиторий apt
перед началом установки:
1
2
3
|
sudo wget -q -O — http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key |
sudo sh -c ‘echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list’
sudo apt-get update
|
Теперь мы можем установить Jenkins:
1
|
sudo apt-get install jenkins
|
После завершения Jenkins будет запущен и доступен через порт 8080. Перейдите в своем браузере по IP-адресу jenkins-box
на порту 8080, и вы увидите целевую страницу Jenkins.
Нажмите ссылку « Управление Jenkins» , а затем ссылку « Управление плагинами» . Перейдите на вкладку « Доступные » и найдите плагин GitHub . Установите флажок « Установить» , а затем нажмите кнопку « Загрузить сейчас и установить после перезагрузки» .
Это инициирует последовательность установки. Плагин GitHub имеет несколько зависимостей, поэтому будет установлено несколько плагинов. В нижней части страницы установите флажок « Перезапустить Jenkins», когда установка будет завершена, и не будет выполнено ни одного задания — это предложит перезапустить Jenkins после завершения установки.
После перезапуска Jenkins пришло время добавить наш проект. Нажмите кнопку « Новый элемент» . Используйте «hello-jenkins» для имени элемента, выберите « Создать проект программного обеспечения в свободном стиле» и нажмите кнопку с надписью « ОК» .
Как только проект будет настроен, вы окажетесь на странице настроек проекта. Добавьте URL-адрес GitHub нашего проекта в окно проекта GitHub :
1
|
https://github.com/<you>/hello-jenkins
|
Затем выберите опцию Git в разделе « Управление исходным кодом» . В появившихся полях добавьте URL-адрес нашего репозитория проекта GitHub в поле URL-адрес репозитория :
1
|
https://github.com/<you>/hello-jenkins.git
|
Прокрутите немного вниз и установите флажок, чтобы включить Build, когда изменения будут переданы в GitHub . Если эта опция включена, наш проект будет собираться каждый раз, когда мы отправляемся в репозиторий GitHub. Конечно, нам нужно, чтобы Дженкинс знал, что делать, когда он запускает сборку. Щелкните раскрывающийся список Добавить шаг сборки и выберите « Выполнить оболочку» . Это сделает диалог Command доступным, и то, что мы поместим в этот диалог, будет выполнено, когда начнется сборка. Добавьте к этому следующее:
1
2
|
npm install
./script/test
|
Наша сборка состоит из двух этапов. Во-первых, он устанавливает зависимости нашего приложения. Затем он выполняет ./script/test
для запуска наших тестов.
Нажмите « Сохранить ».
Чтобы завершить настройку интеграции, перейдите в репозиторий GitHub и нажмите « Настройки» . Нажмите вкладку Webhooks & Services , а затем раскрывающийся список Add service . Выберите сервис Jenkins (плагин GitHub) .
Добавьте следующее как URL-адрес крючка Дженкинса :
1
|
http://JENKINS.SERVER.IP.ADDRESS:8080/github-webhook/
|
Нажмите Добавить сервис . Наш проект готов к первому непрерывному интеграционному тестированию!
Давайте дадим что-то для тестирования. Откройте app.js
локально и измените эту строку:
1
|
res.send(‘hello world’);
|
…к этому:
1
|
res.send(‘hello jenkins’);
|
Сохраните изменения и передайте их:
1
2
|
git add .
git commit -m ‘Switch to hello jenkins’
|
Теперь следите за Jenkins, пока вы вносите изменения в GitHub:
1
|
git push origin master
|
Через секунду или две вы увидите, что для нашего проекта hello-jenkins
в Дженкинсе была начата новая работа — наша непрерывная интеграция работает!
Поток непрерывной интеграции
Но … работа не удалась! Почему?
Хорошо, помните, что наш тест ожидает, что корневой вызов возвратит «hello world», но мы изменили его на «hello jenkins». Итак, давайте изменим ожидания нашего теста. Поменяйте местами эту строку:
1
|
request(app).get(‘/’).expect(‘hello world’, done);
|
… с этой строкой:
1
|
request(app).get(‘/’).expect(‘hello jenkins’, done);
|
Сохраните, подтвердите и снова нажмите:
1
2
3
|
git add .
git commit -m ‘Switch test to hello jenkins’
git push origin master
|
Смотрите Дженкинс — еще раз, вы увидите, что сборка запускается автоматически, и на этот раз она удалась!
Это поток непрерывной интеграции. Тестовый сервер постоянно тестирует любой новый код, который вы нажимаете, поэтому вы быстро получаете информацию о любых неудачных тестах.
Получите это развернуто
Итак, мы автоматически тестируем наши изменения, но как насчет развертывания этих изменений? Нет проблем!
Если вы внимательно следите, вы, несомненно, заметили, что в нашем проекте пока что-то не хватает. В структуре проекта в начале урока есть файл script/deploy
, но мы еще не создали ни одного такого файла. Что ж, теперь мы будем!
Ключ к аутентификации
Но сначала давайте обсудим, как будет работать развертывание. Наш скрипт (запускаемый на этапе сборки Jenkin) войдет в систему на сервере приложений через SSH, перейдет в папку нашего приложения, обновит приложение и затем перезагрузит сервер. Перед написанием нашего сценария развертывания нам нужно разобраться, как наш сервер Jenkins будет использовать SSH на нашем сервере приложений.
До сих пор мы обращались к нашим серверам, вводя пароли вручную, но этот подход не работает для автоматизированных сценариев. Вместо этого мы создадим ключ SSH, который сервер Jenkins будет использовать для аутентификации на сервере приложений.
Когда Jenkins устанавливает, он создает нового пользователя с именем jenkins
. Jenkins выполняет все команды с этим пользователем, поэтому нам нужно сгенерировать наш ключ с пользователем jenkins
чтобы у него был соответствующий доступ к нему.
jenkins-box
в систему как admin
на jenkins-box
, выполните следующее:
1
|
sudo su
|
Укажите свой пароль admin
, и он переключит вас на пользователя root
. Затем выполните:
1
|
su jenkins
|
Теперь вы действуете как пользователь jenkins
. Сгенерируйте ключ SSH:
1
|
ssh-keygen -t rsa
|
Сохраните файл в расположении по умолчанию ( /var/lib/jenkins/.ssh/id_rsa
) и убедитесь, что вы не используете парольную фразу (в противном случае доступ по SSH потребует пароль и не будет работать при автоматизации).
Далее нам нужно скопировать открытый ключ, который был создан. Запустите это:
1
|
cat ~/.ssh/id_rsa.pub
|
… и скопировать вывод. Это должна быть длинная строка, начинающаяся с «ssh-rsa» и заканчивающаяся «jenkins @ jenkins-box».
Выйдите из jenkins-box
и снова войдите на наш сервер приложений ( hello-jenkins
) как пользователь app
. Нам нужно создать файл с именем .ssh
папке пользователя нашего app
.ssh
:
1
2
|
mkdir ~/.ssh
nano ~/.ssh/authorized_keys
|
Вставьте открытый ключ, который вы скопировали, и затем нажмите Ctrl-X / Y / Enter, чтобы сохранить и выйти. Для корректной работы этого файла ему необходимо установить строгие разрешения:
1
2
|
chmod 700 ~/.ssh
chmod 600 ~/.ssh/*
|
Вернитесь в окно jenkins
, переключитесь на пользователя jenkins
и убедитесь, что вы можете войти на наш сервер приложений без ввода пароля:
1
|
Вы должны успешно войти на сервер приложений без необходимости ввода пароля. После этого мы можем перейти к развертыванию.
Отправить его автоматически
Создайте файл в папке script
именем deploy
(обратите внимание, что расширение отсутствует). Добавьте в script/deploy
:
1
2
3
4
5
6
7
8
9
|
#!/bin/sh
ssh [email protected] <<EOF
cd ~/hello-jenkins
git pull
npm install —production
forever restartall
exit
EOF
|
Давайте пройдемся по этому:
- Сначала мы заходим на сервер приложений как пользователь
app
. - Затем мы переходим в папку нашего приложения и обновляем его до последней версии с GitHub.
- После этого мы устанавливаем наши зависимости.
- Наконец, как только код нашего приложения обновляется, мы перезагружаем наш сервер с
forever restartall
.
Сделайте наш новый исполняемый файл скрипта:
1
|
chmod +x script/deploy
|
Добавьте этот новый файл и передайте его:
1
2
|
git add .
git commit -m ‘Add deploy script’
|
Но давайте пока не будем давить. Сначала вернитесь к конфигурации нашего проекта в Jenkins и прокрутите вниз до команды build. Добавьте эту новую строку в конце:
1
|
./script/deploy
|
Сохранить проект Дженкинс.
Теперь перейдите к GitHub и посмотрите, как автоматически создается Jenkins. Как только сборка будет завершена (она должна завершиться успешно), перейдите в браузере по IP-адресу нашего сервера приложений. Presto! Наш восхитительный «привет мир» был заменен на волнующий «привет Дженкинс»!
Наше приложение постоянно разворачивается!
Все хорошо, что хорошо автоматизирует
Уф. Это была настоящая поездка!
В итоге мы успешно настроили как непрерывную интеграцию, так и непрерывное развертывание, что обеспечивает очень хороший уровень автоматизации в нашей повседневной жизни разработчиков. Помните, что компьютерам не скучно, поэтому, пока они занимаются тестированием и развертыванием, вы можете делать важные вещи, например, готовить себе сэндвич. Так иди приготовь этот бутерброд и ешь как чемпион по автоматизации!