自动化运维:一键自动化脚本-shell

shell函数

1、分别在服务器和客户端上创建www用户

useradd www
id wwww
  1. 所有的web服务,都应该使用普通用户,所有的web服务都不应该监听80端口,除非负载均衡。8080
  2. 普通用户能启动80端口吗?通过和科技,比如给命令设置suid
  3. 生产指定uid

2、保证www用户登录其他的节点都不要输入密码

服务器端:

[root@node1 ~]# useradd www
[root@node1 ~]# id www
[root@node1 ~]# passwd www
[root@node1 ~]# su www
[root@node1 ~]# cd /home/www/
[www@node1 ~]$ ssh-copy-id -i .ssh/id_rsa.pub www@172.16.14.116
[www@node1 ~]$ ssh 172.16.14.116
Last failed login: Sat Jan 13 09:56:41 CST 2018 from 172.16.14.115 on ssh:notty
There were 3 failed login attempts since the last successful login.
Last login: Sat Jan 13 09:23:02 2018

客户端:

[root@node2 ~]# useradd www
[root@node2 ~]# id www
[root@node2 ~]# passwd www
[root@node2 ~]# su www
[root@node2 ~]# cd /home/www/
[www@node2 ~]$ ssh-copy-id -i .ssh/id_rsa.pub www@172.16.14.115
[www@node2 ~]$ ssh 172.16.14.115
Last failed login: Sat Jan 13 09:56:41 CST 2018 from 172.16.14.115 on ssh:notty
There were 3 failed login attempts since the last successful login.
Last login: Sat Jan 13 09:23:02 2018

3、写一个复杂的脚本

先把框架写出来,使用echo来测试框架的流程是否正确

#!/bin/bash

	# Shell Env
	SHELL_NAME="deploy.sh"
	SHELL_DIR="/home/www"
	SHELL_LOG="{SHELL_DIR}/${SHELL_NAME}.log"

	# Code Env
	CODE_DIR="/deploy/code/deploy/"
	TMP_DIR="/deploy/config"
	TAR_DIR="/deploy/tar"
	LOCK_FILE="/tmp/deploy.lock"


	usage(){
			echo $"Usage: $0[ deploy|rollback]"
	}

	shell_lock(){
			touch ${LOCK_FILE}
	}

	shell_unlock(){
			rm -f  ${LOCK_FILE}
	}

	code_get(){
			echo code_get;
			sleep 2;
	}

	code_build(){
			echo code_build;
			sleep 2;
	}

	code_config(){
			echo code_config;
			sleep 2;

	}

	code_tar(){
			echo code_tar;
			sleep 2;
	}

	code_scp(){
			echo code_scp;
			sleep 1;

	}

	cluster_node_remove(){
			echo cluster_node_remove;
			sleep 1;

	}

	code_deploy(){
			echo code_deploy;
	}

	config_diff(){
			echo config__diff;
	}

	code_test(){
			echo code_test;
	}

	cluster_node_in(){
			echo cluster_node_in;
	}

	main(){
	   if [ -f $LOCK_FILE ];then
			 echo "Deploy is running"&& exit;
	   fi
	   DEPLOY_METHOD=$1
	   case $DEPLOY_METHOD in
		  deploy)
				   shell_lock;
				   code_get;
				   code_build;
				   code_config;
				   code_tar;
				   code_scp;
				   cluster_node_remove;
				   code_deploy;
				   config_diff;
				   code_test;
				   cluster_node_in;
				   shell_unlock;
				   ;;
		  rollback)
				   shell_lock;
				   rollback;
				   shell_unlock;
				   ;;
		   *)
				   usage;
		esac
	}

4、什么也没提示?在末尾添加 man $1

	main(){
	   DEPLOY_METHOD=$1
	   case $DEPLOY_METHOD in
		  deploy)
				   shell_lock;
				   ;;
		  rollback)
				   shell_lock;
				   ;;
		   *)
				   usage;
		esac
	}
	main $1

本节小结:

1、凡是不记录日志的脚本就是耍流氓

2、这个脚本能不能多个人同时执行?

1、最好不要,但是运维团队有很多人,我如何知道别人有没有执行?

答:我写一个锁文件


1、锁文件放那?

答:放在/tmp/deploy.lock" 因为放在下www没有权限

2、系统的锁文件放哪里?
/var/run/lock

3、看不出来?


答:sleep60秒

 

4、不是脚本的$1匙函数的$1

 5、在两台电脑上都要用到所以定义为变量

 

2、功能实现

1、日志函数

#Date/Time Veriables
LOG_DATE='date "+%Y-%m-%d"'
LOG_TIME='date "+%H-%M-%S"'

CDATE=$(date "+%Y-%m-%d")
CTIME=$(date "+%H-%M-%S")
....
writelog(){
	LOGINFO=$1
	echo "${CDATE} ${CTIME}: ${SHELL_NAME}: ${LOGINFO}" >> ${SHELL_LOG}
}

 

1、希望在很多地方记录日志

echo一行,写到一个文件里,记日志还要记时间

方法1:写一个日志的函数,每次调用这个函数

方法2:在每一个函数里写一个echo,然后写在那个位置,还要记时间

2、日志函数的好处?

  1. 这个函数可以复制,以后写别的脚本直接改改就可以
  2. 每一个函数里都写一个函数

3、shell是如何解析的?

从上倒下逐行执行

4、遇到函数怎么办?

先加载不执行

5、日志的内容从哪来?

从参数来:$1

6、脚本名称:

当前日期+脚本名称+日志内容 

难保你以后会写在一起所以要区分开

7、时间是不是不能变?

我就不需要它变:

1、打包的时候不能变,包里有有日期和时间包名不能变
2、打包如何命名?

是scp时间不对,包就找不着了

LOG_DATE='date "+%Y-%m-%d"'
LOG_TIME='date "+%H-%M-%S"'
  1. 一个是让执行 记日志用的
  2. 一个不让执行 做别的用处
  3. 已经执行了,在后面就不变了

2、get代码函数

#Code Env
PRO_NAME="web-demo"
CODE_DIR="/deploy/code/web-demo"
CONFIG_DIR="/deploy/config/web-demo"
TMP_DIR="/deploy/tmp"
LOCK_FILE="/tmp/deploy.lock"
.......
code_get(){
		writelog "code_get";
		cd $CODE_DIR && echo "git pull"
		cp -r ${CODE_DIR} ${TMP_DIR}/
		API_VERL=$(git show |grep commit | cut -d ' ' -f2)
		API_VER=$(echo ${API_VERL:0:6})

1、代码应该放在那?

放在CODE_DIR="/deploy/code/web-demo"目录下

2、配置文件能直接放CODE_DIR这吗?

不能,专门用于git更新的目录
如果你把文件拷贝到这里,所有的包里面都有这个文件
一不小心多放了一个,那个可不会pull

3、怎样区分是仓库的还是我copy过来的?

也能区分 看git状态,本地状态 正常区分不了
更新完之后copy走,放着也行出故障了你就知道是什么意思了!

4、复制到哪?

TMP_DIR

5、为什么对web-demo要重命名?

  1. 复制过去要重命名
  2. 打包的时候还要重命名
  3. 每次都覆盖那就乱了

6、要怎样重命名?

时间+版本号?

1、svn怎样获取版本号?
2、git如何获取版本号?

API_VERL=$(git show |grep commit | cut -d ' ' -f2)

3、配置文件函数

#Code Env
PRO_NAME="web-demo"
CODE_DIR="/deploy/code/web-demo"
CONFIG_DIR="/deploy/config/web-demo"
TMP_DIR="/deploy/tmp"
LOCK_FILE="/tmp/deploy.lock"
......
code_config(){
		writelog "code_config"
		/bin/cp -r ${CONFIG_DIR}/base/* ${TMP_DIR}/"${PRO_NAME}"
		PKG_NAME="${PKG_NAME}"_"$API_VER"_"${CDATE}-${CTIME}"
		cd ${TMP_DIR} && mv ${PKG_NAME} ${PKG_NAME}}
}

1、我是哪个项目的配置文件?

CONFIG_DIR="/deploy/config/web-demo"

2、为什么加/bin/cp -r?

/bin/cp -r ${CONFIG_DIR}/base/* ${TMP_DIR}/"${PRO_NAME}"

1、有可能我文件下还有目录呢!
2、我拷贝的那个目录下有那个配置文件

有一测试有权限,犯二提交了一个相同的配置文件 你没有覆盖连到测试库了
大家打开的都是测试的库
开发可以错,测试可以错,运维不可以错 为什么你上线的时候也没发现?
每一个小细节都是有意义不是瞎写

3、复制和打包可以放在一个里面,为什么把包名做成一个变量?

  1. 很多地方都用到
  2. 包名很长
  3. 包名本身也包含变量

4、为什么要全写成变量

因为不只为这一个脚本,写别的脚本改改就可以啦!

5、为什么tmp需要定期进行清理?

部署几个月可以,时间久磁盘就满了

有时间了就好删除了,解决了各种方式
只有版本号你怎样删?把15年的全删了

4、打包函数

code_tar(){
		writelog "code_tar"
		cd ${TMP_DIR} && tar czf ${PKG_NAME}.tar.gz ${PKG_NAME}
		writelog "${PKG_NAME}.tar.gz"
}

1、为什么要写&&?

函数和函数之间可不知道上一级目录是什么!!!
不单独搞一行,如果目录不存在进去了可能不是你想要的

5、scp到目标服务器

code_scp(){
		writelog "code_scp"
		for node in $PRE_LIST;do
		for node in $GROUP1_LIST;do
				scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot/
		done
}

1、统一用一个包的好处?

完全可以写一个for循环
我要加机器 在列表里加一行
减机器 在列表里减去一行
标准化的好处

2、为什么不能直接写在/opt?

  1. 没有权限
  2. 在opt创建一个/opt/webroot/复制到这

6、部署函数

group1_deploy(){
		writelog "remove from cluster"
		for node in $GROUP1_LIST;do
			ssh $node "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz"
			ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo"	
		done
		scp ${CONFIG_DIR}/other/192.168.56.12.crontab.xml 192.168.56.12:/webroot/web-demo/crontab.xml
}

1、项目之间应该保持独立才对

2、你的目录放在那?

所有生产的web服务器的家目录都写在/webroot的项目名称下

3、为什么先创建软链接然后在复制差异文件?

路径写的少,要不然你写到解压后的路径下
如果没有生成我就不复制
生产部署的时候,没部署成功结果scp复制过去了


4、第一次手动创建一个因为 没有会报错。

su -www
cd /webroot/
touch web-demo
用salt就要先touch文件

5、&&不能去掉,因为以后部署时候我要先删除才能软链接

6、一个软连接连一毫秒都花不了

3、脚本扩展

1、每个节点上各装一个apache

yum install httpd -y

2、修改配置文件以下两处

vim /etc/httpd/conf/httpd.conf

1、测试函数

url_test(){
		URL=$1
		curl -s --head $URL |grep '200 ok'
		if [ $? -ne 0 ];then
				shell_unlock;
				echo "test error" && exit;
		fi
}

1、测试一能访问就加入集群不能访问就移除集群

2、部署一个测一个通了才能加到集群里

生产是一个组一个组测试
并行和串行相结合
每一个组一个预生产节点
直接部署第二个节点

2、主函数

main(){
	if [ -f $LOCK_FILE ]; then
		echo "Deploy is running" && exit;
	fi
	DEPLOY_METHOD=$1
	ROLLBACK_VER=$2
	case $DEPLOY_METHOD in
		deploy)
				shell_lock;
				code_get;
				code_build;
				code_config;
				code_tar;
				code_scp;
				pre_deploy;
				pre_test;
				group1_deploy;
				group1_test;
				shell_unlock;
				;;
		rollback)
				shell_lock;
				rollback $ROLLBACK_VER;
				shell_unlock;
				;;
		*)
				usage;
	esac
}
main $1 $2

1、先判断是否有文件,存在说明有人在执行直接退出

2、你是要部署还是要回滚,要是是部署先锁住脚本

从git上获取文件
进行编译
复制配置文件进去
打包并重命名
scp到所有机器(不分组)
晚上要做一个不算停机维护,所有机器都需要同时重启
涉及到数据一致性
组一部署集群
测试组一集群

4、秒级回滚

 


在某个地方记住上一个版本是啥,部署把版本写在一个文件里(紧急回滚的一个函数),然后读这个文件

rollback(){
if [ -z $1 ];then
	shell_unlock;
	echo "please input rollback version" && exit;
fi
	case $1 in
		list)
				ls -l /opt/webroot/*.tar.gz
				;;
		*)
				rollback_fun $1
	esac
}
  1. 部署还是回滚$1,回滚到那个版本是$2

  2. 传list的我就列出来,不传我就回滚

  3. 我可以只部署预生产,我部署机肯定有我其他的节点没有

rollback_fun(){
		for node in $ROLLBACK_LIST;do
		ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/$1 /webroot/web-demo"
		done

1、如果list存在我就执行,不存在就结束for循环
2、远程ssh执行命令,引起来才能当成一个,因为中间还有空格

3、脚本的$2传到回滚函数里就是$1

 

5、gitlab部署和回滚


安装gitlab私有仓库,地址见运维社区gitlab

1、登陆修改root密码


2、备份:每天备份每小时也行

越频繁越好
分布式每个人的本地都有

6、完整脚本构造

#!/bin/bash

#Dir List
mkdir -p /deploy/code/web-demo 
mkdir -p /deploy/config/web-demo/base
mkdir -p /deploy/config/web-demo/other
mkdir -p /deploy/tar
mkdir -p /deploy/tmp
mkdir -p /opt/webroot
mkdir /webroot
chown -R www.www /deploy
chown -R www.www /opt/webroot
chown -R www.www /webroot

#Node List
PRE_LIST="192.168.56.11"
GROUP1_LIST="192.168.56.12"
ROLLBACK_LIST="192.168.56.11 192.168.56.12"

#Date/Time Veriables
LOG_DATE='date "+%Y-%m-%d"'
LOG_TIME='date "+%H-%M-%S"'

CDATE=$(date "+%Y-%m-%d")
CTIME=$(date "+%H-%M-%S")

#Shell Env
SHELL_NAME="deploy_all.sh"
SHELL_DIR="/home/www/"
SHELL_LOG="${SHELL_DIR}/${SHELL_NAME}.log"

#Code Env
PRO_NAME="web-demo"
CODE_DIR="/deploy/code/web-demo"
CONFIG_DIR="/deploy/config/web-demo"
TMP_DIR="/deploy/tmp"
LOCK_FILE="/tmp/deploy.lock"

usage(){
		echo $"Usage: $0{deploy|rollback[ list|version ]}"
}

writelog(){
	LOGINFO=$1
	echo "${CDATE} ${CTIME}: ${SHELL_NAME}: ${LOGINFO}" >> ${SHELL_LOG}
}

shell_lock(){
		touch ${LOCK_FILE}
}

url_test(){
		URL=$1
		curl -s --head $URL |grep '200 ok'
		if [ $? -ne 0 ];then
				shell_unlock;
				echo "test error" && exit;
		fi
}

shell_unlock(){
		rm -f ${LOCK_FILE}
}

code_get(){
		writelog "code_get";
		cd $CODE_DIR && echo "git pull"
		cp -r ${CODE_DIR} ${TMP_DIR}/
		API_VERL=$(git show |grep commit | cut -d ' ' -f2)
		API_VER=$(echo ${API_VERL:0:6})
}

code_build(){
		echo code_Build
}

code_config(){
		writelog "code_config"
		/bin/cp -r ${CONFIG_DIR}/base/* ${TMP_DIR}/"${PRO_NAME}"
		PKG_NAME="${PKG_NAME}"_"$API_VER"_"${CDATE}-${CTIME}"
		cd ${TMP_DIR} && mv ${PKG_NAME} ${PKG_NAME}}
}

code_tar(){
		writelog "code_tar"
		cd ${TMP_DIR} && tar czf ${PKG_NAME}.tar.gz ${PKG_NAME}
		writelog "${PKG_NAME}.tar.gz"
}

code_scp(){
		writelog "code_scp"
		for node in $PRE_LIST;do
		for node in $GROUP1_LIST;do
				scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot/
		done
}

pre_deploy(){
		writelog "remove from cluster"
		ssh $PRE_LIST "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz"
		ssh $PRE_LIST "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo"
}

pre_test(){
		url_test "http://${PRE_LIST}/index.html"
		echo "add to cluster"
}

group1_deploy(){
		writelog "remove from cluster"
		for node in $GROUP1_LIST;do
			ssh $node "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz"
			ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo"	
		done
		scp ${CONFIG_DIR}/other/192.168.56.12.crontab.xml 192.168.56.12:/webroot/web-demo/crontab.xml
}

group1_test(){
		url_test "http://192.168.56.12/index.html"
		echo "add to cluster"
}

rollback_fun(){
		for node in $ROLLBACK_LIST;do
		ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/$1 /webroot/web-demo"
		done
}

rollback(){
if [ -z $1 ];then
	shell_unlock;
	echo "please input rollback version" && exit;
fi
	case $1 in
		list)
				ls -l /opt/webroot/*.tar.gz
				;;
		*)
				rollback_fun $1
	esac
}

main(){
	if [ -f $LOCK_FILE ]; then
		echo "Deploy is running" && exit;
	fi
	DEPLOY_METHOD=$1
	ROLLBACK_VER=$2
	case $DEPLOY_METHOD in
		deploy)
				shell_lock;
				code_get;
				code_build;
				code_config;
				code_tar;
				code_scp;
				pre_deploy;
				pre_test;
				group1_deploy;
				group1_test;
				shell_unlock;
				;;
		rollback)
				shell_lock;
				rollback $ROLLBACK_VER;
				shell_unlock;
				;;
		*)
				usage;
	esac
}
main $1 $2

转载地址:https://github.com/unixhot/deploy-shell

posted @ 2018-02-21 08:11  活的潇洒80  阅读(11144)  评论(0编辑  收藏  举报