18.13 页面自动化安装LAMP环境

LAMP环境是Linux+Apache+MySQL+PHP的经典组合,常用来搭建动态网站或者服务器的开源软件。它们本身都是各自独立的程序,但是因为常被放在一起使用,拥有了越来越高的兼容度,因而共同组成了一个强大的Web应用程序平台。随着开源潮流的蓬勃发展,开放源代码的LAMP已经与J2EE和.Net商业软件形成了三足鼎立之势,并且该软件开发的项目在软件方面的投资成本较低,因此受到了整个IT界的关注。从网站的流量上来说,70%以上的访问流量由LAMP提供,所以LAMP是最强大的网站解决方案,很多知名网站都采用了该环境,如表18-1所示。

表18-1 部分采用LAMP环境的著名站点

18.13 页面自动化安装LAMP环境 - 图1

由于LAMP环境的流行度非常高,所以LAMP环境的安装也成了很多网站运维人员的基本技能和日常工作内容。笔者曾为国内某著名云计算公司开发了一套可为Linux主机自动安装软件的系统,对外提供相关API调用方法,其中就包含了安装LAMP环境的功能。本节节选了该系统中的部分代码作为示例,带领读者开发一套可完成LAMP环境安装的系统。读者只需要通过在网页中填写要安装的服务器的IP地址以及该服务器的密码,然后单击按钮,就可以实现LAMP环境的部署。在实际工作时可根据需求再做修改,以实现更多功能。

实验环境:两台预装好Apache、PHP环境的Linux服务器,服务器B(IP:172.16.5.20)模拟前端页面服务器,用于页面化的用户交互;服务器A(IP:192.168.61.130)作为软件安装API服务器,在接到前端服务器安装需求时进行实际的软件安装。在CentOS下只需要一条命令就可以安装完成,如果是没有RHN支持的RedHat系统,则只能采用RPM安装或者按照8.3.3节介绍的方法让RedHat支持yum安装,然后使用yum方式安装。采用yum方式安装的命令如下:


  1. [root@localhost ~]# yum -y install httpd php

服务器A:提供软件安装API。


#

启动httpd

服务

[root@localhost ~]# service httpd start #

varwww/html

目录中创建文件,内容如下所示

#

主程序,用于接收处理前端安装需求

[root@localhost html]# cat install.php <?php

include_once "crypt.php";

$USER = $_REQUEST['user'];

$CRYPT_PASS = $_REQUEST['pass']; $PASS = DeCode($CRYPT_PASS,'D',$key); $IP = $_REQUEST['ip'];

$OS = $_REQUEST['os'];

$SOFT = $_REQUEST['soft'];

$ID = $_REQUEST['id'];

if ($PASS == "") {

print "Error:Password encrypt fail"; $fp = fopen("log/error.log","a"); $time = date("D M j G:i:s T Y"); $fileData = "$time Error:$PASS $IP $OS $SOFT Password encrypt fail\n"; fwrite($fp,$fileData);

fclose($fp);

exit();

}

if ($USER != "root") {

print "Error:User Must be root"; $fp = fopen("log/error.log","a"); $time = date("D M j G:i:s T Y"); $fileData = "$time Not root:$PASS $IP $OS $SOFT\n"; fwrite($fp,$fileData);

fclose($fp);

exit();

}

if (($OS == "centos") && ($SOFT == "lamp")) {

exec("usrbin/sudo varwww/html/nmap_port.sh $IP",$out_1,$status_1); if ($status_1 != 0) {

$fp = fopen("log/error.log","a"); $time = date("D M j G:i:s T Y"); $fileData = "$time Error:$PASS $IP $OS $SOFT Nmap exit code $status_1\n"; fwrite($fp,$fileData); fclose($fp);

}

if ($status_1 == 1) {

print "Error:Host Unreachable"; exit(1);

}

if ($status_1 == 2) {

print "Error:Port 80 Running"; exit(1);

}

if ($status_1 == 3) {

print "Error:Port 3306 Running"; exit(1);

}

exec("usrbin/sudo varwww/html/expect.sh $PASS $IP",$out_2,$status_2); if ($status_2 == 4) {

$fp = fopen("log/error.log","a"); $time = date("D M j G:i:s T Y"); $fileData = "$time Error:$PASS $IP $OS $SOFT Expect exit code $status_2, password not correct\n"; fwrite($fp,$fileData); fclose($fp);

print "Error:IP and password not match"; exit(1);

}

$fp = fopen("log/process.log","a"); $time = date("D M j G:i:s T Y"); $fileData = "$time START:$USER $PASS $IP $OS $SOFT $ID\n"; fwrite($fp,$fileData);

fclose($fp);

$exec_install="usrbin/sudo varwww/html/install_lamp.sh $IP $SOFT $OS"; system("{$exec_install} > devnull &"); print "Sucess";

}

else

print "Error:No such soft or OS not support"; ?>

install_lamp.sh

用于安装并启动相关服务

[root@localhost html]# cat install_lamp.sh #!binbash

IP=$1

SOFT=$2

OS=$3

ssh root@$IP "yum -y install httpd mysql mysql-server php php-mysql php-mbstring php-mcrypt php-xml php-xmlrpc php-gd"

ssh root@$IP "sbinchkconfig httpd on; binsleep 1 && sbinchkconfig mysqld on; binsleep 1 && sbinservice httpd start; binsleep 1 && sbinservice mysqld start"

crypt.php

为加密解密算法,用于对接收到的密码字段进行解密

[root@localhost html]# cat crypt.php <?php

$key = "!hT3^pDs";

function DeCode($string,$operation,$key='') {

$key=md5($key);

$key_length=strlen($key); $string=$operation=='D'?base64_decode($string):substr(md5($string.$key),0,8).$string; $string_length=strlen($string); $rndkey=$box=array();

$result='';

for($i=0;$i<=255;$i++) {

$rndkey[$i]=ord($key[$i%$key_length]); $box[$i]=$i;

}

for($j=$i=0;$i<256;$i++) {

$j=($j+$box[$i]+$rndkey[$i])%256; $tmp=$box[$i];

$box[$i]=$box[$j];

$box[$j]=$tmp;

}

for($a=$j=$i=0;$i<$string_length;$i++) {

$a=($a+1)%256;

$j=($j+$box[$a])%256; $tmp=$box[$a];

$box[$a]=$box[$j];

$box[$j]=$tmp;

$result.=chr(ord($string[$i])^($box[($box[$a]+$box[$j])%256])); }

if($operation=='D')

{

if(substr($result,0,8)==substr(md5(substr($result,8).$key),0,8)) {

return substr($result,8); }

else

{

return'';

}

}

else

{

return str_replace('=','',base64_encode($result)); }

}

?>

expect.sh

用于自动复制公钥,请确保已经生成了root.ssh/id_rsa.pub [root@localhost html]# cat expect.sh #!binbash

PASS=$1

IP=$2

auto_ssh_copy_id () {

expect -c "set timeout -1;

spawn usrbin/ssh-copy-id -i root.ssh/id_rsa.pub root@$2; expect {

(yes/no) {send — yes\r;exp_continue;}

assword: {send — $1\r;exp_continue;}

ssword). {exit 4;}

eof {exit 0;}

}";

}

auto_ssh_copy_id $PASS $IP

nmap_port.sh

用于在安装前测试是否指定服务器已经有相关组件在运行

#

如果是则会导致主程序在运行中途退出,以免发生重新安装造成主机问题

[root@localhost html]# cat nmap_port.sh #!binbash

IP=$1

SOFT=$2

OS=$3

if [ -z <em>usr</em>bin/nmap -p 22 $IP | grep open | awk &#39;{print $1}&#39; ]; then exit 1

fi

if [ ! -z <em>usr</em>bin/nmap -p 80 $IP | grep open | awk &#39;{print $1}&#39; ]; then exit 2

fi

if [ ! -z <em>usr</em>bin/nmap -p 3306 $IP | grep open | awk &#39;{print $1}&#39; ]; then exit 3

fi

exit 0

#

安装完成后检测应该正常运行的端口,返回主程序特定的退出码

[root@localhost html]# cat last_check.sh #!binbash

IP=$1

SOFT=$2

OS=$3

COUNT=2

if [ ! -z <em>usr</em>bin/nmap -p 80 $IP | grep open | awk &#39;{print $1}&#39; ]; then COUNT=$[$COUNT - 1]

fi

if [ ! -z <em>usr</em>bin/nmap -p 3306 $IP | grep open | awk &#39;{print $1}&#39; ]; then COUNT=$[$COUNT - 1]

fi

exit $COUNT

#

创建日志目录log

文件

[root@localhost html]# mkdir log [root@localhost html]# touch log/error.log [root@localhost html]# touch log/process.log


服务器B:提供用户界面。


#

启动httpd

服务

[root@localhost ~]# service httpd start #

varwww/html

目录中创建两个文件,内容如下所示

index.html

,软件安装的用户页面

[root@localhost html]# cat index.html <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd"&gt; <html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>InstallSoft</title> </head>

<body>

<form action="action.php" method="get"> IpAdd:<input type="text" name="ip" /> Pass:<input type="text" name="pass"/> <select name="os"> <option value="centos">centos</option> </select>

<select name="soft"> <option value="lamp">lamp</option> </select>

<input type="submit" value="INSTALL" /> </form>

</body>

</html>

action.php

,用于加密传输过程中的用户密码,以及调用API

的安装方法

HTTP

包在网络中传输时使用明文传输,所以需要将用户密码进行加密

[root@localhost html] cat action.php <?php

$key = "!hT3^pDs";

function DeCode($string,$operation,$key='') {

$key=md5($key);

$key_length=strlen($key); $string=$operation=='D'?base64_decode($string):substr(md5($string.$key),0,8).$string; $string_length=strlen($string); $rndkey=$box=array();

$result='';

for($i=0;$i<=255;$i++) {

$rndkey[$i]=ord($key[$i%$key_length]); $box[$i]=$i;

}

for($j=$i=0;$i<256;$i++) {

$j=($j+$box[$i]+$rndkey[$i])%256; $tmp=$box[$i];

$box[$i]=$box[$j];

$box[$j]=$tmp;

}

for($a=$j=$i=0;$i<$string_length;$i++) {

$a=($a+1)%256;

$j=($j+$box[$a])%256; $tmp=$box[$a];

$box[$a]=$box[$j];

$box[$j]=$tmp;

$result.=chr(ord($string[$i])^($box[($box[$a]+$box[$j])%256])); }

if($operation=='D')

{

if(substr($result,0,8)==substr(md5(substr($result,8).$key),0,8)) {

return substr($result,8); }

else

{

return'';

}

}

else

{

return str_replace('=','',base64_encode($result)); }

}

$password = $_GET["pass"];

$pass = DeCode($password,'E',$key); $os = $_GET["os"];

$soft = $_GET["soft"];

$ip = $_GET["ip"];

$id = rand();

echo

file_get_contents("http://192.168.61.130/install.php?user=root&pass=

$pass&ip=$ip&os=$os&soft=$soft&id=$id"); ?>


创建完成后,使用浏览器访问http://172.16.5.20,可以看到如图18-6所示页面。

18.13 页面自动化安装LAMP环境 - 图2

图18-6 软件安装工具前端

只要输入需要安装LAMP环境的主机的IP和root密码,并单击INSTALL按钮,很快该页面就会显示安装结果,如果安装正常则显示Success。常见的几种安装失败场景如下所示:


  1. Error:Host Unreachable #

  2. 主机不可达

  3. Error:Port 80 Running #

  4. 检测到80

  5. 端口已被占用(已有Web

  6. 服务)

  7. Error:Port 3306 Running #

  8. 检测到3306

  9. 端口已被占用(已有MySQL

  10. 服务)

  11. Error:IP and password not match #

  12. 提供的密码不正确,无法登录远程系统


实测时,该系统在CentOS release 5.5(Final)发行版中能正常运行,但可能会由于系统配置参数等环境差异,而无法保证部署到其他环境中不出现任何问题。如遇运行异常,请查看相关日志(包括但不限于varlog/message、varlog/httpd/error_log)。常见如下两个问题,需要对服务器A系统参数进行相关调整。

第一,由于install.php中使用了sudo命令,而Apache服务在运行时使用的是Apache用户,所以默认情况下Apache用户并没有sudo权限,需要通过/etc/sudoers文件给该用户适当的权限,否则PHP脚本会因为权限问题而无法运行。示例如下:


  1. apache ALL=(ALL) NOPASSWD:varwww/html/nmap_port.sh apache ALL=(ALL) NOPASSWD:varwww/html/expect.sh apache ALL=(ALL) NOPASSWD:varwww/html/install_lamp.sh #

  2. 或者为测试期间方便起见使用下面的设置,但是不建议在生产环境中使用

  3. #apache ALL=(ALL) NOPASSWD:ALL


第二,由于Apache用户并不是一个登录用户,所以即便按照上述方法赋予了正确的权限,也可能会因为没有获得tty而无法运行。在这种情况下在Apache的错误日志中会看到如下所示的报错:


  1. sudo: sorry, you must have a tty to run sudo

解决办法是,注销/erc/sudoers中的Defaults requiretty,表示用户在没有tty的情况下也可以运行命令。命令如下:


  1. #Defaults requiretty