Tag Archives: apache

HOWTO Massive virtual hosting on tomcat

I found out today that tomcat virtual hosts are pretty lame. I thought that these all java based tomcat`s fancy plugins are much more powerful than old, simpleapache httpd. Well they aren`t. I wasn`t able to configure massive virtual hosting for servlets. I wanted to run diffrent web application based on domain name that comes in URL. For example myapp1.example.com would run application myapp1, myapp2.example.com would run myapp2, etc. And there could be hundreds of them so I didn`t want to add hundreds of entries in server.xml config file. I`m a lazy person ;-)

In order to configure virtual hosting based on above assumptions I used apache http server as a proxy to tomcat.
First you need to add wildcard records to your domain (example.com) so that records of all its subdomains can be resolved to IP address of your server. Following record should be added to your bind server zone config:

*   IN   A   1.2.3.4

where 1.2.3.4 is IP address of your server.

Next you need to configure httpd server. Please make sure that you have ajp proxy module installed on your server, as connections to tomcat are based on AJP protocol. On CentOS/RHEL 5 this module is included in standard httpd package (see /etc/httpd/conf.d/proxy_ajp.conf).
Now you need to create configuration for your virual hosts. I created a new file /etc/httpd/conf.d/tomcat-vhosting.conf:

UseCanonicalName Off
RewriteEngine On

# vhost map using perl script
RewriteMap vhost prg:/usr/local/bin/apache-getvhost.pl

# do no rewrite restricted names
RewriteCond %{SERVER_NAME} !^docs\.
RewriteCond %{SERVER_NAME} !^examples\.
RewriteCond %{SERVER_NAME} !^host-manager\.
RewriteCond %{SERVER_NAME} !^ROOT\.

# rewrite it
RewriteRule ^/(.*)$ ajp://localhost:8009/${vhost:%{SERVER_NAME}}/$1 [P]

Quite simple and cool, isn`t it? :-) I`m sure that you probably expected VirtualHost directives, but all you need is a powerfull rewriting feature of apache. This configuration allows to access myapp application located in tomcat`s webapps directory via http://myapp.example.com.
I used custom rewrite map which is a simple perl script. All it does is extract subdomain from server name based on URL.

Put the following in /usr/local/bin/apache-getvhost.pl

#!/usr/bin/perl

$| = 1;

while (<STDIN>) {
  if (/(.*?)\.example\.com/)      {
      print $1."\n";
  } else {
      print $_."\n";
  }
}

and make it executable

chmod +x /usr/local/bin/apache-getvhost.pl

Now all you need to do is provide some applications to tomcat.

Ujarzmione logi apache

Na co dzień pracuję z aplikacją, która wyrzuca bardzo dużo “śmieci” do logów. Tym samym zapycha ona error log, który z nazwy powinien zawierać błędy, a nie informacje o niezainicjalizowanej zmiennej X. Postanowiłem ukrócić ten proceder i filtrować wychodzące z procesów apache`a komunikaty.

Wykorzystałem do tego perla, gdyż dość sprawnie radzi on sobie z obróbką tekstu i wyrażeniami regularnymi. Poniżej zawartość skryptu apache-logfilter.pl

#!/usr/bin/perl -w

use strict;

$|=1;   # use unbuffered output

my $logfile;

# check if logfile was passed as 1st argument
if ($ARGV[0])   {
$logfile=$ARGV[0];
}

# assign /dev/null as logfile unless it is already defined
$logfile="/dev/null" unless ($logfile);

# set umask
umask 0022;
my $loghandler=open(LOGFILE,'>>',$logfile);

# autoflush file handler
select((select(LOGFILE), $| = 1)[0]);

# inifinite loop over input from STDIN
while (<STDIN>) {

# omit unwanted lines matching the following patterns
next if (/^Use of uninitialized value/);
next if (/^"my" variable/);
next if (/^Subroutine \w+ redefined/);
next if (/^Variable ".*?" will not stay shared/);
next if (/^Odd number of elements in hash/);
next if (/^Argument ".*?" isn't numeric/);
next if (/^Useless use of .*? in void context/);
next if (/^substr outside of string at/);
next if (/^Unquoted string ".*?" may clash/);
next if (/^Scalar value @.*? better written as/);
next if (/^Parentheses missing around/);

# print to our logfile
print LOGFILE "$_";
}

Posiadając już taki skrypt należy przekazać w konfiguracji apache`a jego wywołanie. Odbywa się to za pomocą pipe`a przez który przekazywane są komunikaty do skrypty. W tym przypadku jako parametr podana jest ścieżka do właściwego pliku logu, do którego dopisywane są wiadomości (nie należy zapomnieć o właściwiej konfiguracji logrotate!).
Przykładowa część konfiguracji:


ErrorLog "|/usr/local/bin/apache-logfilter.pl /var/log/apache/error_log"

Ciekawą rzeczą jaką napotkałem tutaj był problem buforowanego wyjścia. Otóż do logu nie docierały żadne nowe wiadomości. Wielokrotnie sprawdzałem poprawność skryptu i za każdym razem działał on poprawnie zapuszczony z palca. Po krótkich poszukiwaniach znalazłem zmienną $|, której to niezerowe ustawienie powinno mój problem rozwiązać. A jednak nie rozwiązało. Dopiero wymuszenie tego na uchwycie otwartego pliku dało pożądane rezultaty. Odpowiada za to następujące wywołanie:


select((select(LOGFILE), $| = 1)[0]);

Teraz wszystko śmiga jak należy – czytelność logów zwiększyła się znacząco, a ich rozmiar zmniejszył się kilkukrotnie.