文章目錄
前言
無論是爲新需求添加的代碼,還是靜態配置的變更,應用的任何變動都要經過部署這道工序才能最終落地。但通常,新的部署意味着應用重啓、服務中斷。工程師和測試人員經常在深夜搞得筋疲力盡,甚至焦頭爛額。進入持續交付的時代後,這個痛點只會更加突顯,因爲持續交付意味着持續部署。例如,在測試環境小時級的持續集成場景中,如果沒有辦法將部署過程流程化、自動化,顯然會頻繁打斷最終的交付過程,大幅降低開發測試效率。
因此,我們想要的應該是:一個易用、快速、穩定、容錯力強,必要時有能力迅速回滾的部署系統。
部署的需求
單機部署過程高度抽象後其實就三個步驟:
- 在目標機器上執行命令停掉運行中的服務
- 把提前準備好的變更包傳上機器覆蓋原來的目錄
- 運行命令把服務再跑起來
假設我們實現了一個自動部署程序,簡單地順序執行上面的步驟,讓我們一起來檢驗是否能滿足發佈的需求:
- 易用:執行腳本就好,填入參數,一鍵執行。
- 快速:自動化肯定比手工快,並且有提升空間。比如,因爲有版本的概念,我們可以跳過相同版本的部署,或是某些步驟。
- 穩定:因爲這個程序邏輯比較簡單,而且執行步驟並不多,沒有交叉和並行,所以穩定性也沒什麼大的挑戰。
- 容錯性強:表現一般,腳本碰到異常狀況只能停下來,但因爲版本間是隔離的,不至於弄壞老的服務,通過人工介入仍能恢復。
- 回滾順滑:因爲每個版本都是完整的可執行產物,所以回滾可以視作使用舊版本重新做一次部署。甚至我們可以在目標機器上緩存舊版本產物,實現超快速回滾。
通過這個程序的簡單執行過程,我們可以看到這套流程的簡單實現,基本滿足了我們部署的需求。而且,可以通過添加更復雜的控制流,獲得更大的提升空間。
而如今架構基本上告別了單點世界,面向集羣的部署帶來了更高維度的問題。當部署的目標是一組機器而不是一臺機器時,主要問題就變成了如何協調整個過程。比如,追蹤、同步一組機器目前部署進行到了哪一步,編排集羣的部署命令就成爲了更核心功能。
落地方案
技術架構
主要特點
- 使用 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 不能做爲主控端
具體實現
環境規劃
序號 | 名稱 | 操作系統 | IP地址 | 用途 |
---|---|---|---|---|
1 | enkins-salve | Centos7 | 172.16.106.122 | Jenkins salve |
2 | win7-app-Develop-Dev | win7專業版 | 172.16.106.199 | 開發環境 |
3 | win7-app-Develop-Release | win7專業版 | 172.16.106.153 | 開發環境 |
4 | win7-app-Test-Dev | win7專業版 | 172.16.106.180 | 功能測試環境 |
5 | win7-app-Test-Release | win7專業版 | 172.16.106.185 | 功能測試環境 |
6 | win7-app-AutoTest-Release | win7專業版 | 172.16.106.191 | 自動化測試環境 |
7 | win7-app-AutoTest-Dev | win7專業版 | 172.16.106.14 | 自動化測試環境 |
搭建 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:temp\Upgrade-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:\Software\Microsoft\Windows NT\CurrentVersion\Winlogon"
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:temp\Install-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:temp\ConfigureRemotingForAnsible.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。如果更改此 ansible_winrm_path 設置,則必須將主機 var 設置爲相同的值。
- CertificateThumbprint:如果運行在HTTPS偵聽器上,這是連接中使用的 Windows 證書存儲中證書的指紋。
要獲取證書本身的詳細信息,請在PowerShell中使用相關的證書指紋運行以下命令:
$thumbprint = "E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE"
Get-ChildItem -Path cert:\LocalMachine\My -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
參數說明:
- ansible_ssh_user:用戶名
- ansible_ssh_pass:密碼
- ansible_ssh_port:端口號
- 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
# ---------------------------------
# 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:\app\package
res_path: C:\app\res
PsExec_path: C:\app\tools\PSTools
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}}\Data\Files" -Destination {{deploy.res_path}} -Recurse
ignore_errors: yes
- name: 備份 Data/projects 目錄
win_shell: Copy-Item "{{deploy.app_path}}\Data\projects" -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}}\Data\Files"
args:
executable: cmd.exe
ignore_errors: yes
- name: 刪除原 Data/projects 目錄
win_shell: rmdir /s/q "{{deploy.app_path}}\Data\projects"
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 腳本釘釘自動化通知及跳轉功能
至此,我們要搭建的整個自動部署系統,也算是順利完成了
相關資源:
https://github.com/zuozewei/Jenkins-CI/tree/master/jenkins-ansible-python
參考資料:
[1]:https://blog.51cto.com/191226139/2066936
[2]:https://docs.ansible.com/ansible/latest/user_guide/windows.html
[3]:持續交付36講 王瀟俊