美文网首页工作专题CICD
自动化部署深度实践

自动化部署深度实践

作者: 万越天 | 来源:发表于2017-09-20 21:05 被阅读141次

    1.1 早期手动部署代码

    方式

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

    缺点:

    • 全程运维参与,占用大量时间。
    • 如果节点多,上线速度慢。
    • 人为失误多,目录管理混乱。
    • 回滚不及时,或者难以回退。

    1.2 设计自动部署代码

    流程设计,确定目标

    1.2.1 自动部署环境

    1.开发环境

    开发者本地有自己的环境,运维配置公共开发环境,大家可共用的服务。例如:开发数据库MySQL,redis,Memcached等

    2.测试环境

    功能测试以及性能测试。

    3.预生产环境

    生产环境集群中的某一个节点,并且连接生产库。(不对外,不做破坏性操作。)

    4.灰度环境

    根据不同的区域进行划分。(生产环境)

    5.生产环境

    对用户提供服务的环境。

    预生产环境由来:

    1.数据库不一致,测试环境和生产环境数据库是不一样的。
    2.使用生产环境的联调接口;例如:支付接口。(电商业务)

    1.2.2 自动部署规划

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

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

    实现思路:

    1.代码放置位置

    Git(优先)、Svn

    2.获取最新代码

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

    3.差异解决

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

    4.项目名称如何设计

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

    5.如何更新

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

    6.如何测试

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

    7.记录日志

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

    8.多人同时执行脚本

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

    9.串行,并行

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

    10.如何执行

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

    11.如何实现正常回退,以及紧急回退(回滚的必要性)

    通过软链接的方式来实现代码秒级别回退。

    1.2.3 自动部署难点

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

    自动化推进难点:

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

    可通过如下方法推进:

    • 目标化沟通。
    • 责任划分
    • ITIL
    • 项目管理:PMBOOK

    1.3 自动部署实践

    整个集群自动化部署流程设计如下:
    可根据如下思路,结合公司实际业务来编写shell脚本或者Python。

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

    1.4 正常回退实践

    • 列出回滚版本
    • 目标服务器移除集群
    • 执行回滚
    • 重启并测试
    • 加入集群

    1.5 紧急回退实践

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

    1.6 自动部署采坑

    自动化部署php环境或者java环境的过程中,那么你一定遇到了如下的问题。

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

    1.7 自动化系统构建实践-运维体系

    1.7.1 环境准备

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

    • linux-node1 192.168.90.201
    • linux-node2 192.168.90.202

    1.创建普通用户

    useradd wanyuetian
    passwd wanyuetian
    

    2.配置密钥

    [root@linux-node1 ~]# su - wanyuetian #切换至普通用户
    [wanyongzhen@Linux-node1 ~]$ ssh-keygen -t dsa #生成密钥    
    Generating public/private dsa key pair.
    Enter file in which to save the key (/home/wanyuetian/.ssh/id_dsa):  #默认回车一路
    /home/wanyuetian/.ssh/id_dsa already exists.
    Overwrite (y/n)? y
    Enter passphrase (empty for no passphrase): 
    Enter same passphrase again: 
    Your identification has been saved in /home/wanyuetian/.ssh/id_dsa. #这是钥匙
    Your public key has been saved in /home/wanyuetian/.ssh/id_dsa.pub. #这是锁
    The key fingerprint is:
    39:6f:d8:42:61:d7:c3:f9:4b:e7:fb:b0:af:52:cd:59 wanyuetian@nfs-server
    The key's randomart image is:
    +--[ DSA 1024]----+
    |                 |
    |           o .   |
    |        o . =    |
    |       . +   o  E|
    |        S     oo+|
    |       . =   ..=o|
    |        o +  .o .|
    |         o  .  o.|
    |             .o++|
    +-----------------+
    [wanyuetian@nfs-server ~]$ ssh-copy-id -i ~/.ssh/id_dsa.pub wanyuetian@192.168.90.202  #公钥分发给其他服务器
    wanyuetian@192.168.90.202's password: 
    Now try logging into the machine, with "ssh 'wanyuetian@192.168.90.202'", and check in:
      .ssh/authorized_keys
    to make sure we haven't added extra keys that you weren't expecting.
    [wanyuetian@nfs-server ~]$ ssh -p22 wanyuetian@192.168.90.202       #使用wanyuetian用户登录node2节点服务器
    Last login: Wed Aug 10 17:59:02 2016 from 192.168.90.201
    [wanyuetian@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/tmp
    mkdir -p /home/wanyuetian/webroot
    chown -R www:www /deploy
    chown -R www:www /home/wanyuetian/webroot
    

    4.配置Nginx

    user wanyuetian;  #指定用户
    root  /home/wanyuetian/webroot/rainbow_pro; #指定项目路径
    

    5.配置好git环境

    • centos7部署gitlab
    • 克隆项目可参考 git远程仓库创建与建立

    最终效果如下:

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

    1.7.2 剖析脚本

    查看脚本,具体配和自动化运维之自动化部署系统构建及演变文章来解读

    [wanyuetian@linux-node1 ~]$ cat deploy.sh
    #!/bin/bash
    # useradd wanyuetian  && sshkey需认证
    # Nginx 权限必须让wanyuetian用户可访问
    # 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
    # Web Root Dir
    # mkdir -p /home/wanyuetian/webroot
    # chown web
    # chown -R www:www /deploy
    # chown -R www:www /home/wanyuetian/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/wanyuetian/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/wanyuetian/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清楚opcode缓存
    }
    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
    

    执行上线:

    [wanyuetian@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
    

    查看当前版本

    [wanyuetian@linux-node1 ~]$ sh deploy.sh list
    总用量 16
    lrwxrwxrwx 1 wanyuetian wanyuetian    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 wanyuetian wanyuetian 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
    

    执行回滚操作

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

    查看已经回退到19点的版本

    [wanyuetian@linux-node1 ~]$ sh deploy.sh list
    总用量 16
    -rw-rw-r-- 1 wanyuetian wanyuetian 14224 8月  12 21:16 deploy.sh.log
    lrwxrwxrwx 1 wanyuetian wanyuetian    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 wanyuetian wanyuetian 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
    

    查看日志:

    [wanyuetian@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
    
    

    1.7.3 脚本其他功能

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

    1.7.4 后期更新

    (如上脚本)+gitlab+jenkins+Sonar 构建自动化部署代码,并代码发布前质量管理

    相关文章

      网友评论

        本文标题:自动化部署深度实践

        本文链接:https://www.haomeiwen.com/subject/ukcasxtx.html