将 Ansible 与 Jenkins 集成:自动化您的 CI/CD 流水线
将Ansible与Jenkins集成,从CI/CD流水线中运行Playbook,管理SSH凭据,并实现一致部署。
将Ansible与Jenkins集成:自动化你的CI/CD流水线
将Ansible与Jenkins集成,让你的CI/CD流水线能够构建制品、运行测试,并使用与Jenkins外部相同的Playbook进行部署。主要挑战在于如何连接凭据、清单和Playbook执行,而不会将流水线变成一堆Shell命令。
本指南展示了一个实用设置:Jenkins编排流水线,而Ansible负责目标主机上的部署和配置。示例假设基于SSH的Linux部署,但相同的模式也适用于特定环境的清单和角色。
Jenkins与Ansible如何协同工作
在编写流水线之前,先明确职责划分:
- Jenkins: 检出代码、构建制品、运行测试、收集日志,并决定部署阶段的执行时机。
- Ansible: 连接目标主机、复制制品、编写配置、管理服务,并保持部署步骤的幂等性。
为何将Jenkins与Ansible集成?
这种分工为团队提供了清晰的边界:
- Jenkins将发布流程存储在
Jenkinsfile中。 - Ansible将基础设施操作存储在Playbook和角色中。
- 清单文件决定哪些主机属于预发布、生产或其他环境。
- Jenkins凭据保护SSH密钥、Vault密码和令牌。
集成的前提条件
开始之前,请确保你已具备:
- 一个正常运行的Jenkins控制器,以及至少一个可以运行部署任务的代理。
- 在运行
ansible-playbook的Jenkins代理上安装Ansible。 - 目标机器可从该代理通过SSH访问。
- 一个专用的自动化SSH密钥对。
- 应用程序代码、清单、Playbook和角色已纳入版本控制。
为Ansible设置Jenkins
Jenkins在安全运行Ansible之前需要两样东西:代理上的Ansible运行时和目标主机的凭据。
1. 安装Ansible插件
你可以直接从Shell步骤调用ansible-playbook,但Jenkins Ansible插件可以使配置和输出更易于管理。
- 导航至
Manage Jenkins>Manage Plugins。 - 转到
Available选项卡,搜索“Ansible”。 - 选择“Ansible”插件,点击
Install without restart或Download now and install after restart。
2. 配置SSH凭据
Ansible将使用SSH连接到目标服务器。将私钥存储在Jenkins凭据中,而不是存储在仓库中。
- 转到
Manage Jenkins>Manage Credentials。 - 选择你的任务使用的凭据存储。
- 点击
Add Credentials。 - 选择
SSH Username with private key。 - 设置一个稳定的ID,例如
ansible-ssh-key。 - 输入目标机器上使用的SSH用户名。
- 添加私钥并保存凭据。
提示:密钥管理最佳实践
- 切勿在Jenkinsfile、清单或Playbook中硬编码SSH私钥。
- 使用专用的自动化密钥,而非个人密钥。
- 按照团队安全策略定期轮换密钥。
使用Ansible设计Jenkins流水线
在Jenkinsfile中使用声明式流水线,以便部署逻辑与应用程序代码一起接受审查。
基本流水线结构
以下是一个紧凑的示例,包含构建、预发布部署和带门控的生产部署:
// Jenkinsfile
pipeline {
agent any
environment {
// 根据需要定义环境变量
ANSIBLE_HOST_KEY_CHECKING = 'False' // 在生产环境中请谨慎,优先使用known_hosts
}
stages {
stage('Checkout Source') {
steps {
git 'https://your-scm-url/your-repo.git'
}
}
stage('Build Application') {
// 此阶段可能构建JAR、WAR、Docker镜像等。
// 示例:构建Spring Boot JAR
steps {
sh 'mvn clean package'
}
}
stage('Run Unit Tests') {
steps {
sh 'mvn test'
}
}
stage('Deploy to Staging') {
steps {
script {
// 使用SSH代理使私钥对Ansible可用
sshagent(credentials: ['ansible-ssh-key']) {
// 执行Ansible Playbook
sh 'ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml \n -e "app_version=$(cat target/VERSION)"'
}
}
}
}
stage('Deploy to Production') {
// 此阶段可能需要手动批准
input {
message "是否继续部署到生产环境?"
ok "部署到生产环境"
}
steps {
script {
sshagent(credentials: ['ansible-ssh-key']) {
sh 'ansible-playbook -i inventory/production.ini playbooks/deploy_app.yml \n -e "app_version=$(cat target/VERSION)"'
}
}
}
}
}
post {
always {
echo '流水线完成。'
}
success {
echo '流水线成功!'
// slackSend channel: '#deployments', message: "部署成功:${env.BUILD_URL}"
}
failure {
echo '流水线失败!'
// slackSend channel: '#deployments', message: "部署失败:${env.BUILD_URL}"
}
}
}
关键流水线细节
agent any适用于演示。在生产环境中,请使用保证安装了Ansible的代理标签。sshagent(credentials: ['ansible-ssh-key'])仅在该块内暴露私钥。ansible-playbook -i inventory/staging.ini playbooks/deploy_app.yml明确指定目标环境。- 生产环境的
input块在敏感部署前添加了手动门控。 - 避免在生产环境中禁用主机密钥检查。改为在Jenkins代理上管理
known_hosts。
示例Ansible Playbook:部署Web应用程序
以下是针对Java Web应用程序的简化inventory/staging.ini和playbooks/deploy_app.yml。你的制品名称、服务管理器和路径可能有所不同。
inventory/staging.ini
[web_servers]
web1.example.com
web2.example.com
[database_servers]
db1.example.com
[all:vars]
ansible_user=ubuntu
playbooks/deploy_app.yml
---
- name: 部署Web应用程序
hosts: web_servers
become: yes
vars:
app_name: my-webapp
app_path: /opt/{{ app_name }}
app_port: 8080
app_version: "{{ app_version | default('1.0.0') }}"
tasks:
- name: 确保应用程序目录存在
ansible.builtin.file:
path: "{{ app_path }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: 将应用程序JAR复制到目标
ansible.builtin.copy:
src: "target/{{ app_name }}-{{ app_version }}.jar"
dest: "{{ app_path }}/{{ app_name }}.jar"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
notify: 重启应用服务
- name: 确保systemd服务文件存在
ansible.builtin.template:
src: templates/my-webapp.service.j2
dest: /etc/systemd/system/{{ app_name }}.service
owner: root
group: root
mode: '0644'
notify: 重启应用服务
- name: 确保应用服务已启动并启用
ansible.builtin.systemd:
name: "{{ app_name }}"
state: started
enabled: yes
handlers:
- name: 重启应用服务
ansible.builtin.systemd:
name: "{{ app_name }}"
state: restarted
daemon_reload: yes
templates/my-webapp.service.j2(Systemd服务模板)
[Unit]
Description={{ app_name }} 应用程序
After=network.target
[Service]
User={{ ansible_user }}
ExecStart=/usr/bin/java -jar {{ app_path }}/{{ app_name }}.jar --server.port={{ app_port }}
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
此Playbook创建应用程序目录、复制构建的JAR、编写systemd单元并启动服务。处理程序仅在制品或单元文件发生更改时重启服务。
最佳实践与技巧
- 保持Playbook的幂等性,以便重新运行失败的流水线不会造成额外损害。
- 将Vault密码存储在Jenkins凭据中,并通过临时密码文件或批准的凭据绑定传递。
- 将部署逻辑组织成角色,例如
webserver、database和app_deploy。 - 为预发布和生产环境使用单独的清单或
group_vars。 - 在专用的Jenkins代理上运行Ansible,而不是在控制器上。
- 仅在需要更多细节时使用
-v或-vv;避免将机密泄露到日志中。 - 在Jenkins运行Playbook之前进行测试,特别是针对生产环境的角色。
何时寻求专业帮助
当流水线部署到生产环境、管理共享凭据、写入多台主机或需要审计控制时,请引入Jenkins或平台管理员。凭据处理、主机密钥验证和回滚行为在首次生产运行前值得二次审查。
总结
使用Jenkins进行编排,使用Ansible管理部署状态。将凭据保留在Jenkins中,将Playbook保留在版本控制中,明确指定清单,并在自动化生产部署之前,先在流水线外部测试相同的Ansible命令。