{"id":136,"date":"2017-12-26T09:42:30","date_gmt":"2017-12-26T14:42:30","guid":{"rendered":"http:\/\/blog.voipxswitch.com\/?p=136"},"modified":"2017-12-26T09:43:36","modified_gmt":"2017-12-26T14:43:36","slug":"kamailio-high-availability-using-keepalived","status":"publish","type":"post","link":"https:\/\/blog.voipxswitch.com\/?p=136","title":{"rendered":"kamailio high availability using keepalived"},"content":{"rendered":"<p>This is a quick post on how to use keepalived to setup high-availability on two kamailio machines. In this setup there will be a &#8220;primary&#8221; and &#8220;secondary&#8221; node. Each node will be running kamailio and keepalived with a &#8220;shared&#8221; or sometimes referred to as a &#8220;floating&#8221; IP address.<br \/>\nThe idea is that each node will be running health checks on itself and the other node. If a node detects kamailio as being down, it will move the &#8220;floating&#8221; IP address to itself and become the MASTER node.<br \/>\nAfter the IP address is moved, all SIP traffic will be automatically directed to the new MASTER node. Both kamailio nodes should be running almost identical routing configs in order to ensure traffic is routed properly after fail-over. This setup was tested on Debian9 with kamailio version 5.10.<\/p>\n<p>We will use the following example IP address setup:<\/p>\n<blockquote><p>node 1 IP Address: 1.2.3.4<br \/>\nnode 2 IP Address: 5.6.7.8<br \/>\n&#8220;floating&#8221; IP Address: 5.5.5.5<\/p><\/blockquote>\n<p>In order for this example to work, you need to enable binding to a non-local IP address. On each node run to enable this setting:<br \/>\n<code>$ echo 1 &gt; \/proc\/sys\/net\/ipv4\/ip_nonlocal_bind<\/code><\/p>\n<p>To permanently save this setting, add the following line to a file like <em><strong>\/etc\/sysctl.d\/99-non-local-bind.conf<\/strong><\/em><br \/>\n<code>net.ipv4.ip_nonlocal_bind = 1<\/code><\/p>\n<p>We will want kamailio to bind to the floating IP address and also to allow SIP OPTION pings from each of the other nodes. So you will want something like this in your <strong><em>kamailio.cfg<\/em><\/strong>:<\/p>\n<pre>#!substdef \"!NODE01!1.2.3.4!g\"\r\n#!substdef \"!NODE02!5.6.7.8!g\"\r\n\r\n# bind to local IP and \"floating\" IP\r\nlisten=udp:1.2.3.4:5060 # on node 2, use 5.6.7.8 here\r\nlisten=udp:5.5.5.5:5060\r\n\r\n# main request routing logic\r\nroute {\r\n\r\n  # allow pings from other node\r\n  if (is_method(\"OPTIONS\") &amp;&amp; ($si == \"NODE01\" || $si == \"NODE02\")) {\r\n    options_reply();\r\n  }\r\n\r\n  ...\r\n\r\n}\r\n<\/pre>\n<p>Start kamailio and make sure it&#8217;s running.<\/p>\n<p>Next we will setup keepalived. To install everything we need run:<br \/>\n<code>$ apt-get install libipset3 sipsak keepalived<\/code><\/p>\n<p>We will be using sipsak with a custom bash script to report the &#8220;health&#8221; of each node. Add the following bash script to node 1 at <strong><em>\/etc\/keepalived\/node01.sh<\/em><\/strong><\/p>\n<pre>#!\/bin\/bash\r\n\r\nnode01=1.2.3.4\r\nnode02=5.6.7.8\r\nreturn_code=0 # success\r\n\r\n# check local instance\r\ntimeout 2 sipsak -s sip:$node01:5060\r\nexit_status=$?\r\nif [[ $exit_status -eq 0 ]]; then\r\n  echo \"sip ping successful to node01 [$node01]\"\r\n  exit $return_code\r\nfi\r\n\r\n# local instance failed, check remote\r\ntimeout 2 sipsak -s sip:$node02:5060\r\nexit_status=$?\r\nif [[ $exit_status -eq 0 ]]; then\r\n  echo \"sip ping successful to node02 [$node02]\"\r\n  return_code=1\r\nfi\r\n\r\necho \"return code [$return_code]\"\r\n\r\nexit $return_code\r\n<\/pre>\n<p>Make sure the file is executable:<br \/>\n<code>$ chmod +x \/etc\/keepalived\/node01.sh<\/code><br \/>\nThe script on node 1 checks the health of kamailio locally first (by sending a SIP OPTION ping), if it fails, it checks the health of node 2. If node 1 is healthy, it reports success to keepalived by returning 0. If node 1 is <em>not<\/em> healthy and node 2 is healthy, it reports a failure to keepalived by returning 1.<\/p>\n<p>And add the following bash script to node 2 at <em><strong>\/etc\/keepalived\/node02.sh<\/strong><\/em><\/p>\n<pre>#!\/bin\/bash\r\n\r\nnode01=1.2.3.4\r\nnode02=5.6.7.8\r\nreturn_code=1 # fail\r\n\r\n# check remote instance fist\r\ntimeout 2 sipsak -s sip:$node01:5060\r\nexit_status=$?\r\nif [[ $exit_status -eq 0 ]]; then\r\n  echo \"sip ping successful to node01 [$node01]\"\r\n  exit $return_code\r\nfi\r\n\r\n# remote instance failed, check local\r\ntimeout 2 sipsak -s sip:$node02:5060\r\nexit_status=$?\r\nif [[ $exit_status -eq 0 ]]; then\r\n  echo \"sip ping successful to node02 [$node02]\"\r\n  return_code=0\r\nfi\r\n\r\necho \"return code [$return_code]\"\r\n\r\nexit $return_code\r\n<\/pre>\n<p>Make sure the file is executable:<br \/>\n<code>$ chmod +x \/etc\/keepalived\/node02.sh<\/code><br \/>\nThe script on node 2 checks the health of kamailio on node 1 (remotely) first, if it fails, it checks the health of node 2 (locally). If node 1 is healthy, it reports a &#8220;local&#8221; failure to keepalived by returning 1. If node 1 is <em>not<\/em> healthy and node 2 is healthy, it reports a &#8220;success&#8221; to keepalived by returning 0.<\/p>\n<p>keepalived will promote the node to MASTER based on the return code reported by the bash scripts. You can get more creative and add more logic if you&#8217;d like, but for the purposes of this post, I kept it simple.<\/p>\n<p>Last we will configure keepalived by editing the following file on node 1: <em><strong>\/etc\/keepalived\/keepalived.conf<\/strong><\/em><\/p>\n<pre>vrrp_script check_sip {\r\n  script \"\/etc\/keepalived\/node01.sh\"\r\n  interval 6 # check every 6 seconds\r\n}\r\n\r\nvrrp_instance VI_SBC {\r\n  state MASTER\r\n  interface eth0\r\n  virtual_router_id 51\r\n  advert_int 1\r\n  authentication {\r\n    auth_type PASS\r\n    auth_pass 11111\r\n  }\r\n  virtual_ipaddress {\r\n    5.5.5.5 dev eth0\r\n  }\r\n  track_script {\r\n    check_sip\r\n  }\r\n}<\/pre>\n<p>And on node 2: <em><strong>\/etc\/keepalived\/keepalived.conf<\/strong><\/em><\/p>\n<pre>vrrp_script check_sip {\r\n  script \"\/etc\/keepalived\/node02.sh\"\r\n  interval 6 # check every 6 seconds\r\n}\r\n\r\nvrrp_instance VI_SBC {\r\n  state BACKUP\r\n  interface eth0\r\n  virtual_router_id 51\r\n  advert_int 1\r\n  authentication {\r\n    auth_type PASS\r\n    auth_pass 11111\r\n  }\r\n  virtual_ipaddress {\r\n    5.5.5.5 dev eth0\r\n  }\r\n  track_script {\r\n    check_sip\r\n  }\r\n}<\/pre>\n<p>The settings above basically say to check health of each node every 6 seconds. If the node should become master, enable the floating IP (5.5.5.5).<\/p>\n<p>Start keepalived service on each node and watch the syslog. As long as <em><strong>kamailio<\/strong><\/em> is running and responsive to the <em>SIP OPTION<\/em> pings on node 1, it will be <strong>MASTER<\/strong>. You can test by turning off kamailio on node 1 and watching the IP move to node 2. Use the following command to check for the IP address:<br \/>\n<code>$ ip addr show<\/code><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a quick post on how to use keepalived to setup high-availability on two kamailio machines. In this setup there will be a &#8220;primary&#8221; and &#8220;secondary&#8221; node. Each node will be running kamailio and keepalived with a &#8220;shared&#8221; or sometimes referred to as a &#8220;floating&#8221; IP address. The idea is that each node will [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,21,8,25,15],"tags":[],"class_list":["post-136","post","type-post","status-publish","format-standard","hentry","category-high-availability","category-kamailio","category-keepalived","category-sip","category-voip"],"_links":{"self":[{"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=\/wp\/v2\/posts\/136","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=136"}],"version-history":[{"count":4,"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=\/wp\/v2\/posts\/136\/revisions"}],"predecessor-version":[{"id":140,"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=\/wp\/v2\/posts\/136\/revisions\/140"}],"wp:attachment":[{"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=136"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=136"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.voipxswitch.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=136"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}