简介
本指南将以一台机器为例,演示从安装 PBS,PostgreSQL 到实现一个简单的 apache 日志 ETL 分析程序的完整安装过程。
安装 PBS
编译
下载 TORQUE 源代码:
http://www.clusterresources.com/downloads/torque/
。解压缩后进行编译。
$ tar zxvf torque
- 2.3.3 . tar . gz
$ cd torque
- 2.3.3
$
. /configure --with-scp
$ make
$ sudo make install

如果有多台机器需要安装,可以使用 torque 提供的安装包功能。
$ make packages
这样在目录下会产生 torque-package- 系列包。在 master 机器上需要安装的是 server 包,在节点上需要安装的是 mom 包。在需要提交 PBS 任务的机器上需要安装 clients 包。
配置并启动后台进程
在安装相应的包之后需要进行配置。可以手工进行配置,但是推荐使用 torque 提供的 torque.setup 脚本生成初始化配置。方法是在 master 机器上使用命令:
$ sudo
. /torque.setup yewenb
initializing TORQUE (admin: [email protected])
Max open servers: 4
Max open servers: 4

在正确配置之后可以使用 qmgr -c 'print server' 检查配置是否成功。
$ qmgr
- c 'p s'
#
# Create queues and set their attributes.
#
#
# Create and define queue batch
#
create queue batch
set queue batch queue_type = Execution
set queue batch resources_default . nodes = 1
set queue batch resources_default . walltime = 01 : 00 : 00
set queue batch enabled = True
set queue batch started = True
#
# Set server attributes.
#
set server scheduling = True
set server acl_hosts = ubuntu
set server managers = yewenb@ubuntu . ubuntu - domain
set server operators = yewenb@ubuntu . ubuntu - domain
set server default_queue = batch
set server log_events = 511
set server mail_from = adm
set server scheduler_iteration = 600
set server node_check_rate = 150
set server tcp_timeout = 6
set server mom_job_sync = True
set server keep_completed = 300
然后需要启动调度进程。可以选择使用单独的调度程序比如 maui。这里直接使用 torque 提供的调度程序 pbs_sched。
$ sudo pbs_sched
在各节点上启动 mom 进程:
$ sudo pbs_mom
在启动 mom 之后,需要在 master 机器加上节点:
$ qmgr
- c 'create node ubuntu np=2'
在加入节点之后可以用 pbsnodes 命令检查节点是否可用 (在创建节点之后可能需要过一段时间节点的状态才能变成 free):
$ pbsnodes
ubuntu
     state
= free
     np
= 2
     ntype
= cluster
     status
= opsys = linux , uname = Linux ubuntu 2.6.27 - 7 - generic #1 SMP Tue Nov 4 19:33:20 UTC 2008 i686,sessions=4378 4853 5161 5298 5301 5307 5315 5317 5318 5320 5327 5354 5366 5436 5446 25697 25770 25777 25778 25779 25780 27368,nsessions=22,nusers=3,idletime=935,totmem=640676kb,availmem=319392kb,physmem=319416kb,ncpus=1,loadave=0.13,netload=111712774,state=free,jobs=,varattr=,rectime=1229343008
scp 配置
先安装ssh server
$ sudo apt
- get install openssh - server
对安装了节点的机器还需要配置 ssh 以保证日志文件成顺利传送回到 master 机器上。这需要在节点上产生 SSH key。
$ ssh
- keygen - t rsa
如果不要用passphrase则使用:
$ ssh
- keygen - P "" - t rsa
TIP: ubuntu 8.10的openssh-server用的protocol 2,所以要用rsa,而不能用rsa1
如果设置了passphrase还需要配置 ssh-agent 来记住密码。运行下面的命令:
$
eval `ssh-agent`
$ ssh
- add ~ /.ssh/ id_rsa
在这样设置之后,将 ~/.ssh/id_rsa.pub 添加到 master 机器的 ~/.ssh/authorized_keys 文件中。
修改配置 ~/.ssh/config
StrictHostKeyChecking no
如果在节点上使用下面命令不需要提示密码则应该能正常传递任务的输出文件:
$ scp file user@server_host
: /tmp
对咱们这里搭建的单机系统,只需要测试可以不要密码ssh到单机即可:
$ ssh localhost
提交测试任务
在以上配置和测试都通过,就可以提交一个任务测试一下:
$ echo
'sleep 10 && echo hello world' | qsub
0.ubuntu
$ qstat
- q
server
: ubuntu
Queue              Memory CPU Time Walltime Node    Run Que Lm    State
---------------- ------ -------- -------- ----    --- --- --    -----
batch              
--        --        --        --     1     0 --    E R
                                               
----- -----
                                                   
1       0
$ ls
- l
总用量 8
- rw ------- 1 yewenb yewenb   0 2008 - 12 - 15 20 : 36 STDIN . e0
- rw ------- 1 yewenb yewenb 12 2008 - 12 - 15 20 : 36 STDIN . o0
以上测试通过的话,恭喜你,终于有了一个自己的 PBS 系统了。
Trouble shoot
编译 torque 出错 之 formatcc1
: warnings being treated as errors
pbsd_main
. c : In function lock_out ’:
pbsd_main
. c : 1621 : error : format not a string literal and no format arguments
make
[ 2 ]: *** [ pbsd_main . o ] Error 1
make
[ 2 ]: Leaving directory `/home/yewenb/softwares/torque-2.3.3/src/server'
这个错误我在 Ubuntu 8.10 编译过程中遇到,原因是由于一个十分著名的漏洞
Format string attack
,编译器不予通过编译。
解决办法是将出错代码进行修改,把直接输出非字符串常量的地方都改成使用格式化字符串,如 fprintf(stderr, "%s", var)。
可以用这个命令修复代码中有问题的fprintf使用:
$ cd
/ home / yewenb / softwares / torque - 2.3.3 /
$ find
. - name *. c | xargs - n1 perl - i . bak - e 'while(){if(/\bfprintf\(stderr,/&&!/stderr,$/&&!/stderr, ?"/&&!/msg_momsetlim/){s/stderr,/stderr,"%s",/;print}else{print}}'
$ find
. - name *. bak - exec rm {} \;
如此之后,还有少量prinf也存在问题:
qmgr
. c : 1780 : error : format not a string literal and no format arguments
qmgr
. c : 1782 : error : format not a string literal and no format arguments
手工编辑修复即可(把 prinf( 改为 printf("%s" )
[2.3.3] 无法找到动态链接库 libtorque.so.2
在启动进程时,如果提示如下:
pbs_server
: error while loading shared libraries : libtorque . so .2 : cannot open shared object file : No such file or directory
如果使用的默认的配置选项,应该动态链接库安装路径应该是没有问题,只需要更新 ld 配置的缓存:
$ sudo ldconfig
[2.3.0] 编译 torque 出错 之 warn_unused_result
[2.3.3的编译选项改了,没有这个错误]
编译出错:
pbs_sched
. c : 984 : error : ignoring return value of freopen ’, declared with attribute warn_unused_result
pbs_sched
. c : 998 : error : ignoring return value of freopen ’, declared with attribute warn_unused_result
解决方法:手工编辑src/scheduler.cc/pbs_sched.c,把 freopen(dbfile, "a", stdout) 更改为 if(freopen(dbfile, "a", stdout)==NULL){}; (这么更改纯粹是为了副作用;因为直接在freopen前面加(void)的方法不行,依旧编译报错)
[2.3.0] torque.setup出错$ sudo
. /torque.setup liuqingy
initializing TORQUE (admin: liuqingy@ubuntu)
Max open servers: 4
qmgr obj= svr=default: Bad ACL entry in host list MSG=First bad host: ubuntu
Max open servers: 4
qmgr obj= svr=default: Bad ACL entry in host list MSG=First bad host: ubuntu

这是在ubuntu 8.10 intrepid ibex上出错。
执行命令时多加一个参数 ubuntu.ubuntu-domain可以完成pbs_server的启动:
$ sudo
. /torque.setup liuqingy ubuntu.ubuntu-domain
但是后续server和mom通信总是有问题!所以,这个问题目前没有fix的方案。
解决方案就是不要用2.3.0这个版本!2.3.3就没有这个问题。
安装 PostgreSQL
安装 PostgreSQL 比较简单,如果有 apt 系统的话,直接用 apt-get 安装就好了:
$ sudo apt
- get install postgresql - 8.3
不过由于下面需要安装 pg_bulkload,所以推荐使用源代码编译安装,方法也很简单,从
http://www.postgresql.org/ftp/source/
下载。 解压缩并编译即可:
$ tar zxvf postgresql
- 8.3.5 . tar . gz
$ cd postgresql
- 8.3.5
$
. /configure --with-perl
$ make
$ sudo make install

在安装之后就可以创建自己的数据库。
$
export PGDATA = /path/ to / db
$ initdb
安装 PostgreSQL 扩展
plproxy

http://pgfoundry.org/projects/plproxy
下载源代码。
$ tar zxvf plproxy
- 2.0.7 . tar . gz
$ cd plproxy
- 2.0.7
$ make
$ sudo make install
pg_bulkload

http://pgfoundry.org/projects/pgbulkload
下载源代码。
编译 pg_bulkload 需要有 PostgreSQL 的源代码。
$ cp pg_bulkload
- 2.4beta1.tar . gz postgresql - 8.3.5 / contrib
$ cd postgresql
- 8.3.5 / contrib
$ tar zxvf pg_bulkload
- 2.4beta1.tar . gz
$ cd pg_bulkload
$ make
$ sudo make install
$ mkdir $PGDATA
/ pg_bulkload
plproxy_rehash
下载源代码
http://farsail.googlecode.com/files/plproxy_rehash-0.0.2.tar.gz
编译安装。
$ make
$ sudo make install
Farsail 安装与配置
先用 cpan 安装 Module::Install模块。
然后下载最新的 Farsail 源代码:
$ svn
export http : //farsail.googlecode.com/svn/trunk/ $HOME/farsail
$ cd $HOME
/ farsail
$ perl
Makefile . PL PREFIX = $HOME / local
$ make
$ make install
可以选择不使用 make install,直接使用 $HOME/farsail 目录作为应用的目录,只要将 $HOME/farsail/scripts 路径加到 PATH,或者调用相应命令使用全路径即可。
还需要安装一些未提交到 CPAN 上的模块。 下载
http://farsail.googlecode.com/files/Term-PromptEdit-0.0.1.tar.gz

http://farsail.googlecode.com/files/Pg-Hashtext-0.1.2.tar.gz
,使用常规方法安装:
$ perl
Makefile . PL
$ make
$ make install
最简单的配置文件
下面从最简单的配置开始进行 Farsail 环境配置安装。首先创建一个新目录进行用于我们的项目。
$ mkdir farsail
$ cd farsail
$ cp
- r $HOME / farsail / conf .
$
export FARSAILDIR = `pwd` / conf
现在运行 farsail_do help 应该有如下输出:
$ farsail_do help
global :
        bulkload
        help
rehash_table
:
        setup
manager
:
        config
        install
        
print
        setup
配置 PBS
PBS 的配置是各个节点名字和默认的 queue 的名字。通过下面命令进行自动化配置:
$ farsail_mgr config
-- pbs -- inplace
Get PBS compute nodes list ...
Compute Nodes are list following :
ubuntu
edit the list
? [ n ]
Get PBS queue name ...
PBS queue
is : [ batch ]
farsail_mgr 将通过 pbsnodes 命令得到所有的节点名字,使用 qmgr 得到默认的 queue 的名字。通常这都不需要作修改的。在确认配置之后,将输出新的配 置文件到屏幕,并直接替换原配置文件。如果不需要替换原配置文件,去掉命令行的 --inplace 选项,则将在当前目录中产生一个相同内容的 farsail.conf 文件。
现在 farsail.conf 文件应该如下:
#---------------------------------------------
# PBS settings
#---------------------------------------------
pbs
. queue = batch
pbs
. nodes .0 = ubuntu
#---------------------------------------------
# Pl/Proxy cluster settings
#---------------------------------------------
default_cluster
=
#--------------------------------------------
# Database configuration
#--------------------------------------------
databases_config_file
= farsail_db . yml
#---------------------------------------------
# Actions definition
#---------------------------------------------
action_define_file
= farsail . act
#---------------------------------------------
# Command line options
#---------------------------------------------
global_arguments
= fake !, force !, ni !, cron !
配置 Cluster
plproxy 的数据库集群由一个 proxy 数据库和若干数据库节点组成。数据库节点的数目需要是 2 的幂。下面我们创建一个集群,proxy 数据库名为 proxy,节点数据库有两个,数据库名为 db_1 和 db_2。
初始化 cluster 可以使用如下命令:
$ farsail_mgr config
- cluster - inplace
Configure pl / proxy cluster :
1 ) add cluster
> ( 1 - 1 ) 1
name of cluster
: farsail
Create new database nodes list ? ( y / n ) [ default y ]
Number of database nodes ( power of 2 ) [ default 16 ] 2
Prefix for database name : db_
Known host are :
ubuntu
Add host : ( enter to quit )
Database user : yewenb
由于我们是第一次创建数据库集群,选择 add cluster 选项,然后输入 cluster 的名字。提示是否创建新的节点数据库,选择是时提示输入数据库的 host 列表。这个 host 列表将用于自动产生数据库和 host 关联列表。初始 host 列表由 PBS 节点的 host 给出。
在输入这些信息后会使用编辑器打开一个窗口,提示的内容如下:
## Set proxy database here ##
# If use exists database only db_id need, otherwise
# db_id dbname host user password port
## Set nodes database here ##
# if use exists database only db_id need, otherwise
# node_index db_id dbname user host port
1        db_1    db_1    ubuntu  yewenb   ~        ~
2        db_2    db_2    ubuntu  yewenb   ~        ~
你需要填的是 proxy 数据库的连接配置,至于节点数据库的连接配置可以修改或者直接使用默认产生的列表。比如在第三行之后填上:
proxy proxy ubuntu yewenb
值得注意的是 plproxy 的节点数据库需要以无密码方式进行访问,必要时需要修改各节点数据库的 pg_hba.conf 文件,使 proxy 数据库的访问级别是 trust 类型。
保存后就会生成对应的配置及数据连接配置。现在 farsail.conf 文件如下:
#---------------------------------------------
# PBS settings
#---------------------------------------------
pbs
. queue = batch
pbs
. nodes .0 = ubuntu
#---------------------------------------------
# Pl/Proxy cluster settings
#---------------------------------------------
default_cluster
= farsail
cluster
. farsail . proxy = proxy
cluster
. farsail . nodes .0 = db_1   
cluster
. farsail . nodes .1 = db_2   
#--------------------------------------------
# Database configuration
#--------------------------------------------
databases_config_file
= farsail_db . yml
#---------------------------------------------
# Actions definition
#---------------------------------------------
action_define_file
= farsail . act
#---------------------------------------------
# Command line options
#---------------------------------------------
global_arguments
= fake !, force !, ni !, cron !
在相同的目录下的 farsail_db.yml 文件如下:
---
db_1
:
  dbd
: Pg
  dbname
: db_1
  host
: ubuntu
  user
: yewenb
db_2
:
  dbd
: Pg
  dbname
: db_2
  host
: ubuntu
  user
: yewenb
proxy
:
  dbd
: Pg
  dbname
: proxy
  host
: ubuntu
  user
: yewenb
这只是产生了数据库的配置,数据库并没有真正存在。可以使用下面的命令创建所有的数据库:
$ farsail_mgr setup
-- createdb
createdb
- h ubuntu - U yewenb proxy
createdb
- h ubuntu - U yewenb db_1
createdb
- h ubuntu - U yewenb db_2
安装 pg_bulkload 扩展
使用 farsail_mgr install 命令用于向数据库安装 sql 脚本。如果指定了 -proxy 选项则向 cluster 的 proxy 数据库安装 sql 脚本,否则向所有节点数据库安装脚本。
$ farsail_mgr install
`pg_config --sharedir` / contrib / pg_bulkload . sql
$ farsail_mgr install
`pg_config --sharedir` / contrib / pg_timestamp . sql
为使用 pg_bulkload 扩展,还需要在 farsail.conf 中加入以下配置:
bulkload
. share_directory = .. /tmp/ bulkload
bulkload
. format_directory = bulkload
它们的作用是 format_directory 用于读取 pg_bulkload 的模板文件,它应该已经在 $FARSAILDIR 目录中。share_directory 用于存储临时的 pg_bulkload 配置文件,需要创建这个目录:
$ mkdir
- p $FARSAILDIR /. . /tmp/ bulkload
安装 plproxy 扩展
安装 plproxy 扩展本身只需要执行命令:
$ farsail_mgr install
-- proxy `pg_config --sharedir` / contrib / plproxy . sql
farsail 为方便 plproxy 使用提供一些定制的存储过程。
$ farsail_mgr install
-- proxy $HOME / farsail / sql / plproxy / proxy . sql
$ farsail_mgr install $HOME
/ farsail / sql / plproxy / node . sql
还需要对当前配置文件中的 cluster 生成适当的配置:
$ farsail_mgr setup
SELECT plproxy
. set_default_cluster_name ( 'farsail' );
DELETE FROM plproxy
. conf WHERE cluster_name = 'farsail' ;
INSERT INTO plproxy
. conf ( cluster_name , host , port , dbname , username ) VALUES
( 'farsail' , 'ubuntu' , '5432' , 'db_1' , 'yewenb' ),
( 'farsail' , 'ubuntu' , '5432' , 'db_2' , 'yewenb' );
$ farsail_mgr setup
| psql proxy
安装 plproxy_rehash 扩展
同样需要先在 proxy 数据库和节点数据库安装扩展的 sql:
$ farsail_mgr install
-- proxy `pg_config --sharedir` / contrib / plproxy_rehash_proxy . sql
$ farsail_mgr install
`pg_config --sharedir` / contrib / plproxy_rehash . sql
重哈希扩展还需要在节点数据库安装配置。它需要的配置包括一个共享目录用于存放临时的分桶文件,还需要指定数据加载的方法。目前数据加载的方法可以选择 copy 或 pg_bulkload。可以使用 rehash_table.setup 自动产生 cluster 的配置:
$ farsail_mgr rehash_table
. setup - dir $FARSAILDIR / rehash - loadby pg_bulkload - output rehash_setup . sql
\ c cluster . farsail . proxy
INSERT INTO plproxy
. cluster_config ( name , setting ) VALUES
( 'share_directory' , '/home/yewenb/temp/farsail/conf/rehash' );
\ c cluster . farsail . nodes .0
INSERT INTO plproxy
. cluster_config ( name , setting ) VALUES
( 'db_node' , 1 ),
( 'nof_nodes' , 2 ),
( 'load_method' , 'pg_bulkload' );
\ c cluster . farsail . nodes .1
INSERT INTO plproxy
. cluster_config ( name , setting ) VALUES
( 'db_node' , 2 ),
( 'nof_nodes' , 2 ),
( 'load_method' , 'pg_bulkload' );
这个生成的 sql 语句中 \c 指令是与不是直接提交给 psql 语句,而是由 farsail_mgr install 识别后使用 DBI 在对应的数据库中运行。所以需要使用 farsail_mgr install 进行安装。
$ farsail_mgr install rehash_setup
. sql
至此 farsail 的 PBS 及数据库配置告一段落,让我们进行一个 apache 日志分析。
Trouble shoot
无法联接到数据库
如果创建数据库时,提示:
createdb
: 无法联接到数据库 postgres : could not connect to server : 连接被拒绝
        
Is the server running on host "ubuntu" and accepting
        TCP
/ IP connections on port 5432 ?
需要更改 postgresql.conf 和 pg_hba.conf,使数据库监听客户端的 IP 及允许客户端进行访问。最简单的修改是将 postgresql.conf 中的 listen_addresses 改成 "*",在 pg_hba.conf 中加入一行:
host    all         all         
1.0.0.0 / 0           trust
注意:这些修改将允许任何机器对 pg 服务器进行访问。
Apache 日志分析示例

http://farsail.googlecode.com/files/exampleWebLog.bz2
可以得到演示使用的日志数据。
日志解析使用的 LogParser 模块可以从 Farsail 源代码的 demo 目录中得到。
数据库设计
日志数据库的表结构如下:
CREATE TABLE log_daily
(
   thedate date
,
   ip text
,
   log_time timestamptz
,
   url text
,
   method text
,
   status
int ,
   referer text
,
   user_agent text
);
我们将把日志按 ip 进行 hash 分桶。目前我们使用的数据库有两个,为日后数据库扩容着想,实际的分桶数我们定成 8 个。
使用日期进行分表,每天的日志将存储到单独的表中。创建新表的语句如下:
CREATE TABLE log_daily_20081225
(
   check
( thedate = '20081225' )
) INHERITS ( log_daily );
配置文件设计
首先需要进行配置文件设计。
log
. datadir = .. /data
log.tempdir = ../
tmp
log
. buckets = 8
在所有配置里,为了防止冲突,我们加入 log 前缀。日志文件可能是每天更新的,这里设定为在 $FARSAILDIR/../data 目录下每天都有一个 $date.log 文件。tempdir 用于指定分桶文件写入目录。buckets 用于指定分桶数。
将上面的配置写到 log.conf 文件中,然后保存到 $FARSAILDIR/app 目录中。在 farsail.conf 文件里加上一行:
include
= app /*.conf
这样 app 目录下所有后缀是 conf 的文件都将作为配置读入。
命令行设计
由于日志 ETL 是每天进行,所以至少需要一个日期参数。所以需要写一个这样的 action:
log
:
   
module : Etl :: ApacheLog
   daily
:
     args
:
       date
: { type : date , default : yesterday }
将上面的内容保存到 $FARSAILDIR/app/log.act 文件。程序将自动加载这个文件。
ACTION 代码编写
最后进行我们的 etl 代码编写。
package Etl :: ApacheLog ;
use strict ;
use warnings ;
use Carp ;
use LogParser ;
use Path :: Class ;
use Farsail :: Util qw / parse_date /;
use Pg :: Hashtext qw / hashtext /;
my $farsail ;
sub init {
   
my $pkg = shift ;
    $farsail
= shift ;
    $farsail
-> load_modules ( "Farsail::Bulkload" );
}
sub ACTION_daily {
   
my $config = $farsail -> config ;
   
my $args    = $farsail -> args ;
   
my $date    = parse_date ( $args -> date ) || confess "Argument date need!\n" ;
    $date
= $date -> ymd ( '' );
   
my $buckets = $config -> get ( 'log.buckets' );
   
my $logfile = $config -> get_expand_file ( 'log.datadir' , "$date.log" );
   
my $dir      = dir ( $config -> get_expand_file ( 'log.tempdir' , $date ) );
   
my $prefix   = "log_" ;
   
if ( !- d $dir ) {
        $dir
-> mkpath or die "can't make directory $dir: $!" ;
   
}
   
my @fh = map { file ( $dir , $prefix . "$_.dat" )-> openw } 0 .. ( $buckets - 1 );
   
my $p = LogParser -> new ( $logfile -> openr );
   
while ( my $ent = $p -> next ) {
        
my $date = substr ( $ent ->{ time }, 0 , 11 );
        
print { $fh [ hashtext ( $ent ->{ ip } ) % $buckets ] } join (
            
"\x01" , $date ,
            map
{
               
! defined ( $ent ->{ $_ } )
                  
|| $ent ->{ $_ } eq '-'
                  
? ''
                  
: $ent ->{ $_ }
              
} qw / ip time url method status referer ua /
         
),
         
"\n" ;
   
}
   
my $sql = SQL;
DROP TABLE IF EXISTS \$table;
CREATE TABLE \$table (
    CHECK (thedate = '$date')
) INHERITS (log_daily);
SQL
    Farsail::Bulkload->bulkload(
        dir        => $dir,
        prefix     => $prefix,
        suffix     => 'dat',
        cluster    => 'farsail',
        format     => 'ctrla',
        table => "log_daily_date",
        create_sql => $sql,
    );
    Farsail::PBS::Job->new({command => 'sleep 1'})->submit();
    check_done($config->get('bulkload.done_file'));
}
sub check_done {
    my $file = shift;
    print "Wait done $file\n";
    for ( 1..100 ) {
        if ( !-e $file ) {
            print "Sleep $_\n";
            sleep 30;
        } else {
            return 1;
        }
    }
    confess "The done file '$file' is not generate\n";
}
1;
运行
可以用 farsail_do 来运行定义好的 action:
$ farsail_do log.daily --date 20081224
检查数据确实导入到数据库中:
$ psql proxy -c "SELECT * from dquery('select current_database(), count(*) from log_daily') as(db name, cnt int8)"
  db  |  cnt   
------+--------
db_1 | 111256
db_2 | 138744
(2 笔资料列)