我在写shell自动部署脚本碰到的问题
如题,最早接触shell脚本是大学的时候,感觉好复杂,最近几年工作中也零零散散的接触过很多,很多都是网上找资源进行解决,这两天在写自动部署的初始化脚本,感觉还是挺多不足的。
shell脚本写的有点c语言的感觉,是过程语言,但是如何模块化就非常重要了,首先我的第一个版本是完成了从远端服务器获取资源文件,一键部署tomcat,jdk,生成backup目录,log目录,web目录,script目录,tools目录,生成tomcat自启动脚本。以及和远端服务器的交互等。整个shell脚本写下来花了半天的时间,一个方法搞定全部,没有错误处理机制,没有合适的操作日志。所有就有了下面的重构。
shell脚本中遇到的问题
1.执行脚本的日志打印,错误记录,类似于日志机制没有
v1.0的版本采用的日志方式是,直接print,遇到错误直接return
v2.0的方式,现在的方式是模块化日志方式,看代码
#调试,调用wget记录日志 debug() { if [ $global_is_debug = "true" ];then debug_str=$1 echo "debug:##### ${debug_str} #####\n" fi } #错误,调用wget记录日志 error() { echo "error:########### $1 ############\n" }
2.用wget方式和远程服务器交互,特殊字符处理
v1.0:直接wget –i 一个列表,然后存到本地,一系列操做后,删除文件
优点是,简单粗暴,缺点是不能适应自动化部署
v2.0:封装wget模块,通过和远程服务器交换,动态获取下载列表,根据需要保存到指定目录,优点:更具有扩展性,缺点是,服务器请求多
在和远端服务器交换的时候设计到特殊字符及汉子的处理,v2.0封装了一个urlencode方法,进行转码。代码如下:
#进行编码,编码结果放在全局变量里面 urlEncode() { #需要被编码的字符串 encode_str_params1=$1 debug "${encode_str_params1}" #编码 global_encode_str=`echo "${encode_str_params1}" | tr -d '\n' | xxd -plain | sed 's/\(..\)/%\1/g'` return }
补充一点:wget 对于请求的url带有特殊字符的处理,我后台采用的是struts2,提供的api接口带有 例如 http//t.cn/api/linux!errorDebug?xx=oo&oo=xx
你采用传统的方式都会报bad参数的错误,调试很久都未果,我的经验是,采用post方式,如果有特殊字符的 需要用 ‘’ 包含起来,比如上述的。
下面的代码中wgetPost是封装了一个将请求文件存到指定目录的功能,文件名命名方式为 debug_时间_全局的自增字段。
第一行请求变量参数里有特殊字符,后面跟着的字符串也需要用‘’包含起来,具体为什么,应该是shell的解释器就是这样规定的,‘’小括号里不能包含变量
wgetPost ${global_log_url}'logDebug?' ${global_project_log_debug_path}/ "debug_"$(date +%Y-%m-%d-%H-%M-%S)"_${global_debug_file_log_num}" ${inner_post_data}
#wget简单封装
#inner_url: 访问地址
#inner_dir: 目录
#inner_file: 文件名称
#inner_post_data 请求的数据
wgetPost()
{
inner_url=$1
inner_dir=$2
inner_file=$3
inner_post_data=$4
#容错处理
mkdir -p -m 777 ${inner_dir}
wget --post-data="${inner_post_data}" ${inner_url} -O ${inner_dir}${inner_file}".bk" -o /dev/null
inner_bytes=$(du -s ${inner_dir}${inner_file}.bk | awk '{print $1}')
if [ $inner_bytes -ne 0 ];then
cp -rf ${inner_dir}${inner_file}.bk ${inner_dir}${inner_file}
else
echo "wget ${inner_url} failed"
fi
}
3.sed命令在处理特殊字符替换的时候处理
sed –i ‘s/a/b/g’ 一般是这种方式,但是如果遇到要替换的字符或者被替换的字符有*_/ \的时候,就不适用了,解决的方式是利用#号
可以通用点,就是将源字符串,替换为目标字符串
source_str="abc" target_str="xxoo" sed -i "s#${source_str}#${target_str}#g" str.txt
4.合理的代码编程规范
编码规范很重要,v1.0版本写的比较随意,变量名比较混乱,维护花时间,
v2.0注意这个问题,目前的规范是:
1.所有的静态变量 前缀为 static_xx_xx
2.所有的全局变量 前缀为 global_xx_oxx
3.参数为 in_xx_xx
4.方法内的局部变量为 inner_xxdf
5.方法的命名规则遵循通俗易懂
6.模块化操作,讲究代码
5.目录的权限问题
目录的权限问题是个非常头疼的问题,因为所有的操作为root操作,如果控制到非常精细力度的权限,这块都要头疼死,在v2.0稳定的基础上,保证自动部署系统的如期完成,顶级目录不开放修改,子目录为777权限,任何人可以修改,等自动部署系统前台完成后,就可以完全回收权限了
6.日志记录的级别
日志这个东西很重要,v1.0的版本是采用打印到控制台的方式
v2.0采用方式是进行级别控制 ,分为debug 和error 方式,debug模式有开关配置,error模式之间输出,并且通过wget方式入库,及时反馈到前台
7.应用服务器及依赖环境的准备方式
依赖环境也很重要,自动部署讲究一个自动部署,统一规范,但是线上的版本很多,比如jdk有1.5,1.6,1,7 同时 tomcat也会有各种版本,memcache等都各不相同。为了统一,首先取线上稳定的tomcat版本清理后作为基础版本,例如tomcat.7.39.tar.gz,解压后利用脚本动态更改配置后就可以使用,还有jdk的安装目录等,现在的做法就是 把线上的版本都收集起来,通过后台网页工具进行选择,自定义配置,首先应用环境的基础版本是清洁版本,配置文件已经进行过优化,只需要替换几个常量即可,这里完成的是,1.tomcat的端口号,关闭端口,启动时的jdk目录,自启动脚本的tomcat-home等