OpenBSD Journal

Load-Balanced Apache Server Cluster

Contributed by jose on from the better-uptime dept.

Fred Thompson writes: "I built the load-balanced cluster of apache web servers documented at http://www.thompsonstidbits.com/dads_stuff/mymailserver/webcluster/index.html using OpenBSD on 3 of 4 of the boxes involved. It provides a way to cheaply scale server capacity with a level of fault tolerance which is not available though round robin DNS."

There was a question in the queue on how to do this, good to see an answer came in.

(Comments are closed)


Comments
  1. By Anonymous Coward () on

    "I am a bit tired of upgrading SSHD as every script kiddie in the world tries to penetrate it."

    ??????

    Comments
    1. By Anonymous Coward () on

      SIGH "Unlike SSH, the data will be transfered in plaintext after the session is established, but most is web content anyway." Including the shiny new password S/Key will generate. Oh well, it was a good thought anyway.

    2. By Fred Thompson () me@mymailserver.net on mymailserver.dyndns.org

      With everyone gunning for SSH, there have been an interminable number of patches. Nobody seem to care about S/Key so is stays more static and thus easier to maintain.

  2. By Anonymous Coward () on

    Check out wackamole (http://www.backhand.org/wackamole/). It uses spread to manage a group of servers and make sure they are all still there. It then distributes the IPs you give it over the servers. Just setup round-robin DNS for those IPs and you have a highly available, load-balanced web cluster.

    Comments
    1. By Eduardo Alvarenga () eduardo at thrx dot org on mailto:eduardo at thrx dot org

      Comments
      1. By Eduardo Alvarenga () eduardo at thrx dot org on mailto:eduardo at thrx dot org

        And how about PHP sessions? Anyone has any solution about sharing php sessions around various webservers? MySQL? memory-share?

        Comments
        1. By Jedi/Sector One () j@pureftpd.org on http://www.pureftpd.org

          MySQL works very well.
          Of course because of the write concurrency you must use InnoDB, not MyISAM.
          Here's the preloaded PHP script I'm using in order to share PHP sessions :

          Can't connect to ' . htmlspecialchars($SESS_DBHOST) .
          ' as ' . htmlspecialchars($SESS_DBUSER) . '';
          $error = mysql_error($SESS_DBH);
          echo 'MySQL Error: ' . htmlspecialchars($error) . '';
          @mysql_close($SESS_DBH);
          die();
          }

          if (! mysql_select_db($SESS_DBNAME, $SESS_DBH))
          {
          echo 'Unable to select database ' .
          htmlspecialchars($SESS_DBNAME) . '';
          @mysql_close($SESS_DBH);
          die();
          }
          return TRUE;
          }

          function sess_close()
          {
          global $SESS_DBH;
          @mysql_close($SESS_DBH);

          return TRUE;
          }

          function sess_read($key)
          {
          global $SESS_DBH;

          $qry = 'SELECT `value` FROM `sessions` WHERE `sesskey` = ' .
          '"' . $key . '" AND `expiry` > ' . time() . ' LIMIT 1';
          if (($qid = mysql_query($qry, $SESS_DBH)) === FALSE) {
          return FALSE;
          }
          list($value) = mysql_fetch_row($qid);
          mysql_free_result($qid);

          return $value;
          }

          function sess_write($key, $val)
          {
          global $SESS_DBH, $SESS_LIFE;

          $expiry = time() + $SESS_LIFE;

          if (strlen($value) > 1000000) {
          @mail('patrice@orbus.fr','Session trop grande !',
          "Session :nTaille :" . strlen($value));
          }
          $qry = 'REPLACE INTO `sessions` ' .
          '(`sesskey`, `expiry`, `value`) VALUES (' .
          '"' . $key . '", ' .
          $expiry . ', ' .
          '"' . mysql_real_escape_string($val,$SESS_DBH) . '"' .
          ')';

          return mysql_unbuffered_query($qry, $SESS_DBH);
          }
          function sess_destroy($key)
          {
          global $SESS_DBH;

          return mysql_unbuffered_query
          ('DELETE FROM `sessions` WHERE `sesskey` = "' . $key . '"',
          $SESS_DBH);
          }

          function sess_gc($maxlifetime)
          {
          global $SESS_DBH;

          @mysql_unbuffered_query
          ('DELETE FROM `sessions` WHERE `expiry`

          and the schema :

          +---------+------------------+------+-----+---------+-------+
          | Field | Type | Null | Key | Default | Extra |
          +---------+------------------+------+-----+---------+-------+
          | sesskey | varchar(32) | | PRI | | |
          | expiry | int(11) unsigned | | | 0 | |
          | value | longtext | | | | |
          +---------+------------------+------+-----+---------+-------+

          In your php.ini, add :

          auto_prepend_file = '/path/to/the/previous/script.php'

          Comments
          1. By Anonymous Coward () on

            My comment has been totally mangled by deadly.org .

            Drop me a mail if you need the whole script :)

          2. By Anonymous Coward () on

            Or just use source-hashing on pf.

            Comments
            1. By Anonymous Coward () on

              That doesn't help when one server does go down. If you use a database to store sessions, you can turn off a server while people are in the middle of sessions, and they won't even notice.

            2. By Stuart () on

              Source-hashing doesn't always work well, e.g. if a client is coming from several of a bank of proxies. Using a session-aware reverseproxy like pound (http://www.apsis.ch/pound/) is an alternative (as is maintaining session state in a db accessed by several webservers, of course). Pound looks at a cookie or URL to determine which backend 'owns' a session (though, there's no protection against multiple backends generating the same session-id, so care is required in some situations).

              The pf 'rdr' loadbalancing methods can be nicely augmented by using carp-protected addresses as the destinations to provide resilience (and of course the loadbalancer itself can be protected by carp+pfsync).

              Comments
              1. By Anonymous Coward () on

                Pound should never be used since its proxy based and is not transparent in the system, try pulling back environment variables in Perl or PHP on the backend webservers and you will not get the usual results ie: $_SERVER[REMOTE_ADDR]

                Pound is ghey!

                Comments
                1. By Anonymous Coward () on

                  Pound is a proxy, so complaining that it is "proxy based" is strange, to say the least. In any case it makes the client IP address available in the usual X-Forwarded-for header.

                  In parantheses: if you use or trust REMOTE_ADDR for ANYTHING at all you don't need OpenBSD - Windows will do nicely for you ;-)

        2. By Anonymous Coward () on

          You can define your own custom session handler, which can then store session data wherever you want really. Typically you'd use postgresql or mysql to store it.

        3. By Alejandro Belluscio () baldusi@hotmail.com on mailto:baldusi@hotmail.com

          Use the msessions extension. It's a specifice distributed session protocol for exactly this case.

          Comments
          1. By Eduardo Alvarenga () eduardo at thrx dot org on mailto:eduardo at thrx dot org

            Where can I find it? Is it already built-in on php? Works on OpenBSD?

        4. By Fred Thompson () me@mymailserver.net on http://mymailserver.dyndns.org

          I did not consider PHP (or cold fusion or j2ee) session ids to be an issue since the visitor would be directed to one web server for the duration of his visit. Fault-tolerance does not cover the situation where that server dies while the user in in mid session or where the primary server which is sending the initial replay dies.

      2. By Ray () on

        Comments
        1. By cAPTAIN^k () on

          to expand on that, if you are interested:

          http://www.openbsd.org/faq/pf/pools.html#incoming

    2. By Fred Thompson () me@mymailserver.net on mymailserver.dyndns.org

      I like wackamole but only had one ip address to play with to build mine using ports behind a screening router.

    3. By Fred Thompson () me@mymailserver.net on http://mymailserver.dyndns.org

      If you go and build a web cluster to handle an actual load, have a look here to see how slashdot tweaked theirs on 9/11 to seriously uograde their capacity.

  3. By Tom Helmer Hansen (80.62.63.110) on

    People who want to use this method should notice that many company firewalls restrict outbound traffic to a few ports (eg. 21,80 and 443).

Credits

Copyright © - Daniel Hartmeier. All rights reserved. Articles and comments are copyright their respective authors, submission implies license to publish on this web site. Contents of the archive prior to as well as images and HTML templates were copied from the fabulous original deadly.org with Jose's and Jim's kind permission. This journal runs as CGI with httpd(8) on OpenBSD, the source code is BSD licensed. undeadly \Un*dead"ly\, a. Not subject to death; immortal. [Obs.]