Overview

这次我们开发Bastion4服务器使用了JAVA+Perl的架构,后端用Perl做服务器提供Webservice,用JAVA框架Struts接收处理用户请求,再跟Perl服务器交互。
我们使用Apache2作为Perl服务器,由于Apache2默认并不支持Perl,因此需要简单配置一下,使得Apache2CGI的方式支持Perl运行。在配置的过程中,参考了一些网页,但由于各个网页都没有完整地描述配置过程(至少在我们的服务器上是这样),所以这里记录一下我们配置的全过程。

1. Apache2基本配置

我们为Bastion4申请了一个新的云服务器,版本为NeCTAR Ubuntu 16.04 LTS x86_64,自带了Apache2服务器,下面简单列出了Apache2的一些基本信息:

  • /etc/apache2/ Apache2的配置文件目录
  • /var/www/ Apache2的网站存放目录
  • /var/log/apache2/ Apache2的日志存放目录
  • /etc/apache2/apache2.conf Apache2的配置主文件,以前的Apache版本中,主配置文件名字叫httpd.conf,所以很多网页中还用到这个名字,不知道的话会造成很多混淆。尽管apache2.conf是主配置文件,但是多数具体的配置信息并不是写在这个文件中的,/etc/apache2目录中有还有几个文件夹分别记录了几个模块的配置信息,apache2.conf负责把他们导入整合在一起。
  • /etc/apache2/sites-enabled/ 看名字就可以明白,Apache2的网站配置文件一般都是放在这个目录中的,这个目录下的000-default.conf是我们经常要修改的配置文件。

我们假定Apache存放网站的根目录是/var/www/,如果不确定(因为默认好像是/var/www/html这个目录),打开/etc/apache2/apache2.conf目录,查看下面的配置:

<Directory /var/www/>
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

如果在你的配置里是

<Directory /var/www/html/>

修改成

<Directory /var/www/>

这样,当你把一个名叫Mywebsite的网站(通常是一个文件夹),扔到/var/www中时,你就可以以http://localhost/Mywebsite/访问到这个网站了。

如果你的配置是<Directory /var/www/html/>,那你需要把你的网站扔到/var/www/html中,才能使用http://localhost/Mywebsite/访问,这是因为<Directory>设置了Apache的根目录,当你使用http://****/Mywebsite访问时,Apache会去当前的根目录/Mywebsite找这个网站。

由于Apache的端口号默认是80,而不加端口号的url访问都会被服务器默认定向到80端口,因此这里不需要添加端口号,如果你手动将Apache端口号修改成了别的端口号(比如8888),那你需要使用http://localhost:8888/Mywebsite/访问这个网站。

2. 配置Apache2支持Perl CGI程序

2.1 创建一个Perl网站

  1. /var/www下创建网站的目录,我们创建一个名字叫cgi-bin的网站作为例子:

    mkdir /var/www/cgi-bin
    

    需要使用管理员权限的命令,请自己加上sudo

  2. /var/www/cgi-bin中创建一个cgi_test.pl脚本,输入下面的内容:

    #!/usr/bin/perl -w
    use warnings;
    use CGI qw(:standard);
    #! must use 'my' to define a variable
    print header;
    my $now_string = localtime();
    print "<b>Hello, CGI using Perl!</b><br/>It's $now_string NOW!<br />";
    
  3. cgi_test.pl添加可执行权限:

    chmod +x /var/www/cgi-bin/cgi_test.pl
    
  4. 在命令行运行cgi_test.pl

    /var/cgi-bin/www/cgi_test.pl
    

    会得到下面的结果:

    Content-Type: text/html; charset=ISO-8859-1
    
    <b>Hello, CGI using Perl!</b><br/>It's Mon Aug  1 03:35:42 2016 NOW!<br />
    

    成功运行这个perl脚本需要先安装perlCGI

  5. 尽管现在我们可以在本地以命令行的形式运行这个perl脚本,但还是不能让这个脚本在服务器中运行,访问http://localhost:8888/cgi-bin/cgi_test.pl,浏览器会直接显示整个脚本的内容:

    #!/usr/bin/perl -w
    use warnings;
    use CGI qw(:standard);
    #! must use 'my' to define a variable
    print header;
    my $now_string = localtime();
    print "<b>Hello, CGI using Perl!</b><br/>It's $now_string NOW!<br />";
    

    而不是只把脚本打印的内容显示出来,所以,接下来我们继续配置Apache服务器。

2.2 配置Apache服务器

  1. 打开/etc/apache2/sites-enabled/000-default.conf配置文件,找到下面的配置信息:

    <VirtualHost *:8080>
    ...
    
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www
        
    ...
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
        
    </VirtualHost>
    

    我们需要添加一些配置信息,添加之后,000-default.conf的内容如下:

    <VirtualHost *:8080>
    ...
    
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www
    
    ScriptAlias /cgi-bin/ /var/www/cgi-bin/
    <Directory "/var/www/cgi-bin">
             AllowOverride all
             Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
             Order allow,deny
             Allow from all
             AddHandler cgi-script .cgi .pl
    </Directory>
    ...
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
       
    </VirtualHost>
    

    很容易看得出来,这部分配置主要是用来添加Apacheperl的支持。

  2. 默认情况下,Apache并没有启用CGI模块,我们可以从/etc/apache2/mods-enabled/中确认这点。

    ls -l /etc/apache2/mods-enabled/ | grep cgi
    

    显示结果为空。

    ls -l /etc/apache2/mods-available/ | grep cgi
    

    显示下面的结果:

    -rw-r--r-- 1 root root   74 Mar 19 09:48 authnz_fcgi.load
    -rw-r--r-- 1 root root   58 Mar 19 09:48 cgi.load
    -rw-r--r-- 1 root root  115 Mar 19 09:48 cgid.conf
    -rw-r--r-- 1 root root   60 Mar 19 09:48 cgid.load
    -rw-r--r-- 1 root root   89 Mar 19 09:48 proxy_fcgi.load
    -rw-r--r-- 1 root root   89 Mar 19 09:48 proxy_scgi.load
    

    从名字上就可以看出来,mods-enabled中存放的是已经启用的模块,mods-available中存放的是所有可用的模块,所以启用CGI模块变得很简单,把mods-available中的相关文件在mods-enabled中创建软连接就可以了。

  3. mods-enabled中创建软连接,指向mods-available中的cgid.*文件:

    ln -s /etc/apache2/mods-available/cgid.load /etc/apache2/mods-enabled/
    ln -s /etc/apache2/mods-available/cgid.conf /etc/apache2/mods-enabled/
    

    再次查看mods-enabled

    ls -l /etc/apache2/mods-enabled/ | grep cgi
    

    显示结果为:

    lrwxrwxrwx 1 root root 37 Jul 24 11:55 cgid.conf -> /etc/apache2/mods-available/cgid.conf
    lrwxrwxrwx 1 root root 37 Jul 24 11:55 cgid.load -> /etc/apache2/mods-available/cgid.load
    
  4. 重启Apache服务器:

    service apache2 restart
    

    或者重新载入配置文件:

    service apache2 reload
    

    稳妥的方式是restart Apache服务器,而不只是reload服务器,参见3.2

  5. 在浏览器中访问:http://localhost:8888/cgi-bin/cgi_test.pl就会显示:

    Hello, CGI using Perl!
    It's Mon Aug 1 03:48:37 2016 NOW!
    

可以看到perl脚本在Apache服务器中以CGI脚本的形式运行了。

3. 可能遇到的一些错误

实际上,如果你严格按照上面的步骤,基本上不会出现错误,但是在这里,还是列出一些常见的错误,以便真的遇到这些问题时可以快速查看。

本节主要取自Perl/CGI script with Apache2,所以里面的脚本名字(这里是echo.pl,脚本的路径(这里是/var/cgi-bin而不是/var/www/cgi-bin)和错误信息都沿用了这篇文章的内容,与本文上面使用的脚本名字,脚本路径和脚本内容内容稍有不同,但也都大同小异。

3.1 500 Internal Server Error

如果你使用浏览器访问perl脚本时,看到500 Internal Server Error错误,打开Apache的错误日志/var/log/apache2/error.log

  1. 如果显示下面的错误信息:

    [Wed Mar 19 15:19:15.740781 2014] [cgid:error] [pid 3493:tid 139896478103424] (8)Exec format error: AH01241: exec of '/var/cgi-bin/echo.pl' failed
    [Wed Mar 19 15:19:15.741057 2014] [cgid:error] [pid 3413:tid 139896186423040] [client 192.120.120.120:62309] End of script output before headers: echo.pl
    

    说明脚本的第一行sh-bang line没有正确指向'perl'的安装路径,检查你的perl脚本,确认第一行是下面的样子:

    #!/usr/bin/perl
    

    其实这样的错误非常罕见,除非你是新手,而且又完全自己手写了一段perl脚本。

  2. 如果显示下面的错误信息:

    No such file or directory: AH01241: exec of '/var/cgi-bin/echo.pl' failed
    [Wed Mar 19 15:24:33.505429 2014] [cgid:error] [pid 3412:tid 139896261957376] [client 192.120.120.120:58087] End of script output before headers: echo.pl
    

    说明echo.plDOS格式,而非Unix格式,如果你经常的是Windows,或者喜欢在Windows下写好脚本再上传到Unix/Linux服务器中,那么这会是一个常见而且通用的错误。使用dos2unix命令转换脚本的格式:

    dos2unix /var/cgi-bin/echo.pl
    

    系统默认可能并没有装dos2unix命令,需要自己安装一下。如果你用的是Ubuntu,用sudo apt install dos2unix安装。

  3. 如果显示下面的错误信息:

    [Wed Mar 19 15:40:31.179155 2014] [cgid:error] [pid 4796:tid 140208841959296] (13)Permission denied: AH01241: exec of '/var/cgi-bin/echo.pl' failed
    [Wed Mar 19 15:40:31.179515 2014] [cgid:error] [pid 4702:tid 140208670504704] [client 192.120.120.120:60337] End of script output before headers: echo.pl
    

    说明你没有为perl脚本添加可执行权限,使用chmod命令很容易更正这个错误:

    chmod +x /var/cgi-bin/echo.pl
    
  4. 如果显示下面的错误信息:

    Wed Mar 19 16:02:20.239624 2014] [cgid:error] [pid 4703:tid 140208594970368] [client 192.120.120.120:62841] malformed header from script 'echo.pl': Bad header: hi
    

    上面错误对应的脚本:

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    print "hi\n";
    print qq(Content-type: text/plain\n\n);
    

    perl脚本输出Content-type之前输出了别的字符,而浏览器解析的时候把那些字符当做了Content-type,所以报了Bad header错误。所以,不要在print qq(Content-type: text/plain\n\n);之前,输出别的字符。

  5. 如果显示下面的错误信息:

    [Wed Mar 19 16:08:00.342999 2014] [cgid:error] [pid 4703:tid 140208536221440] [client 192.120.120.120:59319] End of script output before headers: echo.pl
    

    还是一个跟header相关的问题,说明你的脚本在打印Content-type之前就出了错误或者异常,error.log中也可能在上面的错误信息之前提供其他更具体的相关信息。所以,请仔细检查打印Content-type之前的perl代码。

    关于End of script output before headers错误,原文作者认为可能与Premature end of script headers是同样的原因。

3.2 503 Service Unavailable

如果在本文2.2节为mods-enabled创建了软连接之后,并及时重载了apache,会报下面的错误:

[Wed Mar 19 15:30:22.515457 2014] [cgid:error] [pid 3927:tid 140206699169536] (22)Invalid argument: [client 192.120.120.120:58349] AH01257: unable to connect to cgi daemon after multiple tries: /var/cgi-bin/echo.pl

我猜reload只是重新载入了服务器配置信息,并未将CGI进程启动,所以尽管已经配置了,但是由于CGI模块并未启动,所以自然也就没法连接CGI的守护进程。
所以,如果你只是修改了配置文件,reload就可以了,如果你启用或禁用了某个模块,需要restart,因为所有启用的模块,是在服务器启动过程中启动的。

稳妥的方式是restart Apache服务器,而不只是reload服务器。

3.3 404 Not Found

如果显示下面的错误信息:

[Wed Mar 19 15:35:13.487333 2014] [cgid:error] [pid 4194:tid 139911599433472] [client 192.120.120.120:58339] AH01264: script not found or unable to stat: /usr/lib/cgi-bin/echo.pl

echo.pl确实已经存在了,那么检查/etc/apache2/sites-enabled/000-default.conf中的DocumentRootScriptAlias是否配置正确。

3.4 403 Forbidden

如果你遇到403 Forbidden错误,问题一般也出现在/etc/apache2/sites-enabled/000-default.conf这里。检查<Directory>,确保里面涉及到权限的语句正确配置了。

4. 小结

CGI的方式运行perl是最直接最原始的方式,如果你已经成功地以CGI形式运行了perl脚本,建议你尝试下面的方式:

参考文章