Развертывание серверов приложения и обратного прокси-сервера Nginx с помощью map-файлов Salt

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

Данный мануал поможет создать map-файл Salt Cloud и пользовательские зерна Salt для определения ролей серверов и динамической конфигурации обратного прокси-сервера.

Требования

  • Сервер CentOS 7, настроенный по этому мануалу. Все команды в текущем мануале нужно выполнять через пользователя с доступом к sudo.
  • 1 Гб памяти минимум.
  • SSH-ключ для root-пользователя. Запишите имя, которое вы назначили ключу в панели управления хостингом (в мануале используется условное имя salt-master-root-key). Вы также должны указать местоположение закрытого ключа; по умолчанию это /root/.ssh/id_rsa.
  • Токен доступа. Обязательно установите область для чтения и записи.

1: Установка Salt и Salt Cloud

Для начала необходимо установить и настроить Salt Cloud на вашем сервере. Для этого можно использовать скрипт запуска Salt.

Загрузите скрипт.

wget -O install_salt.sh https://bootstrap.saltstack.com

Запустите скрипт. Флаг –М установит salt-master.

sh install_salt.sh -M

Кодовая база Salt Cloud была объединена с основным проектом Salt, но для CentOS она по-прежнему упакована отдельно. К счастью, скрипт install_salt настроил все необходимые репозитории, поэтому вы можете просто установить salt-cloud с помощью yum:

yum install salt-cloud

Теперь проверьте версию Salt Cloud, чтобы убедиться, что установка прошла успешно.

salt-cloud --version
salt-cloud 2015.5.3 (Lithium)

Версия может отличаться.

2: Настройка Salt Cloud

Теперь нужно подключить Salt Cloud к вашим серверам.

Создание файла провайдера

В Salt Cloud файлы провайдера определяют метод создания новых виртуальных машин. Провайдеры определяются в каталоге /etc/salt/cloud.providers.d.

Создайте файл для вашего провайдера:

nano /etc/salt/cloud.providers.d/your_provider.conf

Вставьте нижеприведенный код, заменив условные значения переменных своими данными (укажите свой IP-адрес сервера и токен доступа, а также имя и файл ключа SSH).

### /etc/salt/cloud.providers.d/your_provider.conf ###
######################################################
do:
provider: your_provider
minion:
master: your_server_ip
# Access Token
personal_access_token: your_access_token
# This is the name of your SSH key
# as it appears in the control panel.
ssh_key_name: salt-master-root-key
# This is the path on disk to the private key
ssh_key_file: /root/.ssh/id_rsa

Ограничьте доступ к файлу ключа, иначе SSH откажется использовать его.

chmod 600 /root/.ssh/id_rsa

Настройка профилей для развертываемых серверов

Профилями в Salt Cloud называются отдельные виртуальные машины, привязанные к провайдеру. Они определяются в каталоге /etc/salt/cloud.profiles.d.

Создайте файл профиля:

nano /etc/salt/cloud.profiles.d/your_provider.conf

Вставьте в него следующий код:

### /etc/salt/cloud.profiles.d/your_provider.conf ###
#####################################################
ubuntu_512MB_ny3:
provider: your_provider
image: ubuntu-14-04-x64
size: 512MB
location: nyc3
private_networking: True
ubuntu_1GB_ny3:
provider: your_provider
image: ubuntu-14-04-x64
size: 1GB
location: nyc3
private_networking: True

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

  • Виртуальная машина Ubuntu 14.04, 512 Мб памяти, в регионе New York 3
  • Виртуальная машина Ubuntu 14.04, 1 Гб памяти, регион New York 3

Протестируйте свою настройку с помощью быстрого запроса:

salt-cloud -Q
[INFO    ] salt-cloud starting
your_provider:
----------
your_provider:
----------
centos-salt:
----------
id:
2806501
image_id:
6372108
public_ips:
192.241.247.229
size_id:
63
state:
active

3: Создание простого map-файла

Map-файл – это файл YAML, в котором перечислены профили и количество серверов, которые нужно создать. Начните с простого map-файла, в дальнейшем его можно будет расширить.

Используя приведенные выше профили, предположим, вы хотите, чтобы два сервера приложений по 1 Гб находились за обратным прокси-сервером размером 512 Мб. Создайте файл /etc/salt/cloud.maps.d/app-with-rproxy.map и определите приложение.

nano /etc/salt/cloud.maps.d/app-with-rproxy.map

Вставьте в него следующее:

### /etc/salt/cloud.maps.d/app-with-rproxy.map ####
######################################################
ubuntu_512MB_ny3:
- nginx-rproxy
ubuntu_1GB_ny3:
- appserver-01
- appserver-02

Простой map-файл готов. Теперь разверните эти серверы:

salt-cloud -m /etc/salt/cloud.maps.d/app-with-rproxy.map

После завершения команды проверьте серверы с помощью ping:

salt '*' test.ping
[label salt '*' test.ping
appserver-01:
True
appserver-02:
True
nginx-rproxy:
True

Виртуальные машины были созданы по вашему файлу. Теперь их можно удалить:

salt-cloud -d -m /etc/salt/cloud.maps.d/app-with-rproxy.map

Примечание: Будьте осторожны с этой командой – она удаляет все виртуальные машины, определенные в указанном map-файле.

4: Расширение map-файла

Простой map-файл работает, но для полноценной среды производства он слишком прост. Теперь нужно определить размер приложения. Вернитесь к map-файлу и добавьте еще несколько функций.

nano /etc/salt/cloud.maps.d/app-with-rproxy.map

Удалите его предыдущее содержимое и замените его следующим кодом:

### /etc/salt/cloud.maps.d/app-with-rproxy.map ###
#####################################################
ubuntu_512MB_ny3:
- nginx-rproxy:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: rproxy
ubuntu_1GB_ny3:
- appserver-01:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-02:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver

Кажется, файл стал намного сложнее, но на самом деле в нем появилось всего два новых раздела: mine_functions и grains.

Теперь Salt Cloud может изменить конфигурацию Salt Minion для этих виртуальных машин и добавить пользовательские зерна. Зерна присваивают обратному прокси-серверу роль rproxy, а серверам приложений роль appserver. Это позволяет динамически настраивать обратный прокси.

mine_functions тоже добавляются в конфигурации Salt Minion. Миньон сможет передавать IP-адрес на интерфейсе eth0 мастеру Salt и хранить его в mine. Это означает, что Salt Master автоматически узнает IP-адрес нового сервера. Это пригодится в дальнейшем.

5: Определение обратного прокси-сервера

Теперь установите обратный прокси-сервер и настройте его. Здесь в качестве обратного прокси-сервера используется Nginx.

Определение состояния Nginx

Пришло время написать несколько состояний Salt. Сначала создайте дерево Salt по умолчанию:

mkdir /srv/salt

Перейдите в этот каталог и создайте в нем новый каталог для nginx:

cd /srv/salt
mkdir /srv/salt/nginx

Перейдите в новый каталог и создайте файл rproxy.sls:

cd /srv/salt/nginx
nano /srv/salt/nginx/rproxy.sls

Добавьте в файл такие строки:

### /srv/salt/nginx/rproxy.sls ###
##################################
### Install Nginx and configure it as a reverse proxy, pulling the IPs of
### the app servers from the Salt Mine.
nginx-rproxy:
# Install Nginx
pkg:
- installed
- name: nginx
# Place a customized Nginx config file
file:
- managed
- source: salt://nginx/files/awesome-app.conf.jin
- name: /etc/nginx/conf.d/awesome-app.conf
- template: jinja
- require:
- pkg: nginx-rproxy
# Ensure Nginx is always running.
# Restart Nginx if the config file changes.
service:
- running
- enable: True
- name: nginx
- require:
- pkg: nginx-rproxy
- watch:
- file: nginx-rproxy
# Restart Nginx for the initial installation.
cmd:
- run
- name: service nginx restart
- require:
- file: nginx-rproxy

Этот файл:

  • Устанавливает Nginx.
  • Помещает пользовательские конфигурации в файл /etc/nginx/conf.d/awesome-app.conf.
  • Проверяет работу Nginx.

Это состояние Salt просто устанавливает Nginx и добавляет конфигурационный файл.

Создание конфигурационного файла для обратного прокси-сервера Nginx

Создайте еще один каталог:

mkdir /srv/salt/nginx/files
cd /srv/salt/nginx/files

Откройте в нем конфигурационный файл:

nano /srv/salt/nginx/files/awesome-app.conf.jin

Поместите в конфигурационный файл следующие строки. Никаких изменений не требуется, если вы не используете частные сети (если да – измените значение 1 на 0 в выделенной красным строке):

### /srv/salt/nginx/files/awesome-app.conf.jin ###
##################################################
### Configuration file for Nginx to act as a
### reverse proxy for an app farm.
# Define the app servers that we're in front of.
upstream awesome-app {
{% for server, addrs in salt['mine.get']('roles:appserver', 'network.ip_addrs', expr_form='grain').items() %}
server {{ addrs[0] }}:1337;
{% endfor %}
}
# Forward all port 80 http traffic to our app farm, defined above as 'awesome-app'.
server {
listen       80;
server_name  {{ salt['network.ip_addrs']()[1] }};  # <-- change the '1' to '0' if you're not using
#     Private networking.
access_log  /var/log/nginx/awesome-app.access.log;
error_log  /var/log/nginx/awesome-app.error.log;
## forward request to awesome-app ##
location / {
proxy_pass  http://awesome-app;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

Используйте расширение .jin, чтобы указать, что файл содержит шаблоны Jinja. Jinja помогает организовать логику файлов, чтобы вы могли динамически генерировать детали конфигурации.

Этот конфигурационный файл позволяет Nginx принимать весь HTTP-трафик с порта 80 и перенаправлять его на серверы приложения. Он состоит из двух частей: upstream (определяет серверы приложений) и конфигурации прокси-сервера.

Давайте рассмотрим раздел upstream. Обычный upstream (не в шаблоне) содержит набор IP-адресов. Однако сейчас мы не знаем IP-адреса миньонов, поскольку их просто не существует, а редактировать файлы конфигурации вручную нет смысла (так ничего автоматизировать не получится, вы все будете делать сами).

Помните строки mine_function в map-файле? Миньоны передают свои IP-адреса мастеру Salt, который хранит их как раз для такого случая.

{% for server, addrs in salt['mine.get']('roles:appserver', 'network.ip_addrs', expr_form='grain').items() %}

Это цикл for Jinja, который запускает произвольные функции Salt. В данном случае он запускает mine.get. Вот все параметры:

  • roles:appserver собирает данные только от миньонов с ролью appserver.
  • network.ip_addrs определяет данные, которые нужно выбрать из mine-хранилища.
  • expr_form=’grain’ настраивает таргетинг миньонов по зернам.

После выполнения этого цикла переменная {{addrs}} содержит список IP-адресов (даже если в нем только один адрес).

Теперь рассмотрим строку:

server_name  {{ salt['network.ip_addrs']()[0] }};

Это то же самое, что и вызов mine (вызов функции Salt в Jinja), только проще. Эта строка вызывает network.ip_addrs и принимает первый элемент из возвращаемого списка. Она позволяет не редактировать файл вручную.

6: Определение серверов приложения

Обратный прокси-сервер не очень полезен, если у него нет приложения. Давайте создадим небольшое приложение Node.js, которое просто сообщит IP-адрес сервера, на котором оно работает (чтобы вы могли убедиться, что обе машины работают правильно).

Создайте новый каталог awesome-app и перейдите в него:

mkdir -p /srv/salt/awesome-app
cd /srv/salt/awesome-app

Создайте новое состояние:

nano /srv/salt/awesome-app/app.sls

Вставьте в него такой код:

### /srv/salt/awesome-app/app.sls ###
#####################################
### Install Nodejs and start a simple
### web application that reports the server IP.
install-app:
# Install prerequisites
pkg:
- installed
- names:
- node
- npm
- nodejs-legacy  # workaround for Debian systems
# Place our Node code
file:
- managed
- source: salt://awesome-app/files/app.js
- name: /root/app.js
# Install the package called 'forever'
cmd:
- run
- name: npm install forever -g
- require:
- pkg: install-app
run-app:
# Use 'forever' to start the server
cmd:
- run
- name: forever start app.js
- cwd: /root

Это состояние:

  • Устанавливает пакеты nodejs, npm и nodejs-legacy.
  • Добавляет файл JavaScript,который будет простым приложением.
  • Устанавливает Forever с помощью NPM.
  • Запускает приложение.

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

mkdir /srv/salt/awesome-app/files
cd /srv/salt/awesome-app/files

В нем создайте файл:

nano /srv/salt/awesome-app/files/app.js

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

/* /srv/salt/awesome-app/files/app.js
A simple Node.js web application that
reports the server's IP.
Shamefully stolen from StackOverflow:
http://stackoverflow.com/questions/10750303/how-can-i-get-the-local-ip-address-in-node-js
*/
var os = require('os');
var http = require('http');
http.createServer(function (req, res) {
var interfaces = os.networkInterfaces();
var addresses = [];
for (k in interfaces) {
for (k2 in interfaces[k]) {
var address = interfaces[k][k2];
if (address.family == 'IPv4' && !address.internal) {
addresses.push(address.address)
}
}
}
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(JSON.stringify(addresses));
}).listen(1337, '0.0.0.0');
console.log('Server listening on port 1337');

Это простой сервер Node.js, который делает одно: принимает HTTP-запросы на порт 1337 и в ответ отправляет IP-адреса сервера.

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

/srv/salt
├── awesome-app
│   ├── app.sls
│   └── files
│       └── app.js
└── nginx
├── rproxy.sls
└── files
└── awesome-app.conf.jin

7: Развертывание приложения

Развертывание серверов с помощью Salt Cloud

Запустите команду для развертывания серверов:

salt-cloud -m /etc/salt/cloud.maps.d/app-with-rproxy.map

Подождите, пока Salt Cloud завершит работу. Затем убедитесь, что развертывание прошло успешно.

salt -G 'roles:appserver' test.ping

Вы увидите:

appserver-02:
True
appserver-01:
True

Пропингуйте обратный прокси:

salt -G 'roles:rproxy' test.ping
nginx-rproxy:
True

Сборка приложения

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

salt -G 'roles:appserver' state.sls awesome-app.app

Вывод должен заканчиваться так:

------------
Succeeded: 6 (changed=6)
Failed:    0
------------
Total states run:     6

Теперь соберите обратный прокси:

salt -G 'roles:rproxy' state.sls nginx.rproxy

Вывод должен заканчиваться так:

------------
Succeeded: 4 (changed=4)
Failed:    0
------------
Total states run:     4

Первая команда (для серверов приложений) приняла состояние Salt и выполнила его на двух серверах приложений. Это привело к тому, что у вас появились две машины с идентичной конфигурацией и идентичными версиями кода.

Вторая команда (для обратного прокси-сервера) выполнила состояние Salt, предназначенное для Nginx. Она установила Nginx и добавила файл конфигурации, динамически заполняя IP-адреса серверов приложений.

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

salt -G 'roles:rproxy' network.ip_addrs

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

Введите внешний IP-адрес в браузер и посетите страницу. Обновите страницу несколько раз, чтобы убедиться, что Nginx проксирует запросы между двумя серверами приложений. Вы должны увидеть, что IP-адреса меняются.

Если вы видите один и тот же IP-адрес, даже обновив страницу несколько раз, это, вероятно, связано с кэшированием браузера. Вместо этого вы можете попробовать использовать curl. Выполните эту команду несколько раз и следите за выводом:

curl http://ip-of-nginx-rproxy

Полностью автоматизировать развертывание приложений можно через OverState. Это позволяет создать единую команду, с помощью которой Salt будет собирать серверы приложений, прежде чем перейти к созданию обратного прокси, что обеспечит правильный порядок процесса сборки.

8: Масштабирование (опционально)

Цель Salt – автоматизировать процесс сборки; цель Salt Cloud и map-файлов –упростить масштабирование развертывания. Если вы хотите добавить еще несколько серверов приложений (скажем, еще два), вы должны обновить свой map-файл так:

### /etc/salt/cloud.maps.d/app-with-rproxy.map ###
#####################################################
ubuntu_512MB_ny3:
- nginx-rproxy:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: rproxy
ubuntu_1GB_ny3:
- appserver-01:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-02:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-03:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-04:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver

Снова запустите команду salt-cloud и команды salt:

salt-cloud -m /etc/salt/cloud.maps.d/app-with-rproxy.map
salt -G 'roles:appserver' state.sls awesome-app.app
salt -G 'roles:rproxy' state.sls nginx.rproxy

Повторный запуск Salt не повлияет на существующие серверы, новые серверы будут собраны по спецификации, а конфигурация Nginx будет обновлена для поддержки маршрутизации трафика на новые серверы приложений.

Заключение

На примере этого простого приложения Node.js вы можете развернуть более сложное приложение. К слову, Salt поддерживает множество других языков программирования.

Если вы хотите использовать эту инфраструктуру для развертывания своего собственного приложения, вам просто нужно автоматизировать задачу установки вашего приложения на сервере (либо с помощью сценария, либо с помощью состояний Salt) и заменить название приложения awesome-app именем своего приложения.

Tags: , , , ,