Googolflex!!
  • Home
  • About
  • Contracting

Recent Posts

  • Sprint’s new “Simply ‘Almost’ Everything®” Plans
  • CSS Changes in Flex 4
  • Dotted Underline LinkButton (Flex)

About The Author : jwd

This is John Dusbabek's tech blog. John is a software engineer and Flex developer in Provo, UT, where he lives with his lovely wife and four sons.

Recent Comments

  • nodmonkey on PHP Warning: mysql_connect(): Can’t connect to MySQL server on… (13)
  • Can't connect to mysql with php/apache but can with cli | That-Matt on PHP Warning: mysql_connect(): Can’t connect to MySQL server on… (13)

Archive for Shell Scripting

Feb
04

Apache mod_proxy_balancer Self Registration : Part 3

Posted by: jwd | Comments (0)

I’ll start off by going over the basic high level architecture for my self registration procedure:

There is a register.php script residing on the load balancer, accessible via HTTP.
There is a deregister.php script residing on the load balancer, accessible via HTTP.
There is a register_with_lb.pl script residing on the web server, in /usr/local/bin/.
There is a deregister_with_lb.pl script residing on the web server, in /usr/local/bin/.
There is a MySQL database that stores the current configuration state, on it are two stored procedures register_lb and deregister_lb.


register.php

No changes were made to register.php as described in this post , though I’m considering some alterations to increase its security.


deregister.php

The biggest difference between register.php and deregister.php (aside from their purpose) is where the insert/delete database code is called from and why. When register.php is called by the web server, it will have already inserted information about itself into the database, including its hash. I made the decision that I did not want the load balancer responsible for inserting servers into the database. It would merely check that the requesting server inserted itself, and then regenerate the balancer_members.conf.

In the case of deregister.php I decided I wanted the server making the call to still be in the database so the script could verify the identity before removing it and regeneration the balancer_members. And since the deregistration SQL is contained within a stored procedure, I needed to make some changes to the script (as compared to register.php) regarding the database.

Specifically, the standard mysql library cannot call stored procedures. So I had to convert it to using mysqli, which is a similar, though more OO approach. The portion of the code that regenerates the balancer_members.conf is similar enough that I won’t re-list it here, but I will show how to connect using mysqli, and how to call a stored procedure.

$mysqli = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
if (mysqli_connect_errno()) {
  printf("Connect failed: %s\n", mysqli_connect_error());
}

$query = "SELECT count(*) as count FROM " . $dbtable . " WHERE ip='" . $_SERVER['REMOTE_ADDR'] . "';";
$result = $mysqli->query($query);
$row = $result->fetch_row();
echo $row[0];

if ($row[0] >= 1) {
  $del_query = "call deregister_lb('" . $_SERVER['REMOTE_ADDR'] . "');";
  $del_result = $mysqli->query($del_query);

  //<code for regenerating the conf file removed here>

  echo exec('echo "' . $file . '" > /etc/httpd/conf.d/balancer_members.conf');
  echo exec("sudo /usr/local/bin/reload_httpd");
}

As you can see, I’m using the actual REMOTE_ADDR to determine the validity of the request.


(de)register_lb.sql Stored Procedure

Here is the code for the deregister_lb stored procedure:

DROP PROCEDURE IF EXISTS deregister_lb $$

CREATE PROCEDURE deregister_lb ( ip VARCHAR(100) )
  BEGIN
    DELETE FROM lb2_members
	WHERE ip=_ip;
  END $$

and also for the register_lb stored procedure:

DROP PROCEDURE IF EXISTS register_lb $$
CREATE PROCEDURE register_lb (
  _hostname VARCHAR(100),
  _ip VARCHAR(40),
  _loadfactor INT,
  _hash VARCHAR(100)
  )

  BEGIN
    DECLARE already_exists INT DEFAULT 0;
    SELECT count(*) INTO already_exists FROM lb2_members WHERE hash=_hash;

    IF already_exists=1 THEN
	  UPDATE lb2_members
	  SET hostname=_hostname, ip=_ip, loadfactor=_loadfactor
	  WHERE hash=_hash;
    ELSE
      INSERT INTO lb2_members (ip, hostname, loadfactor, hash)
	  VALUES (_ip, _hostname, _loadfactor, _hash);
    END IF;
  END $$

Note that I’ve omitted the code that changes the delimiter to $$ instead of a semicolon.


register_with_lb.pl

This perl script uses perl DBI for accessing the database. I had to get that installed on my web server since it wasn’t already. Normally you can install perl packages using the cpan command. In which case you would issue the following commands to install DBI and a MySQL driver for it:

cpan DBI
cpan DBD::mysql

If it’s the first time you’ve run cpan, you will need to go through some configuration. It’s pretty much self explanatory, and I just accepted all of the defaults. Everything installed correctly except for the MySQL driver, which I ended up having to install from source. If I had executed the command:

yum install mysql-devel.i386

first, then my cpan install of DBD::mysql might have worked, but I didn’t realize that until installing from source. In case you ever need to install a perl module from source, particularly the DBD::mysql driver, enter these commands (which I think is basically what cpan does):

yum install mysql-devel.i386 #(only requred in this particular instance)
wget http://www.cpan.org/modules/by-module/DBD/DBD-mysql-4.011.tar.gz
gzip -cd DBD-mysql-4.011.tar.gz | tar xf -
cd DBD-mysql-4.011 #(or whatever version you downloaded)
perl Makefile.PL
make

Here is how you connect to the database and call a stored procedure:

my $dsn = "DBI:mysql:host=mysql.host;database=lb_register";
my $dbh = DBI->connect ($dsn, "lbuser", "lbpasswd")
  or die "Cannot connect to MySQL server\n";

my $sql = "call register_lb('" . $localhost . "', '" . $localip . "', " . $loadfactor . ", '" .  $hash . "')";
$dbh->do($sql);

$dbh->disconnect();

After that, register_with_lb.pl opens a socket to the load balancer and makes an HTTP request over the socket. There are probably easier ways to do this, I just happened to have the socket code lying around and was glad to be able to reuse it. Here’s the gist of it, in case you’re interested:

# Parse the URI.
my $url = URI->new("http://load.balancer.com/register/register.php?hash=" . $hash);

# Parse these in from the command line
$host = $url->host;
$port = $url->port;
$resource = $url->path;
$query = $url->query;

# Initialize the socket
$socket = IO::Socket::INET->new ( Proto => "tcp", PeerAddr => $host, PeerPort => $port,);
unless ($socket) { die "Error connecting to $host" }
$socket->autoflush(1);

# Format the request
my $request = "GET " . $resource . (($query)?"?" . $query : "") . " HTTP/1.1" . $EOL . "Host: " . $host . $EOL . "User-agent: register_script" . $EOR;

# Use send() to make the request, and output the response.
# Not necessary in this example, but informational.
if ( $socket->send($request) ) {
  while ( <$socket> ) { print }
}

# Close the socket
close $socket;

The above code pretty much sums up deregister_from_lb.pl, since no database calls are made, a call is simply made to the deregister script. The line you would change is as follows:

my $url = URI->new("http://my.balancer.com/register/deregister.php");

Then make the files executable, and copy them to be used by the startup script described in the previous post:

chmod a+x register_with_lb.pl
chmod a+x deregister_with_lb.pl
cp register_with_lb.pl /usr/local/bin/
cp deregister_with_lb.pl /usr/local/bin

I don’t show it here, but right now my IP addresses are hard coded. There are a number of ways you can find out your actual IP address from within perl, I’m just not doing that right now.


Securing the register scripts

As an additional security measure, I’ve restricted access to the /register/ location on the load balancer to the IP address range I expect my web servers to be from, like this:

<Location /register>
  Order Deny,Allow
  Deny from all
  Allow from 10.0.0.
</Location>

And now you have a web server that can register automatically (if you’ve gone through the previous two posts as well) with a mod_proxy_balancer load balancer.


Update

I did some searching around to find a way to determine your IP address from inside the perl script. This is a simple way if your server has a public IP address and reverse DNS set up correctly for that IP address:

use Socket;
use Sys::Hostname;
my $host = hostname();
my $addr = inet_ntoa(scalar(gethostbyname($host)) || 'localhost');

If your slave web servers are on a private network, the above command will return the loopback IP address (127.0.0.1) which isn’t useful for the load balancer (I wonder if it would start an infinite loop and crash the load balancer?). I found a function that prints out the IP address by parsing it out from the results of the ifconfig command.

It seemed a little long to just rip off and copy verbatim. So here’s a link to that code (which is what I’m using now) in case you’d like to use it. Perl script to get IP address.

Categories : Apache Web Server, Architecture, MySQL, Perl, Scalability, Shell Scripting, client/server
Comments (0)
Feb
04

Apache mod_proxy_balancer Running Scripts at Startup/Shutdown : Part 2

Posted by: jwd | Comments (0)

I think I have to break the self-registration into two posts, it took a lot longer than I expected last night. This post deals with getting scripts to run at startup and shutdown on Linux. I did this on Fedora, I imagine the process would be similar on Ubuntu, etc.

This is actually the last thing I did, but I’m ordering it first because it’s a bit more general. My goal was to have a script that I could call as follows to register with my load balancer manually:

service lb_register start|stop|status|restart|reload|force-reload

And then also to register this service to be called at startup and shutdown, to register and deregister with the load balancer respectively.

Let’s start with creating the script. You can find tutorials for creating shell scripts just about anywhere on the web. I found it easier to take an existing script from the /etc/init.d/ directory, clear it out and start from that shell. Ironically the one that I chose was the startup script for Pound, another load balancer (a very good one, I might add). It shouldn’t matter which one you use, since all we’re really after is the format. The basic format is essentially the same for all of them: you have your function definitions at the top, and a case statement at the bottom.

The cases match the option parameters (start and stop are very common, as well as the others listed above in the callout). And the case blocks generally call one or more of the functions. I won’t list the case statement in this post as they are all very similar. There are some important things to note in the function definitions, though.

Here is the definition for my start() function:

start() {
   echo -n $&quot;Registering with the load balancer:&quot;
   ./usr/local/bin/register_with_lb.pl
   touch /var/lock/subsys/lb_register
}

You can see that I call a perl script (which presumably registers with the load balancer). Then I touch a file in the /var/lock/subsys/ directory. This is very important if you want a script to automatically run at shutdown. At shutdown the rc script will check for the presence of this file, if it is there it will call stop(). If it is not there, it assumes the service is stopped already and will not call stop() on the service.

My stop function:

stop() {
   echo -n $&quot;Deregistering with the load balancer: &quot;
   ./usr/local/bin/deregister_with_lb.pl
   rm -f /var/lock/subsys/lb_register
}

This calls the deregister perl script, and then removes the lock file for good housekeeping. There is one more thing about the script itself I want to mention before moving on to the registration. There is a line in the comments of most of the scripts (if not all) in /etc/init.d/ that will look something like this:

# chkconfig: - 85 10

or

# chkconfig: 2345 55 15

These are directives to chkconfig (the command we’ll be getting to in a moment) on how to set this script up. The – or the first grouping of numbers deal with the run levels. It tells chkconfig what levels this script will be turned “on”. It is assumed that it will be “off” in the omitted ones.

The second number is a startup priority, different scripts can have different startup priorities in case there are prerequisite dependencies. In the two examples above, the script containing the bottom directive would run before the script containing the bottom one. The last number is the shutdown priority, which is the same thing only during the shutdown process.

Once you have your script, you will need to run the chkconfig command. To add your script to the startup process:

chkconfig --add lb_register

And should you need to remove it:

chkconfig --del lb_register

If you’ve done everything correctly, register_with_lb.pl will be called at startup, and deregister_with_lb.pl will be called at shutdown. If it doesn’t, check that you’re touching the pid file, and that your scripts are executable. You will also be able to make calls like this to deregister and register manually:

service lb_register start
service lb_register restart
service lb_register stop

I should mention a few sites that helped me out quite a bit:
Linux Init Processes
Introduction to BASH Programming
An interesting script for creating new scripts

Categories : Apache Web Server, Shell Scripting, client/server
Comments (0)

Search

Feedburner

Subscribe to

Get the latest updates delivered via email

Calendar

July 2010
M T W T F S S
« Jun    
 1234
567891011
12131415161718
19202122232425
262728293031  

Archives

  • July 2010 (1)
  • June 2010 (2)
  • May 2010 (1)
  • February 2010 (11)
  • January 2010 (3)
  • December 2009 (5)
  • November 2009 (1)
  • August 2009 (8)
  • July 2009 (8)
  • May 2009 (4)
  • April 2009 (1)
  • March 2009 (6)
  • January 2009 (1)
  • November 2008 (4)
  • October 2008 (5)
  • September 2008 (1)
  • August 2008 (5)
  • July 2008 (1)
  • June 2008 (2)
  • May 2008 (8)
  • April 2008 (5)
  • March 2008 (2)
  • February 2008 (3)
  • January 2008 (1)
  • December 2007 (6)
  • November 2007 (9)
  • October 2007 (1)
  • September 2007 (2)

Categories

Tag Cloud

adobe apache Architecture book review C++ centos client server architecture Custom Components database Design error message fedora flash catalyst flex Flex 3 Flex 4 fms iis 6 Interaction Design load balancing master-master master-slave mod_proxy_balancer Monkey Patching MySQL no protocol p2p peer to peer Perl PHP Red5 regex replication self registration selinux Shell Scripting shortcut manager skins socket policy file sockets states stored procedures stratus tools workflow

Coworkers

  • Casey Jackman
  • Sean Murphy

Family

  • Emily & CJ
  • Family Blog
  • Gary Dusbabek

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org

RSS FlexExamples

  • Setting the header height on a Spark Panel container in Flex 4
  • Adding a hover glow filter to an MX Image control in Flex 4
  • Fading an item renderer background fill on a Spark List control in Flex 4
  • Setting the border color on the MX Accordion container headers in Flex
  • Setting the tab width on an MX TabNavigator container in Flex 3

Spam Blocked

842 spam comments
blocked by
Akismet

Sponsored Links

JUICE Chat

BYU Adobe Users Group


Copyright © 2010 All Rights Reserved
Flexx Theme by iThemes
Powered by WordPress