持續交付之Jenkins+Ansible+Python搭建自動化部署框架(win版)

  • 2019 年 11 月 25 日
  • 筆記

前言

無論是為新需求添加的代碼,還是靜態配置的變更,應用的任何變動都要經過部署這道工序才能最終落地。但通常,新的部署意味着應用重啟、服務中斷。工程師和測試人員經常在深夜搞得筋疲力盡,甚至焦頭爛額。進入持續交付的時代後,這個痛點只會更加突顯,因為持續交付意味着持續部署。例如,在測試環境小時級的持續集成場景中,如果沒有辦法將部署過程流程化、自動化,顯然會頻繁打斷最終的交付過程,大幅降低開發測試效率。

因此,我們想要的應該是:一個易用、快速、穩定、容錯力強,必要時有能力迅速回滾的部署系統。

部署的需求

單機部署過程高度抽象後其實就三個步驟:

  • 在目標機器上執行命令停掉運行中的服務
  • 把提前準備好的變更包傳上機器覆蓋原來的目錄
  • 運行命令把服務再跑起來

假設我們實現了一個自動部署程序,簡單地順序執行上面的步驟,讓我們一起來檢驗是否能滿足發佈的需求:

  • 易用:執行腳本就好,填入參數,一鍵執行。
  • 快速:自動化肯定比手工快,並且有提升空間。比如,因為有版本的概念,我們可以跳過相同版本的部署,或是某些步驟。
  • 穩定:因為這個程序邏輯比較簡單,而且執行步驟並不多,沒有交叉和並行,所以穩定性也沒什麼大的挑戰。
  • 容錯性強:表現一般,腳本碰到異常狀況只能停下來,但因為版本間是隔離的,不至於弄壞老的服務,通過人工介入仍能恢復。
  • 回滾順滑:因為每個版本都是完整的可執行產物,所以回滾可以視作使用舊版本重新做一次部署。甚至我們可以在目標機器上緩存舊版本產物,實現超快速回滾。

通過這個程序的簡單執行過程,我們可以看到這套流程的簡單實現,基本滿足了我們部署的需求。而且,可以通過添加更複雜的控制流,獲得更大的提升空間。

而如今架構基本上告別了單點世界,面向集群的部署帶來了更高維度的問題。當部署的目標是一組機器而不是一台機器時,主要問題就變成了如何協調整個過程。比如,追蹤、同步一組機器目前部署進行到了哪一步,編排集群的部署命令就成為了更核心功能。

落地方案

技術架構

主要特點

  • 使用 Jenkins 作為一站式部署平台,方便選擇參數,自動協調各主機,自動運行部署命令,自動通知等
  • 支持快速回滾指定舊版本
  • 支持面向集群進行編排、追蹤和同步任務
  • 實現釘釘自動化通知及跳轉功能

技術選型

  • 執行引擎:Ansible
  • 自動通知:釘釘webhook & python
  • Jenkins 插件:
    • Shell:執行 shell 腳本
    • Active Choices Plugin:動態交互參數
    • AnsiColor:彩色輸出,非必須

環境配置

  • Ansible: 2.9.0
  • Python: 2.7.5
  • CentOS: 7.6
  • Java: 1.8.0_73
  • Jenkins: 2.164.3

預備知識

Ansible

Ansible是什麼?

Ansible 是一個自動化運維管理工具,支持 Linux/Windows 跨平台的配置管理,任務分發等操作,可以幫我們大大減少在變更環境時所花費的時間。與其他三大主流的配置管理工具 Chef、Puppet、Salt 相比,Ansible 最大的特點在於「agentless」,即無需在目標機器裝安裝 agent 進程,即可通過 SSH 或者 PowerShell 對一個環境中的集群進行中心化的管理。 所以,這個「agentless」 特性,可以大大減少我們配置管理平台的學習成本,尤其適合於第一次嘗試使用此類配置管理工具。

Ansible能做什麼?

正如其他配置管理工具一樣,Ansible 可以幫助我們完成一些批量任務,或者完成一些需要經常重複的工作

  • 比如:同時在 100 台服務器上安裝 nginx 服務,並在安裝後啟動它們
  • 比如:將某個文件一次性拷貝到 100 台服務器上
  • 比如:每當有新服務器加入工作環境時,你都要為新服務器部 redis 服務,也就是說你需要經常重複的完成相同的工作

這些場景中我們都可以使用到 Ansible

Ansible架構

Ansible工作原理

Ansible特性

  • 模塊化:調用特定的模塊,完成特定任務
  • 有 Paramiko,PyYAML,Jinja2(模板語言)三個關鍵模塊
  • 支持自定義模塊
  • 基於 Python 語言實現
  • 部署簡單,基於 python 和 SSH(默認已安裝),agentless
  • 安全,基於 OpenSSH
  • 支持 playbook 編排任務
  • 冪等性:一個任務執行1遍和執行n遍效果一樣,不因重複執行帶來意外情況
  • 無需代理不依賴 PKI(無需ssl)
  • 可使用任何編程語言寫模塊
  • YAML 格式,編排任務,支持豐富的數據結構
  • 較強大的多層解決方案

Ansible主要組成部分

  • PLAYBOOKS:任務劇本(任務集),編排定義 Ansible 任務集的配置文件,由 Ansible 順序依次執行,通常是 JSON 格式的 YML 文件
  • INVENTORY:Ansible 管理主機的清單 /etc/anaible/hosts
  • MODULES:Ansible 執行命令的功能模塊,多數為內置的核心模塊,也可自定義, ansible-doc–l 可查看模塊
  • PLUGINS:模塊功能的補充,如連接類型插件、循環插件、變量插件、過濾插件等,該功能不常用
  • API:供第三方程序調用的應用程序編程接口
  • ANSIBLE:組合 INVENTORY、 API、 MODULES、PLUGINS 的綠框,可以理解為是 Ansible 命令工具,其為核心執行工具

注意事項

  • 執行 Ansible 的主機一般稱為主控端,中控,master 或堡壘機
  • 主控端 Python 版本需要2.6或以上
  • 被控端 Python 版本小於2.4需要安裝 python-simplejson
  • 被控端如開啟 SELinux 需要安裝 libselinux-python
  • windows 不能做為主控端

具體實現

環境規劃

搭建 Master 環境(Linux)

這裡以 Centos 7.x yum安裝為例:

# yum install ansible

查看版本:

# ansible --version  ansible 2.9.0    config file = /etc/ansible/ansible.cfg    configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']    ansible python module location = /usr/lib/python2.7/site-packages/ansible    executable location = /usr/bin/ansible    python version = 2.7.5 (default, Jun 20 2019, 20:27:34) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]

配置文件:

配置文件

描述

/etc/ansible/ansible.cfg

主配置文件,配置ansible工作特性

/etc/ansible/hosts

主機清單

/etc/ansible/roles/

存放角色的目錄

/usr/bin/ansible

主程序,臨時命令執行工具

/usr/bin/ansible-doc

查看配置文檔,模塊功能查看工具

/usr/bin/ansible-galaxy

下載/上傳優秀代碼或Roles模塊的官網平台

/usr/bin/ansible-playbook

定製自動化任務,編排劇本工具

/usr/bin/ansible-pull

遠程執行命令的工具

/usr/bin/ansible-vault

文件加密工具

/usr/bin/ansible-console

基於Console界面與用戶交互的執行工具

搭建受控端環境(window)

主機要求

Ansible 從 1.7+ 版本開始支持 Windows,但前提是管理機必須為 Linux 系統,遠程主機的通信方式也由SSH變更為PowerShell,同時管理機必須預安裝 Python 的 Winrm 模塊,方可和遠程 Windows 主機正常通信,但 PowerShell 需4.0+版本且Management Framework 4.0+版本。 Ansible 可以管理包括 Windows 7、8.1和10的桌面操作系統以及包括Windows Server 2008、2008 R2、2012、2012 R2、2016和2019的服務器操作系統。

簡單總結如下:

  • 管理機必須為 Linux 系統且需預安裝 Python 和 Winrm 模塊
  • 底層通信基於 PowerShell,版本為3.0+,Management Framework 版本為4.0+
  • 遠程主機開啟 Winrm 服務

升級 Upgrading PowerShell 和 .NET Framework

可以使用 Upgrade-PowerShell.ps1 腳本來更新它們

這是如何從PowerShell運行此腳本的示例:

$url = "https://raw.githubusercontent.com/jborean93/ansible-windows/master/scripts/Upgrade-PowerShell.ps1"  $file = "$env:tempUpgrade-PowerShell.ps1"  $username = "Administrator"  $password = "Password"    (New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)  Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force    # Version can be 3.0, 4.0 or 5.1  &$file -Version 5.1 -Username $username -Password $password -Verbose

完成後,將需要刪除自動登錄並將執行策略重新設置為默認值 Restricted。可以使用以下PowerShell命令執行此操作:

# This isn't needed but is a good security practice to complete  Set-ExecutionPolicy -ExecutionPolicy Restricted -Force    $reg_winlogon_path = "HKLM:SoftwareMicrosoftWindows NTCurrentVersionWinlogon"  Set-ItemProperty -Path $reg_winlogon_path -Name AutoAdminLogon -Value 0  Remove-ItemProperty -Path $reg_winlogon_path -Name DefaultUserName -ErrorAction SilentlyContinue  Remove-ItemProperty -Path $reg_winlogon_path -Name DefaultPassword -ErrorAction SilentlyContinue

該腳本通過檢查是否需要安裝哪些程序(例如.NET Framework 4.5.2)以及所需的PowerShell版本來工作。如果需要重新啟動 username 並且 password 已設置和參數,則腳本將從重新啟動後自動重新啟動並登錄。該腳本將繼續執行,直到不需要其他操作並且PowerShell版本與目標版本匹配為止。如果未設置 usernam 和 password 參數,腳本將提示用戶手動重新啟動並在需要時登錄。下次登錄用戶時,腳本將從上次停止的地方繼續,然後繼續該過程,直到不需要其他操作為止。

注意:

  • 如果在 Server 2008 上運行,則必須安裝SP2。如果在 Server 2008 R2 或 Windows 7 上運行,則必須安裝SP1
  • Windows Server 2008 只能安裝 PowerShell 3.0,指定較新的版本將導致腳本失敗
  • 在 username 和 password 參數都存儲在註冊表中的純文本。確保腳本完成後運行清除命令,以確保主機上仍沒有存儲憑據。

WinRM 內存修補程序

在 PowerShell v3.0 上運行時,WinRM 服務存在一個錯誤,該錯誤會限制 WinRM 可用的內存量。沒有安裝此修補程序,Ansible 將無法在 Windows 主機上執行某些命令。這些修補程序應作為系統引導或映像過程的一部分進行安裝 腳本 Install-WMF3Hotfix.ps1可用於在受影響的主機上安裝此修補程序

$url = "https://raw.githubusercontent.com/jborean93/ansible-windows/master/scripts/Install-WMF3Hotfix.ps1"  $file = "$env:tempInstall-WMF3Hotfix.ps1"    (New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)  powershell.exe -ExecutionPolicy ByPass -File $file -Verbose

WinRM 安裝程序

一旦將 Powershell 升級到至少3.0版,最後一步就是配置 WinRM 服務,以便 Ansible 可以連接到它。WinRM 服務的兩個主要組件決定着 Ansible 與 Windows 主機的接口方式:listener和和service配置設置。可以使用腳本 ConfigureRemotingForAnsible.ps1 來設置基礎。該腳本使用自簽名證書設置HTTP和HTTPS偵聽器,並Basic 在服務上啟用身份驗證選項。

要使用此腳本,請在PowerShell中運行以下命令:

$url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"  $file = "$env:tempConfigureRemotingForAnsible.ps1"    (New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)    powershell.exe -ExecutionPolicy ByPass -File $file

WinRM 監聽

WinRM 服務在一個或多個端口上偵聽請求。這些端口中的每一個都必須具有創建和配置的偵聽器。要查看 WinRM 服務上正在運行的當前偵聽器,請運行以下命令:

winrm enumerate winrm/config/Listener    Listener      Address = *      Transport = HTTP      Port = 5985      Hostname      Enabled = true      URLPrefix = wsman      CertificateThumbprint      ListeningOn = 10.0.2.15, 127.0.0.1, 192.168.56.155, ::1, fe80::5efe:10.0.2.15%6, fe80::5efe:192.168.56.155%8, fe80::  ffff:ffff:fffe%2, fe80::203d:7d97:c2ed:ec78%3, fe80::e8ea:d765:2c69:7756%7    Listener      Address = *      Transport = HTTPS      Port = 5986      Hostname = SERVER2016      Enabled = true      URLPrefix = wsman      CertificateThumbprint = E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE      ListeningOn = 10.0.2.15, 127.0.0.1, 192.168.56.155, ::1, fe80::5efe:10.0.2.15%6, fe80::5efe:192.168.56.155%8, fe80::  ffff:ffff:fffe%2, fe80::203d:7d97:c2ed:ec78%3, fe80::e8ea:d765:2c69:7756%7

在上面的示例中,激活了兩個偵聽器。一種是通過 HTTP 監聽端口5985,另一種是通過HTTPS監聽端口5986。一些有用的關鍵選項是:

  • Transport:無論偵聽器是通過HTTP還是HTTPS運行,建議對HTTPS使用偵聽器,因為數據已加密,無需進行任何進一步更改。
  • Port:監聽器運行的端口,默認情況下是5985用於HTTP和5986 TTPS的端口。該端口可以更改為所需的任何端口,並與主機var對應ansible_port。
  • Prefix:要偵聽的URL前綴,默認為wsman。如果更改此 ansiblewinrmpath 設置,則必須將主機 var 設置為相同的值。
  • CertificateThumbprint:如果運行在HTTPS偵聽器上,這是連接中使用的 Windows 證書存儲中證書的指紋。

要獲取證書本身的詳細信息,請在PowerShell中使用相關的證書指紋運行以下命令:

$thumbprint = "E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE"  Get-ChildItem -Path cert:LocalMachineMy -Recurse | Where-Object { $_.Thumbprint -eq $thumbprint } | Select-Object *

設置 WinRM 偵聽器

可以通過三種方式設置WinRM偵聽器:

  • 使用了 HTTP 或 HTTPS的。在域環境之外運行並且需要一個簡單的偵聽器時,這是最容易使用的選項。與其他選項不同,此過程還具有為所需的端口打開防火牆並啟動WinRM服務的額外好處。
winrm quickconfigwinrm quickconfig -transport:https
  • 使用組策略對象。當主機是域的成員時,這是創建偵聽器的最佳方法,因為配置是自動完成的,無需任何用戶輸入。有關組策略對象的更多信息,請參閱 組策略對象文檔。
  • 使用 PowerShell 創建具有特定配置的偵聽器。這可以通過運行以下 PowerShell 命令來完成:
$selector_set = @{      Address = "*"      Transport = "HTTPS"  }  $value_set = @{      CertificateThumbprint = "E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE"  }    New-WSManInstance -ResourceURI "winrm/config/Listener" -SelectorSet $selector_set -ValueSet $value_set

設置Windows遠端管理

查看 winrm service listener:

winrm e winrm/config/listener

為 winrm service 配置 auth:

winrm set winrm/config/service/auth @{Basic="true"}

為 winrm service 配置加密方式為允許非加密:

winrm set winrm/config/service @{AllowUnencrypted="true"}

好了,遠程 Windows 主機配置到此結束,我們驗證配置的是否有問題。

Inventory 主機清單

Ansible 必須通過 Inventory 來管理主機。Ansible 可同時操作屬於一個組的多台主機,組和主機之間的關係通過 inventory 文件配置。

# vi /etc/ansible/hosts  [Dev_ALL]  172.16.106.14 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore  172.16.106.180 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore  172.16.106.199 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore    [Dev_AutoTest]  172.16.106.14 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore    [Dev_FunctionTest]  172.16.106.180 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore    [Dev_Develop]  172.16.106.199 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore    [Release_AutoTest]  172.16.106.191 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore    [Release_Develop]  172.16.106.153 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore    [Release_FunctionTest]  172.16.106.185 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore    [Release_ALL]  172.16.106.191 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore  172.16.106.153 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore  172.16.106.185 ansible_ssh_user="xxxx" ansible_ssh_pass="xxxx" ansible_ssh_port=5985 ansible_connection="winrm" ansible_winrm_server_cert_validation=ignore

參數說明:

  • ansiblesshuser:用戶名
  • ansiblesshpass:密碼
  • ansiblesshport:端口號
  • ansible_connection:與主機的連接類型

主機說明:

  • Dev_ALL:所有dev版本環境
  • Dev_AutoTest:dev版本自動化測試環境
  • Dev_FunctionTest:dev版本功能測試環境
  • Dev_Develop:dev版本開發環境
  • Release_ALL:所有release版本環境
  • Release_AutoTest:release版本自動化測試環境
  • Release_Develop:release版本開發環境
  • Release_FunctionTest:release版本功能測試環境

使用 ansible 對 Release_AutoTest 組內的主機進行 ping 模塊測試

# ansible Release_AutoTest -m win_ping  172.16.106.191 | SUCCESS => {      "changed": false,      "ping": "pong"  }

PlayBook 任務劇本

PlayBook 是 Ansible 的腳本文件,使用 YAML 語言編寫,包含需要遠程執行的核心命令、定義任務具體內容,等等。

通常情況下,我們用腳本的方式使用 Ansible,只要使用好 Inventory 和 PlayBook 這兩個組件就可以了,即:使用 PlayBook 編寫 Ansible 腳本,然後用 Inventory 維護好需要管理的機器列表。這樣,就能解決 90% 以上使用 Ansible 的需求。

但如果你有一些更複雜的需求,比如通過代碼調用 Ansible,可能還要用到 API 組件。感興趣的話,你可以參考 Ansible 的官方文檔。

劇本、資源路徑

/home/ansible/playbooks 劇本存放目錄  /home/ansible/python python攪拌

完整劇本

# vi server-deploy.yaml  [root@localhost playbooks]# vi v3c-deploy.yaml  # ---------------------------------  # 1.各變量賦值  # 2.初始化目錄,包括:程序目錄,下載目錄,資源備份目錄(如果不存在)  # 3.結束正在運行的服務進程(等待3秒)  # 4.清空資源目錄  # 5.備份 Data/Files 目錄  # 6.備份 Data/projects 目錄  # 7.清空程序目錄  # 8.下載 server 程序文件  # 9.解壓文件  # 10.清空&還原 Data/projects 目錄  # 11.啟動 server 服務  # ---------------------------------    - hosts: "{{target}}"    remote_user: htsd    vars:      package:        root_url: "http://172.16.106.188:8081/repository/app-{{branch}}-info/server/"      deploy:        app_path: "C:\app\app-{{branch}}-info\server"        package_path: C:apppackage        res_path: C:appres        PsExec_path: C:apptoolsPSTools    tasks:   # --------------------初始化目錄--------------------    - name: 創建程序目錄      win_file:        path: "{{deploy.app_path}}"        state: directory        become: yes    - debug:        msg: "{{deploy.app_path}}"    - name: 創建下載目錄      win_file:        path: "{{deploy.package_path}}"        state: directory        become: yes    - debug:        msg: "{{deploy.package_path}}"    - name: 創建資源備份目錄      win_file:        path: "{{deploy.res_path}}"        state: directory        become: yes    - debug:        msg: "{{deploy.package_path}}"    # -------------------備份及結束進程------------------    - name: 結束 Server 進程      win_shell: Stop-Process -Name "app.Server" -Force      ignore_errors: true    - name: 等待3秒停止 Server 進程      win_wait_for_process:        process_name_pattern: app.Server        state: absent        timeout: 3    - name: 清空資源目錄      win_shell: |        $TargetFolder = "{{deploy.res_path}}"        $Files = get-childitem $TargetFolder -force        Foreach ($File in $Files)        {        $FilePath=$File.FullName        Remove-Item -Path $FilePath -Recurse -Force        }    - name: 備份 Data/Files 目錄      win_shell: Copy-Item "{{deploy.app_path}}DataFiles" -Destination {{deploy.res_path}} -Recurse      ignore_errors: yes    - name: 備份 Data/projects 目錄      win_shell: Copy-Item "{{deploy.app_path}}Dataprojects" -Destination {{deploy.res_path}} -Recurse      ignore_errors: yes    - name: 清空程序目錄      win_shell: |        $TargetFolder = "{{deploy.app_path}}"        $Files = get-childitem $TargetFolder -force        Foreach ($File in $Files)        {        $FilePath=$File.FullName        Remove-Item -Path $FilePath -Recurse -Force        }  # ----------------下載&更新程序------------------------    - name: 下載 server 程序文件      win_get_url:        url: "{{package.root_url}}{{package_name}}"        dest: "{{deploy.package_path}}"        force: no    - debug:        msg: "{{package_name}}"    - name: 遞歸解壓文件後刪除zip包      win_unzip:       src:  "{{deploy.package_path}}/{{package_name}}"       dest:  "{{deploy.app_path}}"       recurse: yes       delete_archive: yes    - name: 刪除原 Data/Files 目錄      win_shell: rmdir /s/q "{{deploy.app_path}}DataFiles"      args:          executable: cmd.exe      ignore_errors: yes    - name: 刪除原 Data/projects 目錄      win_shell: rmdir /s/q "{{deploy.app_path}}Dataprojects"      args:          executable: cmd.exe      ignore_errors: yes    - name: 還原 Data/Files 目錄      win_shell: Copy-Item "{{deploy.res_path}}Files" -Destination "{{deploy.app_path}}Data" -Recurse      ignore_errors: yes    - name: 還原 Data/projects 目錄      win_shell: Copy-Item "{{deploy.res_path}}projects" -Destination "{{deploy.app_path}}Data" -Recurse      ignore_errors: yes  # -------------------------啟動-------------------------    - name: 啟動 app-server      win_shell: "{{deploy.PsExec_path}}/psexec.exe  -accepteula -nobanner -i 1 -s -d {{deploy.app_path}}//app.Server.exe"      register: output      ignore_errors: yes  #  - name: 打印日誌  #    debug: var=output

回滾部署

由於各種各樣的原因,部署的版本可能會出現異常,這時候可能需要緊急回滾版本,我們可以手動去回滾版本,但是缺點也很明顯,當主機實例過多時,手動回滾明顯是不再明智的,所以我們可結合 Jenkins+Ansible 這兩者來做到一個通用的服務版本回滾策略。

Jenkins 執行

#!/usr/bin/env bash  echo '版本類型:'$Branch  echo '環境類型:'$Hosts  echo '文件名稱:'$Package_Name    ansible-playbook /home/ansible/playbooks/server-deploy.yaml --extra-vars "package_name=$Package_Name branch=$Branch target=$Hosts"

具體參考上文:持續交付之解決Jenkins自動發佈中交互式參數應用

Jenkins 執行日誌:

釘釘通知

Jenkins 調用:

python 腳本:

# coding=utf-8    '''  @author: zuozewei  @file: notification.py  @time: 2019/4/25 18:00  @description:dingTalk通知類  '''  import os, jenkins, json  from dingtalkchatbot.chatbot import DingtalkChatbot  from jsonpath import jsonpath    JOB_NAME = str(os.getenv("JOB_NAME"))  BUILD_URL = str(os.getenv("BUILD_URL")) + "console"  BUILD_NUMBER = str(os.getenv("BUILD_NUMBER"))  Package_Name = str(os.getenv("Package_Name"))  VERSION = Package_Name.split('-')[4].replace('.zip','')  Branch = str(os.getenv("Branch"))  Hosts =  str(os.getenv("Hosts"))    Branch_Name = ''  Eev = ''  Host_name = ''  if Branch == 'dev':      Branch_Name = '開發版'      if Hosts == 'Dev_ALL':          Eev = 'Dev所有環境'          Host_name =  '- 172.16.106.175' + 'n' +                        '- 172.16.106.155' + 'n' +                        '- 172.16.106.115' + 'n'      elif Hosts == 'Dev_AutoTest':          Eev = 'Dev自動化測試環境'          Host_name =  '- 172.16.106.175' + 'n'      elif Hosts == 'Dev_FunctionTest':          Eev = 'Dev功能測試環境'          Host_name =  '- 172.16.106.155' + 'n'      elif Hosts == 'Dev_Develop':          Eev = 'Dev開發環境'          Host_name =  '- 172.16.106.115' + 'n'  elif Branch == 'release':      Branch_Name = '預覽版'      if Hosts == 'Release_ALL':          Eev = 'Release所有環境'          Host_name =  '- 172.16.106.58' + 'n' +                        '- 172.16.106.168' + 'n' +                        '- 172.16.106.203' + 'n'      elif Hosts == 'Release_AutoTest':          Eev = 'Release自動化測試環境'          Host_name =  '- 172.16.106.58' + 'n'      elif Hosts == 'Release_FunctionTest':          Eev = 'Release功能測試環境'          Host_name =  '- 172.16.106.203' + 'n'      elif Hosts == 'Release_Develop':          Eev = 'Release開發環境'          Host_name =  '- 172.16.106.168' + 'n'  print("【版本類型】:" + Branch_Name)  print("【環境類型】:" + Eev)  print("【主機列表】:" + Host_name)    # 連接jenkins  server = jenkins.Jenkins(url="http://172.16.106.251:8080", username='xxx', password="xxx")    # 獲取指定項目編譯狀態  BUILD_STATUS = server.get_build_info(JOB_NAME, int(BUILD_NUMBER))['result']  print("【BUILD_STATUS】:" + BUILD_STATUS)    build_info = server.get_build_info(JOB_NAME, int(BUILD_NUMBER))  # dict字典轉json數據  build_info_json = json.dumps(build_info)  # 把json字符串轉json對象  build_info_jsonobj = json.loads(build_info_json)  causes = jsonpath(build_info_jsonobj, '$.actions..shortDescription')    def packagNotification():      title = 'xxx部署通知'        textFail = '#### ' + JOB_NAME + ' # ' + BUILD_NUMBER + ' n' +                  '##### <font color=#FF0000 size=6 face="黑體">部署狀態: ' + BUILD_STATUS + '</font> n' +                  '##### **版本類型**: ' + Branch_Name + 'n' +                  '##### **當前版本**: ' + VERSION + 'n' +                  '##### **文件名稱**: ' + Package_Name + 'n' +                  '##### **觸發類型**: ' + str(causes[0]) + 'n' +                  '##### **部署日誌**: [查看詳情](' + BUILD_URL + ') n' +                  '##### **關注人**: @186xxxx2487 n' +                  '##### **部署環境**: ' + Eev + 'n' +                  '##### **執行主機**: n' +                   Host_name + 'n' +                  '> ###### xxx技術團隊 n '        textSuccess = '#### ' + JOB_NAME + ' # ' + BUILD_NUMBER + ' n' +                 '##### **部署狀態**: ' + BUILD_STATUS + 'n' +                 '##### **版本類型**: ' + Branch_Name + 'n' +                 '##### **當前版本**: ' + VERSION + 'n' +                 '##### **文件名稱**: ' + Package_Name + 'n' +                 '##### **觸發類型**: ' + str(causes[0]) + 'n' +                 '##### **部署日誌**: [查看詳情](' + BUILD_URL + ') n' +                 '##### **部署環境**: ' + Eev + 'n' +                 '##### **執行主機**: n' +                  Host_name + 'n' +                 '> ###### xxx技術團隊 n '        if BUILD_STATUS == 'SUCCESS':          dingText = textSuccess      else:          dingText = textFail        sendding(title, dingText)    def sendding(title, content):      at_mobiles = ['186xxxx2487']      Dingtalk_access_token_v3c = 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxx'      # 初始化機械人小丁      xiaoding1 = DingtalkChatbot(Dingtalk_access_token_v3c)      # Markdown消息@指定用戶      xiaoding1.send_markdown(title=title, text=content, at_mobiles=at_mobiles)    if __name__ == "__main__":      packagNotification()

通知效果:

注意:如果主機比較多的情況,建議不要使用這種硬編碼的方式,可以考慮放到一個配置文件進行讀取。

小結

在今天這篇文章中,主要基於 Ansible 系統的能力,和大家分享了搭建一套部署系統的過程。在搭建過程中,你最需要關注的幾部分內容是:

  • 利用 Inventory 做好部署目標的管理
  • 利用 PlayBook 編寫部署過程的具體邏輯
  • 利用 Jenkins 對主機集群進行調度、追蹤和同步任務
  • 利用 Python 腳本釘釘自動化通知及跳轉功能

至此,我們要搭建的整個自動部署系統,也算是順利完成了。

參考資料:

[1]:https://blog.51cto.com/191226139/2066936

[2]:https://docs.ansible.com/ansible/latest/user_guide/windows.html

[3]:持續交付36講 王瀟俊

本文資源:

https://github.com/7DGroup/Jenkins-CI/tree/master/jenkins-ansible-python