List members,

My previous post "SUCCESS - 512 Simultaneous Calls with Digital Recording" documents using a RAM disk to eliminate the I/O bottleneck associated with digitally recording calls via the Monitor application. By recording directly to a RAM disk I was able to maintain good call quality on 512 simultaneous calls.

This post documents moving the calls from the RAM disk to a hard disk on a 
remote machine via NFS. The setup is not resource intensive on the Asterisk 
server and should not impact call quality. As always, I welcome suggestions for 
improvement and the identification of errors and omissions.

------------------------------------------------------------

Asterisk Configuration
======================

The two leg files of a call will be moved immediately after the call is 
complete via the MONITOR_EXEC and MONITOR_EXEC_ARGS variables. MONITOR_EXEC is 
generally used to replace soxmix as the application for mixing the raw leg 
files, but I'm using it as a hook to move them to an NFS mounted drive 
specified by MONITOR_EXEC_ARGS as follows:

-From the dialplan (extensions.conf):
exten => _XXXX,1,SetVar(MONITOR_EXEC=mvdr)
exten => _XXXX,2,SetVar(MONITOR_EXEC_ARGS=/digrec-nfs/)
exten => _XXXX,3,Monitor(pcm||m)

mvdr is a shell script sitting in /usr/sbin/:
#!/bin/bash
/bin/nice -n 19 mv $1 $4 &
/bin/nice -n 19 mv $2 $4 &
Digitally recording via Monitor can also be initiated from agent channels and 
queues. The MONITOR_EXEC and MONITOR_EXEC_ARGS variables are still set from the 
dialplan, but you must tell Asterisk to mix the files for them to be used. This 
is accomplished as follows:

-Agents: The channels must be configured in agents.conf for recording:
recordagentcalls=yes    ; The leg files are always joined

-Queues: Each queue must be configured in queues.conf for recording and joining 
the leg files:
monitor-format=pcm
monitor-join=yes

Using this hook to trigger the moves of the leg files has two distinct 
advantages. First, the leg files are removed from the RAM disk as soon as 
possible, minimizing the amount of RAM needed to buffer the calls. Secondly, 
the RAM disk is volatile storage so moving the leg files to stable storage as 
soon as possible minimizes the number of digital recordings that will be lost 
in the event of an Asterisk server crash.

NFS Configuration
=================

A fast NFS connection is needed for two reasons. First, the size of the RAM 
disk is limited by the amount of physical memory so we have to move data off it 
as quickly as possible to avoid filling it. Secondly, minimizing the amount of 
time needed to transmit the leg files prevents a large number of moves from 
building up on the system. Too many background processes leads to resource 
consumption which inhibits Asterisk's ability to maintain call quality.

To attain the needed speed, I chose asynchronous NFS (version 3) using UDP and 8K block sizes transmitted via a crossover Gigabit connection configured for jumbo frames. The Asterisk server is the NFS client in order to minimize resource consumption, and the Digital Recording server runs the NFS daemons. I decided to use an asynchronous NFS transfer because it allows the NFS server to reply to NFS client requests as soon as it has processed the request, without waiting for the data to be written to disk. This yields better performance at the cost of possible data corruption in the event of an NFS server crash.
UDP was chosen because it is a stateless protocol and will not cause the NFS 
client to hang if the NFS server crashes in the middle of a packet 
transmission. The Asterisk server (NFS client) uses a soft, interruptable mount 
to prevent hanging if the Digital Recording server (NFS server) crashes, as 
well.

Jumbo frames are used to minimize the number of CPU interrupts and general proccessing overhead for a given data transfer size. The 8K block sizes (plus packet headers) fit into the 9000 byte MTU allowing for efficient transfers between the NFS client and server without packet fragmentation.
nfsd is started at boot on the Digital Recording server (NFS server) in 
runlevels 3, 4, and 5.

Note that throughout the configuration I am sacrificing data integrity at the 
expense of speed and NFS client reliability. I feel this is an acceptable 
trade, given the realtime nature of Asterisk and the criticality of speed to 
this application.

This configuration involves hardware and software, so I'll review both. If you 
would like full copies of any configuration files, please contact me off list.

Hardware Profiles
-----------------

-Asterisk Server (NFS Client)
-   Machine: Dell PowerEdge 6850
-       CPU: Four Intel Xeon MP CPUs at 3.16 GHz - Hyperthreaded
-       RAM: 20 GB (4 GB System / 16 GB RAM Disk)
-       NIC: Intel Pro/1000 MT Dual Port Server Adapter
- Exp. Slot: PCI-X, 64 bit, 133 MHz

-Digital Recording Server (NFS Server)
-   Machine: Dell PowerEdge 1850
-       CPU: One Intel Xeon CPU 2.80 GHz - Hyperthreaded
-       RAM: 2 GB
-       NIC: Intel Pro/1000 MT Gigabit Ethernet Adapter - Onboard
-Hard Disks: Two 73 Gigabyte SCSI Drives Configured in a RAID 1 (Mirrored)
-Controller: PERC 4e/Si RAID Controller - Onboard

-Physical Connection
-Cat5e Crossover Cable

Software Profiles
-----------------

-Asterisk Server (NFS Client)
-         OS: Fedora Core 3 - 2.6.12-1.1376_FC3smp Kernel
-RAM Disk FS: ext2
- NIC Driver: e1000

-Digital Recording Server (NFS Server)
- OS: Fedora Core 3 - 2.6.12-1.1376_FC3smp Kernel -Hard Disk FS: ext3
-  NIC Driver: e1000

RAM Disk Setup
--------------

-Asterisk Server (NFS Client)
-Edit the bootloader configuration file (/boot/grub/grub.conf in my case) to 
pass the ramdisk_size parameter to the kernel:
 kernel /vmlinuz-2.6.12-1.1376_FC3smp ro root=LABEL=/ quiet 
ramdisk_size=16777216
-Append lines to the /etc/rc.local init script to format and mount the RAM disk 
at boot time:
 # Formats and mounts the RAM disk used to buffer digital recordings.
 /sbin/mke2fs -q -b 1024 -m 0 /dev/ram0
 /bin/mount /dev/ram0 /digrec

Subnet Setup
------------

-Asterisk Server (NFS Client)
-Edit the /etc/sysconfig/network-script/ifcfg-eth# file for the dedicated NIC 
so that it is on a different subnet than the other NICS in the system. No 
default gateway is necessary. The following values work on my network:
 IPADDR=192.168.55.2
 NETMASK=255.255.255.0
-Digital Recording Server (NFS Server)
-Edit the /etc/sysconfig/network-script/ifcfg-eth# file for the dedicated NIC 
so that it is on a different subnet than the other NICS in the system. No 
default gateway is necessary. The following values work on my network:
 IPADDR=192.168.55.1
 NETMASK=255.255.255.0
After rebooting both machines you can confirm the setup using the route command:

[EMAIL PROTECTED] ~]# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.55.0    *               255.255.255.0   U     0      0        0 eth3
192.168.51.0    *               255.255.255.0   U     0      0        0 eth2
XXX.XXX.X.X     *               255.255.0.0     U     0      0        0 eth2
default         XXX.XXX.XX.XX   0.0.0.0         UG    0      0        0 eth2

Note that the eth3 interface is only used for the 192.168.55.0 subnet. If you 
see similar behavior on both of your machines, your subnet setup was successful.

Jumbo Frame Setup
-----------------

-Both Servers (NFS Client and NFS Server)
-Edit the /etc/sysconfig/network-scripts/ifcfg-eth# file for the dedicated NIC 
so that it has a 9000 byte MTU:
 MTU=9000

After rebooting both machines you can confirm the setup using the tracepath 
command:

[EMAIL PROTECTED] ~]# tracepath 192.168.55.1
1:  192.168.55.2 (192.168.55.2)                            0.147ms pmtu 9000
1:  192.168.55.1 (192.168.55.1)                            1.852ms reached
    Resume: pmtu 9000 hops 1 back 1
Note that the pmtu (path MTU) is 9000. If you see similar behavior on both of your machines, your jumbo frame setup was successful.
Gigabit Ethernet Confirmation
-----------------------------

As long as both of the NICs you configured are Gigabit NICs they should 
auto-negotiate a speed of 1000 Mbps. You can confirm this by using the ethtool 
command on both machines:

[EMAIL PROTECTED] ~]# ethtool eth3
Settings for eth3:
       Supported ports: [ TP ]
       Supported link modes:   10baseT/Half 10baseT/Full
                               100baseT/Half 100baseT/Full
                               1000baseT/Full
       Supports auto-negotiation: Yes
       Advertised link modes:  10baseT/Half 10baseT/Full
                               100baseT/Half 100baseT/Full
                               1000baseT/Full
       Advertised auto-negotiation: Yes
       Speed: 1000Mb/s
       Duplex: Full
       Port: Twisted Pair
       PHYAD: 0
       Transceiver: internal
       Auto-negotiation: on
       Supports Wake-on: umbg
       Wake-on: d
       Current message level: 0x00000007 (7)
       Link detected: yes
NFS Server Optimizations
------------------------

-Digital Recording Server (NFS Server)
-Edit the NFS init script (/etc/rc.d/init.d/nfs in my case) so that it starts 
20 nfsd threads. This number is an estimate. Increase the number if netstat -s 
reports UDP socket overflows, decrease it if uptime reports a rising load 
average:
 # Number of servers to be started by default
[ -z "$RPCNFSDCOUNT" ] && RPCNFSDCOUNT=20 -Edit the start section of the NFS init script (/etc/rc.d/init.d/nfs in my case) to increase the socket input queue for nfsd. This is important for NFS servers with heavy write loads, because each instance of nfsd uses this queue to store write requests while processing them:
 start)
    # Save the original size of the socket input queue - MJR
    rmem_default=`cat /proc/sys/net/core/rmem_default`
    rmem_max=`cat /proc/sys/net/core/rmem_max`

    # Increase the size of the socket input queue (32K per nfsd) - MJR
    rmem_nfsd=`echo "32768*$RPCNFSDCOUNT" | bc -l`
    echo $rmem_nfsd > /proc/sys/net/core/rmem_default
    echo $rmem_nfsd > /proc/sys/net/core/rmem_max

# Start daemons. ... # Return the socket input queue to its original size - MJR
    echo $rmem_default > /proc/sys/net/core/rmem_default
    echo $rmem_max > /proc/sys/net/core/rmem_max
;;
Export Options
--------------

-Digital Recording Server (NFS Server)
-Edit /etc/exports so that the desired directory is exported asynchronously for 
reading and writing, without squashing root:
 /var/Recorded   192.168.55.2(rw,async,no_root_squash)

Mount Options
-------------

-Asterisk Server (NFS Client)
-Edit /etc/fstab so that the exported directory is mounted with the appropriate 
options for a fast connection (see NFS Configuration):
 192.168.55.1:/var/Recorded  /digrec-nfs  nfs  
rw,soft,intr,bg,nolock,rsize=8192,wsize=8192,nfsvers=3,udp  0 0

NFS and RAM Disk Confirmation
-----------------------------

After rebooting both machines, you can confirm the setups using the df command 
on the Asterisk server (NFS client):

[EMAIL PROTECTED] ~]# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda6              5162796    535948   4364592  11% /
/dev/sda2               124443     20067     97950  18% /boot
none                  10286816         0  10286816   0% /dev/shm
/dev/sda5              5162796   1645400   3255140  34% /home
/dev/sda7             10317828   4500676   5293036  46% /usr
/dev/sda8             47524452   1687956  43422332   4% /var
/dev/ram0             16510000      3855  16506145   1% /digrec
192.168.55.1:/var/Recorded
                     47564616   1257512  43890936   3% /digrec-nfs
Note the last two entries. The RAM disk is shown as /dev/ram0 mounted on /digrec and the NFS mount is shown as 192.168.55.1:/var/Recorded mounted on /digrec-nfs. If you see similar behavior, your NFS and RAM disk setups were successful.

Further Optimization Options
----------------------------

The disks of my NFS server are configured as a RAID 1 (mirrored). This offers 
redundancy at the expense of slower writes, since each write is made to both 
disks. Configuring the disks as a RAID 0 (striped) would yield better NFS 
performance by decreasing write times if disk I/O on the NFS server proves to 
be a bottleneck.

The e1000 drivers used by my Intel NICs can be configured via command line 
parameters set in /etc/modprobe.conf. By increasing the number of RxDescriptors 
and TxDescriptors the drivers could buffer more incoming packets and queue more 
transmits, respectively. As per Intel's driver guide, the default settings are 
generally the recommended settings.

NFS Benchmarking
================

My first benchmarks were performed by creating files of zeroed bytes from 
/dev/zero across the NFS mount. The commands to do this for each set of 
benchmarks follows:

  2 GB - time dd if=/dev/zero of=/digrec-nfs/testfile bs=16k count=131072
  1 GB - time dd if=/dev/zero of=/digrec-nfs/testfile bs=16k count=65536
256 MB - time dd if=/dev/zero of=/digrec-nfs/testfile bs=16k count=16384
 30 MB - time dd if=/dev/zero of=/digrec-nfs/testfile bs=16k count=1920
  3 MB - time dd if=/dev/zero of=/digrec-nfs/testfile bs=16k count=192
For reference, a 3 MB file is roughly 6 minutes of PCM audio and a 30 MB file is roughly 1 hour of PCM audio. These sizes represent one leg of an average call and one leg of a long call for my application. The results of testing on these files are of the most interest to me, and the larger files are used simply to stress test the NFS configuration under heavier loads than should ever be seen.

I used sar on the Asterisk server (NFS client) to monitor CPU usage and iostat 
on the Digital Recording server (NFS server) to monitor CPU and disk usage. The 
NFS mount was unmounted and remounted after each test to clear any caches. 
Without further ado, here are the numbers:

                          Client CPU  Server CPU  Server Max. Blocks
Filesize  Runtime    Mbps  Min. Idle%  Min. Idle%  Written / Sec.
--------  ---------  ----  ----------  ----------  ------------------
 2 GB    0m54.756s  299   84.00       03.50       134768.00
 2 GB    0m45.843s  357   84.65       06.44       121317.65
 2 GB    0m48.347s  339   77.13       07.04       126800.00
 1 GB    0m23.355s  351   87.30       07.46       119041.58
 1 GB    0m22.110s  371   87.41       07.46       114035.64
 1 GB    0m24.061s  340   83.41       08.42       138233.66
256 MB    0m03.146s  651   90.91       40.50        75896.00
256 MB    0m03.150s  650   92.54       41.00         7045.65 *
256 MB    0m03.145s  651   93.40       41.79       106488.00
30 MB    0m00.374s  642   97.38       80.50         6579.06 *
30 MB    0m00.373s  643   97.62       83.00        60966.34
30 MB    0m00.373s  643   98.13       83.50         5328.15 *
 3 MB    0m00.041s  585   99.62       95.17         4902.11
 3 MB    0m00.041s  585   99.75       95.48         4584.44
 3 MB    0m00.041s  585   99.63       95.60         4456.03
* These numbers look a little off. I think I stopped iostat before the NFS server's write buffers had been flushed to disk. Ooops!

My second benchmarks were performed by creating 120 files, each of which was 3 
MB in size (360 MB total). They were named in pairs to simulate sixty pairs of 
leg files, each containing 6 minutes of PCM audio. The goal of this test was to 
measure the stress on both machines when sixty average calls end and are 
transferred using the mvdr script over a given interval. The first set of tests 
had a 1 second sleep between each call termination (typical call volume), the 
second set had a 0.1 second sleep (high call volume), and the third set of 
tests had no sleep (unrealistically high call volume/stress test). The script 
used to conduct these benchmarks had the following format:

#!/bin/bash
mvdr 001a.txt 001b.txt dummy /digrec-nfs/ &
sleep 1         # 0.1   # 0.0
mvdr 002a.txt 002b.txt dummy /digrec-nfs/ &
sleep 1         # 0.1   # 0.0
mvdr 003a.txt 003b.txt dummy /digrec-nfs/ &
sleep 1         # 0.1   # 0.0
...

The approximate run times that I reported are just that. On both the NFS client 
and the NFS server these times were approximated by reviewing the sar and 
iostat logs and noting when the CPU began to be utilized and when it returned 
to idle. The maximum number of simultaneous processes on the NFS client 
(Asterisk server) is also an approximation. I simply ran ps repeatedly during 
the tests and counted how many instances of the mvdr script were running.

It is important to note that the leg files were being transferred from the RAM 
disk of the Asterisk server (NFS client) to the hard disk of the Digital 
Recording server (NFS server) in order to simulate the actual scenario of the 
production environment. Once again, the NFS mount was unmounted and remounted 
after each test to clear any caches.

                   Client    Client    Client     Server    Server    Server
Total Sleep Approx. CPU Min. Max. Sim. Approx. CPU Min. Max. Blocks Filesize Interval Run Time Idle% Processes Run Time Idle% Written / Sec.
--------  --------  --------  --------  ---------  --------  --------  
--------------
360 MB    1.0s      60s       97.63       2        60s       55.28      61624.00
360 MB    1.0s      60s       92.39       0        60s       75.00      62865.31
360 MB    1.0s      60s       95.13       2        60s       67.33      62214.14
360 MB    0.1s      07s       90.75       2        16s       18.91      97615.69
360 MB    0.1s      07s       90.51       2        15s       24.88     110518.81
360 MB    0.1s      07s       90.75       8        14s       23.38      95264.00
360 MB    0.0s      05s       75.00      95        14s       20.10      93259.41
360 MB    0.0s      05s       80.75     103        14s       24.50     106352.00
360 MB    0.0s      05s       81.40      99        14s       21.00     114091.09


If you would like a zip file containing the raw test data or copies of my test 
scripts and files, please contact me off list.

Conclusions
-----------

The speed of the NFS connection is more than adequate for this application and 
at realistic call volume levels the added strain on the Asterisk server should 
not effect call quality. The bottleneck looks to be CPU and possibly disk I/O 
on the NFS server. This is not unexpected, since NFS uses code embedded in the 
kernel which must be executed on the host CPU and large amounts of data are 
being transferred across the connection in small periods of time.

I will monitor memory usage (to see how large the RAM disk actually needs to 
be) and CPU (for stress) on the production system once it is up and report my 
findings to the list.

Future Plans and Unresolved Issues
==================================

I wrote Windows software for another project that mixes leg files, indexes them 
by call time, and archives them after a given period of time. I plan to port 
that code to a set of shell scripts that will be run on the Digital Recording 
server out of cron. If anyone knows of an existing project that has 
accomplished this already, please let me know.

Before writing these scripts, I have two questions that need answered:

1) How can I tell when a file is complete on the NFS server?

2) What will happen on the NFS client if the NFS server crashes (I expect the 
leg files to be written to the local mount point until the mount is 
reesablished)?

If you can answer either of these questions, I would greatly appreciate your 
contribution.

NFS Test Tools
==============

df
ethtool
ifconfig
lspci
netstat
nfsstat
showmount
tracepath
uptime
/proc/net/rpc/nfsd

Sources
=======

Linux Administration Handbook
by Evi Nemeth, Garth Snyder, Trent R. Hein
Chapter 17 - The Network File System
http://www.amazon.com/exec/obidos/tg/detail/-/0130084662/103-5601970-3037440?v=glance

Linux NFS-HOWTO
by Tavis Barr, Nicolai Langfeldt, Seth Vidal, Tom McNeal
Chapter 5 - Optimizing NFS Performance
http://nfs.sourceforge.net/nfs-howto/

Gigabit Ethernet Jumbo Frames and Why You Should Care
by Phil Dykstra
http://64.233.161.104/search?q=cache:_XJNulIGbj0J:sd.wareonearth.com/~phil/jumbo.html+%22gigabit+ethernet+jumbo+frames%22+%22and+why+you+should+care%22&hl=en

Linux* Base Driver for the IntelĀ® PRO/1000 Family of Adapters
by Intel Corporation
http://www.intel.com/support/network/sb/cs-009209.htm

Linux Ramdisk mini-HOWTO
by Van Emery
http://www.vanemery.com/Linux/Ramdisk/ramdisk.html

------------------------------------------------------------

Thank you for taking the time to read this post. I will try create a wiki page 
from it (with all of your additions, of course) when time permits. If anyone 
would like to perform this task feel free to do so, but please contact me 
first. I want to avoid multiple pages with the same content being created.

Yours truly,

Matthew Roth
InterMedia Marketing Solutions
Software Engineer and Systems Developer
_______________________________________________
--Bandwidth and Colocation sponsored by Easynews.com --

Asterisk-Users mailing list
Asterisk-Users@lists.digium.com
http://lists.digium.com/mailman/listinfo/asterisk-users
To UNSUBSCRIBE or update options visit:
  http://lists.digium.com/mailman/listinfo/asterisk-users

Reply via email to