apache process tuning

A long, long time ago I had an opportunity to fine-tune apache processes on a couple hundred hosts. It took one script run on some of the highly-loaded hosts to get specific numbers, and those numbers are what are important.

For shell processing you’ll need bc.

Running the below program on my current host presents the following output:

sudo ./apache_process_tuning.pl
1035:11956224 1036:19152896 1037:16871424 1038:32567296 1039:11386880 1040:13230080 1041:15876096 1042:20291584 2131:14344192 2132:14438400 2133:13373440 10578:15732736 29812:2068480
==========
There are 13 Apache Processes that consume 201,289,728 bytes of RAM
Each process takes an average of 15,483,825 bytes of RAM

You have 2,103,779,328 bytes of RAM, with 1,756,119,040 unused if Apache were not running

You can be ok with a MAX_CLIENTS setting lower than 113

To the gods of perl, I present thee!

#!/usr/bin/perl
use strict;
use warnings
# http://www.perlmonks.org/?node_id=110137
sub commify {
   my $text = reverse $_[0];
   $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
   return scalar reverse $text
}
# step 0, make sure we are sudo
die("Need to sudo this command") if ( not defined $ENV{'SUDO_USER'} );
# Step 1, get system memory information
my $free = `/usr/bin/free -mb`;
my ($mem_total, $mem_used, $mem_free);
for (split /^/, $free) {
   if ($_ =~ /Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) {
      $mem_total = $1;
      $mem_used = $2;
      $mem_free = $3;
   }
}
die("Cannot get memory statistics") if (not defined $mem_total);
# Step 2, get httpd process
my $processes = `/bin/ps aux`;
my ($process_count, $process_total, $process_mem);
for (split /^/, $processes) {
   if ($_ =~ /(\w+)\s+(\d+)\s+[\d\.]+\s+[\d\.]+\s+\d+\s+\d+\s+[\w\?\/]+\s+[\w\+<]+\s+[\d\w:]+\s+[\d:]+\s+(.+)/) {
      my $proc_owner = $1;
      my $proc_pid = $2;
      my $proc_cmd = $3;
      if ($proc_cmd =~ /httpd$/) {
         # Step 2a, Get some Private_Dirty
         # http://stackoverflow.com/questions/118307/a-way-to-determine-a-processs-real-memory-usage-i-e-private-dirty-rss
         $process_mem = `awk '/Private_Dirty/ {print \$2,\$3}' /proc/${proc_pid}/smaps | sed 's/ tB/*1024 gB/;s/ gB/*1024 mB/;s/ mB/*1024 kB/;s/ kB/*1024/;1!s/^/+/;' | tr -d '\\n' | sed 's/\$/\\n/' | bc`;
         chomp($process_mem);
         $process_count++;
         $process_total+=$process_mem;
         print $proc_pid . ":" . $process_mem . " ";
      }
   }
}
die("Having a problem getting active httpd info") if (not defined $process_count);
# step 3, give some helpful output
my ($process_average, $unused_mem, $max_clients);
print "\n==========\n";
$process_average = sprintf("%d", $process_total / $process_count);
print "There are " . $process_count . " Apache Processes that consume " . commify($process_total) . " bytes of RAM\n";
print "Each process takes an average of " . commify($process_average) . " bytes of RAM\n";
print "\n";
$unused_mem = sprintf("%d", $mem_used - $process_total);
print "You have " . commify($mem_total) . " bytes of RAM, with " . commify($unused_mem) . " unused if Apache were not running\n";
print "\n";
# Math is: "available" RAM with no httpd divided by average process total
$max_clients = sprintf("%d", $unused_mem / $process_average);
print "You can be ok with a MAX_CLIENTS setting lower than " . $max_clients . "\n";
print "\n";