Tag Archives: Load Balancer

Kamailio Bytes – Gotchas with Kamailio as an Asterisk Load Balancer

How do I make Kamailio work with Asterisk?

It’s a seemingly simple question, the answer to which is – however you want, sorry if that’s not a simple answer.

I’ve talked about the strengths and weaknesses of Kamailio and Asterisk in my post Kamailio vs Asterisk, so how about we use them to work together?

The State of Play

So before we go into the nitty gritty, let’s imagine we’ve got an Asterisk box with a call queue with Alice and Bob in it, set to ring those users if they’re not already on a call.

Each time a call comes in, Asterisk looks at who in the queue is not already on a call, and rings their phone.

Now let’s imagine we’re facing a scenario where the single Asterisk box we’ve got is struggling, and we want to add a second to share the load.

We do it, and now we’ve got two Asterisk boxes and a Kamailio load balancer to split the traffic between the two boxes.

Now each time a call comes in, Kamailio sends the SIP INVITE to one of the two Asterisk boxes, and when it does, that Asterisk box looks at who is in the queue and not already on a call, and then rings their phone.

Let’s imagine a scenario where a Alice & Bob are both on calls on Asterisk box A, and another call comes in this call is routed to Asterisk box B. Asterisk box B looks at who is in the queue and who is already on a call, the problem is Alice and Bob are on calls on Asterisk box A, so Asterisk box B doesn’t know they’re both on a call and tries to ring them.

We have a problem.

Scaling stateful apps is a real headache,

So have a good long hard think about how to handle these issues before going down this path!

Kamailio Bytes – Dispatcher Module

The Dispatcher module is used to offer load balancing functionality and intelligent dispatching of SIP messages.

Let’s say you’ve added a second Media Gateway to your network, and you want to send 75% of traffic to the new gateway and 25% to the old gateway, you’d use the load balancing functionality of the Dispatcher module.

Let’s say if the new Media Gateway goes down you want to send 100% of traffic to the original Media Gateway, you’d use the intelligent dispatching to detect status of the Media Gateway and manage failures.

These are all problems the Dispatcher Module is here to help with.

Before we get started….

Your Kamailio instance will need:

  • Installed and running Kamailio instance
  • Database configured and tables created (We’ll be using MySQL but any backed is fine)
  • kamcmd & kamctl working (kamctlrc configured)
  • Basic Kamailio understanding

The Story

So we’ve got 4 players in this story:

  • Our User Agent (UA) (Softphone on my PC)
  • Our Kamailio instance
  • Media Gateway 1 (mg1)
  • Media Gateway 2 (mg2)

Our UA will make a call to Kamailio. (Send an INVITE)

Kamailio will keep track of the up/down status of each of the media gateways, and based on rules we define pick one of the Media Gateways to forward the INVITE too.

The Media Gateways will playback “Media Gateway 1” or “Media Gateway 2” depending on which one we end up talking too.

Configuration

Parameters

You’ll need to load the dispatcher module, by adding the below line with the rest of your loadmodules:

loadmodule "dispatcher.so"

Next we’ll need to set the module specific config using modparam for dispatcher:

modparam("dispatcher", "db_url", DBURL)                 #Use DBURL variable for database parameters
modparam("dispatcher", "ds_ping_interval", 10)          #How often to ping destinations to check status
modparam("dispatcher", "ds_ping_method", "OPTIONS")     #Send SIP Options ping
modparam("dispatcher", "ds_probing_threshold", 10)      #How many failed pings in a row do we need before we consider it down
modparam("dispatcher", "ds_inactive_threshold", 10)     #How many sucessful pings in a row do we need before considering it up
modparam("dispatcher", "ds_ping_latency_stats", 1)      #Enables stats on latency
modparam("dispatcher", "ds_probing_mode", 1)            #Keeps pinging gateways when state is known (to detect change in state)

Most of these are pretty self explanatory but you’ll probably need to tweak these to match your environment.

Destination Setup

Like the permissions module, dispatcher module has groups of destinations.

For this example we’ll be using dispatch group 1, which will be a group containing our Media Gateways, and the SIP URIs are sip:mg1:5060 and sip:mg2:5060

From the shell we’ll use kamctl to add a new dispatcher entry.

kamctl dispatcher add 1 sip:mg1:5060 0 0 '' 'Media Gateway 1'
kamctl dispatcher add 1 sip:mg2:5060 0 0 '' 'Media Gateway 2'

Alternately you could do this in the database itself:

INSERT INTO `dispatcher` (`id`, `setid`, `destination`, `flags`, `priority`, `attrs`, `description`) VALUES (NULL, '1', 'sip:mg3:5060', '0', '0', '', 'Media Gateway 3'); 

Or you could use Siremis GUI to add the entries.

You can use kamctl to show you the database entries:

kamctl dispatcher show

A restart to Kamailio will make our changes live.

Destination Status / Control

Checking Status

Next up we’ll check if our gateways are online, we’ll use kamcmd to show the current status of the destinations:

kamcmd dispatcher.list

Here we can see our two media gateways, quick response times to each, and everything looks good.

Take a note of the FLAGS field, it’s currently set to AP which is good, but there’s a few states:

  • AP – Active Probing – Destination is responding to pings & is up
  • IP – Inactive Probing – Destination is not responding to pings and is probably unreachable
  • DX – Destination is disabled (administratively down)
  • AX – Looks like is up or is coming up, but has yet to satisfy minimum thresholds to be considered up (ds_inactive_threshold)
  • TX – Looks like or is, down. Has stopped responding to pings but has not yet satisfied down state failed ping count (ds_probing_threshold)

Adding Additional Destinations without Restarting

If we add an extra destination now, we can add it without having to restart Kamailio, by using kamcmd:

kamcmd dispatcher.reload

There’s some sanity checks built into this, if the OS can’t resolve a domain name in dispatcher you’ll get back an error:

Administratively Disable Destinations

You may want to do some work on one of the Media Gateways and want to nicely take it offline, for this we use kamcmd again:

kamcmd dispatcher.set_state dx 1 sip:mg1:5060

Now if we check status we see MG1’s status is DX:

Once we’re done with the maintenance we could force it into the up state by replacing dx with ap.

It’s worth noting that if you restart Kamailio, or reload dispatcher, the state of each destination is reset, and starts again from AX and progresses to AP (Up) or IP (Down) based on if the destination is responding.

Routing using Dispatcher

The magic really comes down to single simple line, ds_select_dst();

The command sets the destination address to an address from the pool of up addresses in dispatcher.

You’d generally give ds_select_dst(); two parameters, the first is the destination set, in our case this is 1, because all our Media Gateway destinations are in set ID 1. The next parameter is is the algorithm used to work out which destination from the pool to use for this request.

Some common entries would be random, round robin, weight based or priority value.

In our example we’ll use a random selection between up destinations in group 1:

if(method=="INVITE"){
   ds_select_dst(1, 4);    #Get a random up destination from dispatcher
   route(RELAY);           #Route it
}

Now let’s try and make a call:

UA > Kamailio: SIP: INVITE sip:1111111@Kamailio SIP/2.0

Kamailio > UA: SIP: SIP/2.0 100 trying -- your call is important to us

Kamailio > MG1: SIP: INVITE sip:1111111@MG1 SIP/2.0

MG1 > Kamailio: SIP: SIP/2.0 100 Trying

Kamailio > UA : SIP: SIP/2.0 100 Trying

MG1 > Kamailio: SIP: SIP/2.0 200 OK

Kamailio > UA : SIP: SIP/2.0 200 OK

And bingo, we’re connected to a Media Gateway 1.
If I try it again I’ll get MG2, then MG1, then MG2, as we’re using round robin selection.

Destination Selection Algorithm

We talked a little about the different destination select algorithm, let’s dig a little deeper into the common ones, this is taken from the Dispatcher documentation:

  • “0” – hash over callid
  • “4” – round-robin (next destination).
  • “6” – random destination (using rand()).
  • “8” – select destination sorted by priority attribute value (serial forking ordered by priority).
  • “9” – use weight based load distribution.
  • “10” – use call load distribution. 
  • “12” – dispatch to all destination in setid at once

For select destination sorted by priority (8) to work you need to include a priority, you can do this when adding the dispatcher entry or after the fact by editing the data. In the below example if MG1 is up, calls will always go to MG1, if MG1 is down it’ll go to the next highest priority (MG2).

The higher the priority the more calls it will get

For use weight based load distribution (9) to work, you’ll need to set a weight as well, this is similar to priority but allows you to split load, for example you could put weight=25 on a less powerful or slower destination, and weight=75 for a faster or more powerful destination, so the better destination gets 75% of traffic and the other gets 25%. (You don’t have to do these to add to 100%, I just find it easier to think of them as percentages).

use call load distribution (10) allows you to evenly split the number of calls to each destination. This could be useful if you’ve got say 2 SIP trunks with x channels on each trunk, but only x concurrent calls allowed on each. Like adding a weight you need to set a duid= value with the total number of calls each destination can handle.

dispatch to all destination in setid at once (12) allows you to perform parallel branching of your call to all the destinations in the address group and whichever one answers first will handle the call. This adds a lot of overhead, as for each destination you have in that set will need a new dialog to be managed, but it sure is quick for the user. The other major issue is let’s say I have three carriers configured in dispatcher, and I call a landline.

That landline will receive three calls, which will ring at the same time until the called party answers one of the calls. When they do the other two calls will stop ringing. This can get really messy.

Managing Failure

Let’s say we try and send a call to one of our Media Gateways and it fails, we could forward that failure response to the UA, or, better yet, we could try on another Media Gateway.

Let’s set a priority of 10 to MG1 and a priority of 5 to MG2, and then set MG1 to reject the call.

We’ll also need to add a failure route, so let’s tweak our code:

   if(method=="INVITE"){
                ds_select_dst(1, 12);
                t_on_failure("DISPATCH_FAILURE");
                route(RELAY);
        }

And the failure route:

route[DISPATCH_FAILURE]{
        xlog("Trying next destination");
        ds_next_dst();
        route(RELAY);

}

ds_next_dst() gets the next available destination from dispatcher. Let’s see how this looks in practice:

 
UA > Kamailio: SIP: INVITE sip:1111111@Kamailio SIP/2.0

Kamailio > UA: SIP: SIP/2.0 100 trying -- your call is important to us

Kamailio > MG1: SIP: INVITE sip:1111111@MG1 SIP/2.0

MG1 > Kamailio: SIP: SIP/2.0 100 Trying

MG1 > Kamailio: SIP: SIP/2.0 404 Not Found

Kamailio > MG1 : SIP: SIP/2.0 ACK

Kamailio > MG2: SIP: INVITE sip:1111111@MG2 SIP/2.0

MG2 > Kamailio: SIP: SIP/2.0 100 Trying

MG2 > Kamailio: SIP: SIP/2.0 200 OK

Kamailio > UA : SIP: SIP/2.0 200 OK

Here’s a copy of my entire code as a reference.