持續交付之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