自動化運維:一鍵自動化腳本-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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM