15脚本发布

6 阅读7分钟

脚本发布

简单脚本

命令罗列

192.168.91.102

/data/scripts/tar_code.sh

#!/bin/bash
# 功能:打包代码

cd /data/codes
[ -f django.tar.gz ] && rm -f django.tar.gz
tar zcf django.tar.gz django	
# 脚本编写完成后,进行测试
sed -i 's#2.0#2.1#' /data/codes/django/views.py
bash /data/scripts/tar_code.sh
# 查看压缩文件内容
zcat /data/codes/django.tar.gz | grep -a web
   return HttpResponse("web_site V2.1\n")

变量转化

脚本里面手写的固定的内容太多了,更改时候费劲,所以通过变量的方式实现信息的固化

192.168.91.102

/data/scripts/tar_code.sh

#!/bin/bash
# 功能:打包代码

FILE='django.tar.gz'
CODE_DIR='/data/codes'
CODE_PRO='django'

cd "${CODE_DIR}"
[ -f "${FILE}" ] && rm -f "${FILE}"
tar zcf "${FILE}" "${CODE_PRO}"
# 脚本编写完成后,进行测试
sed -i 's#2.1#2.2#' /data/codes/django/views.py
bash /data/scripts/tar_code.sh
# 查看压缩文件内容
zcat /data/codes/django.tar.gz | grep -a web
   return HttpResponse("web_site V2.2\n")

功能函数

三条命令其实是一个组合,实现的是一个功能,应该定义成一个函数

192.168.91.102

/data/scripts/tar_code.sh

#!/bin/bash
# 功能:打包代码

FILE='django.tar.gz'
CODE_DIR='/data/codes'
CODE_PRO='django'

code_tar(){
	cd "${CODE_DIR}"
	[ -f "${FILE}" ] && rm -f "${FILE}"
	tar zcf "${FILE}" "${CODE_PRO}"	
}
code_tar
# 脚本编写完成后,进行测试
sed -i 's#2.2#2.3#' /data/codes/django/views.py
bash /data/scripts/tar_code.sh
# 查看压缩文件内容
zcat /data/codes/django.tar.gz | grep -a web
   return HttpResponse("web_site V2.3\n")

远程执行

有时候,我们需要通过远程方式到另外一台主机进行脚本的执行
格式:
	ssh 远程主机登录用户名@远程主机ip地址 "执行命令"
# 192.168.91.101
ssh root@192.168.91.102 "ifconfig ens33 | grep netmask"
        inet 192.168.91.102  netmask 255.255.255.0  broadcast 192.168.91.255

# 远程更新文件内容
ssh root@192.168.91.102 "sed -i 's#2.3#2.4#' /data/codes/django/views.py"
# 远程执行脚本
ssh root@192.168.91.102 "/bin/bash /data/scripts/tar_code.sh"
# 远程检查更新效果
ssh root@192.168.91.102 "zcat /data/codes/django.tar.gz | grep -a web"
   return HttpResponse("web_site V2.4\n")

大型脚本

功能框架

问题:为什么不按照简单脚本的思路进行编写

为什么?
    1、命令多
    2、功能多
    3、不好组合
解决方案:
    一句话:化整为零,各个击破

脚本框架

编写大型脚本有一个流程:
    一、脚本框架
    二、命令填充
    三、完善功能
        增加日志功能
        增加锁文件功能
        增加主函数逻辑
        增加参数安全措施

需求

完成代码发布流程框架,一个流程(步骤)即一个功能
	- 用函数来实现

脚本

#!/bin/bash
# 功能:打包代码	
# 版本: v0.1
# 作者: example
# 联系: org.example.com

# 获取代码
get_code(){
  echo "获取代码"
}

# 打包代码
tar_code(){
  echo "打包代码"
}

# 传输代码
scp_code(){
  echo "传输代码"
}

# 关闭应用
stop_serv(){
  echo "关闭应用"
  echo "关闭nginx应用"
  echo "关闭django应用"
}

# 解压代码
untar_code(){
  echo "解压代码"
}

# 放置代码
fangzhi_code(){
  echo "放置代码"
  echo "备份老文件"
  echo "放置新文件"
}

# 开启应用
start_serv(){
  echo "开启应用"
  echo "开启django应用"
  echo "开启nginx应用"
}

# 检查
check(){
  echo "检查项目"
}

# 部署函数
deploy_pro(){
  get_code
  tar_code
  scp_code
  stop_serv
  untar_code
  fangzhi_code
  start_serv
  check
}

# 主函数
main(){
  deploy_pro
}

# 执行主函数
main

命令填充

需求:
	在流程跑通的情况下,执行完整的代码部署过程
方案:
	在脚本框架中,填写执行成功的命令
#!/bin/bash
# 功能:打包代码	
# 版本: v0.2
# 作者: example
# 联系: org.example.com

# 获取代码
get_code(){
  echo "获取代码"
}

# 打包代码
tar_code(){
  echo "打包代码"
  ssh root@192.168.91.102 "/bin/bash /data/scripts/tar_code.sh"
}

# 传输代码
scp_code(){
  echo "传输代码"
  cd /data/codes
  [ -f django.tar.gz ] && rm -f django.tar.gz
  scp root@192.168.91.102:/data/codes/django.tar.gz ./
}

# 关闭应用
# /etc/nginx/conf.d/update.conf和/usr/share/nginx/update/index.html 已经在上面的手工发布过程中修改过,下面修改nginx配置只需要重新加载配置即可,不用重新启动nginx
stop_serv(){
  echo "关闭应用"
  # 使用数据迁移配置
  echo "location /hello/ { proxy_pass http://localhost:6666/; }" > /etc/nginx/default.d/django.conf
  echo "重新加载nginx配置"
  systemctl reload nginx
  # 用于判断nginx配置修改是否生效
  curl localhost/hello/ 2> /dev/null | xargs -I {} echo "localhost/hello/ {}"
  echo "关闭django应用"
  kill $(lsof -Pti :8000)
}

# 解压代码
untar_code(){
  echo "解压代码"
  cd /data/codes
  tar xf django.tar.gz
}

# 放置代码
fangzhi_code(){
  echo "放置代码"
  echo "备份老文件"
  mv /data/server/web_site/app1/views.py /data/backup/views.py-$(date +%Y%m%d%H%M%S)
  echo "放置新文件"
  mv /data/codes/django/views.py /data/server/web_site/app1/
}

# 开启应用
start_serv(){
  echo "开启应用"
  echo "开启django应用"
  source /data/virtual/venv/bin/activate
  cd /data/server/web_site/
  python manage.py runserver >> /dev/null 2>&1 &
  deactivate
  echo "开启nginx应用"
  /data/server/nginx/sbin/nginx
}
# 检查
check(){
  echo "检查项目"
  netstat -tnulp | grep ':80'
}

# ...

日志功能

需求:
	1、追踪记录
	2、数据说话
方案:
	增加日志功能
	1、日志文件
		/data/logs/deploy.log
	2、日志格式
		日期	时间	脚本名称	步骤
知识点:
	文件内容追加: >>
	日期:date +%F
	时间:date +%T
	脚本:$0
#!/bin/bash
# ...

LOG_FILE='/data/logs/deploy.log'
# 增加日志功能
write_log(){
  DATE=$(date +%F)
  TIME=$(date +%T)
  buzhou="$1"
  echo "${DATE} ${TIME} $0 : ${buzhou}" >> "${LOG_FILE}"
}
# 获取代码
get_code(){
  write_log "获取代码"
}
# 将脚本中所有echo输出的全部改为调用write_log
# ...

锁文件

需求:
	同一时间段内,只允许有一个用户来执行这个脚本
	如果脚本执行的时候,有人在执行,那么输出报错:
	脚本 deploy.sh 正在运行,请稍候...
设计:
	1、锁文件	/tmp/deploy.pid
	2、存在锁文件时候,输出报错信息
	3、脚本执行的时候,需要创建锁文件
	4、脚本执行结束的时候,需要删除锁文件
知识点:
    条件和结果: 双分支if语句
    文件表达式: -f  file_name
    验证表达式: [ 表达式 ]
    创建和删除命令:touch、rm -f
#!/bin/bash
# ...
PID_FILE='/tmp/deploy.pid'
# ...
# 增加锁文件功能
add_lock(){
  echo "增加锁文件"
  touch "${PID_FILE}"
  write_log "增加锁文件"
}

# 删除锁文件功能
del_lock(){
  echo "删除锁文件"
  rm -f "${PID_FILE}"
  write_log "删除锁文件"
}

# 部署函数
deploy_pro(){
  add_lock
  # ...
  del_lock
}

# 脚本报错信息
err_msg(){
  echo "脚本 $0 正在运行,请稍候..."
}

# 主函数
main(){
  if [ -f "${PID_FILE}" ]
  then
    err_msg
  else
    deploy_pro
  fi
}

# 执行主函数
main

流程控制

需求:
	如果我给脚本输入的参数是deploy,那么脚本才执行,否则的话,提示该脚本的使用帮助信息,然后退出
提示信息:脚本 deploy.sh 的使用方式: deploy.sh [ deploy ]
分析:
    1、脚本传参,就需要在脚本内部进行调用参数
    2、脚本的帮助信息
    3、脚本内容就需要对传参的内容进行判断
知识点:
    1、shell内置变量:$n
    2、帮助信息: 简单函数定义和调用
    3、内容判断: 多if语句或者case语句
方案:
	1、脚本的传参
        脚本执行:bash deploy.sh deploy
        位置参数的调用: $1
	2、脚本的帮助信息
        定义一个usage函数,然后调用。
        提示信息格式:
        脚本 deploy.sh 的使用方式: deploy.sh [ deploy ]
	3、内容判断
        main函数体调用函数传参: $1
        在main函数中,结合case语句,对传入的参数进行匹配
            如果传入参数内容是"deploy",那么就执行代码部署流程
            如果传入参数内容不是"deploy",那么输出脚本的帮助信息
        if语句和case语句的结合
        	case语句在外,if语句在内
#!/bin/bash
...

# 脚本帮助信息
usage(){
  echo "脚本 $0 的使用方式: $0 [deploy]"
  exit
}

# 主函数
main(){
  case "$1" in
    "deploy")
      if [ -f "${PID_FILE}" ]
      then
        err_msg
      else
        deploy_pro
      fi
    ;;
    *)
      usage
    ;;
  esac
}

# 执行主函数
main $1

参数安全

需求:
	对脚本传入的参数的数量进行判断,如果参数数量不对,提示脚本的使用方式,然后退出
分析:
    1、脚本参数数量判断
    2、条件判断
        数量对,那么执行主函数
        数量不对,那么调用脚本帮助信息
知识点:
    1、脚本参数数量判断
        shell内置变量: 	$#
        条件表达式:	   [ $# -eq 1 ]
    2、条件判断:
    	双分支if语句    
方案:
	1、双分支if语句 + main函数调用
#!/bin/bash
# ...

# 执行主函数
if [ $# -eq 1 ] 
then
  main $1
else
  usage
fi

脚本调试

我们介绍脚本调试的时候,主要分三种方式来介绍:

    -n	检查脚本中的语法错误
    -v	先显示脚本所有内容,然后执行脚本,结果输出,如果执行遇到错误,将错误输出。
    -x	将执行的每一条命令和执行结果都打印出来

完整脚本实践

192.168.91.101

/data/deploy.sh

#!/bin/bash
# 功能:打包代码	
# 版本: v0.1
# 作者: example
# 联系: org.example.com

LOG_FILE='/data/logs/deploy.log'
PID_FILE='/tmp/deploy.pid'
mkdir -p /data/logs

# 增加日志功能
write_log(){
  DATE=$(date +%F)
  TIME=$(date +%T)
  buzhou="$1"
  echo "${DATE} ${TIME} $0 : ${buzhou}" >> "${LOG_FILE}"
}

# 增加锁文件功能
add_lock(){
  echo "增加锁文件"
  touch "${PID_FILE}"
  write_log "增加锁文件"
}

# 删除锁文件功能
del_lock(){
  echo "删除锁文件"
  rm -f "${PID_FILE}"
  write_log "删除锁文件"
}


# 获取代码
get_code(){
  write_log "获取代码"
}

# 打包代码
tar_code(){
  write_log "打包代码"
  ssh root@192.168.91.102 "/bin/bash /data/scripts/tar_code.sh"
}

# 传输代码
scp_code(){
  write_log "传输代码"
  cd /data/codes
  [ -f django.tar.gz ] && rm -f django.tar.gz
  scp root@192.168.91.102:/data/codes/django.tar.gz ./
}

# 关闭应用
# /etc/nginx/conf.d/update.conf和/usr/share/nginx/update/index.html 已经在上面的手工发布过程中修改过,下面修改nginx配置只需要重新加载配置即可,不用重新启动nginx
stop_serv(){
  write_log "关闭应用"
  # 使用数据迁移配置
  echo "location /hello/ {proxy_pass http://localhost:6666/;}" > /etc/nginx/default.d/django.conf
  write_log "重新加载nginx配置"
  systemctl reload nginx;sleep 1
  # 用于判断nginx配置修改是否生效
  write_log "curl localhost/hello/ $(curl localhost/hello/ 2> /dev/null)"
  write_log "关闭django应用"
  kill $(lsof -Pti :8000)
}

# 解压代码
untar_code(){
  write_log "解压代码"
  cd /data/codes
  tar xf django.tar.gz
}

# 放置代码
fangzhi_code(){
  write_log "放置代码"
  write_log "备份老文件"
  mv /data/server/web_site/app1/views.py /data/backup/views.py-$(date +%Y%m%d%H%M%S)
  write_log "放置新文件"
  mv /data/codes/django/views.py /data/server/web_site/app1/
}

# 开启应用
start_serv(){
  write_log "开启应用"
  write_log "开启django应用"
  source /data/virtual/venv/bin/activate
  cd /data/server/web_site/
  python manage.py runserver >> /dev/null 2>&1 &
  deactivate
  echo "location /hello/ {proxy_pass http://localhost:8000;}" > /etc/nginx/default.d/django.conf
  write_log "重新加载nginx配置"
  systemctl reload nginx;sleep 1
}

# 检查
check(){
  write_log "检查项目"
  write_log "curl localhost/hello/ $(curl localhost/hello/ 2> /dev/null)"
}

# 部署函数
deploy_pro(){
  add_lock
  get_code
  tar_code
  scp_code
  stop_serv
  untar_code
  fangzhi_code
  start_serv
  check
  del_lock
}

# 脚本报错信息
err_msg(){
  echo "脚本 $0 正在运行,请稍候..."
}

# 脚本帮助信息
usage(){
  echo "脚本 $0 的使用方式: $0 [deploy]"
  exit
}

# 主函数
main(){
  case "$1" in
    "deploy")
      if [ -f "${PID_FILE}" ]
      then
        err_msg
      else
        deploy_pro
      fi
    ;;
    *)
      usage
    ;;
  esac
}

# 执行主函数
if [ $# -eq 1 ] 
then
  main $1
else
  usage
fi
# 修改代码,并部署验证
ssh root@192.168.91.102 "sed -i 's#2.4#2.5#' /data/codes/django/views.py"

/bin/bash deploy.sh deploy
增加锁文件
django.tar.gz                                                                                                                                         100%  256   212.8KB/s   00:00
删除锁文件

cat /data/logs/deploy.log
2023-12-29 16:45:52 deploy.sh : 增加锁文件
2023-12-29 16:45:52 deploy.sh : 获取代码
2023-12-29 16:45:52 deploy.sh : 打包代码
2023-12-29 16:45:53 deploy.sh : 传输代码
2023-12-29 16:45:53 deploy.sh : 关闭应用
2023-12-29 16:45:53 deploy.sh : 重新加载nginx配置
2023-12-29 16:45:54 deploy.sh : curl localhost/hello/ 数据迁移中,请耐心等待,抱歉!!!
2023-12-29 16:45:54 deploy.sh : 关闭django应用
2023-12-29 16:45:54 deploy.sh : 解压代码
2023-12-29 16:45:54 deploy.sh : 放置代码
2023-12-29 16:45:54 deploy.sh : 备份老文件
2023-12-29 16:45:54 deploy.sh : 放置新文件
2023-12-29 16:45:54 deploy.sh : 开启应用
2023-12-29 16:45:54 deploy.sh : 开启django应用
2023-12-29 16:45:54 deploy.sh : 重新加载nginx配置
2023-12-29 16:45:55 deploy.sh : 检查项目
2023-12-29 16:45:55 deploy.sh : curl localhost/hello/ web_site V2.5
2023-12-29 16:45:55 deploy.sh : 删除锁文件

脚本技巧

简单脚本

	1、手工执行的命令一定要可执行
	2、命令简单罗列
	3、固定的内容变量化
	4、功能函数化

复杂脚本

	1、手工执行的命令一定要可执行
	2、根据发布流程编写脚本的框架
	3、将手工执行的命令填充到对应的框架函数内部
	4、增加日志功能,方便跟踪脚本历史执行记录
	5、主函数中逻辑流程控制好
	6、设计安全的方面:
		增加锁文件,保证代码发布的过程中不受干扰,
		输入参数数量
		输入参数匹配
		脚本帮助信息
	7、调试脚本

注意事项

	1、命令一定要保证能正常执行
	2、成对的符号,要成对写,避免丢失
	3、函数调用,
		写好函数后,一定要在主函数中进行调用
	4、避免符号出现中文
	5、命令变量的写法一定要规范
	6、固定的内容一定要变量实现,方便以后更改
	7、日志的输出
	8、脚本的传参和函数的传参要区别对待