更新时间:2018-01-12 16:12:44

  

滚动发布

滚动发布(rolling update)是最常见的一种发布模式。比如我有10台机器,一台一台的进行部署。每台机器进行部署时,需要保证没有请求会派发到该机器,否则用户就会看到502的错误。所以需要有一个“下线”的操作,把当前机器从负载均衡中摘除,然后在部署完成之后,再把自己挂回到负载均衡中,这个过程称为“上线”。接下来会讲解,配合阿里云SLB如何做上线/下线操作。

基于阿里云SLB的滚动发布

在SLB中进行如下配置:

slb config

slb config1

图中的关键点:

  1. 健康检查路径,需要由实例上的web服务器提供,在本例中是/nginx-status 。
  2. 健康检查间隔,配置为2S。
  3. 健康阈值,配置为2,也就说2次健康检查失败,则认为该后端服务器不可用。同样的,两次连续的健康检查成功,就会认为该后端服务器可用。

按照这个配置,如果/nginx-status这个URL不可用超过4S,则SLB会把该服务器摘除。在这4S内,应用服务仍需要是可用的,因为还会有请求派发过来。可以通过如下方式达到这个效果。

配置nginx

/nginx-status这个URL派发到一个本地文件,使用如下配置文件:

  1. server {
  2. location ~ ^/(nginx-status) {
  3. root /home/admin/status;
  4. }
  5. }

并在机器上放置文件:/home/admin/status/nginx-status。当该文件被删除时候,/nginx-status这个请求会返回404,4S之后,该实例就会被从SLB中摘除。这个过程也就是“下线”的过程。

与之对应,touch /home/admin/status/nginx-status这个操作就是上线的过程,也是4S之后生效。

上/下线脚本

所以对应的,应用启停的脚本就是这样样子:

  1. #下线
  2. rm /home/admin/status/nginx-status
  3. sleep 6 #按理说是4S,留一些buffer
  4. #重启应用
  5. #上线
  6. touch /home/admin/status/nginx-status
  7. sleep 6 #按理说是4S,留一些buffer

前面的sleep保证,下线完成了再进行重启;后面的sleep,保证上线完成了再进行下一台(批)机器的部署。

应用重启脚本

上线和下线之间是重启的脚本。对于tomcat应用来说,典型的启动脚本就是sh catalina.sh start。执行完这一句,命令就退出了。但是事实上应用还没起来,有些比较大的应用可能需要一两分钟才能启动完成。如果在启动完成之前就上线,那么打到这台实例的请求还是有问题。所以需要有一个脚本来判断tomcat是否真的起来了。通常可以判断某个URL是否返回200。比如类似/health这样的URL

完整示例

下面是是一个完整的例子,您可以以该例子为基础,按照自己项目的实际情况进行修改使用。

SLB与机器实例

SLB的创建与配置此处略,详见SLB的文档。SLB的后端服务器需要加入到云效的管理中,此处也不再赘述。

代码库

本例使用的代码库是https://code.aliyun.com/rdc-demos/springboot-example,其中也包括了构建配置。

部署配置及脚本

本例是一个springboot的应用,打出来的jar包名字为app-0.1.0.jar。相应环境中的部署配置如下:部署配置

需要拷贝到机器上的有两个文件:appctl.shnginx.conf。其中appctl.sh中包含了应用启停、判断应用是否启动成功,及上下线的操作;nginx.conf是nginx配置文件,包含了SLB使用的健康检查的URL,/nginx-status,的配置。

拷贝及修改相应文件

首先,将appctl.sh拷贝到/home/admin下。

appctl.sh

  1. #!/bin/bash
  2. PROG_NAME=$0
  3. ACTION=$1
  4. ONLINE_OFFLINE_WAIT_TIME=6 # 实例上下线的等待时间
  5. APP_START_TIMEOUT=50 # 等待应用启动的时间
  6. APP_PORT=8080 # 应用端口
  7. HEALTH_CHECK_URL=http://127.0.0.1:${APP_PORT}/health # 应用健康检查URL
  8. HEALTH_CHECK_FILE_DIR=/home/admin/status # 脚本会在这个目录下生成nginx-status文件
  9. APP_HOME=/home/admin/application # 从package.tgz中解压出来的jar包放到这个目录下
  10. JAR_NAME=app-0.1.0.jar # jar包的名字
  11. APP_LOG=${APP_HOME}/logs/app.log # 应用的日志文件
  12. PID_FILE=${APP_HOME}/pid # 应用的pid会保存到这个文件中
  13. # 创建出相关目录
  14. mkdir -p ${HEALTH_CHECK_FILE_DIR}
  15. mkdir -p ${APP_HOME}
  16. mkdir -p ${APP_HOME}/logs
  17. usage() {
  18. echo "Usage: $PROG_NAME {start|stop|online|offline|restart}"
  19. exit 2
  20. }
  21. online() {
  22. # 挂回SLB
  23. touch -m $HEALTH_CHECK_FILE_DIR/nginx-status || exit 1
  24. echo "wait app online in ${ONLINE_OFFLINE_WAIT_TIME} seconds..."
  25. sleep ${ONLINE_OFFLINE_WAIT_TIME}
  26. }
  27. offline() {
  28. # 摘除SLB
  29. rm -f $HEALTH_CHECK_FILE_DIR/nginx-status || exit 1
  30. echo "wait app offline in ${ONLINE_OFFLINE_WAIT_TIME} seconds..."
  31. sleep ${ONLINE_OFFLINE_WAIT_TIME}
  32. }
  33. health_check() {
  34. exptime=0
  35. echo "checking ${HEALTH_CHECK_URL}"
  36. while true
  37. do
  38. status_code=`/usr/bin/curl -L -o /dev/null --connect-timeout 5 -s -w %{http_code} ${HEALTH_CHECK_URL}`
  39. if [ x$status_code != x200 ];then
  40. sleep 1
  41. ((exptime++))
  42. echo -n -e "\rWait app to pass health check: $exptime..."
  43. else
  44. break
  45. fi
  46. if [ $exptime -gt ${APP_START_TIMEOUT} ]; then
  47. echo
  48. echo 'app start failed'
  49. exit 1
  50. fi
  51. done
  52. echo "check ${HEALTH_CHECK_URL} success"
  53. }
  54. start_application() {
  55. echo "start jar"
  56. if [ -f "$PID_FILE" ] && kill -0 "$(cat ${PID_FILE})"; then
  57. echo "Application is running, exit"
  58. exit 0
  59. fi
  60. rm -rf ${APP_HOME}/${JAR_NAME}
  61. tar -zxvf /home/admin/package.tgz -C ${APP_HOME}
  62. java -jar ${APP_HOME}/${JAR_NAME} > ${APP_LOG} 2>&1 &
  63. echo $! > ${PID_FILE}
  64. }
  65. stop_application() {
  66. echo "stop jar"
  67. if [ -f "$PID_FILE" ]; then
  68. kill -9 `cat $PID_FILE`
  69. rm $PID_FILE
  70. else
  71. echo "pid file $PID_FILE does not exist, do noting"
  72. fi
  73. }
  74. start() {
  75. start_application
  76. health_check
  77. online
  78. }
  79. stop() {
  80. offline
  81. stop_application
  82. }
  83. case "$ACTION" in
  84. start)
  85. start
  86. ;;
  87. stop)
  88. stop
  89. ;;
  90. online)
  91. online
  92. ;;
  93. offline)
  94. offline
  95. ;;
  96. restart)
  97. stop
  98. start
  99. ;;
  100. *)
  101. usage
  102. ;;
  103. esac

然后将nginx.conf拷贝到/etc/nginx/nginx.conf。注意,/etc/nginx/nginx.conf是已经存在的文件,请确保只将nginx.conf中有用的部分放入您现有的文件中。如果只是测试,可以先把/etc/nginx/nginx.conf备份,然后用nginx.conf覆盖。

nginx.conf:

  1. http {
  2. server {
  3. listen 80;
  4. server_name localhost default;
  5. location ~ ^/(nginx-status) {
  6. root /home/admin/status;
  7. }
  8. location / {
  9. proxy_pass http://127.0.0.1:8080;
  10. }
  11. }
  12. }

配置好之后,使用云效,进行发布,

Leave a Reply

Your email address will not be published. Required fields are marked *