自动部署难驾驭-这篇文章分分钟拯救你的运维体系

2016-08-01 分类:Linux 阅读(4532) 评论(5)

徐亮伟, 江湖人称标杆徐。多年互联网运维工作经验,曾负责过大规模集群架构自动化运维管理工作。擅长Web集群架构与自动化运维,曾负责国内某大型电商运维工作。
个人博客"徐亮伟架构师之路"累计受益数万人。
笔者Q:552408925、572891887
架构师群:471443208

1.1早期手动部署代码

1.纯手动scp上传代码。
2.纯手动登陆,git pull 或者svn update。
3.纯手动xftp上传代码。
4.开发发送压缩包,rz上传,解压部署代码。

缺点:

1.全程运维参与,占用大量时间。
2.如果节点多,上线速度慢。
3.人为失误多,目录管理混乱。
4.回滚不及时,或者难以回退。
5.日志无法保留,或难以查看。

流程设计,确定目标.

1.2.1自动部署环境

  • 1.开发环境, 开发者本地有自己的环境,运维配置公共开发环境,大家可共用的服务。例如:开发数据库MySQL,redis,Memcached等
  • 2.测试环境, 功能测试以及性能测试。
  • 3.预生产环境, 生产环境集群中的某一个节点,并且连接生产库。(不对外,不做破坏型操作。)
  • 4.灰度环境, 根据不同的区域进行划分分。(生产环境)
  • 5.生产环境, 对用户提供服务的环境。
  • 预生产环境由来
  • 1.数据库不一致,测试环境和生产环境数据库是不一样的。
  • 2.使用生产环境的联调接口;例如:支付接口。(电商业务)

1.2.2自动部署规划

1.已经有一个可以上线的代码在git仓库。
2.我们现在要做10个集群节点的一键部署,秒级回滚。
3.所有的web服务,都应该使用普通用户。(强烈建议)
4.所有的web服务都不应该监听80端口,除了负载均衡。
5.那我们如何设计一套生产自动化部署系统。

1.规划。
2.实现。
3.总结和扩展。(PDCA方法论)
4.生产环境应用。

实现思路:

1.代码放置位置

Git(首先)、Svn

2.获取最新代码

1.git pull获取最新分支(更新非常频繁,没有特别严格的项目管理团队。)
2.git tag 获取指定标签版本(更新没有那么频繁,有一定的项目管理的团队。)
3.git commit获取指定版本号

3.差异解决

1.各个节点之间差异
2.代码仓库和实际的差异。配置文件是否放在代码仓库中。(配置单独进行存放,config.example )短信接口,支付,等敏感信息不让所有开发知道
3.统一的.集群有10个节点。(Job节点 crontab.xml 配置文件不一样)

4.项目名称如何设计

项目名称_环境名称_版本_分支_时间_某开发提交
测试: rainbow_test_v1.1.1_dev_2016-08-11_12:12_xuliangwei
生产: rainbow_pro_v1.1.1_master_2016-08-11_11:11_xuliangwei

5.如何更新

php,tomcat需要重启,重新软链接。

6.如何测试

测试(关键的页面,API,后台等)
测试一个预生产环境,通过则继续部署,如果失败,退出部署操作。

7.记录日志

可以部署统计。
成功多少次。
失败多少次。
回滚多少次。

8.多人同时执行脚本

防止多人操作导致重复上线失败。通过lock锁对文件进行控制。

9.串行,并行

机器少的情况串行感觉不出什么。如果机器过多则会很慢。
分组部署并行部署,以及分组测试。
测试一个预生产环境,通过则继续部署,如果失败,退出部署操作。

10.部署服务器双机

防止部署系统down机,部署机代码丢失,误操作。

11.如何执行

1.shell执行
2.web界面点击(自定义或jenkins)

12.如何回退

正常回退,紧急回退(回滚的必要性)。
通过软链接的方式来实现代码秒级别回退。

1.2.3自动部署难点

在大公司推进自动化部署上线,是有许多的难点,根据个人公司的不同,来选择不同的方法来进行推进。

自动化推进难点:

1.能力(个人能力,团队能力)
2.责任(责任能否承担,敢于承担责任)
3.公司流程、人员、组织架构。

可通过如下方法推进:

1.目标化沟通。
2.责任划分
3.ITIL
4.项目管理:PMBOOK

1.2.4自动部署思路

整个集群自动化部署流程设计如下:

1.获取最新代码
2.编译(可选)
3.配置文件(软连接或者拷贝)。
4.打包(tar,加速传输)
5.文件分发(Scp Rsync Salt)(不需要密码验证)
6.将目标服务器移除集群(注释配置文件)
7.解压
8.防止webroot站点目录
9.scp差异文件(可能有一个节点配置文件不一样)
10.重启Web服务
11.测试

1.2.5正常回退思路

1.列出回滚版本
2.目标服务器移除集群
3.执行回滚
4.重启并测试
5.加入集群

1.2.6紧急回退思路

1.列出回滚版本(ls -l或find查出对应的历史版本)。
2.执行回滚操作(删除软链接,重建软链接)。
3.重启对应服务。

1.2.7自动部署采坑

自动化部署php环境或者java环境的过程中,存在的坑

1.如何应用到你的生产环境。
2.回退到“上一个”“正常”版本。
3.自动部署软连接的坑。
1.PHP如果开启Opcache,需要重启PHP,或者清理opcache
2.Java Tomcat是必须要重启,最好每次清理work,tmp缓存目录。

1.3自动化部署环境准备

首先在执行脚本前,先做好初始化环境,最好是通过saltstack来完成。我这里没有那么多机器,就先用两台演示。

linux-node1 192.168.90.201
linux-node2 192.168.90.202

1.创建普通用户

useradd xuliangwei
passwd xuliangwei

2.配置密钥

#切换至普通用户
[root@linux-node1 ~]# su - xuliangwei

#生成密钥
[xuliangwei@Linux-node1 ~]$ ssh-keygen  
#公钥分发给其他服务器
[xuliangwei@nfs-server ~]$ ssh-copy-id -i ~/.ssh/id_dsa.pub xuliangwei@192.168.90.202  

#使用xuliangwei用户登录node2节点服务器
[xuliangwei@nfs-server ~]$ ssh -p22 xuliangwei@192.168.90.202

#成功登录   
Last login: Wed Aug 10 17:59:02 2016 from 192.168.90.201
[xuliangwei@linux-node2 ~]$

3.创建相关目录

mkdir /deploy/
mkdir -p /deploy/code/rainbow_pro
mkdir -p /deploy/source/rainbow_pro
mkdir -p /deploy/config/rainbow_pro/config
mkdir -p /deploy/config/rainbow_pro/admin_config
mkdir -p /deploy/logs
mkdir -p /deploy/tmp
mkdir -p /home/xuliangwei/webroot
chown -R www:www /deploy
chown -R www:www /home/xuliangwei/webroot

4.配置Nginx

#指定用户
user xuliangwei;

#指定项目路径
root  /home/xuliangwei/webroot/rainbow_pro;

5.配置好git环境

1.centos7部署gitlab
2.克隆项目可参考 git远程仓库创建与建立
3.最终效果如下:

[xuliangwei@linux-node1 ~]$ ll /deploy/source/rainbow_pro/
总用量 4
-rw-rw-r-- 1 xuliangwei xuliangwei 34 8月 12 18:05 index.html

1.4自动化部署脚本剖析

查看脚本。

[xuliangwei@linux-node1 ~]$ cat deploy.sh
#!/bin/bash

# useradd xuliangwei  && sshkey需认证
# Nginx 权限必须让xuliangwei用户可访问

# Dir List
# mkdir -p /deploy/code/rainbow_pro  # web code的仓库
# mkdir -p /deploy/source/rainbow_pro #存放git源码仓库
# mkdir -p /deploy/config/rainbow_pro/config  #存放config信息
# mkdir -p /deploy/config/rainbow_pro/admin_config  #存放后台config信息
# mkdir -p /deploy/tmp
# mkdir -p /deploy/logs


# Web Root Dir
# mkdir -p /home/xuliangwei/webroot

# chown web
# chown -R www:www /deploy
# chown -R www:www /home/xuliangwei/webroot


# Code Env
PRO_NAME="rainbow_pro"
CODE_DIR="/deploy/code/$PRO_NAME"
SOURCE_DIR="/deploy/source/$PRO_NAME"
CONFIG_DIR="/deploy/config/$PRO_NAME"
WEB_DIR="/home/xuliangwei/webroot"
TMP_DIR="/deploy/tmp"


# Node List
PRE_LIST="192.168.90.201"
GROUP1_LIST="192.168.90.202"
ROLLBACK_LIST="192.168.90.201 192.168.90.202"


# Date/Time Veriables
CTIME=$(date "+%F-%H-%M")

# Shell Env
SHELL_NAME="deploy.sh"
SHELL_DIR="/home/xuliangwei/webroot"


# Log
LOG_FILE="${SHELL_DIR}/${SHELL_NAME}".log
LOCK_FILE="/tmp/deploy.lock"

# Lock
shell_lock(){
    touch ${LOCK_FILE}
}

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


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

# log 
writelog(){
    LOGINFO=$1
    echo "${CTIME}: ${SHELL_NAME} : ${LOGINFO}" >> ${LOG_FILE}
}



code_get(){
    writelog "code_get"
    cd $SOURCE_DIR && git pull
    GIT_CID=$(git log|awk 'NR==1{print $2}'|cut -c 1-6)
    PKG_VER="${CTIME}_${GIT_CID}"
    PKG_NAME="${PRO_NAME}_${PKG_VER}"
    cp -r ${SOURCE_DIR} ${TMP_DIR}/${PKG_NAME}
}

code_bulid(){
    echo code_bulid #由于php没有编译过程
}

code_config(){
    writelog "code_config"
    /bin/cp -r ${CONFIG_DIR}/config.php ${TMP_DIR}/${PKG_NAME}/config.php
}

code_tar(){
    writelog "code_tar"
    cd ${TMP_DIR} && tar czf ${PKG_NAME}_tar.gz ${PKG_NAME} --exclude=.git --exclude=.gitignore
    writelog "${PKG_NAME}_tar.gz"
}

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

    for node in $GROUP1_LIST;do
        scp ${TMP_DIR}/${PKG_NAME}_tar.gz $node:${TMP_DIR}/
    done

}


pre_deploy(){
    writelog "remove from cluster"
    ssh $PRE_LIST "cd ${TMP_DIR} && tar xf ${PKG_NAME}_tar.gz -C ${CODE_DIR}/"
    ssh $PRE_LIST "rm -f $WEB_DIR/${PRO_NAME} && ln -s ${CODE_DIR}/${PKG_NAME} $WEB_DIR/${PRO_NAME}"
}


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


group1_deploy(){
        writelog "remove from cluster"
    for node in $GROUP1_LIST;do
            ssh $node "cd ${TMP_DIR} && tar xf ${PKG_NAME}_tar.gz -C ${CODE_DIR}/"
            ssh $node "rm -f $WEB_DIR/${PRO_NAME} && ln -s ${CODE_DIR}/${PKG_NAME} $WEB_DIR/${PRO_NAME}"
    done
   #scp ${CONFIG_DIR}/other/192.168.90.201.crontab.xml 192.168.90.201:/$WEB_DIR/${PRO_NAME}/crontab.xml 额外不一样的配置可能需要用到
}


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



code_reload(){
    systemctl restart php-fpm  #重启php
}


rollback_list(){
  for node in $GROUP1_LIST;do
        ssh $node ls -l "$WEB_DIR" &&\
        ssh $node find "$CODE_DIR/" -maxdepth 1 -mtime -2|sed 1d|awk -F '/' '{print $5}'
        #find "$CODE_DIR" -type d -name "$PRO_NAME*" -mtime +30|xargs rm -fr
  done
}

rollback_fun(){
  if [ -z $ROOLBACK ];then
    shell_unlock;
    echo "Please input rollback version" && exit;
else
    for node in $ROLLBACK_LIST;do
    ssh $node rm -f $WEB_DIR/${PRO_NAME} && \
    ssh $node ln -s ${CODE_DIR}/$ROOLBACK $WEB_DIR/${PRO_NAME}
    done
fi
}


main(){
  if [ -f "$LOCK_FILE" ];then
    echo "Deploy is Running" && exit;
  fi

  DEPLOY_METHOD="$1"
  ROOLBACK="$2"
  case $DEPLOY_METHOD in
    deploy)
        shell_lock;
        code_get;
        code_bulid;
        code_config;
        code_tar;
        code_scp;
        pre_deploy;
        pre_test;
        group1_deploy;
        group1_test;
    #   group2_deploy;
    #   group2_test;
        shell_unlock;
        ;;
    list)
    rollback_list;
        ;;
    rollback)
        shell_lock;
        rollback_fun $ROLLBACK;
        shell_unlock;
        ;;
    *)
        echo "$Usage:$0 [ deploy | list | rollback ]"
    esac
}

main $1 $2

执行上线:

[xuliangwei@linux-node1 ~]$ sh deploy.sh deploy
Already up-to-date.
code_bulid
rainbow_pro_2016-08-12-21-16_46236b_tar.gz                                     100% 6016KB   5.9MB/s   00:00
rainbow_pro_2016-08-12-21-16_46236b_tar.gz                                     100% 6016KB   5.9MB/s   00:00
rainbow_pro_2016-08-12-21-16_46236b_tar.gz                                     100% 6016KB   5.9MB/s   00:00
HTTP/1.1 200 OK
Rre add to cluster
HTTP/1.1 200 OK
HTTP/1.1 200 OK
group1_node add to cluster
php-fpm restart

查看当前版本

[xuliangwei@linux-node1 ~]$ sh deploy.sh list
总用量 16
lrwxrwxrwx 1 xuliangwei xuliangwei    60 8月  12 21:16 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-08-12-21-16_46236b
rainbow_pro_2016-08-12-19-24_46236b
rainbow_pro_2016-08-12-21-16_46236b
总用量 0
lrwxrwxrwx 1 xuliangwei xuliangwei 60 8月  12 21:16 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-08-12-21-16_46236b
rainbow_pro_2016-08-12-19-24_46236b
rainbow_pro_2016-08-12-21-16_46236b

执行回滚操作

[xuliangwei@linux-node1 ~]$ sh deploy.sh rollback #直接执行是不允许的
Please input rollback version
[xuliangwei@linux-node1 ~]$ sh deploy.sh rollback rainbow_pro_2016-08-12-19-24_46236b #这个操作一定要配合list来做。

查看已经回退到19点的版本
[xuliangwei@linux-node1 ~]$ sh deploy.sh list
总用量 16
-rw-rw-r-- 1 xuliangwei xuliangwei 14224 8月  12 21:16 deploy.sh.log
lrwxrwxrwx 1 xuliangwei xuliangwei    60 8月  12 21:18 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-08-12-19-24_46236b
rainbow_pro_2016-08-12-19-24_46236b
rainbow_pro_2016-08-12-21-16_46236b
总用量 0
lrwxrwxrwx 1 xuliangwei xuliangwei 60 8月  12 21:18 rainbow_pro -> /deploy/code/rainbow_pro/rainbow_pro_2016-08-12-19-24_46236b
rainbow_pro_2016-08-12-19-24_46236b
rainbow_pro_2016-08-12-21-16_46236b

查看日志:

[xuliangwei@linux-node1 ~]$ cat webroot/deploy.sh.log
2016-08-12-21-35: deploy.sh : code_get
2016-08-12-21-35: deploy.sh : code_config
2016-08-12-21-35: deploy.sh : code_tar
2016-08-12-21-35: deploy.sh : rainbow_pro_2016-08-12-21-35_46236b_tar.gz
2016-08-12-21-35: deploy.sh : code_scp
2016-08-12-21-35: deploy.sh : remove from cluster
2016-08-12-21-35: deploy.sh : remove from cluster

当然此脚本功能不仅仅如此,可以通过git分支,git tag包等方式来上线,这些根据公司的具体业务来进行调整,也可以自己开发一个web界面来调用此脚本,这样就可以将上线工作交给开发,当然也可以使用开源jenkins来实现。

1.5自动化部署整体脚本

自动化部署脚本2016-11-27

欢迎新朋友你的到来!
已经有5 条评论抢在你前面了~
昵称
邮箱
网站
  1. Peter

    code_get(){ writelog "code_get" cd $SOURCE_DIR

    #1
  2. Peter

    是不是这样理解要3个不同机器的版本库a、上线serverb、远程库c、自用机上线时,先c push到 b再从a pull b

    #2
  3. xuliangwei

    @Peter 我觉得你对上线不理解,并且你也不理解git的原理。我建议你先了解下git以及上线流程。https://www.xuliangwei.com/xubusi/626.html

  4. leon

    你的文章:https://www.xuliangwei.com/xubusi/626.html没有了???

    #3
  5. xuliangwei 官方

    @leon 合并成了一篇文章

登录

忘记密码 ?

切换登录

注册

Copy Protected by Chetan's WP-Copyprotect.