《轉載》Jenkins持續整合-自動化部署指令碼的實現《python》

weixin_30924079發表於2020-04-04

本文轉載自慕課網

讀者須知:
1、本手記本著記續接前面的兩張手記內容整理
2、本手記針對tomcat部署測試環境實現

最近工作比較繁忙,導致這章一直拖延,沒有太抽出時間來總結。要實現Jenkins端的持續整合,其實在CI服務配置端很容易,難點呢?就是如何實現自動化的部署。我的指令碼設計就是為了解決以下難題:

難點一、如何使得自動化部署指令碼更通用

我用的指令碼,依賴依賴一個配置檔案的模組化,讓每一個應用業務模組更加通用。自動化所執行的命令呢?
我也是設計想法本著更加通用平臺的原則,至少對於tomcat+java or java+socket這種模式通用。

難點二、如何使得自動化的部署簡單有效

本指令碼,更加切合實際,例:自動化部署關閉tomcat,我們常常會選擇catalina.sh stop去關閉,然而這裡我選擇了kill的方式,如果檢測中沒有順利關閉,我將呼叫kill -9 去直接關閉。(沒辦法,選擇了通用,你是沒遇到過tomcat 一直有執行緒卡住,關閉不了的情況吧。)
難點三、需要讓我們自動化的部署實現高的可靠性
體現在:
1、每一次的部署,配置選項中都會進行備份。(備份只一次)
2、服務自動備份完畢後,需要實現檢測(這裡我在配置模組中有加入日誌終端輸出)

當然,這個指令碼中還有未完成的地方:
1、服務啟動後,自動化的檢測機制還是太薄弱。
2、暫時此指令碼還無法通用到php,python等環境的更多平臺
3、程式碼備份方式上,應該更加具更多靈活性。
這裡,如果你覺得我的指令碼可以補充,不如大家一起加入進來(留言、或者郵件我jeson@imoocc.com),我也很想學習學習大家的想法。

一、自動化部署原理圖示
1、2、3、4 表示執行的4個部署基礎步驟
圖片描述
二、實現

1、Tomcat 程式設定唯一標記

用於識別出一個伺服器中唯一的Tomcat程式
java -Dmyapp.name=“project_name”
如果用的tomcat我們可以,直接修改啟動控制檔案catalina.sh。
JAVA_OPTS="-Dmyapp.name:"project_name" -server -Xms1024M -Xmx4096M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=1024M"
2、實現自動化登入

一般有兩種方式:
方式一、模擬使用者密碼互動方式,大家可以學習家py中依賴pexcept模組。
方式二、用ssh + Key方式,這個是ssh的一種公鑰認證機制。本指令碼中用的這種,大家自行學習。

3、CI伺服器需要在服務端配置rsync服務端
實現中,主要用於客戶端服務拖取程式碼。

4、配置檔案解釋
[imoocc.com-server] //服務的模組名稱,這裡需要注意,需要具有唯一性,且服務的程式標示命名一致
CI_IP=ci.iaskjob.com //CI伺服器地址
Login_ISA_Key=/back/key/jeson_iaskjob_rsa //登入ISA的key
Project_IP=192.168.10.99 //工程IP
Project_Login_User=root //對應工程機器登入使用者
Project_Login_Port=22 //對應工程機器登入埠
Project_Code_File=/tmp/imoocc //對應工程在CI伺服器構建後的執行程式碼目錄
Project_Exclude_File="/var/cache","/var/tmp","jdbc.profile" //不需要向線上同步的檔案,如配置檔案等等
Project_Backup_Dir=/opt/Jenkins_Backup_Dir //工程備份目錄
Project_Deploy_Path=/opt/imoocc //工程部署目錄
Project_Start_Command=tomcat/bin/catalina.sh start //工程啟動方式
Project_Log_Path=/tmp/imoocc.log //啟動後日志的檢視方式

分享程式碼如下:

# -*- coding: utf-8 -*-
#!/usr/bin/python
#########################################################################
# File Name: Jenkins_Project_Deploy.py
# Program function: ueed for deployment code in CI server.
# Author:Jeson
# mail:jeson@imoocc.com
# Created Time: 一  6/20 19:24:33 2016
#========================================================================
import ConfigParser
import sys
from sys import argv
import paramiko

def Gennerate_cmd(Project_Name,CI_IP,Project_Code_File,Project_Exclude_File,Project_Backup_Dir,Project_Deploy_Path,Project_Start_Command,Project_Log_Path):
    cmd = []
    #生成備份命令
    if Project_Backup_Dir:
        #生成備份原有檔案
        cmd.append("mkdir -p /opt/")
        cmd.append("rsync -avzP %s %s && touch /tmp/CI_%s_backuptrue"%(Project_Deploy_Path,Project_Backup_Dir,Project_Name))
    #生成關閉原有程式命令
    cmd.append('test -e /tmp/CI_%s_backuptrue && ps -auxgrep "name:%s"grep -v grepawk \'{print $2}\'xargs kill'%(Project_Name,Project_Name))
    #生成命令-確認原來服務已經順利關閉
    cmd.append('ps -auxgrep "name:%s"grep -v grepawk \'{print $2}\'&& touch /tmp/CI_killSucc  ps -auxgrep "name:%s"grep -v grepawk \'{print $2}\'xargs kill -9'%(Project_Name,Project_Name))
    #生成命令-用於同步新的程式碼檔案
    if Project_Code_File == "":
        cmd.append('test -e "/tmp/CI_%s_killSucc" && rsync -avzP --delete-after --exclude "{%s}" %s::%s %s'%(Project_Name,Project_Exclude_File,CI_IP,Project_Name,Project_Deploy_Path))
    else:
        cmd.append('test -e "/tmp/CI_%s_killSucc" && rsync -avzP --delete-after --exclude "{%s}" %s::%s/%s %s'%(Project_Name,Project_Exclude_File,CI_IP,Project_Name,Project_Code_File,Project_Deploy_Path))
    #生成命令-用於啟動服務
    cmd.append(Project_Start_Command)
    #生成命令-檢視服務啟動後的日誌
    cmd.append('tail -n 200 -f %s'%Project_Log_Path)
    #生成命令-清理此次生成的臨時檔案
    # cmd.append('rm -f /tmp/\*%s\*'%Project_Name)
    return cmd

def Login_Execute_Command(Project_IP,Project_Login_Port,Project_Login_User,Login_ISA_Key,cmds):
    Project_Port = int(Project_Login_Port) if Project_Login_Port else 22
    if Project_IP and Project_Login_User and Login_ISA_Key:
        key = paramiko.RSAKey.from_private_key_file(Login_ISA_Key)
        s = paramiko.SSHClient()
        s.load_system_host_keys()
        s.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        s.connect(hostname=Project_IP,port=Project_Port,username=Project_Login_User,pkey=key)
        for cmd_item in cmds:
            stdin,stdout,stderr=s.exec_command(cmd_item)
            print stdout.read()
        return True
    else:
        return false

def main():
    if len(argv) > 2:
        Config_File = argv[1] if argv[1] else "./Jenkins_Project_Deploy.conf "
        Project_Name = argv[2] if argv[2] else ""
    else:
        print "Error 0:Input argv format error! ./Jenkins_Project_Deploy.py ./test.conf testname"
        sys.exit()
    try:
        cf = ConfigParser.ConfigParser()
        cf.read(Config_File)
        CI_IP = cf.get(Project_Name, "CI_IP")
        Project_Code_File = cf.get(Project_Name, "Project_Code_File")
        Project_Exclude_File = cf.get(Project_Name, "Project_Exclude_File")
        Project_Backup_Dir = cf.get(Project_Name, "Project_Backup_Dir")
        Project_Deploy_Path = cf.get(Project_Name, "Project_Deploy_Path")
        Project_Start_Command = cf.get(Project_Name, "Project_Start_Command")
        Project_Log_Path = cf.get(Project_Name, "Project_Log_Path")

        execute_command = Gennerate_cmd(Project_Name,CI_IP,Project_Code_File,Project_Exclude_File,Project_Backup_Dir,Project_Deploy_Path,Project_Start_Command,Project_Log_Path)
        print "{Step 1: Command collected over!}",execute_command
        Project_IP = cf.get(Project_Name, "Project_IP")
        Project_Login_Port = cf.get(Project_Name, "Project_Login_Port")
        Project_Login_User = cf.get(Project_Name, "Project_Login_User")
        Login_ISA_Key = cf.get(Project_Name, "Login_ISA_Key")
    except Exception as e:
        print "Error 1: %s file open or read error!"%Config_File
        sys.exit()
    print "{Step 2:Prepare to loging...}",Project_IP
    result = Login_Execute_Command(Project_IP,Project_Login_Port,Project_Login_User,Login_ISA_Key,cmds=execute_command)
    if result is False:
        print "Error 3:Execute command failed!"
if __name__ == '__main__':
    main()

 

轉載於:https://www.cnblogs.com/abc8023/p/8056897.html

相關文章