Ansible 流程控制:条件与循环
Ansible 流程控制:条件与循环
条件判断
Ansible 提供了强大的条件判断功能,让你可以根据不同情况执行不同的任务。
when 条件
基本条件
---
- name: 条件执行示例
hosts: all
tasks:
- name: 只在 Debian 系统上安装
apt:
name: apt-transport-https
state: present
when: ansible_os_family == "Debian"
- name: 只在 CentOS 系统上安装
yum:
name: yum-utils
state: present
when: ansible_os_family == "RedHat"
多条件组合
# AND 条件
- name: 同时满足多个条件
debug:
msg: "这是 Ubuntu 20.04 系统"
when:
- ansible_distribution == "Ubuntu"
- ansible_distribution_version == "20.04"
# OR 条件
- name: 满足任一条件
debug:
msg: "这是 CentOS 或 RHEL"
when: ansible_distribution in ["CentOS", "RedHat"]
# NOT 条件
- name: 非 Debian 系统
debug:
msg: "不是 Debian 系统"
when: ansible_os_family != "Debian"
布尔条件
# 变量为真
- name: 当变量为真时执行
debug:
msg: "功能已启用"
when: feature_enabled
# 变量为假
- name: 当变量为假时执行
debug:
msg: "功能未启用"
when: not feature_enabled
# 检查变量是否存在
- name: 检查变量定义
debug:
msg: "变量已定义"
when: my_var is defined
# 检查变量是否未定义
- name: 检查变量未定义
debug:
msg: "变量未定义"
when: my_var is undefined
字符串比较
- name: 字符串比较
debug:
msg: "环境是生产环境"
when: deployment_env == "production"
- name: 字符串匹配
debug:
msg: "主机名包含 web"
when: "'web' in inventory_hostname"
- name: 正则表达式匹配
debug:
msg: "匹配 IP 地址"
when: inventory_hostname | match('192\\.168\\..*')
数字比较
# 大于
- name: 磁盘使用率超过 80%
debug:
msg: "磁盘空间不足"
when: disk_usage_percent > 80
# 小于等于
- name: 内存大于 8GB
debug:
msg: "内存充足"
when: ansible_memtotal_mb >= 8192
# 模运算
- name: 索引是偶数
debug:
msg: "偶数索引"
when: index % 2 == 0
列表条件
# 列表包含元素
- name: 检查列表是否包含元素
debug:
msg: "包含 nginx"
when: "'nginx' in installed_packages"
# 列表为空
- name: 检查列表是否为空
debug:
msg: "列表为空"
when: my_list | length == 0
# 列表不为空
- name: 检查列表不为空
debug:
msg: "列表不为空"
when: my_list | length > 0
字典条件
# 检查键是否存在
- name: 检查字典键
debug:
msg: "包含该键"
when: "'port' in service_config"
# 检查值
- name: 检查字典值
debug:
msg: "端口是 80"
when: service_config.port == 80
循环
标准循环(loop)
---
- name: 循环示例
hosts: all
tasks:
- name: 创建多个用户
user:
name: "{{ item }}"
state: present
loop:
- alice
- bob
- charlie
- name: 安装多个软件包
package:
name: "{{ item }}"
state: present
loop:
- vim
- git
- curl
- wget
字典循环
- name: 使用字典循环
debug:
msg: "用户 {{ item.name }} 的 ID 是 {{ item.id }}"
loop:
- { name: 'alice', id: 1001 }
- { name: 'bob', id: 1002 }
- { name: 'charlie', id: 1003 }
列表字典
- name: 列表字典
debug:
msg: "键: {{ item.key }}, 值: {{ item.value }}"
loop: "{{ my_dict | dict2items }}"
带索引的循环
- name: 带索引的循环
debug:
msg: "索引 {{ index }}: {{ item }}"
loop:
- apple
- banana
- cherry
loop_control:
index_var: index
循环控制
# 限制循环次数
- name: 只处理前 3 个
debug:
msg: "{{ item }}"
loop: "{{ my_list }}"
loop_control:
loop_var: item
when: loop_var_index < 3
# 跳过特定元素
- name: 跳过特定元素
debug:
msg: "{{ item }}"
loop: "{{ my_list }}"
when: item != "skip_me"
循环变量命名
# 自定义循环变量名
- name: 嵌套循环
debug:
msg: "外层: {{ outer_item }}, 内层: {{ inner_item }}"
loop:
- group1
- group2
loop_control:
loop_var: outer_item
include_tasks:
file: inner_loop.yml
loop:
- user1
- user2
loop_control:
loop_var: inner_item
until 循环
until 循环会重复执行任务,直到满足条件为止。
---
- name: 等待服务启动
hosts: all
tasks:
- name: 等待 Nginx 启动
uri:
url: http://localhost/health
status_code: 200
register: result
until: result.status == 200
retries: 30
delay: 5
- name: 等待文件出现
stat:
path: /tmp/deploy_complete
register: file_status
until: file_status.stat.exists
retries: 60
delay: 1
until 循环与 register
- name: 重试命令直到成功
shell: /opt/app/check_status.sh
register: app_status
until: app_status.rc == 0
retries: 10
delay: 2
changed_when: false
高级条件判断
嵌套条件
- name: 嵌套条件
debug:
msg: "满足所有嵌套条件"
when:
- ansible_distribution == "Ubuntu"
- ansible_distribution_version in ["20.04", "22.04"]
- (ansible_memtotal_mb | int) >= 4096
过滤器条件
# 使用过滤器
- name: 检查文件是否存在
debug:
msg: "文件存在"
when: result.stat.exists | default(false)
# 使用 truthy/falsy
- name: 检查是否启用
debug:
msg: "已启用"
when: feature_enabled | bool
# 使用默认值
- name: 使用默认值
debug:
msg: "值是 {{ config_value | default('default', true) }}"
复杂条件
# 检查多种条件
- name: 复杂条件判断
debug:
msg: "满足条件"
when: >
(ansible_distribution == "Ubuntu" and ansible_distribution_version == "22.04")
or
(ansible_distribution == "CentOS" and ansible_distribution_version == "8")
高级循环
嵌套循环
- name: 嵌套循环
debug:
msg: "{{ item[0] }} - {{ item[1] }}"
loop: "{{ ['server1', 'server2'] | product(['user1', 'user2']) | list }}"
循环注册
- name: 循环并注册结果
shell: echo "{{ item }}"
register: echo_results
loop:
- one
- two
- three
changed_when: false
- name: 显示所有结果
debug:
msg: "{{ item.stdout }}"
loop: "{{ echo_results.results }}"
循环中的条件
- name: 循环中的条件
debug:
msg: "{{ item }}"
loop:
- apple
- banana
- cherry
when: item.startswith('b')
失败处理
failed_when
- name: 自定义失败条件
shell: /usr/local/bin/check_disk.sh
register: disk_check
failed_when:
- "'CRITICAL' in disk_check.stdout"
- disk_check.rc != 0
ignore_errors
- name: 忽略错误继续执行
command: /usr/local/bin/optional_command.sh
ignore_errors: yes
register: optional_result
- name: 根据上一步结果决定
debug:
msg: "上一步失败了,但继续执行"
when: optional_result.failed
block 和 rescue
---
- name: 错误处理示例
hosts: all
tasks:
- name: 使用 block 和 rescue
block:
- name: 尝试执行
command: /usr/bin/some_command
rescue:
- name: 出错时执行
debug:
msg: "命令执行失败,执行恢复操作"
always:
- name: 无论成功失败都执行
debug:
msg: "清理工作"
实战示例
系统初始化
---
- name: 系统初始化
hosts: all
become: yes
tasks:
- name: 更新软件包(仅 Debian)
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: 更新软件包(仅 RedHat)
yum:
update_cache: yes
when: ansible_os_family == "RedHat"
- name: 安装常用工具
package:
name: "{{ item }}"
state: present
loop:
- vim
- git
- curl
- wget
- htop
- iotop
- name: 配置时区(仅当未配置时)
timezone:
name: Asia/Shanghai
when: ansible_date_time.tz != 'CST'
- name: 配置 NTP
template:
src: chrony.conf.j2
dest: /etc/chrony.conf
notify: restart chronyd
when: ansible_service_mgr == 'systemd'
应用部署
---
- name: 应用部署
hosts: app_servers
become: yes
tasks:
- name: 检查是否已安装
stat:
path: "{{ app_dir }}/current"
register: app_installed
- name: 备份现有版本
shell: mv {{ app_dir }}/current {{ app_dir }}/backup_{{ ansible_date_time.epoch }}
when:
- app_installed.stat.exists
- app_installed.stat.isdir
- name: 拉取新版本
git:
repo: "{{ app_repo }}"
dest: "{{ app_dir }}/release_{{ app_version }}"
version: "{{ app_version }}"
- name: 更新当前链接
file:
src: "{{ app_dir }}/release_{{ app_version }}"
dest: "{{ app_dir }}/current"
state: link
- name: 等待服务健康
uri:
url: http://localhost:8080/health
status_code: 200
register: health_check
until: health_check.status == 200
retries: 30
delay: 2
- name: 清理旧版本
shell: find {{ app_dir }} -maxdepth 1 -type d -name 'release_*' ! -name 'release_{{ app_version }}' -mtime +7 -exec rm -rf {} \;
when: cleanup_old_releases
配置管理
---
- name: 配置管理
hosts: config_servers
become: yes
tasks:
- name: 为每个环境创建配置
template:
src: "{{ item.env }}.conf.j2"
dest: "/etc/myapp/{{ item.env }}.conf"
loop: "{{ environments }}"
when: item.enabled
- name: 更新主配置
template:
src: main.conf.j2
dest: /etc/myapp/main.conf
notify: reload service
- name: 验证配置
command: myapp --validate-config
register: config_validation
changed_when: false
failed_when: config_validation.rc != 0
- name: 配置验证成功
debug:
msg: "配置验证通过"
when: config_validation.rc == 0
最佳实践
- 条件优先: 尽量使用条件判断而非创建多个 Play
- 循环控制: 使用 loop 代替 with_items
- 错误处理: 合理使用 block 和 rescue
- 性能优化: 循环中的条件会降低性能,考虑使用 Jinja2 过滤器
- 代码清晰: 复杂条件使用多行格式
- 测试验证: 充分测试所有条件分支
总结
通过本教程,你已经学会了:
- when 条件的使用方法
- 各种条件判断技巧
- 标准循环和高级循环
- until 循环的使用
- 失败处理和错误恢复
- 实战应用场景
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。







