shell函数
1、分别在服务器和客户端上创建www用户
1
2
|
useradd www id wwww |
- 所有的web服务,都应该使用普通用户,所有的web服务都不应该监听80端口,除非负载均衡。8080
- 普通用户能启动80端口吗?通过和科技,比如给命令设置suid
- 生产指定uid
2、保证www用户登录其他的节点都不要输入密码
服务器端:
1
2
3
4
5
6
7
8
9
10
|
[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 |
客户端:
1
2
3
4
5
6
7
8
9
10
|
[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来测试框架的流程是否正确
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#!/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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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、系统的锁文件放哪里?
1
|
/var/run/lock |
3、看不出来?
答:sleep60秒
4、不是脚本的$1匙函数的$1
5、在两台电脑上都要用到所以定义为变量
2、功能实现
1、日志函数
1
2
3
4
5
6
7
8
9
10
11
|
#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、日志函数的好处?
- 这个函数可以复制,以后写别的脚本直接改改就可以
- 每一个函数里都写一个函数
3、shell是如何解析的?
从上倒下逐行执行
4、遇到函数怎么办?
先加载不执行
5、日志的内容从哪来?
从参数来:$1
6、脚本名称:
当前日期+脚本名称+日志内容
难保你以后会写在一起所以要区分开
7、时间是不是不能变?
我就不需要它变:
1、打包的时候不能变,包里有有日期和时间包名不能变
2、打包如何命名?
是scp时间不对,包就找不着了
1
2
|
LOG_DATE= 'date "+%Y-%m-%d"' LOG_TIME= 'date "+%H-%M-%S"' |
- 一个是让执行 记日志用的
- 一个不让执行 做别的用处
- 已经执行了,在后面就不变了
2、get代码函数
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#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要重命名?
- 复制过去要重命名
- 打包的时候还要重命名
- 每次都覆盖那就乱了
6、要怎样重命名?
时间+版本号?
1、svn怎样获取版本号?
2、git如何获取版本号?
API_VERL=$(git show |grep commit | cut -d ' ' -f2)
3、配置文件函数
1
2
3
4
5
6
7
8
9
10
11
12
13
|
#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、我是哪个项目的配置文件?
1
|
CONFIG_DIR= "/deploy/config/web-demo" |
2、为什么加/bin/cp -r?
1
|
/bin/cp -r ${CONFIG_DIR} /base/ * ${TMP_DIR}/ "${PRO_NAME}" |
1、有可能我文件下还有目录呢!
2、我拷贝的那个目录下有那个配置文件
有一测试有权限,犯二提交了一个相同的配置文件 你没有覆盖连到测试库了
大家打开的都是测试的库
开发可以错,测试可以错,运维不可以错 为什么你上线的时候也没发现?
每一个小细节都是有意义不是瞎写
3、复制和打包可以放在一个里面,为什么把包名做成一个变量?
- 很多地方都用到
- 包名很长
- 包名本身也包含变量
4、为什么要全写成变量
因为不只为这一个脚本,写别的脚本改改就可以啦!
5、为什么tmp需要定期进行清理?
部署几个月可以,时间久磁盘就满了
有时间了就好删除了,解决了各种方式
只有版本号你怎样删?把15年的全删了
4、打包函数
1
2
3
4
5
|
code_tar(){ writelog "code_tar" cd ${TMP_DIR} && tar czf ${PKG_NAME}. tar .gz ${PKG_NAME} writelog "${PKG_NAME}.tar.gz" } |
1、为什么要写&&?
函数和函数之间可不知道上一级目录是什么!!!
不单独搞一行,如果目录不存在进去了可能不是你想要的
5、scp到目标服务器
1
2
3
4
5
6
7
|
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?
- 没有权限
- 在opt创建一个/opt/webroot/复制到这
6、部署函数
1
2
3
4
5
6
7
8
|
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、第一次手动创建一个因为 没有会报错。
1
2
3
4
|
su -www cd /webroot/ touch web-demo 用salt就要先 touch 文件 |
5、&&不能去掉,因为以后部署时候我要先删除才能软链接
6、一个软连接连一毫秒都花不了
3、脚本扩展
1、每个节点上各装一个apache
1
|
yum install httpd -y |
2、修改配置文件以下两处
1
|
vim /etc/httpd/conf/httpd .conf |
1、测试函数
1
2
3
4
5
6
7
8
|
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、主函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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、秒级回滚
在某个地方记住上一个版本是啥,部署把版本写在一个文件里(紧急回滚的一个函数),然后读这个文件
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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,回滚到那个版本是$2
-
传list的我就列出来,不传我就回滚
-
我可以只部署预生产,我部署机肯定有我其他的节点没有
1
2
3
4
|
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、完整脚本构造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
#!/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