管理大项目
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: connection hosts: node1 tasks: - name: conneciton shell: sleep 10 async: 5 poll: 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: connection hosts: node1 tasks: - name: download get_url: url: http://192.168.48.100/ISOS/openEuler-24.03-LTS-x86_64-dvd.iso dest: /home/yuxb async: 100 poll: 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: connection hosts: node1 tasks: - name: conneciton shell: sleep 10 async: 0 poll: 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 for hosts: node1 tasks: - shell: sleep 10 && touch /tmp/hello # async时间要大于sleep的时间 async: 20 poll: 0 register: out - name: wait for create /tmp/hello wait_for: path: /tmp/hello state: present delay: 5 timeout: 30 sleep: 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_for hosts: node1,node2 tasks: - name: reboot node1 shell: shutdown -r now "Ansible updates triggered" async: 1 poll: 0 when: inventory_hostname == "node1" - name: wait for node1 come back wait_for: host: node1 port: 22 state: started # delay,设置检测前延迟时间。 delay: 10 # sleep,设置检测时间间隔。 sleep: 2 # timeout,设置检测超时时间。 timeout: 300 when: 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_status hosts: node1 tasks: - shell: sleep 10 async: 20 poll: 0 register: out - name: wait for async_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 server import_playbook: pre_web.yml - name: prepare the vsftpd server import_playbook: pre_vsftpd.yml - name: prepare the databse server import_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 yubb yubb ├── defaults │ └── main.yml ├── files ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── README.md ├── tasks │ └── main.yml ├── templates ├── tests │ ├── inventory │ └── test.yml └── vars └── main.yml 8 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 web yum: 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 motd template: src: motd.j2 # 模板文件路径(位于 templates/) dest: /etc/motd # 渲染后生成的目标文件 # 4. 渲染 Apache 虚拟主机配置 - name: prepare yuxb site template: src: yuxb.conf.j2 # 虚拟主机配置模板 dest: /etc/httpd/conf.d/yuxb.conf notify: # 如果文件有变化,触发 handlers - restart_web # 5. 创建站点目录(以主机名区分,避免冲突) - name: prepare DocumentRoot file: path: "/var/www/html/{{ ansible_hostname }}" # 每台主机单独目录 state: directory # 确保存在目录 # 6. 渲染首页 index.html - name: prepare index.html template: 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.j2 hello 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.log CustomLog logs/{{ ansible_hostname }}-common.log common DocumentRoot /var/www/html/{{ ansible_hostname }}/ <Directory /var/www/html/{{ ansible_hostname }}/> Options +Indexes +FollowSymlinks +Includes Order allow,deny Allow 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_web service: name: "{{ web_service }}" # 使用变量,默认为 httpd state: 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 apache hosts: node2 roles: - 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 node2 Last login: Tue Aug 19 11:52:43 2025 from 10.1.8.10 hello 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 = ./inventory remote_user = yuxb vault_password_file = ./password-for-vault roles_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=True become_method=sudo become_user=root become_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 servers hosts: all vars: timesync_ntp_servers: - hostname: ntp.aliyun.com iburst: true roles: - 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 apache Found 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 LB hosts: LBs vars: haproxy_backend_servers: - name: node1 address: 10.1.8.11:80 - name: node2 address: 10.1.8.12:80 - name: node3 address: 10.1.8.13:80 - name: node4 address: 10.1.8.14:80 roles: - haproxy - name: deploy apache hosts: WEBs roles: - apache [yuxb@controller web 14:29:02]$ vim inventory [yuxb@controller web 14:30:26]$ cat inventory [LBs] controller [WEBs]s 674321`234CXa node[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://controller Welcome to node2.yuxb.cloud ! [yuxb@controller web 14:33:25]$ curl http://controller Welcome to node3.yuxb.cloud ! [yuxb@controller web 14:33:29]$ curl http://controller Welcome to node4.yuxb.cloud ! [yuxb@controller web 14:33:29]$ curl http://controller Welcome to node1.yuxb.cloud !