Ansible 大项目管理实践笔记:并行任务、角色管理与负载均衡架构部署
管理大项目
Ansible 在大项目管理中的“并行配置与异步任务管理”方法
配置 async
async 异步任务
测试 async + poll 参数对任务调度的影响。
示例1: 任务执行失败,在规定时间内容任务没有执行完成。
async: 5
+ poll: 2
,任务超时 → 演示 异步任务超时失败的情况。
[yuxb@controller web 09:53:08]$ vim playbook.yml [yuxb@controller web 09:54:09]$ cat playbook.yml ---- name: connectionhosts: node1tasks:- name: conneciton shell: sleep 10async: 5poll: 2# 执行[yuxb@controller web 09:51:33]$ ansible-playbook playbook.yml PLAY [connection] ***********************************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]TASK [conneciton] ***********************************************************************************fatal: [node1]: FAILED! => {"changed": false, "msg": "async task did not complete within the requested time - 5s"}PLAY RECAP ******************************************************************************************node1 : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
示例2: 放入后台下载,立刻执行下一个任务。
async: 100
+ poll: 0
,任务后台运行 → 演示 放到后台执行,立即执行下一个任务。
[yuxb@controller web 09:54:12]$ vim playbook.yml [yuxb@controller web 09:55:22]$ cat playbook.yml ---- name: connectionhosts: node1tasks:- name: downloadget_url: url: http://192.168.48.100/ISOS/openEuler-24.03-LTS-x86_64-dvd.isodest: /home/yuxbasync: 100poll: 0# 执行[yuxb@controller web 09:54:43]$ ansible-playbook playbook.yml PLAY [connection] ***********************************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]TASK [download] *************************************************************************************changed: [node1]PLAY RECAP ******************************************************************************************node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例3: ansible 默认行为,等该任务执行完,再执行下一个任务。
async: 0
+ poll: 2
,相当于默认 → 演示 同步等待,按顺序执行任务。
[yuxb@controller web 09:55:24]$ vim playbook.yml [yuxb@controller web 09:56:13]$ cat playbook.yml ---- name: connectionhosts: node1tasks:- name: conneciton shell: sleep 10async: 0poll: 2# 执行[yuxb@controller web 09:55:37]$ ansible-playbook playbook.yml PLAY [connection] ***********************************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]TASK [conneciton] ***********************************************************************************changed: [node1]PLAY RECAP ******************************************************************************************node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
wait_for 模块
测试 如何让 Playbook 在后台任务完成前,智能等待资源就绪。
示例1: 测试文件是否存在
等待文件 /tmp/hello
被创建 → 演示 检测文件存在性。
[yuxb@controller web 09:56:16]$ vim playbook.yml [yuxb@controller web 10:11:46]$ cat playbook.yml ---- name: test wait forhosts: node1tasks:- shell: sleep 10 && touch /tmp/hello# async时间要大于sleep的时间async: 20poll: 0register: out- name: wait for create /tmp/hellowait_for:path: /tmp/hellostate: presentdelay: 5timeout: 30sleep: 2# 测试[yuxb@controller web 09:56:39]$ ansible-playbook playbook.yml PLAY [test wait for] ********************************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]TASK [shell] ****************************************************************************************changed: [node1]TASK [wait for create /tmp/hello] *******************************************************************ok: [node1]PLAY RECAP ******************************************************************************************node1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
示例2: 测试主机端口是否打开
等待主机端口(22)恢复 → 演示 等待主机重启并恢复服务。
[yuxb@controller web 10:11:49]$ vim playbook.yml [yuxb@controller web 10:12:49]$ cat playbook.yml ---- name: test wait_forhosts: node1,node2tasks:- name: reboot node1shell: shutdown -r now "Ansible updates triggered"async: 1poll: 0when: inventory_hostname == "node1"- name: wait for node1 come backwait_for:host: node1port: 22state: started# delay,设置检测前延迟时间。delay: 10# sleep,设置检测时间间隔。sleep: 2# timeout,设置检测超时时间。timeout: 300when: inventory_hostname == "node2"# 测试# node1会重启,连接会断开[yuxb@controller web 10:12:17]$ ansible-playbook playbook.yml PLAY [test wait_for] ********************************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]ok: [node2]TASK [reboot node1] *********************************************************************************skipping: [node2]changed: [node1]TASK [wait for node1 come back] *********************************************************************skipping: [node1]ok: [node2]PLAY RECAP ******************************************************************************************node1 : ok=2 changed=1 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 node2 : ok=2 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0
async_status 模块
使用
async_status
跟踪任务的ansible_job_id
,直到finished
。
演示 如何主动检查后台任务的执行状态,而不是被动等待。
测试 如何追踪和确认异步任务的完成情况。
使用 async_status 模块检查之前的任务是否运行完成。
[yuxb@controller web 10:12:51]$ vim playbook.yml [yuxb@controller web 10:14:35]$ cat playbook.yml ---- name: test async_statushosts: node1tasks:- shell: sleep 10 async: 20poll: 0register: out- name: wait forasync_status:# 通过任务的 ansible_job_id 属性跟踪任务jid: "{{ out.ansible_job_id }}"register: job_result# 根据当前任务执行结果的finished值,判断跟踪任务是否执行完成until: job_result.finished#retries,设置重试次数,默认值为3。retries: 30# delay,设置检测时间间隔,默认5秒检测一次。delay: 2# 测试[yuxb@controller web 10:14:53]$ ansible-playbook playbook.yml PLAY [test async_status] ****************************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node1]TASK [shell] ****************************************************************************************changed: [node1]TASK [wait for] *************************************************************************************FAILED - RETRYING: wait for (30 retries left).FAILED - RETRYING: wait for (29 retries left).FAILED - RETRYING: wait for (28 retries left).FAILED - RETRYING: wait for (27 retries left).FAILED - RETRYING: wait for (26 retries left).changed: [node1]PLAY RECAP ******************************************************************************************node1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Including 和 importing 文件
Ansible 的 Playbook 拆分与调用
playbook 级别(在 Playbook 层级做 include/import)
[yuxb@controller web 10:38:50]$ vim playbook.yml [yuxb@controller web 10:39:34]$ cat playbook.yml - name: prepare the web serverimport_playbook: pre_web.yml- name: prepare the vsftpd serverimport_playbook: pre_vsftpd.yml- name: prepare the databse serverimport_playbook: pre_db.yml
其余文件
[yuxb@controller web 10:40:06]$ cat > pre_web.yml << EOF> - name: Play web> hosts: node1> tasks:> - name: install httpd> yum:> name: httpd> state: present> EOF[yuxb@controller web 10:40:10]$ cat > pre_vsftpd.yml << EOF> - name: Play vsftpd> hosts: node1> tasks:> - name: install vsftpd> yum:> name: vsftpd> state: present> EOF[yuxb@controller web 10:40:17]$ cat > pre_db.yml << EOF> - name: Play db> hosts: node1> tasks:> - name: install mariadb-server> yum:> name: mariadb-server> state: present> EOF
# 执行[yuxb@controller web 10:15:07]$ ansible-playbook playbook.yml
Ansible 角色管理
Ansible 角色
什么是角色(Role)
角色(Role) 是 Ansible 提供的一种 分层组织 Playbook 的方式。
它可以把任务(tasks)、变量(vars)、文件(files)、模板(templates)等内容,按功能模块化、目录化管理。
适合 大项目 或 多人协作,让 Playbook 更加清晰、可复用。
角色目录结构
一个标准角色目录通常长这样:
roles/└── webserver/ # 角色名称├── tasks/ # 主要任务(main.yml 必须有)│ └── main.yml├── files/ # 静态文件├── templates/ # Jinja2 模板├── vars/ # 变量(优先级较高)│ └── main.yml├── defaults/ # 默认变量(优先级最低)│ └── main.yml├── handlers/ # handlers(如服务重启)│ └── main.yml└── meta/ # 角色依赖信息└── main.yml
Ansible 角色结构
[yuxb@controller web 11:28:30]$ ansible-galaxy init yubb- Role yubb was created successfully[yuxb@controller web 11:29:28]$ tree yubbyubb├── defaults│ └── main.yml├── files├── handlers│ └── main.yml├── meta│ └── main.yml├── README.md├── tasks│ └── main.yml├── templates├── tests│ ├── inventory│ └── test.yml└── vars└── main.yml8 directories, 8 files
Ansible 角色目录位置
本地项目目录:
./roles/
(最常用)全局目录:
/etc/ansible/roles/
、/usr/share/ansible/roles/
自定义目录:通过
ansible.cfg
设置roles_path
优先级从上到下依次降低。
可以在ansible.cfg配置文件[defaults]块中通过变量roles_path定义role位置:
[defaults]roles_path = ./roles......
多个路径使用冒号分隔:
roles_path = /etc/ansible/roles:/home/student/web/roles
:w
用 Ansible 创建并编写一个角色(Role)来自动化部署 Apache(httpd)Web 服务。
创建角色
[yuxb@controller web 11:29:33]$ mkdir roles[yuxb@controller web 11:30:33]$ ansible-galaxy init apache- Role apache was created successfully[yuxb@controller web 11:30:39]$ mv apache/ roles# 命令一样[yuxb@controller web 11:30:45]$ ansible-galaxy init apache --init-path=./roles# 生成了 roles/apache/ 目录结构(tasks、handlers、templates、defaults 等)。# 查看角色列表[yuxb@controller web 11:31:34]$ ansible-galaxy list# /usr/share/ansible/roles# /etc/ansible/roles[WARNING]: - the configured path /home/yuxb/.ansible/roles does not exist.[yuxb@controller web 11:31:54]$ cd roles/apache/
# 核心任务逻辑:安装、启动、配置、创建目录和首页[yuxb@controller apache 11:31:55]$ vim tasks/main.yml[yuxb@controller apache 11:33:11]$ cat tasks/main.yml ---# tasks file for apache# 1. 安装 Apache 软件包(默认为 httpd)- name: install webyum:name: "{{ web_package }}" # 使用变量,便于修改state: latest # 确保安装最新版本# 2. 启动并设置 Apache 开机自启- name: "start {{ web_service }}"service:name: "{{ web_service }}" # 服务名(默认为 httpd)state: started # 确保服务已启动enabled: yes # 开机自动启动# 3. 渲染 motd 模板,更新 /etc/motd 欢迎信息- name: prepare motdtemplate:src: motd.j2 # 模板文件路径(位于 templates/)dest: /etc/motd # 渲染后生成的目标文件# 4. 渲染 Apache 虚拟主机配置- name: prepare yuxb sitetemplate:src: yuxb.conf.j2 # 虚拟主机配置模板dest: /etc/httpd/conf.d/yuxb.confnotify: # 如果文件有变化,触发 handlers- restart_web# 5. 创建站点目录(以主机名区分,避免冲突)- name: prepare DocumentRoot file:path: "/var/www/html/{{ ansible_hostname }}" # 每台主机单独目录state: directory # 确保存在目录# 6. 渲染首页 index.html- name: prepare index.htmltemplate:src: index.html.j2 # 首页模板dest: "/var/www/html/{{ ansible_hostname }}/index.html"
# 定义默认变量,便于在 tasks 中引用[yuxb@controller apache 11:33:15]$ vim defaults/main.yml[yuxb@controller apache 11:34:04]$ cat defaults/main.yml ---# defaults file for apache# 定义默认软件包和服务名称,方便在 tasks 中调用web_package: httpd # Apache 软件包web_service: httpd # Apache 服务名[yuxb@controller apache 11:34:18]$ vim templates/motd.j2[yuxb@controller apache 11:34:33]$ cat templates/motd.j2hello guys!Welcome to {{ ansible_fqdn }}![yuxb@controller apache 11:36:48]$ cat templates/yuxb.conf.j2 # {{ ansible_managed }}<VirtualHost *:80>ServerAdmin yuxb@{{ ansible_fqdn }}ServerName {{ ansible_fqdn }}ErrorLog logs/{{ ansible_hostname }}-error.logCustomLog logs/{{ ansible_hostname }}-common.log commonDocumentRoot /var/www/html/{{ ansible_hostname }}/<Directory /var/www/html/{{ ansible_hostname }}/>Options +Indexes +FollowSymlinks +IncludesOrder allow,denyAllow from all</Directory></VirtualHost># 定义“触发器”,当配置文件变化时执行[yuxb@controller apache 11:36:56]$ vim handlers/main.yml[yuxb@controller apache 11:37:42]$ cat handlers/main.yml ---# handlers file for apache- name: restart_webservice:name: "{{ web_service }}" # 使用变量,默认为 httpdstate: restarted # 重启 Apache 服务# 模板文件:系统欢迎信息(motd)[yuxb@controller apache 11:37:46]$ vim templates/index.html.j2[yuxb@controller apache 11:38:31]$ cat templates/index.html.j2 Welcome to {{ ansible_fqdn }} !
调用角色
[yuxb@controller web 11:46:25]$ cat playbook.yml ---- name: deploy apachehosts: node2roles:- apache[yuxb@controller web 11:52:31]$ ansible-playbook playbook.yml PLAY [deploy apache] ********************************************************************************TASK [Gathering Facts] ******************************************************************************ok: [node2]TASK [apache : install web] *************************************************************************ok: [node2]TASK [apache : start httpd] *************************************************************************ok: [node2]TASK [apache : prepare motd] ************************************************************************changed: [node2]TASK [apache : prepare yuxb site] *******************************************************************changed: [node2]TASK [apache : prepare DocumentRoot] ****************************************************************changed: [node2]TASK [apache : prepare index.html] ******************************************************************changed: [node2]RUNNING HANDLER [apache : restart_web] **************************************************************changed: [node2]PLAY RECAP ******************************************************************************************node2 : ok=8 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
验证
[yuxb@controller web 11:52:45]$ curl http://node2/Welcome to node2.yuxb.cloud ![yuxb@controller web 11:53:37]$ ssh node2Last login: Tue Aug 19 11:52:43 2025 from 10.1.8.10hello guys!Welcome to node2.yuxb.cloud!
使用系统角色
系统角色安装和说明
[yuxb@controller web 13:36:00]$ sudo yum install -y rhel-system-roles
角色使用案例-timesync
使用 Ansible 的系统角色(system roles)来管理 NTP 时间同步服务
[yuxb@controller web 13:45:52]$ vim ansible.cfg [yuxb@controller web 13:46:53]$ cat ansible.cfg [defaults]inventory = ./inventoryremote_user = yuxbvault_password_file = ./password-for-vaultroles_path = ./roles:/usr/share/ansible/roles/ #设置了角色搜索路径,保证 Ansible 可以找到系统角色和本地自定义角色。#ask_pass = True#module_name = command#private_key_file = /opt/id_rsa#host_key_checking = False[privilege_escalation]become=Truebecome_method=sudobecome_user=rootbecome_ask_pass=False# playbook内容取自如下[yuxb@controller web 13:47:23]$ vim /usr/share/ansible/roles/rhel-system-roles.timesync/README.md # 调用了 rhel-system-roles.timesync 角色,自动配置时间同步服务。# 通过变量 timesync_ntp_servers 指定 NTP 服务器(这里是 ntp.aliyun.com)。# 角色内部会根据变量去修改系统配置(如 /etc/chrony.conf 或 /etc/ntp.conf),启动时间同步服务,并确保服务开机自启。[yuxb@controller web 13:46:56]$ vim playbook.yml [yuxb@controller web 13:51:24]$ cat playbook.yml - name: Manage timesync with 1 servershosts: allvars:timesync_ntp_servers:- hostname: ntp.aliyun.comiburst: trueroles:- rhel-system-roles.timesync# 执行# Ansible 会在所有目标主机上执行系统角色 tasks,实现 自动化时间同步配置。[yuxb@controller web 13:54:08]$ ansible-playbook playbook.yml
Ansible 自动化部署“负载均衡 + Web 服务”架构
准备工具
下载两个“现成脚本包”:
apache
:能一键帮你装好 Apache Web 服务。haproxy
:能一键帮你装好 HAProxy 负载均衡。
写了“任务清单”(Playbook)
让 controller 机器去装 HAProxy,配置好后端的 4 台 Web 节点。
让 node1-4 机器去装 Apache,每台都能跑网站。
写了“通讯录”(Inventory)
controller
这一台 = 负载均衡器。node1~node4
四台 = 网站服务器。
一键执行任务
运行 Playbook,Ansible 就自动去所有机器执行任务,不用你一台一台手动配置。
验证效果
访问
http://controller
,请求被 HAProxy 自动分发到不同的 Web 节点。所以可以看到:一会儿是 node2 的欢迎语,一会儿是 node3、node4、node1 的。
这就是 负载均衡:把访问请求平均分到多台服务器上。
[yuxb@controller web 14:10:27]$ ansible-galaxy role search --author geerlingguy apacheFound 2 roles matching your search:Name Description---- -----------geerlingguy.apache Apache 2.x for Linux.geerlingguy.apache-php-fpm Apache 2.4+ PHP-FPM support for Linux.[yuxb@controller web 14:10:52]$ ansible-galaxy role install geerlingguy.apache- downloading role 'apache', owned by geerlingguy- downloading role from https://github.com/geerlingguy/ansible-role-apache/archive/4.0.0.tar.gz- extracting geerlingguy.apache to /home/yuxb/web/roles/geerlingguy.apache- geerlingguy.apache (4.0.0) was installed successfully[yuxb@controller web 14:12:02]$ ls roles/apache geerlingguy.apache[yuxb@controller web 14:17:18]$ ansible-galaxy install http://192.168.42.100/%E8%BD%AF%E4%BB%B6/ansible-role-haproxy-1.3.1.tar.gz- downloading role from http://192.168.42.100/%E8%BD%AF%E4%BB%B6/ansible-role-haproxy-1.3.1.tar.gz- extracting ansible-role-haproxy-1.3.1 to /home/yuxb/web/roles/ansible-role-haproxy-1.3.1- ansible-role-haproxy-1.3.1 was installed successfully[yuxb@controller web 14:20:18]$ mv roles/ansible-role-haproxy-1.3.1/ roles/haproxy[yuxb@controller web 14:21:19]$ ls roles/apache geerlingguy.apache haproxy[yuxb@controller web 14:24:02]$ vim playbook.yml [yuxb@controller web 14:28:59]$ cat playbook.yml - name: deploy LBhosts: LBsvars:haproxy_backend_servers:- name: node1address: 10.1.8.11:80- name: node2address: 10.1.8.12:80- name: node3address: 10.1.8.13:80- name: node4address: 10.1.8.14:80roles:- haproxy- name: deploy apachehosts: WEBsroles:- apache [yuxb@controller web 14:29:02]$ vim inventory [yuxb@controller web 14:30:26]$ cat inventory [LBs]controller[WEBs]s 674321`234CXanode[1:4][yuxb@controller web 14:32:02]$ ansible all -a 'systemctl disable nginx --now'[yuxb@controller web 14:30:49]$ ansible-playbook playbook.yml [yuxb@controller web 14:33:09]$ curl http://controllerWelcome to node2.yuxb.cloud ![yuxb@controller web 14:33:25]$ curl http://controllerWelcome to node3.yuxb.cloud ![yuxb@controller web 14:33:29]$ curl http://controllerWelcome to node4.yuxb.cloud ![yuxb@controller web 14:33:29]$ curl http://controllerWelcome to node1.yuxb.cloud !