運維與自動化系列③自動化部署基礎與shell腳本實現

自動化部署基礎與shell腳本實現html

關於自動化的基礎知識:前端

1.1:當前代碼部署的實現方式:java

運維純手工scp到web服務器
純手工登陸git服務器執行git pull或svn服務器執行svn update更新代碼
經過xftp上傳代碼
開發打壓縮包上傳到服務器而後解壓node

缺點:
1.須要運維全程參與,佔用大量的工做時間
2.上線時間比較慢
3.人爲形成的失誤較多,管理比較混亂
4.回滾複雜並且慢,還不及時nginx

1.2:運行環境規劃:
開發環境:開發者本地有本身的環境,而後運維須要設置開發環境的公用服務,例如開發數據庫、redis、memcached等
測試環境:功能測試環境和性能測試環境
預生產環境:由生產環境集羣中的某一個節點擔任測試,此節點只作測試不對外提供服務
生產環境:直接對外提供服務的環境git

爲何有預生產環境?
多是生產環境預測試環境的數據庫或數據庫版本不同致使語句出現問題
或者是生產環境調用的接口不同,例如支付接口在測試環境沒法調用web

1.3:設計一套生產環境的代碼自動化部署系統:redis

開發環境 --> 功能測試/性能測試 --> 預生產環境 --> 生產環境shell

1.4:整體規劃流程:
一個服務的集羣節點數量,是一次部署仍是分次部署
一鍵回滾到上個版本
一鍵回滾到任意版本
代碼保存在SVN仍是Git
獲取指定分支或master的指定版本號的代碼,svn指定版本號,git指定tag標籤,或直接拉取某個分支
配置文件差別化,即測試環境和生產環境的配置文件不同,如IP不同或主機名不同或數據庫鏈接不同等等
代碼倉庫和實際的差別,即配置文件是否放在代碼倉庫中,若是保存在git則全部人均可以從配置文件看到數據庫用戶密碼等信息,可使用單獨分支保存配置文件,或配置文件只在部署服務器的某個項目的目錄,好比是config.example
如何更新代碼,java tomcat須要重啓
測試部署後的web頁面是否能夠正常訪問是不是想要的頁面
並行(saltstack)或並行(shell)的問題,涉及到分組部署重啓服務
如何執行,shell執行仍是web執行數據庫

1.5:整體規劃以下:

獲取代碼(git pull拉取) --> 是否編譯(java須要編譯) --> 配置文件(統一和差別) --> 打包 --> scp到目標服務器(或者用saltstack) --> 將服務器移除集羣 --> 解壓代碼包 --> 放置到目標目錄(如webroot) --> scp差別文件 --> 重啓服務(可選) --> 測試服務(訪問web或者post請求) --> 將節點從新加入集羣

二:實現代碼自動化部署

2.1:經過shell腳本實現,shell腳本規劃以下:

2.1.1:各web服務器添加一個uid相同的普通用戶,並且全部的web服務都以此普通用戶啓動,默認狀況下全部的wenb服務除了負載均衡以外都不能監聽80端口,好比能夠監聽8008端口

2.1.2:部署服務器的用戶登陸其餘服務器免密碼登陸,所以須要作祕鑰認證,在各主機執行如下命令:

# useradd www -u 1010
# su – www
$ ssh-keygen
#將部署機www用戶的公鑰複製到各web服務器的 /home/www/.ssh/authorized_keys或執行ssh-copy-id www@192.168.3.13

$ chmod 600 /home/www/.ssh/authorized_keys

2.1.3:測試部署服務器是否能夠免祕鑰用www用戶登陸各個web服務器

2.2:編寫shell腳本:
2.2.1:完成框架編寫:

#!/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"    # 代碼目錄
CONFIG_DIR="/deploy/config"    # 配置文件目錄
TMP_DIR="/deploy/tmp"        # 臨時目錄
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 60
}

code_build(){
    echo "code_build"
}

code_config(){
    echo "code_config"
}

code_tar(){
    echo "code_tar"
}

code_scp(){
    echo "code_scp"
}

cluster_node_remove(){
    echo "cluster_node_remove"
}

code_deploy(){
    echo "code_deploy"
}

config_diff(){
    echo "config_diff"
}

code_test(){
    echo "code_test"
}

cluster_node_in(){
    echo "cluste_node_in"
}

rollback(){
    echo "rollback"
}

# 主函數
main(){    
    if [ -f $LOCK_FILE ];then    # 先判斷鎖文件在不在
        echo "Deploy is running" && exit 10;     # 若是有鎖文件直接退出
    fi 
    DEPLOY_METHOD=$1    # 避免出錯誤將腳本的第一個參數做爲變量
    case $DEPLOY_METHOD in
        deploy)        # 若是第一個參數是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)    # 若是第一個參數是rollback就執行如下操做
            shell_lock;    # 回滾以前也是先建立鎖文件
            rollback;    # 執行完成刪除鎖文件
            shell_unlock;
            ;;
        *)    # 其餘輸入執行如下操做
            usage;
    esac
}
# 執行主函數並把第一個變量當參數
main $1

 

2.2.2:完成腳本:實現代碼部署、測試、回滾等操做:

代碼回滾設計:
正常回滾是回滾已經在web服務器部署過的版本,所以就不須要獲取代碼打包和部署的過程了

列出回滾版本
將模板服務器移除集羣
執行回滾
重啓和測試
將模板服務器加入集羣

#!/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 /deploy/tmp
# mkdir /deploy/tar

# chown -R www.www /deploy
# chown -R www.www /webroot
# chown -R www.www /opt/webroot/
# chown -R www.www /webroot

# 須要在客戶端節點作的操做
# mkdir /opt/webroot
# mkdir /webroot
# chown -R www.www /webroot
# chown -R www.www /opt/webroot/
# chown -R www.www /webroot
# [www@ ~]$ touch /webroot/web-dem


# Node List 服務器節點
PRE_LIST="192.168.3.12"        # 預生產節點
GROUP1_LIST="192.168.3.12 192.168.3.13"
GROUP2_LIST="192.168.3.13"
ROLLBACK_LIST="192.168.3.12 192.168.3.13"

# 日誌日期和時間變量
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.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"            # 臨時目錄
TAR_DIR="/deploy/tar"            # 打包目錄
LOCK_FILE="/tmp/deploy.lock" # 鎖文件路徑

usage(){ # 使用幫助函數
    echo $"Usage: $0 [ deploy | rollback [ list | version ]"
}

writelog(){ # 寫入日誌的函數
    LOGINFO=$1 # 將參數做爲日誌輸入
    echo "${CDATE} ${CTIME} : ${SEHLL_NAME} : ${LOGINFO}" >> ${SHELL_LOG}
}

# 鎖函數
shell_lock(){
    touch ${LOCK_FILE}
}

# 解鎖函數
shell_unlock(){
    rm -f ${LOCK_FILE}
}

# 獲取代碼的函數
code_get(){
    echo "code_get"
    writelog code_get
    cd $CODE_DIR && echo "git pull" # 進入到代碼目錄更新代碼,此處必須免密碼更新,此目錄僅用於代碼更新不能放其餘任何文件
    cp -rf ${CODE_DIR} ${TMP_DIR}/ # 臨時保存代碼並重命名,包名爲時間+版本號,準備複製到web服務器
    API_VER="123"  # 版本號
}

code_build(){ # 代碼編譯函數
    echo code_build
}

code_config(){ # 配置文件函數
    writelog code_config
    /bin/cp -rf ${CONFIG_DIR}/base/* ${TMP_DIR}/"${PRO_NAME}" # 將配置文件放在本機保存配置文件的臨時目錄,用於暫時保存代碼項目
    PKG_NAME="${PRO_NAME}"_"$API_VER"_"${CDATE}-${CTIME}"    # 定義代碼目錄名稱
    cd ${TMP_DIR} && mv ${PRO_NAME} ${PKG_NAME}    # 重命名代碼文件爲web-demo_123-20170629-11-19-10格式
    
}

code_tar(){    # 對代碼打包函數
    writelog code_tar
    cd ${TMP_DIR} && tar czf ${PKG_NAME}.tar.gz ${PKG_NAME}
    writelog "${PKG_NAME}.tar.gz" 
}

code_scp(){ # 代碼壓縮包scp到客戶端的函數
    writelog  "code_scp"
    for node in $PRE_LIST;do # 循環服務器節點列表
        scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot/ # 將壓縮後的代碼包複製到web服務器的/opt/webroot
    done

    for node in $GROUP1_LIST;do # 循環服務器節點列表
        scp ${TMP_DIR}/${PKG_NAME}.tar.gz $node:/opt/webroot/ # 將壓縮後的代碼包複製到web服務器的/opt/webroot
    done
}


url_test(){
    URL=$1
    curl -s --head $URL |grep '200 OK'
    if [ $? -ne 0 ];then
        shell_unlock;
        writelog "test error" && exit;
    fi
}

cluster_node_add(){ #將web服務器添加至前端負載
    echo cluster_node_add
}

cluster_node_remove(){ # 將web服務器從集羣移除函數(正在部署的時候應該不處理業務)
    writelog "cluster_node_remove"
}

pre_deploy(){
    writelog "pre_deploy"
    for node in ${PRE_LIST};do # 循環預生產服務器節點列表
        cluster_node_remove  ${node} # 部署以前將節點從前端負載刪除
        echo  "pre_deploy, cluster_node_remove ${node}"
        ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" #分別到web服務器執行壓縮包解壓命令
        ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" # 整個自動化的核心,建立軟鏈接
        done
}

pre_test(){ # 預生產主機測試函數
    for node in ${PRE_LIST};do # 循環預生產主機列表
        curl -s --head http://${node}:9999/index.html | grep "200 OK" # 測試web界面訪問
            if [ $? -eq 0 ];then  # 若是訪問成功
                writelog " ${node} Web Test OK!" # 記錄日誌
                echo " ${node} Web Test OK!"
                cluster_node_add ${node} # 測試成功以後調用添加函數把服務器添加至節點,
                writelog "pre,${node} add to cluster OK!" # 記錄添加服務器到集羣的日誌
            else # 若是訪問失敗
                writelog "${node} test no OK" # 記錄日誌
                echo "${node} test not OK"
                shell_unlock # 調用刪除鎖文件函數
            break # 結束部署
        fi
    done

}

group1_deploy(){ # 代碼解壓部署函數
    writelog "group1_code_deploy"
    for node in ${GROUP1_LIST};do # 循環生產服務器節點列表
        cluster_node_remove $node  
        echo "group1, cluster_node_remove $node"
        ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" # 分別到各web服務器節點執行壓縮包解壓命令
        ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" # 整個自動化的核心,建立軟鏈接
    done
    scp ${CONFIG_DIR}/other/192.168.3.13.server.xml 192.168.3.13:/webroot/web-demo/server.xml  # 將差別項目的配置文件scp到此web服務器並以項目結尾
}    

group1_test(){ # 生產主機測試函數
    for node in ${PRE_LIST};do # 循環生產主機列表
        curl -s --head http://${node}:9999/index.html | grep "200 OK" #測試web界面訪問
        if [ $? -eq 0 ];then  #若是訪問成功
            writelog " ${node} Web Test OK!" #記錄日誌
            echo "group1_test,${node} Web Test OK!"
            cluster_node_add
            writelog " ${node} add to cluster OK!" #記錄將服務器 添加至集羣的日誌
        else #若是訪問失敗
            writelog "${node} test no OK" #記錄日誌
            echo "${node} test no OK"
            shell_unlock # 調用刪除鎖文件函數
            break # 結束部署
        fi
    done
}

rollback_fun(){ 
    for node in $ROLLBACK_LIST;do # 循環服務器節點列表
        # 注意必定要加"號,不然沒法在遠程執行命令
        ssh $node "rm -f /webroot/web-demo && ln -s /opt/webroot/$1 /webroot/web-demo" # 當即回滾到指定的版本,$1即指定的版本參數
        echo "${node} rollback success!"
        done
}

rollback(){ # 代碼回滾主函數
    if [ -z $1 ];then
        shell_unlock # 刪除鎖文件
        echo "Please input rollback version" && exit 3;
    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 10
    fi
    DEPLOY_METHOD=$1 # 避免出錯誤將腳本的第一個參數做爲變量
    ROLLBACK_VER=$2
    case $DEPLOY_METHOD in
        deploy) # 若是第一個參數是deploy就執行如下操做
            shell_lock; # 執行部署以前建立鎖。若是同時有其餘人執行則提示鎖文件存在
            code_get; # 獲取代碼
            code_build; # 若是要編譯執行編譯函數
            code_config; # cp配置文件
            code_tar;    # 打包
            code_scp;    # scp到服務器
            pre_deploy;  # 預生產環境部署
            pre_test;    # 預生產環境測試
            group1_deploy; # 生產環境部署
            group1_test;   # 生產環境測試
            shell_unlock; # 執行完成後刪除鎖文件
            ;;
        rollback) # 若是第一個參數是rollback就執行如下操做
            shell_lock; # 回滾以前也是先建立鎖文件
            rollback $ROLLBACK_VER;
            shell_unlock; # 執行完成刪除鎖文件
            ;;
        *)
            usage;
    esac
}
main $1 $2

 

3.經過剛纔編寫的shell腳本將html官網頁面部署到nginx中

①將代碼上傳到部署節點的/deploy/code/web-demo目錄中

[www@master web-demo]$ pwd
/deploy/code/web-demo
[www@master web-demo]$ ll
total 20
drwxr-xr-x 6 www www 4096 Jun 6 13:46 assets
-rw-r--r-- 1 www www 1150 Jun 6 17:59 favicon.ico
drwxr-xr-x 2 www www 4096 Jun 6 15:32 images
-rw-r--r-- 1 www www 4323 Jun 6 16:19 index.html

 

部署節點執行如下操做:

# mkdir -p /deploy/code/web-demo
# mkdir -p /deploy/config/web-demo/base
# mkdir -p /deploy/config/web-demo/other
# mkdir /deploy/tmp
# mkdir /deploy/tar

# chown -R www.www /deploy
# chown -R www.www /webroot
# chown -R www.www /opt/webroot/

 

②須要在客戶端作的操做
# 安裝nginx
# yum install -y nginx

編輯nginx

vim /etc/nginx/conf.d/cloudeye.conf

server {
        listen 9999;
        server_name 192.168.3.13; # 實際生產環境中須要填寫域名

        location / {
                alias /webroot/web-demo/; # 這個web-demo目錄不須要建立,有軟連接指向/webroot/web-demo目錄
                index index.html;
        }
}

 

建立相關目錄並修改權限

mkdir /opt/webroot
mkdir /webroot
chown -R www.www /webroot
chown -R www.www /opt/webroot/
[www@ ~]$ touch /webroot/web-demo

 

③執行腳本
測試部署:

[www@master ~]$ ./deploy.sh deploy
code_get
git pull
code_build
web-demo_123_2017-06-26-12-18-09.tar.gz 100% 1214KB 1.2MB/s 00:00 
web-demo_123_2017-06-26-12-18-09.tar.gz 100% 1214KB 1.2MB/s 00:00 
web-demo_123_2017-06-26-12-18-09.tar.gz 100% 1214KB 1.2MB/s 00:00 
pre_deploy, cluster_node_remove 192.168.3.12
HTTP/1.1 200 OK
192.168.3.12 Web Test OK!
cluster_node_add
group1, cluster_node_remove 192.168.3.12
group1, cluster_node_remove 192.168.3.13
/deploy/config/web-demo/other/192.168.3.13.server.xml: No such file or directory
HTTP/1.1 200 OK
group1_test,192.168.3.12 Web Test OK!
cluster_node_add

 

訪問客戶端,能夠看到可以正常訪問,說明部署成功
http://192.168.3.13:9999

修改代碼,測試回滾效果

[www@master ~]$ vim /deploy/code/web-demo/index.html 
[www@master ~]$ ./deploy.sh deploy
code_get
git pull
code_build
web-demo_123_2017-06-26-12-18-57.tar.gz 100% 1214KB 1.2MB/s 00:00 
web-demo_123_2017-06-26-12-18-57.tar.gz 100% 1214KB 1.2MB/s 00:00 
web-demo_123_2017-06-26-12-18-57.tar.gz 100% 1214KB 1.2MB/s 00:00 
pre_deploy, cluster_node_remove 192.168.3.12
HTTP/1.1 200 OK
192.168.3.12 Web Test OK!
cluster_node_add
group1, cluster_node_remove 192.168.3.12
group1, cluster_node_remove 192.168.3.13
/deploy/config/web-demo/other/192.168.3.13.server.xml: No such file or directory
HTTP/1.1 200 OK
group1_test,192.168.3.12 Web Test OK!
cluster_node_add

[www@master ~]$ ./deploy.sh rollback list
-rw-rw-r-- 1 www www 1243347 Jun 26 11:36 /opt/webroot/web-demo_123_2017-06-26-11-36-44.tar.gz
-rw-rw-r-- 1 www www 1243347 Jun 26 11:39 /opt/webroot/web-demo_123_2017-06-26-11-39-02.tar.gz
-rw-rw-r-- 1 www www 1243351 Jun 26 12:04 /opt/webroot/web-demo_123_2017-06-26-12-04-19.tar.gz
-rw-rw-r-- 1 www www 1243347 Jun 26 12:16 /opt/webroot/web-demo_123_2017-06-26-12-16-49.tar.gz
-rw-rw-r-- 1 www www 1243347 Jun 26 12:18 /opt/webroot/web-demo_123_2017-06-26-12-18-09.tar.gz
-rw-rw-r-- 1 www www 1243369 Jun 26 12:18 /opt/webroot/web-demo_123_2017-06-26-12-18-57.tar.gz

 修改部署成功頁面

測試回滾,頁面再次回到修改前,說明回滾成功

[www@master ~]$ ./deploy.sh rollback web-demo_123_2017-06-26-12-18-09
192.168.3.12 rollback success!
192.168.3.13 rollback success!

 

相關文章
相關標籤/搜索