Пример настройки кластера Keepalived
Для работы АТС Asterisk в режиме кластера, когда используется 2 сервера (один главный, второй резервный) необходимо применять один общий ip адрес для регистрации sip абонентов. Когда главный работающий сервер выходит из строя, ip адрес должен переключиться на запасной сервер, тем самым устройства пере подключатся на резервный сервер. Для реализации данного функционала отвечает сервис keepalived.
Перед началом работы проверьте настройки даты и времени, должна быть синхронизация с NTP сервером. Если будет рассинхронизация Keepalived может вести себя непредсказуемо.

Схема резервирования
В инструкции ниже используются следующие параметры узлов кластера:
Узел 1
Имя хоста: node1-keepalived
IP-адрес на eth0: 192.168.77.88/24
IP-адрес на eth1: 10.255.255.1/30
Узел 2
Имя хоста: node2-keepalived
IP-адрес на eth0: 192.168.77.89/24
IP-адрес на eth1: 10.255.255.2/30
IP-адрес кластера: 192.168.77.90/24
Интерфейсом eth1 сервера соединены между собой для надежности канала.
Настройка keepalived
Для работоспособности такой схемы необходимо. Чтобы резервный сервер регулярно получал все изменения основного сервера. Для этого применяется технология WarmSpare, когда на главном сервере делается бэкап конфигурации, далее бэкап переносится на резервный сервер и на резервном сервере разворачивается.
Первое, что необходимо сделать, это установить keepalived утилиты keepalived и sipsak на обоих серверах.
yum install keepalived
cd /usr/src/
git clone https://github.com/nils-ohlmeier/sipsak.git
cd sipsak/
autoreconf --install
./configure
make && make install
Keepalived утилита непосредственно управляет состоянием кластера.
Sipsak опрашивает каждый сервер Asterisk по порту 5160, отправляет OPTIONS, на запрос должен получить ответ 200 OK, если получен иной ответ, Asterisk считается неработоспособным и утилита Keepalived на основании ответа будет принимать решение, что делать с кластером. Если резервный сервер получает ответ на запрос 200 OK, а главный сервер нет, то будет переключение телефонии на резервный сервер.
Основная конфигурация утилиты keepalived расположена в файле:
/etc/keepalived/keepalived.conf эту конфиграцию необходимо разместить на обоих серверах с учетом адресации.
global_defs {
router_id PBX
script_user root
enable_script_security
#script_user keepalived_script
}
vrrp_script check_sip {
script "/etc/keepalived/check_sip.sh"
interval 10
fall 2
rise 1
}
vrrp_instance VI_PBX {
state BACKUP
interface eth1
virtual_router_id 51
advert_int 1
priority 100
authentication {
auth_type PASS
auth_pass XXXX
}
preempt
preempt_delay 15
# nopreempt – если узел стал MASTER, он останется им, даже если вернётся более приоритетный узел.
unicast_src_ip 10.255.255.1
unicast_peer {
10.255.255.2
}
virtual_ipaddress {
192.168.77.90/24 dev eth0 label eth0:0
}
virtual_routes {
default via 192.168.77.1 dev eth0 src 192.168.77.90
192.168.77.0/24 via 192.168.77.1 dev eth0 src 192.168.77.90
}
track_script {
check_sip
}
notify_master "/etc/keepalived/master.sh"
notify_backup "/etc/keepalived/backup.sh"
#notify_fault "/etc/keepalived/fault.sh"
}
unicast_src_ip прописывается локальный адрес сервера с которого будут отправляться запросы ко второму серверу.
unicast_peer прописывается адрес резервного сервера.
virtual_ipaddress прописывается ip разделяемый (мигрирующий)
virtual_routes прописывается маршрут с src разделяемым адресом
notify_master «/etc/keepalived/master.sh» скрипт указывающий, что нужно сделать, при переключении текущего сервера в MASTER
notify_backup «/etc/keepalived/backup.sh» скрипт указывающий, что нужно сделать, при переключении текущего сервера в BACKUP
Скрипт /etc/keepalived/check_sip.sh опрашивает сервера Asterisk на доступность по порту 5160. Размещаем на двух серверах. Ставим права на исполнение.
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
my_hosts="$(getent hosts)"
local_node_ip="$(echo "$my_hosts" | fgrep a-local | awk '{print $1}')"
remote_node_ip="$(echo "$my_hosts" | fgrep a-other | awk '{print $1}')"
if [ "$local_node_ip" == "" ]; then
logger -p local0.err "ERROR (keepalived check_sip.sh) Failed to determine local_node_ip";
exit 0
fi
if [ "$remote_node_ip" == "" ]; then
logger -p local0.err "ERROR (keepalived check_sip.sh) Failed to determine remote_node_ip";
exit 0
fi
sipsak -s sip:keepalived@localhost:5160 -D 2 >/dev/null 2>&1
if [ $? -eq 0 ]; then
exit 0
fi
sipsak -s sip:keepalived@"$remote_node_ip":5160 -D 2 >/dev/null 2>&1
if [ $? -eq 0 ]; then
exit 1
fi
exit 0
Тут стоит отметить, что опрос нужно делать на chan_sip протокол, т.к. на любой options он дает ответ 200 OK, PJSIP требует аутентификацию, более строгий. Если нет соответствующего endpoint-a ответ будет с кодом 401, SipSak вернет .
Также стоит обратить внимание, что запросы отправляются на адреса, взятые из файла /etc/hosts.
Пример для первой ноды:
192.168.77.89 a-other
192.168.77.88 a-local
Пример для второй ноды:
192.168.77.88 a-other
192.168.77.89 a-local
Содержимое файла /etc/keepalived/master.sh Размещается на двух серверах.
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/remi/php56/root/usr/bin
mysqlpass=»$(grep «AMPDBPASS» /etc/freepbx.conf | awk ‘{printf $3}’ | sed -e «s/[‘|;]//g»)»
mysqlscript=»update trunks set disabled=’off’;»
mysql —user=freepbxuser —password=»$mysqlpass» asterisk <<< «$mysqlscript»
systemctl restart asterisk > /dev/null 2>&1
/usr/sbin/fwconsole reload > /dev/null 2>&1 &
curl https://api.telegram.org/botXXXXXXXXXXXXXXX/sendMessage?chat_id=-XXXXXXXXX -F text=»Server $(hostname) switched to MASTER mode» > /dev/null 2>&1 &
curl https://api.telegram.org/botXXXXXXXXXXXXXXXX/sendMessage?chat_id=-XXXXXXXXX -F text=»Server $(hostname) switched to MASTER mode» > /dev/null 2>&1 &
echo «Server $(hostname) switched to MASTER mode» | mail -s «Server $(hostname) switched to MASTER mode» -S smtp=»mail.XXXXXX.ru:25″ -r from@email.ru to@email.ru
Как видно из содержания файла, на текущем сервере включаются транки и перезагружается астериск, также отправляются сообщения в телеграмм и на email
Содержимое файла /etc/keepalived/backup.sh Размещается на двух серверах.
#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/remi/php56/root/usr/bin
mysqlpass=»$(grep «AMPDBPASS» /etc/freepbx.conf | awk ‘{printf $3}’ | sed -e «s/[‘|;]//g»)»
mysqlscript=»update trunks set disabled=’on’;»
mysql —user=freepbxuser —password=»$mysqlpass» asterisk <<< «$mysqlscript»
systemctl restart asterisk > /dev/null 2>&1
fwconsole reload > /dev/null 2>&1 &
curl https://api.telegram.org/botXXXXXXXXXXX/sendMessage?chat_id=-XXXXXXXXX -F text=»Server $(hostname) switched to SLAVE mode» > /dev/null 2>&1 &
curl https://api.telegram.org/XXXXXXXXXXXXX/sendMessage?chat_id=-XXXXXXX -F text=»Server $(hostname) switched to SLAVE mode» > /dev/null 2>&1 &
echo «Server $(hostname) switched to SLAVE mode» | mail -s «Server $(hostname) switched to SLAVE mode» -S smtp=»mail-XXXXX.ru:25″ -r from@email.ru to@email.ru
Как видно из скрипта /etc/keepalived/backup.sh при переключении текущего сервера в режим BACKUP выключаем транки, перезагружаем asterisk и отправляем сообщение о состоянии в телеграмм и на email.
Аналогичная конфигурация на резервном сервере.
Настройка WarmSpare
Необходимо сгенерировать новый ключ ssh:
ssh-keygen -t rsa -b 4096 -m PEM -C «asterisk»
Обязательно ключ получить нужно в старом формате который использовался в openssh < 7.8 версии. Используем ключ -m PEM
Если сгенерировать в новом формате, то Freepbx не сможет подключиться по ssh ко второму серверу.
На основном сервере в первую очередь необходимо настроить удаленное хранилище для переноса бэкапов на резервный сервер. Это делается в веб интерфейсе FreepBX Settings -> Filestore (Рисунок 1)

Рисунок 1. Выбор пункта меню настройки хранилища
Тип хранилища необходимо выбрать SSH (Рисунок 2)

Рисунок 2. Выбор типа хранилища
Добавляем новый сервер SSH (Рисунок 3)

Рисунок 3. Добавление нового сервера
Server Name – Указать имя хранилища
Description – указать описание
Hostname – указать ip адрес резервного сервера
Port – порт ssh подключения
Username – логин подключения
Key – ключ подключения
Path – путь к папке хранения бэкапов
Далее переходим к настройке бэкапов. Admin -> Backup & Restore (Рисунок 4)

Рисунок 4. Выбор меню настройки бэкапов
Создаем новый Backup. (Рисунок 5)

Рисунок 5. Создание бэкапа.
Backup Name указываем имя будущего бэкапа
Backup Description — описание
В Custom Files выбираем все, что необходимо перенести на резервный сервер.
В разделе Storage выбирает место хранения бэкапа. Выбираем наш ранее созданный ssh сервер.
В Scheduling выбираем периодичность создания бэкапа.
Delete After Runs – указываем, сколько последних файлов бэкапа нужно хранить
Pre-Backup Hook – выполнить скрипт перед созданием бэкапа.
Его содержание такое:
Файл /usr/local/bin/disable_trunks.sh
#!/bin/bash
search=»secondary eth0:0″
address=$(ip -o addr)
mysqlpass=»$(grep «AMPDBPASS» /etc/freepbx.conf | awk ‘{printf $3}’ | sed -e «s/[‘|;]//g»)»
if [[ «$address» == *»$search»* ]];
then
mysqlscript=»update trunks set disabled=’on’;»
mysql —user=freepbxuser —password=»$mysqlpass» asterisk <<< «$mysqlscript»
else mysqlscript=»update trunks set disabled=’off’;»
mysql —user=freepbxuser —password=»$mysqlpass» asterisk <<< «$mysqlscript»
fi
Принцип его работы такой, если это мастер сервер, то в базе данных необходимо отключить транки. Это делается для того, чтобы при развертывании бэкапа на резервной ноде транки были выключены.
Post-Backup Hook – скрипт /usr/local/bin/post_backup.sh который необходимо выполнить после создания бэкапа.
#!/bin/bash
dumpfile="/tmp/mysql_unsupported_tables.sql"
mysqlpass="$(grep "AMPDBPASS" /etc/freepbx.conf | awk '{printf $3}' | sed -e "s/['|;]//g")"
search="secondary eth0:0"
address=$(ip -o addr)
if [[ "$address" == *"$search"* ]];
then
mysqlscript="update trunks set disabled='off';"
mysql --user=freepbxuser --password="$mysqlpass" asterisk <<< "$mysqlscript"
else
mysqlscript="update trunks set disabled='on';"
mysql --user=freepbxuser --password="$mysqlpass" asterisk <<< "$mysqlscript"
fi
tables_context="customcontexts_contexts customcontexts_contexts_list customcontexts_includes_list customcontexts_includes customcontexts_module"
tables_endpointman="endpointman_brand_list endpointman_custom_configs endpointman_global_vars endpointman_line_list endpointman_mac_list endpointman_model_list endpointman_oui_list endpointman_product_list endpointman_template_list"
mysqldump --user=freepbxuser --password="$mysqlpass" asterisk $tables_context $tables_endpointman | gzip > $dumpfile.gz
my_hosts="$(getent hosts)"
remote_node_ip="$(echo "$my_hosts" | fgrep a-other | awk '{print $1}')"
remote_node_ip="${remote_node_ip//[[:space:]]/}"
scp -i /var/lib/asterisk/.ssh/id_rsa $dumpfile.gz $astdbfile root@$remote_node_ip:/tmp/ >/dev/null 2>&1
remote_command="gunzip < $dumpfile.gz | mysql --user=freepbxuser --password=$mysqlpass asterisk"
/usr/bin/ssh -i /var/lib/asterisk/.ssh/id_rsa root@$remote_node_ip "$remote_command" >/dev/null 2>&1 & disown
/usr/bin/ssh -i /var/lib/asterisk/.ssh/id_rsa root@$remote_node_ip "/usr/bin/chown -R asterisk:asterisk /var/spool/asterisk/backup/warmspare/*" >/dev/null 2>&1 & disown
Его принцип работы следующий: бэкап стандартный переносит не все модули FreePBX, Unsupported модули он не переносит и их необходимо переносить вручную. Скриптом мы переносим настройки автопровижининга и настройки custom context на резервный сервер, а также включаем транки, они могли быть выключены предыдущим скриптом перед копированием.
Apply Configs – выбираем, чтобы на резервном сервере была применена конфигурация
Enable – ставим yes
Warm Spare Server – выбираем удаленный резервный сервер.

Рисунок 6. Создание бэкапа.
Настройка FreePBX
Чтобы появился список доступных интерфейсов, необходимо включить опцию «Show Advanced Settings», как показано ниже на скриншоте.
Для правильной работы сервера Asterisk на протоколе pj_sip необходимо указать транспорт на разделяемый интерфейс, удобнее, когда транспорт можно выбрать в веб интерфейсе и им управлять (Рисунок 7). Для этого необходимо доработать исходный файл во FreePBX.

Рисунок 7. Разделяемый интерфейс в Freepbx.
Открываем файл /var/www/html/admin/modules/sipsettings/Sipsettings.class.php на редактирование и находим код:
public function parseIpAddr($ipaddr, $interfaces = false) {
if (!is_array($interfaces)) {
$interfaces = array ( 'auto' => array('0.0.0.0', 'All', '0') );
}
foreach ($ipaddr as $line) {
$vals = preg_split("/\s+/", $line);
if (empty($vals[1]) || $vals[1] == "lo" || $vals[1] == "lo:") {
continue;
}
// We only care about ipv4 (inet) lines, or definition lines
if ($vals[2] != "inet" && $vals[3] != "mtu") {
continue;
}
if (preg_match("/(.+?)(?:@.+)?:$/", $vals[1], $res)) { // Matches vlans, which are eth0.100@eth0
// It's a network definition.
// This won't clobber an exsiting one, as it always comes
// before the IP addresses.
$interfaces[$res[1]] = array();
continue;
}
if (!isset($vals[8])) {
// FREEPBX-12382 - Can't parse this line. Probably openvz machine.
continue;
}
// Is this a named secondary?
if ($vals[8] == "secondary") {
// I shall call him sqishy and he shall be mine, and he shall be my squishy.
if (isset($vals[9])) {
$intname = $vals[9];
if (!isset($interfaces[$intname])) {
$interfaces[$intname] = array();}
} else {
// Whatevs. I don't care. Fine. Be unnamed.
$intname = $vals[1];
}
} else if (strpos($vals[8], ":") !== false) {
// this is an UNNAMED secondary, eg eth0:0
$intname = trim($vals[8]);
// Depending on the version of 'ip', there may be a backslash here.
$intname = rtrim($intname, '\\');
if (!isset($interfaces[$intname])) {
$interfaces[$intname] = array();
}
} else {
else переносим на новую строку и сверху между закрывающей фигурной скобкой и else вставляем код:
//ADD FOR KEEPALIVED
else if (strpos($vals[7], ":") !== false) {
// this is an UNNAMED secondary, eg eth0:0
$intname = trim($vals[7]);
// Depending on the version of 'ip', there may be a backslash here.
$intname = rtrim($intname, '\\');
if (!isset($interfaces[$intname])) {
$interfaces[$intname] = array();
}
}
//END
После этого в веб интерфейсе отобразится разделяемый интерфейс.
И еще правим один файл, для корректной работы Apply Config после развертывания бэкапа: /var/www/html/admin/libraries/Console/Reload.class.php
Находим строчку 502 rename($targetdir, «$targetdir.badasset»);
И над ней вставляем код : system(‘rm -rf ‘.$targetdir.».badasset»);
Asterisk хранит не всю информацию в конфигурационных файлах. Часть информации хранит в базе данных astdb.
Модуль «Backup and Restore» по умолчанию берёт из astdb только часть разделов (например, DND, CF, DEVICE, AMPUSER и т.п.), а Q/ игнорирует. Таким образом состав динамических операторов в очередях не переносится. На случай этой необходимости скрипт на Master сервере /usr/local/bin/post_backup.sh делает бэкап базы данных astdb и переносит его на второй сервер. Но восстановление бэкапа требует остановки asterisk. На резервном сервере необходимо создать скрипт /usr/local/bin/post_restore.sh со следующим содержимым:
#!/bin/bash
astdbfile="/tmp/astdb.sql"
systemctl stop asterisk >/dev/null 2>&1
rm -f /var/lib/asterisk/astdb.sqlite3
sqlite3 /var/lib/asterisk/astdb.sqlite3 < $astdbfile
chown asterisk:asterisk /var/lib/asterisk/astdb.sqlite3 >/dev/null 2>&1
systemctl start asterisk >/dev/null 2>&1
В в cron на резервном сервере добавляем выполнение скрипта немного позже, чем делается бэкап, если он выполнится во время развертывания бэкапа, то бэкап не выполнится. Т.к. производится остановка asterisk.
20 2 * * * /usr/local/bin/post_restore.sh
На этом настройка завершена.
Остались вопросы?
Я - Першин Артём, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.
категории
- DECT
- Linux
- Вспомогательный софт при работе с Asterisk
- Интеграция с CRM и другими системами
- Интеграция с другими АТС
- Использование Elastix
- Использование FreePBX
- Книга
- Мониторинг и траблшутинг
- Настройка Asterisk
- Настройка IP-телефонов
- Настройка VoIP-оборудования
- Новости и Статьи
- Подключение операторов связи
- Разработка под Asterisk
- Установка Asterisk
VoIP оборудование
ближайшие курсы
Новые статьи
10 доводов в пользу Asterisk
Распространяется бесплатно.
Asterisk – программное обеспечение с открытым исходным кодом, распространяется по лицензии GPL. Следовательно, установив один раз Asterisk вам не придется дополнительно платить за новых абонентов, подключение новых транков, расширение функционала и прочие лицензии. Это приближает стоимость владения станцией к нулю.
Безопасен в использовании.
Любое программное обеспечение может стать объектом интереса злоумышленников, в том числе телефонная станция. Однако, сам Asterisk, а также операционная система, на которой он работает, дают множество инструментов защиты от любых атак. При грамотной настройке безопасности у злоумышленников нет никаких шансов попасть на станцию.
Надежен в эксплуатации.
Время работы серверов некоторых наших клиентов исчисляется годами. Это значит, что Asterisk работает несколько лет, ему не требуются никакие перезагрузки или принудительные отключения. А еще это говорит о том, что в районе отличная ситуация с электроэнергией, но это уже не заслуга Asterisk.
Гибкий в настройке.
Зачастую возможности Asterisk ограничивает только фантазия пользователя. Ни один конструктор шаблонов не сравнится с Asterisk по гибкости настройки. Это позволяет решать с помощью Asterisk любые бизнес задачи, даже те, в которых выбор в его пользу не кажется изначально очевидным.
Имеет огромный функционал.
Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.
Интегрируется с любыми системами.
То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.
Позволяет телефонизировать офис за считанные часы.
В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.
Отличная масштабируемость.
Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.
Повышает управляемость бизнеса.
Asterisk дает не просто набор полезных функций, он повышает управляемость организации, качества и комфортности управления, а также увеличивает прозрачность бизнеса для руководства. Достичь этого можно, например, за счет автоматизации отчетов, подключения бота в Telegram, санкционированного доступа к станции из любой точки мира.
Снижает расходы на связь.
Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.
База знаний
IP-АТС
Оборудование
О нас




