第六章:Playbook 进阶
掌握 Ansible Playbook 的高级特性,包括委托、异步、错误处理等。
最后更新: 2024-01-20
页面目录
Playbook 进阶
本章介绍 Ansible Playbook 的高级特性,帮助你编写更复杂、更健壮的自动化任务。
错误处理
ignore_errors
tasks:
- name: Try to stop service (ignore error if not running)
service:
name: myapp
state: stopped
ignore_errors: yes
- name: Continue with next task
debug:
msg: "Service stopped or not running"
failed_when
tasks:
- name: Check application status
command: /opt/myapp/bin/health-check
register: health_check
failed_when: health_check.rc not in [0, 2]
# 条件判断
- name: Fail on specific error
command: /usr/local/bin/deploy.sh
register: deploy_result
failed_when: "'ERROR' in deploy_result.stdout"
changed_when
tasks:
- name: Check if configured
command: /opt/myapp/bin/is-configured
register: config_status
changed_when: config_status.rc == 0
failed_when: config_status.rc >= 2
block 和 rescue
tasks:
- name: Deploy application
block:
- name: Backup current version
command: /opt/myapp/backup.sh
register: backup
- name: Deploy new version
command: /opt/myapp/deploy.sh
register: deploy
- name: Verify deployment
command: /opt/myapp/verify.sh
register: verify
rescue:
- name: Rollback on failure
command: /opt/myapp/rollback.sh
when: backup is succeeded
- name: Notify failure
debug:
msg: "Deployment failed, rollback executed"
always:
- name: Cleanup temporary files
file:
path: /tmp/deploy_temp
state: absent
force_handlers
# 即使 Play 失败也执行 Handlers
- name: Deploy application
hosts: webservers
force_handlers: yes
tasks:
- name: Install package
apt:
name: custom-package
state: present
- name: This will fail
command: /bin/false
handlers:
- name: Restart services
service:
name: nginx
state: restarted
委托和本地执行
delegate_to
tasks:
# 在本地执行任务
- name: Get load balancer info
uri:
url: "http://lb.example.com/api/status"
register: lb_status
# 委托到负载均衡器
- name: Add server to load balancer
haproxy:
state: enabled
host: "{{ ansible_host }}"
socket: /var/run/haproxy.sock
delegate_to: lb-server
# 委托到本地主机
- name: Save facts locally
copy:
content: "{{ hostvars[inventory_hostname].ansible_facts | to_nice_json }}"
dest: "/tmp/facts/{{ inventory_hostname }}.json"
delegate_to: localhost
# 委托到特定主机
- name: Update DNS
dnsupdate:
record: "{{ inventory_hostname }}"
type: A
value: "{{ ansible_host }}"
delegate_to: "{{ dns_server }}"
run_once
tasks:
# 仅在第一台主机执行
- name: Initialize database
command: /opt/init-db.sh
run_once: yes
# 结合 delegate_to
- name: Create deployment lock
command: /usr/local/bin/create-lock.sh
run_once: yes
delegate_to: localhost
# 条件 run_once
- name: Wait for database initialization
wait_for:
port: 5432
host: "{{ db_host }}"
timeout: 300
run_once: yes
delegate_to: localhost
when: inventory_hostname == groups['dbservers'][0]
异步任务和轮询
async 和 poll
tasks:
# 启动长时间运行的任务
- name: Long running backup
command: /usr/local/bin/backup.sh
async: 3600 # 最大执行时间(秒)
poll: 0 # 不等待,立即继续
register: backup_job
# 轮询异步任务
- name: Check backup progress
async_status:
jid: "{{ backup_job.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 60
delay: 60
# 后台执行
- name: Run report generation
command: /usr/local/bin/generate-report.sh
async: 1800
poll: 15
async_status
tasks:
- name: Start report generation
command: /usr/local/bin/generate-report.sh
async: 3600
poll: 0
register: report_job
- name: Wait for report to complete
async_status:
jid: "{{ report_job.ansible_job_id }}"
register: result
until: result.finished
retries: 720
delay: 5
- name: Download report
fetch:
src: "/tmp/reports/report.pdf"
dest: "./reports/{{ inventory_hostname }}.pdf"
when: result.finished
等待条件和等待模块
wait_for
tasks:
# 等待端口可用
- name: Wait for database to be ready
wait_for:
port: 5432
host: "{{ db_host | default('localhost') }}"
delay: 5
timeout: 60
# 等待文件存在
- name: Wait for config file
wait_for:
path: /etc/myapp/config.yml
state: present
delay: 5
timeout: 60
# 等待文件包含内容
- name: Wait for service to initialize
wait_for:
path: /var/log/myapp/ready
search_regex: "Service started"
timeout: 120
# 等待连接建立
- name: Wait for server to accept connections
wait_for:
host: "{{ inventory_hostname }}"
port: "{{ app_port }}"
state: started
timeout: 300
# 等待进程消失
- name: Wait for old process to stop
wait_for:
path: /var/run/myapp.pid
state: absent
timeout: 60
wait_for_connection
tasks:
# 等待主机连接可用
- name: Wait for server to boot
wait_for_connection:
delay: 10
timeout: 300
# 等待后执行配置
- name: Wait and then configure
wait_for_connection:
timeout: 300
register: connection_result
- name: Install packages after boot
apt:
name: nginx
state: present
when: connection_result is successful
查找和过滤器
lookup 插件
tasks:
# 读取文件
- name: Read SSH public key
debug:
msg: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
# 读取环境变量
- name: Get environment variable
debug:
msg: "{{ lookup('env', 'HOME') }}"
# 读取模板
- name: Template lookup
debug:
msg: "{{ lookup('template', 'vars.j2') }}"
# 读取INI文件
- name: Read INI values
debug:
msg: "{{ lookup('ini', 'value section=section1 file=~/test.ini') }}"
# 读取 AWS SSM 参数
- name: Get SSM parameter
debug:
msg: "{{ lookup('aws_ssm', '/path/to/parameter') }}"
# 读取密码
- name: Generate random password
debug:
msg: "{{ lookup('password', '/dev/null length=20 chars=ascii_letters,digits') }}"
# 多值查找
- name: Read multiple files
debug:
msg: "{{ item }}"
loop:
- "{{ lookup('file', 'file1.txt') }}"
- "{{ lookup('file', 'file2.txt') }}"
Jinja2 过滤器
tasks:
# 默认值
- debug:
msg: "{{ my_var | default('default_value') }}"
# 类型转换
- debug:
msg: "{{ '123' | int }}"
msg: "{{ 123 | string }}"
msg: "{{ list | list }}"
# 字符串过滤器
- debug:
msg: "{{ name | upper }}"
msg: "{{ name | lower }}"
msg: "{{ path | basename }}"
msg: "{{ path | dirname }}"
msg: "{{ 'hello world' | capitalize }}"
msg: "{{ 'hello world' | title }}"
# 列表过滤器
- debug:
msg: "{{ [1,2,3] | sum }}"
msg: "{{ [1,2,3,4,5] | min }}"
msg: "{{ [1,2,3,4,5] | max }}"
msg: "{{ [3,1,2] | sort }}"
msg: "{{ list1 | union(list2) }}"
msg: "{{ list1 | intersect(list2) }}"
# 字典过滤器
- debug:
msg: "{{ dict | keys }}"
msg: "{{ dict | values }}"
msg: "{{ dict | items }}"
msg: "{{ dict | combine(other_dict) }}"
# JSON 过滤器
- debug:
msg: "{{ python_dict | to_json }}"
msg: "{{ python_dict | to_nice_json }}"
msg: "{{ json_string | from_json }}"
# YAML 过滤器
- debug:
msg: "{{ data | to_yaml }}"
msg: "{{ yaml_string | from_yaml }}"
# 路径过滤器
- debug:
msg: "{{ path | expanduser }}"
msg: "{{ path | realpath }}"
# UUID 生成
- debug:
msg: "{{ inventory_hostname | hash('sha1') }}"
msg: "{{ 128 | random }}"
msg: "{{ 50 | random(start=1, end=100) }}"
条件测试
tests 操作符
tasks:
# 定义变量
- set_fact:
my_list: [1, 2, 3, 4, 5]
my_dict: {a: 1, b: 2}
# 包含测试
- debug:
msg: "Contains 3"
when: "3 in my_list"
# 定义测试
- debug:
msg: "Variable is defined"
when: my_var is defined
- debug:
msg: "Variable is not defined"
when: my_var is not defined
# truthy/falsy 测试
- debug:
msg: "Value is truthy"
when: my_value is truthy
# 比较测试
- debug:
msg: "Greater"
when: my_num|int > 10
# 字符串匹配
- debug:
msg: "Matches"
when: my_string is match("^prefix.*")
敏感数据处理
no_log
tasks:
# 隐藏任务输出
- name: Set database password
set_fact:
db_password: "{{ lookup('password', '/dev/null length=32') }}"
no_log: yes
- name: Configure database
mysql_user:
name: app
password: "{{ db_password }}"
priv: 'app.*:ALL'
no_log: yes
# 整个 Play 隐藏
- name: Security sensitive play
hosts: databases
no_log: yes
tasks:
- name: Set secrets
command: /opt/set-secrets.sh
环境变量
tasks:
- name: Use API with environment
uri:
url: "https://api.example.com/data"
headers:
Authorization: "Bearer {{ api_token }}"
environment:
HTTPS_PROXY: "{{ proxy_url }}"
HTTP_PROXY: "{{ proxy_url }}"
动态主机模式
主机组操作
tasks:
- name: Get all webservers
debug:
msg: "{{ groups['webservers'] }}"
- name: Get first database server
debug:
msg: "{{ groups['dbservers'][0] }}"
- name: Get all hostnames
debug:
msg: "{{ inventory_hostname }}"
- name: Check if in group
debug:
msg: "In production"
when: "'production' in group_names"
- name: Loop over all hosts in group
debug:
msg: "{{ item }}"
loop: "{{ groups['webservers'] }}"
hostvars
tasks:
- name: Get fact from another host
debug:
msg: "{{ hostvars['db1']['ansible_host'] }}"
- name: Get all facts from group
debug:
msg: "{{ hostvars[item]['ansible_default_ipv4'] }}"
loop: "{{ groups['all'] }}"
- name: Check if host is reachable
debug:
msg: "{{ item }} is reachable"
loop: "{{ groups['webservers'] }}"
when: hostvars[item].ansible_connection != 'none'
下一步
现在你已经掌握了 Playbook 的进阶特性。接下来让我们学习 Handlers 处理器。