Ansible playbooks 使用介绍

环境信息

  • ansible-core 2.16
  • Docker image python:3.12.3
Ansible 安装部署参考 Ansible templates 使用介绍

Ansible Playbook 语法

Playbooks 使用 YAML 语法定义(描述)。一个 playbook 由一个或多个 play 依序组成。每个 play 运行一个或多个 task,每个 task 也称为一个 module

每一个 play 中包含了一个 tasks 列表,tasks 列表中的每个 task 在其对应的 hosts依次执行即一个 task 执行完毕,下一个 task 才会执行

在运行 playbook 的过程中,如果一个 host 执行 task 失败,这个 host 将从整个 playbook 中移除。如果发生执行失败的情况,需要修正 playbook 中的错误,重新执行。

Ansible playbook 示例:

playbook.yml
---
- name: Update web servers
hosts: webservers
remote_user: root

tasks:
- name: Ensure apache is at the latest version
ansible.builtin.yum:
name: httpd
state: latest

- name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2
dest: /etc/httpd.conf

- name: Update db servers
hosts: databases
remote_user: root
vars:
port: 8080

tasks:
- name: Ensure postgresql is at the latest version
ansible.builtin.yum:
name: postgresql
state: latest

- name: Ensure that postgresql is started
ansible.builtin.service:
name: postgresql
state: started

---
- name: Install multiple packages
hosts: webservers
tasks:
- name: Install packages
apt:
name: "{{ item }}"
state: present
loop:
- nginx
- git
- curl

一个 Ansible playbook 由一个或多个 plays 组成,每个 play 包含以下部分:

  • name : 描述性的名称
  • hosts : 指定目标主机
  • become : 提升权限(默认是使用 sudo 提升到 root 用户)
  • remote_user : 用于连接到远程主机的账户。(如果 Inventory 中定义了远程连接的用户,会覆盖此处的配置)
  • tasks : 要执行的一系列任务列表
  • vars : 用于定义变量,便于管理和重用
  • gather_facts : 收集 Facts, 默认值为 yes

tasks 是一个任务列表,每个任务执行特定的操作。任务包含以下元素:

  • name : 描述任务的目的。
  • module_name : Ansible 模块名称,如 aptservice 等。
  • module_options : 模块的参数,以键值对的形式提供。
  • when : 条件语句,控制任务是否执行。
  • loop : 循环执行任务

执行以下命令运行 playbook.yml

ansible-playbook playbook.yml -f 10

常用选项说明

选项 说明 示例
-f
--forks
指定并发执行的数量,默认为 5
-v
--verbose
-vvvvvv
打印 debug 信息,详细程度从 -v-vvvvvv
-C
--check
Check mode,不执行任何实际操作,而是对要执行的操作进行验证
-D
--diff
- 只使用 --diff 会执行 play 定义的实际操作,并对所有受影响的文件或者模板显示其变更前后的具体差异
- --check 一起使用,不会执行 play 定义的实际操作,只显示变更前后的差异,可以在实际执行前,调试/预览将要进行的变更,防止意外配置变更或文件修改
主要用于文件或者模板的变更,对于其他类型的任务(如包安装、服务管理、修改主机名等),不会显示具体的差异( 配合 --check 使用时,结果会显示为 skipping,实际执行时结果为 changed )。
--list-hosts 不执行任何实际操作,只列出符合 pattern 的目标主机
--list-tasks 不执行任何实际操作,只列出将要执行的 task
--syntax-check 不执行任何实际操作,只检查 playbook 文件是否有语法错误

主机与用户

play 中一般都需要选择要操作的目标主机,也可能需要指明以哪个用户身份来执行 tasks

  • hosts 一个或多个组或主机的 patterns,以 , 分割
  • remote_user 用户名。
---
- hosts: webservers
remote_user: root

在每一个 task 中,也可以定义自己的远程账号

- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: user1

sudo 提权操作

在需要提权操作的场景下,需要切换到更高权限的用户,可以使用 become 指令。become 可以在全局(play)级别使用或者 task 级别使用。

如果要提权到其他用户而非 root,可以使用指令 become_user

---
- hosts: master-nodes
become: yes
tasks:
- name: ping
ping:

- name: cat authorize
command: cat /root/.ssh/authorized_keys
become: yes
become_user: centos

register

在 Ansible 中,register 用于捕获任务执行的输出,并存储在一个变量中,以便后续任务使用。假设你的任务如下:

tasks:
- name: Fetch remote file list with MD5
shell: |
find {{ remote_dir }} -type f -exec md5sum {} + | sed "s|{{ remote_dir }}||"
register: remote_file_list

这表示:

  1. 执行 find {{ remote_dir }} -type f -exec md5sum {} + | sed "s|{{ remote_dir }}||" 命令 在远程主机上获取目录下的文件列表以及其 md5 值。
  2. 使用 register 关键字 把命令的执行结果存储到 remote_file_list 变量中。

register 变量是一个字典(dict) ,其主要字段包括:

  • stdout : 标准输出(命令执行的结果)
  • stderr : 标准错误输出
  • rc : 返回码(0 表示成功)
  • stdout_lines : 标准输出的列表格式(按行拆分)
  • stderr_lines : 标准错误输出的列表格式(按行拆分)

可以使用 debug 模块调试变量值

- debug:
msg: "远程文件列表: {{ remote_file_list.stdout_lines }}"

变量

task 中可以使用变量。假设在 vars 那里定义了变量,可以这样使用它:

- hosts: webservers
vars:
http_port: 80
max_clients: 200
tasks:
- name: create a web for port {{ http_port }}
template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ http_port }}

playbook 执行之前,默认会执行 setup 收集目标主机的 facts 信息,这是 play 中默认的第一个 task

# ansible-playbook test.yml 

PLAY [k8s-master-nodes] ******************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************

ok: [k8s-master1]

ok: [k8s-master3]

ok: [k8s-master2]

playbook 中的 task 可以使用 facts 中的变量的值

# ansible k8s-master-nodes[0] -m setup -a "filter=ansible_distribution"

k8s-master1 | SUCCESS => {
"ansible_facts": {
"ansible_distribution": "CentOS Linux",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}

# cat test.yml
---
- hosts: k8s-master-nodes
# become: yes
tasks:
- name: ping
ping:

- name: test os distribution
debug:
msg: "This is Centos"
when: ansible_distribution == "CentOS Linux"



# ansible-playbook test.yml

PLAY [k8s-master-nodes] ******************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************

ok: [k8s-master1]

ok: [k8s-master3]

ok: [k8s-master2]

TASK [ping] *************************************************************************************************************************************
ok: [k8s-master2]
ok: [k8s-master1]
ok: [k8s-master3]

TASK [test os distribution] *********************************************************************************************************************
ok: [k8s-master1] => {
"msg": "This is Centos"
}
ok: [k8s-master2] => {
"msg": "This is Centos"
}
ok: [k8s-master3] => {
"msg": "This is Centos"
}

PLAY RECAP **************************************************************************************************************************************
k8s-master1 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
k8s-master2 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
k8s-master3 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

指示命令或者模块在本地运行

在很多场景中,Ansible Playbook 中的某个任务需要在本地主机运行,而不是在远处主机上执行,要达到此目的,可以参考以下方法

  • local_actionlocal_action 是 Ansible 任务(Task)中的一个特殊关键字,它用于 在控制节点(localhost) 执行任务,而 不是在远程主机上执行 。通常,Ansible 任务默认是在 hosts 指定的远程主机上执行,但 local_action 让任务强制在本地执行,相当于 delegate_to: localhost 的简写。
    - name: 将远程文件列表写入本地文件
    local_action:
    module: copy
    content: "{{ remote_file_list.stdout_lines | join('\n') }}"
    dest: "./remote_files.txt"

    local_action 基本语法
    - name: 在本地执行任务
    local_action:
    module: <模块名>
    参数1: 值1
    参数2: 值2
    等效于:
    - name: 在本地执行任务
    action: <模块名> 参数1=值1 参数2=值2
    delegate_to: localhost

  • delegate_to 指令。 delegate_to: localhost 让任务在本地控制节点(localhost)执行,而不是远程主机
    - name: 将远程文件列表写入本地文件
    copy:
    content: "{{ remote_file_list.stdout_lines | join('\n') }}"
    dest: "./remote_files.txt"
    delegate_to: localhost

module

palybook 中的每个 task 都是为了运行某个 module。在 task 中配置 module,是以 module: options 的格式

- hosts: webservers
tasks:
- name: test connection
ping:
- name: make sure apache is running
service: name=httpd state=running
- name: disable selinux
shell: setenforce 0

handlers

handlers 中定义了在发生改动时执行的操作。

Handlers 也是一些 task 的列表,通过名字来引用,它们和一般的 task 并没有什么区别。Handlers 是由通知者进行 notify, 如果没有被 notify,handlers 不会执行。不管有多少个通知者进行了 notify,等到 play 中的所有 task 执行完成之后,handlers 也只会被执行一次。

handlers 会按照声明的顺序执行

Handlers 最佳的应用场景是用来重启服务,或者触发系统重启操作

notify

(当发生改动时)notify actions 会在 playbook 的每一个 task 结束时被触发,而且即使有多个不同的 task 通知改动的发生, notify actions 只会被触发一次。

- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache

when 语句

在 Ansible playbook 中,when 关键字用于条件执行任务。它允许你根据特定的条件来决定是否执行某个任务。这个功能非常强大,可以帮助你在不同的主机、不同的环境或不同的配置下灵活地执行任务。

when 表达式基于 Jinja2 模板语言,其中的变量主要来自: [1]

变量优先级 参考说明

when 关键字后面跟随一个条件表达式,当条件为真时,任务会执行;当条件为假时,任务会被跳过。

tasks:
- name: Install nginx on Debian
apt:
name: nginx
state: present
when: ansible_facts['os_family'] == 'Debian'

when 关键字支持多种表达式,包括:

  • 简单条件

    • 基于变量 的条件:when: variable == 'value'

      - name: Install nginx only if nginx_install is true
      apt:
      name: nginx
      state: present
      when: nginx_install

      在这个示例中,nginx_install 是一个布尔变量。当 nginx_install 为真时,任务将执行。

    • 基于事实(facts) 的条件:when: ansible_facts['os_family'] == 'Debian'

      - name: Install nginx on Debian systems
      apt:
      name: nginx
      state: present
      when: ansible_facts['os_family'] == 'Debian'
  • 逻辑操作

    • 与操作when: condition1 and condition2
      - name: Install nginx on Debian systems
      apt:
      name: nginx
      state: present
      when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'
      当使用多个条件时,也先当于 and 操作
      tasks:
      - name: Shut down CentOS 6 systems
      ansible.builtin.command: /sbin/shutdown -t now
      when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"
    • 或操作when: condition1 or condition2
    • 非操作when: not condition
  • 列表和字典操作

    • 列表包含when: 'item' in mylist
      - name: Ensure package is installed if it is in the list
      apt:
      name: "{{ item }}"
      state: present
      loop:
      - nginx
      - git
      when: item in packages_to_install
    • 字典键存在when: 'key' in mydict
      - name: Run only if the key 'run_task' is present in mydict and its value is true
      debug:
      msg: "Running task"
      when: mydict.get('run_task', False)

      这个任务仅在 mydict 中存在键 run_task 且其值为真时执行。

      when: mydict.get('run_task', False)False 为默认值,如果 mydict 字典中不存在 run_task 键,mydict.get('run_task', False) 将返回 False。这种用法确保了在键不存在时,条件判断不会抛出错误。

  • 复杂条件

    • 组合多个条件when: (condition1 and condition2) or condition3

以下是一个完整的示例

---
- name: Example playbook using when conditions
hosts: all
become: yes

vars:
nginx_install: true
packages_to_install:
- nginx
- git
mydict:
run_task: true

tasks:
- name: Install nginx only if nginx_install is true
apt:
name: nginx
state: present
when: nginx_install

- name: Install nginx on Debian or Ubuntu
apt:
name: nginx
state: present
when: ansible_facts['os_family'] == 'Debian' or ansible_facts['os_family'] == 'Ubuntu'

- name: Ensure package is installed if it is in the list
apt:
name: "{{ item }}"
state: present
loop: "{{ packages_to_install }}"
when: item in packages_to_install

- name: Run only if the key 'run_task' is present in mydict and its value is true
debug:
msg: "Running task"
when: mydict.get('run_task', False)

引用 Facts 变量值

Ansible-playbook 运行过程中,默认会收集目标主机的 Facts 信息。可以在 Playbook 定义中引用这些值 [2]

原始的 facts 信息可以通过 setup 模块获取

$ ansible <hostname> -m ansible.builtin.setup

$ ansible all[0] -m setup -a 'filter=*hostname*'
172.31.2.13 | SUCCESS => {
"ansible_facts": {
"ansible_hostname": "nginx1",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}

要在 playbook 或者 template 中引用,可以参考以下方法:

{{ ansible_facts['devices']['xvda']['model'] }}

{{ ansible_facts['hostname'] }}

需要注意在 Playbook 引用 Facts 变量时,变量前缀无需 ansible_ ,否则会找不到变量

ansible playbook 常用模块参考

iptables

ansible iptables 模块官方文档

  • iptables 模块用于配置 Linux iptables 防火墙 [1]
  • iptables 模块只修改当前系统生效的 iptables 规则(动态),不会将规则写入 iptables 服务的配置文件以实现静态(持久)配置,它的行为和 iptables 命令相同

iptables 模块参数参考 [1]

参数 说明 示例
action rule 是被 append 到最后还是插入到最前面。
默认 append
如果 rule 已经存在,不会做变更
chain 要操作的 chain。
可以是自定义的 chain 或者默认存在的 chain 如 INPUT, FORWARD, OUTPUT, PREROUTING, POSTROUTING, SECMARK or CONNSECMARK
table This option specifies the packet matching table which the command should operate on
chain_management If true and state is present, the chain will be created if needed.
If true and state is absent, the chain will be deleted if the only other parameter passed are chain and optionally table. 默认为 false
comment 规则的注释说明
ctstate
list / elements=string
A list of the connection states to match in the conntrack module.
Possible values are INVALID, NEW, ESTABLISHED, RELATED, UNTRACKED, SNAT, DNAT.
Default: []
source Source specification
destination
string
Destination specification.
Address can be either a network name, a hostname, a network IP address (with /mask), or a plain IP address.
Hostnames will be resolved once only, before the rule is submitted to the kernel. Please note that specifying any name to be resolved with a remote query such as DNS is a really bad idea.
The mask can be either a network mask or a plain number, specifying the number of 1’s at the left side of the network mask. Thus, a mask of 24 is equivalent to 255.255.255.0. A ! argument before the address specification inverts the sense of the address.
destination_port
string
Destination port or port range specification
destination_ports
list / elements=string
This specifies multiple destination port numbers or port ranges to match in the multiport module
dst_range
string
Specifies the destination IP range to match in the iprange module.
source_port Source port or port range specification.
flush
boolean
Flushes the specified table and chain of all rules.
If no chain is specified then the entire table is purged.
默认值 false
jump This specifies the target of the rule
protocol The protocol of the rule or of the packet to check.
The specified protocol can be one of tcp, udp, udplite, icmp, ipv6-icmp or icmpv6, esp, ah, sctp or the special keyword all
rule_num Insert the rule as the given rule number.
This works only with action=insert.

lineinfile

lineinfile 用于以下场景:

  • 确定文件中存在特定的一行,替换存在的行
  • 修改文件中特定的一行

常用参数:

参数 说明 示例
state 指定的行是否应该存在。
- absent
- present 默认值
path
aliases: dest, destfile, name
required
要修改的目标文件
line
aliases: value
要插入或者替换修改的行
state=present 时为必须参数
如果 backrefs=true,可以使用 regexp 正则表达式捕获的值,使用 \g<1>\g<2> 的方式引用捕获到的内容
backup
boolean
默认值 false 。是否创建备份文件

修改防火墙中某个 IP 示例

- name: Modify iptables rule in /etc/sysconfig/iptables
hosts: nginx_1
become: yes
tasks:

- name: Replace the IP address in iptables rule
lineinfile:
path: /etc/sysconfig/iptables
regexp: (.*)18\.32\.100\.90(.*)
line: \g<1>180.232.100.99\g<2>

state: present
backup: yes
backrefs: true

playbook 示例

修改主机名

以下示例展示修改主机名可使用的 playbook,主机名称修改为 Inventory 中主机的主机名(Alias)

假设 Inventory 文件内容如下:

/etc/ansible/inventory/hosts
test_target1:
hosts:
ansible-target-centos79-1:
ansible_host: ansible-target-centos79-1
ansible_user: root
ansible-target-centos79-2:
ansible_host: ansible-target-centos79-2
ansible_port: 22
ansible_user: root

Playbook 内容如下

change_hostname.yml
---
- name: Change hostname based on inventory alias
hosts: test_target1
become: yes
tasks:
- name: Set hostname from alias
hostname:
name: "{{ inventory_hostname }}"

- name: Update /etc/hosts file with new hostname
lineinfile:
path: /etc/hosts
regexp: '^(127\.0\.1\.1\s+).*'
line: "127.0.1.1 {{ inventory_hostname }}"
state: present
when: ansible_facts['distribution'] == 'Ubuntu'

- name: Update /etc/sysconfig/network with new hostname (CentOS/RedHat)
lineinfile:
path: /etc/sysconfig/network
regexp: '^HOSTNAME=.*'
line: "HOSTNAME={{ inventory_hostname }}"
state: present
when: ansible_facts['distribution'] in ['CentOS', 'RedHat']

- name: Set hostname using hostnamectl
command: "hostnamectl set-hostname {{ inventory_hostname }}"
when: ansible_facts['distribution'] in ['CentOS', 'RedHat', 'Ubuntu']

- name: Reboot the system to apply changes
reboot:
msg: "Rebooting to apply hostname changes"
pre_reboot_delay: 5
post_reboot_delay: 30
reboot_timeout: 300

相关模板变量说明

模板变量 说明 示例
inventory_hostname 引用 Inventory 中的主机名称(Alias) {{ inventory_hostname }}

相关模块使用参考

模块 参考链接 示例
hostname hostname
lineinfile lineinfile
reboot reboot
command command

备份目录并删除

假如需要删除某个指定目录,可以参考以下配置,为保险起见,删除之前进行备份,并保留指定数量的备份文件

ansile_playbook_del_dir.yml
- name: Backup and Remove Directory
hosts: all
become: yes # 提升权限(如果需要)
vars:
target_dir: "/opt/data" # 需要删除的目录
backup_dir: "/opt/backup" # 备份存放位置
keep_backups: 3 # 需要保留的备份数量

tasks:
- name: Ensure backup directory exists
file:
path: "{{ backup_dir }}"
state: directory
mode: '0755'

- name: Backup directory before deletion
archive:
path: "{{ target_dir }}"
dest: "{{ backup_dir }}/backup-{{ ansible_date_time.iso8601_basic }}.tar.gz"
format: gz
register: backup_result
when: target_dir is directory

- name: Remove the original directory
file:
path: "{{ target_dir }}"
state: absent

- name: Clean up old backups (keep latest {{ keep_backups }})
shell: "ls -t {{ backup_dir }}/backup-*.tar.gz | tail -n +{{ keep_backups + 1 }} | xargs -r rm -f"
args:
executable: /bin/bash
when: keep_backups | int > 0

同步 Nginx 服务配置文件

以下示例是一个 Ansible Playbook,用于 备份并同步 Nginx 配置检查 Nginx 配置是否正确 (nginx -t) ,如果 配置正确,则重载 Nginx (nginx -s reload)

ansible_playbook_sync_nginx_conf.yaml
#  Nginx 配置到其他 Nginx
- name: Backup And Sync Nginx Conf
hosts: nginx_server
become: yes # 提升权限(如果需要)
vars:
nginx_home: "/usr/local/nginx-1.24.0/" # 需要删除的目录
nginx_conf_dir: "{{ nginx_home }}conf/"
backup_dir: "/opt/backup/" # 备份存放位置
keep_backups: 5 # 需要保留的备份数量

tasks:
- name: Ensure backup directory exists
file:
path: "{{ backup_dir }}"
state: directory
mode: '0755'

- name: Backup nginx conf dir before sync
archive:
path: "{{ nginx_conf_dir }}"
dest: "{{ backup_dir }}{{ ansible_facts['hostname'] }}_nginx_conf_backup-{{ ansible_date_time.iso8601_basic }}.tar.gz"
format: gz
register: backup_result
when: nginx_conf_dir is directory

- name: Clean up old backups (keep latest {{ keep_backups }})
shell: "ls -t {{ backup_dir }} | tail -n +{{ keep_backups + 1 }} | xargs -r rm -f"
args:
executable: /bin/bash
when: keep_backups | int > 0

- name: Sync nginx conf
copy:
src: "{{ nginx_conf_dir }}"
dest: "{{ nginx_conf_dir }}"
when: nginx_conf_dir is directory

- name: Check nginx config
command: "{{ nginx_home }}sbin/nginx -t"
register: nginx_conf_test_result
ignore_errors: yes # 忽略错误,继续执行
notify: restart nginx service

- name: Debug nginx conf test
debug:
msg: "nginx config test result: {{ nginx_conf_test_result }}"

handlers:
- name: restart nginx service
when: nginx_conf_test_result.rc == 0
service:
name: "nginx"
state: "reloaded"

以上的 Playbook 使用 Ansible 模块 copy 将管理主机上的目录拷贝到远程主机,实际使用可能面临以下问题:

  • 文件太多时,copy 过程会比较慢(耗时较久)
  • 无法保证远程主机上的内容和本地管理主机内容一致 ,比如出现远程主机存在文件,管理主机不存在

基于以上问题,推荐使用 Ansible 模块 synchronize 进行同步,synchronize 模块是 rsync 的封装,可以实现基本的 rsync 功能 ,相较于 copy 模块,其同步速度非常快,同时可以使用 delete: True 选项实现 删除目标路径中(dest)存在而源路径(src)中不存在的文件,保证源(src)和目标(dest)中的内容完全一致

使用 synchronize 模块需要确保管理主机和远程主机上都存在 rsync 程序
默认使用 rsyncssh 模式连接远程主机,ssh 端口信息来自 Inventory 中的配置

以下 Playbook 使用 synchronize 同步 Nginx 配置文件

ansible_playbook_sync_nginx_conf.yaml
---
# 同步 Nginx 配置到其他 Nginx
- name: Backup And Sync Nginx Conf
hosts: nginx_server
become: yes # 提升权限(如果需要)
vars:
nginx_home: "/usr/local/nginx/" # 需要删除的目录
nginx_conf_dir: "{{ nginx_home }}conf/"
backup_dir: "/opt/backup/" # 备份存放位置
keep_backups: 5 # 需要保留的备份数量

tasks:
- name: Ensure backup directory exists
file:
path: "{{ backup_dir }}"
state: directory
mode: '0755'

- name: Backup directory before sync
archive:
path: "{{ nginx_conf_dir }}"
dest: "{{ backup_dir }}{{ ansible_facts['hostname'] }}_nginx_conf_backup-{{ ansible_date_time.iso8601_basic }}.tar.gz"
format: gz
register: backup_result
when: nginx_conf_dir is directory

- name: Clean up old backups (keep latest {{ keep_backups }})
shell: "ls -t {{ backup_dir }} | tail -n +{{ keep_backups + 1 }} | xargs -r rm -f"
args:
executable: /bin/bash
when: keep_backups | int > 0

- name: Sync nginx conf
synchronize:
src: "{{ nginx_conf_dir }}"
dest: "{{ nginx_conf_dir }}"
archive: True
mode: push
delete: True
when: nginx_conf_dir is directory

- name: Check nginx config
command: "{{ nginx_home }}sbin/nginx -t"
register: nginx_conf_test_result
ignore_errors: yes
notify: restart nginx service

- name: Debug nginx conf test
debug:
msg: "nginx config test result: {{ nginx_conf_test_result }}"

handlers:
- name: restart nginx service
when: nginx_conf_test_result.rc == 0
service:
name: "nginx"
state: "reloaded"


参考链接

Ansible playbooks
ansible 官方文档
Ansible中文权威指南

脚注