Friday, May 3, 2013

OpenSIPS 1.8.2: Load Balancer on a Redis Cluster



OpenSIPS 1.8.2: Load Balancer on a Redis Cluster


OpenSIPS 1.8.2: Load Balancer on a Redis Cluster
In this article we will see how to configure OpenSIPS with two servers in a cluster module Redis Load_Balancer (a database non-relational NoSQL Family). This module via chachedb_redis. In this way the two servers share the load in real time server configuration presented in BALANCING load. This type of scenario may be an optimal solution for SIP termination providers or generally for any needed configuration where redundant media server and OpenSIPS Server Cluster.
In the two servers is installed minimal version of CentOS 6.3

A server Local IP: 192.168.168.60

Local IP Server B: 192.168.180.70

In both servers:

yum update -y
reboot
We entered again on both servers and continue with the installation of the required packages for the whole process:

yum install mysql mysql-devel mysql-server gcc gcc-c++ bison bison-devel flex make expat expat-devel unixODBC-devel net-snmp-devel
yum install subversion libxml2 libxml2-devel openssl-devel xmlrpc-c-devel lynx pcre pcre-devel ncurses-devel ncurses-libs
To Redis:
yum install tcl git ruby
OpenSIPS CACHEDB_REDIS module is based on client Redis "hiredis". It will download, compile and install:
cd /usr/src
git clone https://github.com/redis/hiredis.git hiredis
cd hiredis
make
make install
The download version 1.8.2 of OpenSIPS:
cd /usr/src
svn co https://opensips.svn.sourceforge.net/svnroot/opensips/branches/1.8 opensips_1_8
cd opensips_1_8
make menuconfig
You enter the menu "Set Compile Options" and then "Configure Excluded Modules". Modules are selected "cachedb_redis", "db_mysql" and "regex".

He turns back to the <-, select "Save Changes" and press the Send key twice. He turns back to the <- and select "" Exit & Save All Changes ".

Shipping key is pressed to exit the menu. It compiles and installs:
make prefix=/ all
make prefix=/ install
Is installed OpenSIPS startup script:
cd packaging/fedora
nano opensips.init
We change this line:
oser=/usr/sbin/$prog
to read:
oser=/sbin/$prog
Save the changes and end the configuration:
chmod 755 opensips.init
cp opensips.init /etc/init.d/opensips
chkconfig --add opensips
chkconfig opensips on
We can pass compilation and installation of Redis. The version that supports the Cluster is unstable that came down from github:
cd /usr/src
git clone https://github.com/antirez/redis.git redisuns
cd redisuns
make
make test
make install


After installation pass to the server configuration A.

Server A

On this server we will create the database for OpenSIPS then be shared with the second server OpenSIPS:
chkconfig mysqld on
service mysqld start
mysqladmin-u root password sesame
We entered on the client:
mysql-u root-psesamo
We created the OpenSIPS database:
mysql> create database OpenSIPS;
We left the client and configure OpenSIPS to create tables:
mv / etc / OpenSIPS / opensipsctlrc / etc / OpenSIPS / opensipsctlrc.old
nano / etc / OpenSIPS / opensipsctlrc
copy the following lines:
SIP_DOMAIN = 198.168.168.60
DBENGINE = MYSQL
DBHOST = localhost
DBNAME = OpenSIPS
DBRWUSER = OpenSIPS
DBRWPW = "opensipsrw"
DBROOTUSER = "root"
INSTALL_EXTRA_TABLES = ask
INSTALL_PRESENCE_TABLES = ask
OSIPS_FIFO = "/ tmp / opensips_fifo"
VERIFY_ACL = 1
PID_FILE = / var / run / opensips.pid
SIP_DOMAIN is the IP address or domain name of our server Linux.Guardamos changes and create the tables:
opensipsdbctl create
 





We return to enter the MySQL client and create access permissions for the second server OpenSIPS:
mysql-u root-psesamo
mysql> grant all privileges on OpenSIPS. * to 'OpenSIPS' @ '192 .168.180.70 'identified by' opensipsrw ';
We insert two servers load_balancer table:
mysql> insert into load_balancer (group_id, dst_uri, resources, probe_mode, description) values ​​('1 ',' sip: sip.servidor1.org ',' voip / s = 20 ', '2', '20 SIP channels') , ('1 ',' sip: sip.servidor2.org ',' voip / s = 20 ', '2', '20 SIP channels');
mysql> quit
When we share resources through the Redis Cluster, we have to indicate the name of the resource / s at the end. In this case: voip / s
We passed OpenSIPS configuration:
mv / etc / OpenSIPS / opensips.cfg / etc / OpenSIPS / opensips.cfg.old
nano / etc / OpenSIPS / opensips.cfg
We copy the following lines:
# # # # # # # Global Parameters # # # # # # # # #
debug = 3
log_stderror = no
log_facility = LOG_LOCAL6
fork = yes
children = 4
listen = udp: 192.168.168.60:5060
disable_tcp = yes
disable_tls = yes
auto_aliases = no
# # # # # # # Modules Section # # # # # # # #
# Set module path
mpath = "/ lib / OpenSIPS / modules /"
# # # # SIGNALING module
loadmodule "signaling.so"
# # # # Stateless module
loadmodule "sl.so"
# # # # Transaction Module
loadmodule "tm.so"
modparam ("tm", "fr_timer", 5)
modparam ("tm", "fr_inv_timer", 30)
modparam ("tm", "restart_fr_on_each_reply", 0)
modparam ("tm", "onreply_avp_mode", 1)
# # # # Record Route Module
loadmodule "rr.so"
modparam ("rr", "append_fromtag", 0)
# # # # MAX FORWARD module
loadmodule "maxfwd.so"
# # # # SIP MSG Operations Module
loadmodule "sipmsgops.so"
# # # # FIFO Management Interface
loadmodule "mi_fifo.so"
modparam ("mi_fifo", "fifo_name", "/ tmp / opensips_fifo")
modparam ("mi_fifo", "fifo_mode", 0666)
# # # # URI module
loadmodule "uri.so"
modparam ("uri", "use_uri_table", 0)
# # # # User module LOCation
loadmodule "usrloc.so"
modparam ("usrloc", "nat_bflag", 10)
modparam ("usrloc", "db_mode", 0)
# # # # REGISTER module
loadmodule "registrar.so"
modparam ("registrar", "tcp_persistent_flag", 7)
# # # # Accounting module
loadmodule "acc.so"
modparam ("acc", "early_media", 0)
modparam ("acc", "report_cancels", 0)
modparam ("acc", "detect_direction", 0)
modparam ("acc", "failed_transaction_flag", 3)
modparam ("acc", "log_flag", 1)
modparam ("acc", "log_missed_flag", 2)
# # # # CACHEDB_REDIS module
loadmodule "cachedb_redis.so"
modparam ("cachedb_redis", "cachedb_url", "redis :/ / localhost: 6379 /")
loadmodule "db_mysql.so"
loadmodule "dialog.so"
loadmodule "load_balancer.so"
modparam ("dialog", "cachedb_url", "redis :/ / localhost: 6379 /")
modparam ("load_balancer", "db_url", "mysql :/ / opensipsrw: sesame @ localhost / OpenSIPS")
modparam ("load_balancer", "probing_interval", 60)
modparam ("load_balancer", "probing_reply_codes", "404")
# # # # # # # Routing Logic # # # # # # # #
# Main request routing logic
route {
if (! mf_process_maxfwd_header ("10")) {
sl_send_reply ("483", "Too Many Hops");
exit;
}
if (has_totag ()) {
# Sequential request withing a dialog Should
# Take the path determined to by record-routing
if (loose_route ()) {
if (is_method ("BYE")) {
setflag (1); # do accounting ...
setflag (3); # ... even if the transaction fails
} Else if (is_method ("INVITE")) {
# Even if in most of it the cases is useless, do RR for
# Re-INVITEs alos, as some buggy clients do change route in September
# During the dialog.
RECORD_ROUTE ();
}
           
# Route it out to whatever destination was September by loose_route ()
# In $ du (destination URI).
route (1);
Else {}
if (is_method ("ACK")) {
if (t_check_trans ()) {
# Non loose-route, but stateful ACK; must be an ACK after
# A 487 or eg 404 from upstream server
t_relay ();
exit;
Else {}
# ACK without matching transaction ->
# Ignore and discard
exit;
}
}
sl_send_reply ("404", "Not here");
}
exit;
}
# CANCEL processing
if (is_method ("CANCEL"))
{
if (t_check_trans ())
t_relay ();
exit;
}
t_check_trans ();
# Preloaded route checking
if (loose_route ()) {
xlog ("L_ERR"
"Attempt to route with preloaded Route's [$ fu / $ tu / $ ru / $ ci]");
if (! is_method ("ACK"))
sl_send_reply ("403", "Preload Route denied");
exit;
}
# Record routing
if (! is_method ("REGISTER | MESSAGE"))
RECORD_ROUTE ();
# Account only INVITEs
if (is_method ("INVITE")) {
setflag (1); # do accounting
}
if ($ rU == NULL) {
# Request with no Username in RURI
sl_send_reply ("484", "Address Incomplete");
exit;
}
if (! load_balance ("1", "voip / s", "1")) {
send_reply ("500", "Failure to route");
exit;
}
route (1);
}
route [1] {
# For INVITEs enable some additional helper routes
if (is_method ("INVITE")) {
t_on_branch ("2");
t_on_reply ("2");
t_on_failure ("1");
}
if (! t_relay ()) {
send_reply ("500", "Internal Error");
};
exit;
}
branch_route [2] {
xlog ("new branch at $ ru \ n");
}
onreply_route [2] {
xlog ("incoming reply \ n");
}
failure_route [1] {
if (t_was_cancelled ()) {
exit;
}
}
The important lines:
  • modparam ("cachedb_redis", "cachedb_url", "redis :/ / localhost: 6379 /") - To connect to the local Redis server
  • modparam ("dialog", "cachedb_url", "redis :/ / localhost: 6379 /") - To share the dialogues through Redis Cluster
  • if (! load_balance ("1", "voip / s", "1")) {- To call resources configured in the table load_balancer

Save the changes and ended up with Redis:
mkdir / etc / redis
nano / etc / redis / redis.conf
not daemonize
pidfile / var / run / redis.pid
port 6379
timeout 0
loglevel notice
logfile stdout
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave and error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir. /
slave-serve-stale-data yes
read-only slave-yes
slave-priority 100
maxclients 10000
3GB maxmemory
-policy maxmemory noeviction
AppendOnly not
appendfsync everysec
-not-on-rewrite appendfsync not
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
cluster-enabled yes
cluster-config-file / etc/redis/nodes-6379.conf
slowlog-log-slower-than 10000
ZipList hash-max-512-entries
ZipList hash-max-64-value
ZipList list-max-512-entries
ZipList list-max-64-value
set-max-512-entries IntSet
ZipList zset-max-128-entries
ZipList zset-max-64-value
activerehashing yes
client-limit output-buffer-Normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit 8mb 32mb pubsub 60
The important lines:
  • cluster-enabled yes - To enable the Cluster
  • cluster-config-file / etc/redis/nodes-6379.conf - Redis file name will be stored in automatic data where Cluster and nodes
Save the changes and start Redis:
redis-server / etc / redis / redis.conf









Redis Cluster not found any yet because it has not yet set the second server. To exit: CTRL-C
Server B
first configure OpenSIPS:
mv / etc / OpenSIPS / opensips.cfg / etc / OpenSIPS / opensips.cfg.old
nano / etc / OpenSIPS / opensips.cfg
We copy the following lines:
# # # # # # # Global Parameters # # # # # # # # #
debug = 3
log_stderror = no
log_facility = LOG_LOCAL6
fork = yes
children = 4
listen = udp: 192.168.180.70:5060
disable_tcp = yes
disable_tls = yes
auto_aliases = no
# # # # # # # Modules Section # # # # # # # #
# Set module path
mpath = "/ lib / OpenSIPS / modules /"
# # # # SIGNALING module
loadmodule "signaling.so"
# # # # Stateless module
loadmodule "sl.so"
# # # # Transaction Module
loadmodule "tm.so"
modparam ("tm", "fr_timer", 5)
modparam ("tm", "fr_inv_timer", 30)
modparam ("tm", "restart_fr_on_each_reply", 0)
modparam ("tm", "onreply_avp_mode", 1)
# # # # Record Route Module
loadmodule "rr.so"
modparam ("rr", "append_fromtag", 0)
# # # # MAX FORWARD module
loadmodule "maxfwd.so"
# # # # SIP MSG Operations Module
loadmodule "sipmsgops.so"
# # # # FIFO Management Interface
loadmodule "mi_fifo.so"
modparam ("mi_fifo", "fifo_name", "/ tmp / opensips_fifo")
modparam ("mi_fifo", "fifo_mode", 0666)
# # # # URI module
loadmodule "uri.so"
modparam ("uri", "use_uri_table", 0)
# # # # User module LOCation
loadmodule "usrloc.so"
modparam ("usrloc", "nat_bflag", 10)
modparam ("usrloc", "db_mode", 0)
# # # # REGISTER module
loadmodule "registrar.so"
modparam ("registrar", "tcp_persistent_flag", 7)
# # # # Accounting module
loadmodule "acc.so"
modparam ("acc", "early_media", 0)
modparam ("acc", "report_cancels", 0)
modparam ("acc", "detect_direction", 0)
modparam ("acc", "failed_transaction_flag", 3)
modparam ("acc", "log_flag", 1)
modparam ("acc", "log_missed_flag", 2)
# # # # CACHEDB_REDIS module
loadmodule "cachedb_redis.so"
modparam ("cachedb_redis", "cachedb_url", "redis :/ / localhost: 6380 /")
loadmodule "db_mysql.so"
loadmodule "dialog.so"
loadmodule "load_balancer.so"
modparam ("dialog", "cachedb_url", "redis :/ / localhost: 6380 /")
modparam ("load_balancer", "db_url", "mysql :/ / OpenSIPS: opensipsrw@192.168.168.60 / OpenSIPS")
modparam ("load_balancer", "probing_interval", 60)
modparam ("load_balancer", "probing_reply_codes", "404")
# # # # # # # Routing Logic # # # # # # # #
# Main request routing logic
route {
if (! mf_process_maxfwd_header ("10")) {
sl_send_reply ("483", "Too Many Hops");
exit;
}
if (has_totag ()) {
# Sequential request withing a dialog Should
# Take the path determined to by record-routing
if (loose_route ()) {
if (is_method ("BYE")) {
setflag (1); # do accounting ...
setflag (3); # ... even if the transaction fails
} Else if (is_method ("INVITE")) {
# Even if in most of it the cases is useless, do RR for
# Re-INVITEs alos, as some buggy clients do change route in September
# During the dialog.
RECORD_ROUTE ();
}
# Route it out to whatever destination was September by loose_route ()
# In $ du (destination URI).
route (1);
Else {}
if (is_method ("ACK")) {
if (t_check_trans ()) {
# Non loose-route, but stateful ACK; must be an ACK after
# A 487 or eg 404 from upstream server
t_relay ();
exit;
Else {}
# ACK without matching transaction ->
# Ignore and discard
exit;
}
}
sl_send_reply ("404", "Not here");
}
exit;
}
# CANCEL processing
if (is_method ("CANCEL"))
{
if (t_check_trans ())
t_relay ();
exit;
}
t_check_trans ();
# Preloaded route checking
if (loose_route ()) {
xlog ("L_ERR"
"Attempt to route with preloaded Route's [$ fu / $ tu / $ ru / $ ci]");
if (! is_method ("ACK"))
sl_send_reply ("403", "Preload Route denied");
exit;
}
# Record routing
if (! is_method ("REGISTER | MESSAGE"))
RECORD_ROUTE ();
# Account only INVITEs
if (is_method ("INVITE")) {
setflag (1); # do accounting
}
if ($ rU == NULL) {
# Request with no Username in RURI
sl_send_reply ("484", "Address Incomplete");
exit;
}
if (! load_balance ("1", "voip / s", "1")) {
send_reply ("500", "Failure to route");
exit;
}
route (1);
}
route [1] {
# For INVITEs enable some additional helper routes
if (is_method ("INVITE")) {
t_on_branch ("2");
t_on_reply ("2");
t_on_failure ("1");
}
if (! t_relay ()) {
send_reply ("500", "Internal Error");
};
exit;
}
branch_route [2] {
xlog ("new branch at $ ru \ n");
}
onreply_route [2] {
xlog ("incoming reply \ n");
}
failure_route [1] {
if (t_was_cancelled ()) {
exit;
}
}

Save the changes and configure redistribution:
mkdir / etc / redis
nano / etc / redis / redis.conf
We copy the configuration to the second server:
not daemonize
pidfile / var / run / redis.pid
port 6380
timeout 0
loglevel notice
logfile stdout
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave and error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir. /
slave-serve-stale-data yes
read-only slave-yes
slave-priority 100
maxclients 10000
3GB maxmemory
-policy maxmemory noeviction
AppendOnly not
appendfsync everysec
-not-on-rewrite appendfsync not
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
lua-time-limit 5000
cluster-enabled yes
cluster-config-file / etc/redis/nodes-6380.conf
slowlog-log-slower-than 10000
ZipList hash-max-512-entries
ZipList hash-max-64-value
ZipList list-max-512-entries
ZipList list-max-64-value
set-max-512-entries IntSet
ZipList zset-max-128-entries
ZipList zset-max-64-value
activerehashing yes
client-limit output-buffer-Normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit 8mb 32mb pubsub 60
Save the changes and start Redis on both servers:
Server A:
redis-server / etc / redis / redis.conf
Server B:
redis-server / etc / redis / redis.conf
For each cluster node must know the other "introduce him." On Server A console enter Redis (in another terminal window):
redis-cli
We look at the situation of the Cluster:
redis 127.0.0.1:6379> cluster nodes
28cc1625f86f777065a057fd19cc0cb8d49cf4b3: 0 myself - 0 0 connected
There is still only one (local). We write:
redis 127.0.0.1:6379> 6380 192.168.180.70 cluster meet
OK
We look again:
redis 127.0.0.1:6379> cluster nodes
192.168.180.70:6380 a862b68040167034f20a1ec535769fc6e2e175e5 master - 1352482127 1352482127 connected
28cc1625f86f777065a057fd19cc0cb8d49cf4b3: 0 myself - 0 0 connected
Since the local node knows the other and vice versa.
We leave the console:
redis 127.0.0.1:6380> quit
In the console of both servers Redis appear:
A:
[15170] November 9 17:28:40.440 * Connecting with Node d376333ff55910a6d54c9c7dace20d7d92a5ef9a at 192.168.180.70:16380
B:
[13763] November 9 17:29:03.183 * Connecting with Node 86d0dc855d790c8ec42f63486e08b87613971d05 at 192.168.168.60:16379
Redis Cluster system provides a total of 4096 nodes Hash Slots . As nodes are two, the Hash Slots are allocated as follows:
Server A
Open another terminal window and type:
echo '(0 .. 2047). each {| x | puts "CLUSTER ADDSLOTS" + x.to_s}' | ruby | redis-cli-p 6379> / dev / null
Server B
Open another terminal window and type:
echo '(2048 .. 4095). each {| x | puts "CLUSTER ADDSLOTS" + x.to_s}' | ruby | redis-cli-p 6380> / dev / null
We left both Redis server with CTRL-C to return to start:
redis-server / etc / redis / redis.conf
We entered Redis server console A:
redis-cli
redis 127.0.0.1:6379> cluster info
cluster_state: ok
cluster_slots_assigned: 4096
cluster_slots_ok: 4096
cluster_slots_pfail: 0
cluster_slots_fail: 0
cluster_known_nodes 2
Both Cluster are working perfectly.
We leave the console:
redis 127.0.0.1:6379> quit
Bookseller OpenSIPS libhiredis.so.0.10 looking in the / usr / lib while in / usr / local / lib. In both servers create a symbolic link:
cd / usr / lib
ln-s / usr/local/lib/libhiredis.so.0.10 libhiredis.so.0.10
Now we can start both servers OpenSIPS:
OpenSIPS service start
Test
On another server (AsteriskA) in the same local network with Asterisk installed, send all calls to OpenSIPS servers:
nano / etc / asterisk / sip.conf
end of the file add the two OpenSIPS servers:
[OpensipsA]
type = peer
context = from-OpenSIPS
host = 192.168.180.70
disallow = all
allow = alaw
qualify = yes
[OpensipB]
type = peer
context = from-OpenSIPS
host = 192.168.168.60
disallow = all
allow = alaw
qualify = yes
Save the changes and reload the SIP settings
asterisk-rx "sip reload"
Timely Configure dialplan to send the calls to the server OpenSIPsA and if the server fails OpenSIPsB
Asterisk configure both servers present in the loading configuration BALANCING to accept INVITE arriving from the two servers OpenSIPS:
[OpensipA]
type = peer
context = opensipscluster
host = 192.168.180.70
disallow = all
allow = alaw
dtmfmode = rfc2833
nat = no
[OpensipB]
type = peer
context = opensipcluster
host = 192.168.160.60
disallow = all
allow = alaw
dtmfmode = rfc2833
nat = no
Save the changes and reload the SIP configuration:
asterisk-rx "sip reload"
Now create the context "opensipscluster" nel dialplan:
nano / etc / asterisk / extensions.conf
[Opensipscluster]
exten => _X., 1, Answer
same => n, Playback (tt-monkeys)
same => n, hangup
Save the changes and reload the dialplan:
asterisk-rx "dialplan reload"
Now from a softphone connected to the server AsteriskA, mark any number that comes out of the trunk OpeSIPs configured:
The result in the Asterisk where the call arrives:

 OpenSIPsA:
 OpenSIPsB:
 The Redis Cluster and chachedb_redis module, function correctly.
Final note: it is not advisable to leave the MySQL server in one of the two servers is installed OpenSIPS. Better to have it on a third server.
Sources:
OpenSIPS CACHEDB_REDIS module
Presentation " Distributed SIP clusters with OpenSIPS "By Bogdan

Reference Link
http://www.voztovoice.org/?q=node/585 
 

No comments:

Post a Comment