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