1、什么是playbooks
playbooks是ansible的脚本、如同shell脚本同样,它是控制远程主机的一系列命令的集合,经过YAML语言编写。执行一些简单的任务,咱们可使用ad-hoc命令就能够解决,对于一些较复杂的任务,ad-hoc就不能胜任了,这时候playbooks就派上用场了,在playbooks中能够编排有序的执行过程,甚至能够在多组机器间来回有序的执行特定的步骤,而且能够同步或异步发起任务。html
2、YAML语法
一、文件开始符mysql
---
二、数组linux
- name - hosts - user
三、字典nginx
name: restart apache service: name=httpd state=restarted
字典与字典的嵌套:git
vars: http_port: 80 max_clients: 200
字典与数组的嵌套:github
tasks: - name: ensure apache is at the latest version yum: pkg=httpd state=latest - name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name=httpd state=started
此外,Ansible 使用 "{{ var }}"来引用变量.,若是一个值以 "{" 开头, YAML 将认为它是一个字典, 因此咱们必须引用它, 像这样:foo: "{{ variable }}"web
3、playbooks的基本用法
最基本的playbook分为四部分:sql
hosts users
variable
tasks
handlers
简单示例:shell
--- - name: Install apache hosts: webservers user: root gather_facts: false vars: http_port: 80 max_clients: 200 tasks: - name: ensure apache is at the latest version yum: pkg=httpd state=latest - name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name=httpd state=started handlers: - name: restart apache service: name=httpd state=restarted
说明:apache
一、主机和用户
key | 含 义 |
hosts | 为主机的IP,或者主机组名,或者关键字all |
user | 在远程以哪一个用户身份执行。 |
become | 切换成其它用户身份执行,值为yes或者no |
become_method | 与became一块儿用,指能够为‘sudo’/’su’/’pbrun’/’pfexec’/’doas’ |
become_user | 与bacome一块儿用,能够是root或者其它用户名 |
通常用法:
--- - hosts: webserver, [all] user: root
还能够在每一个 task 中,定义远程执行用户
--- - hosts: webserver user: root tasks: - name: test connection ping: remote_user: root
也支持 sudo 方法,在 task中一样支持,在sudo须要密码时,能够加上选项 –ask-sudo-pass
--- - hosts: all user: test sudo: yes task: - service: name=nginx state=started sudo: yes sudo_user: root
二、任务列表
tasks 是从上到下顺序执行,若是中间发生错误,整个 playbook 便会中断。
每个 task 是对module的一次调用,一般会带有特定参数,参数可使用变量。
每个 task 有一个 name 属性,name 值会在命令行中输出,以提示用户,若是没有定义,aciton 的值会做为输出信息来标记task。
语法:
tasks: - name: make sure apache is running service: name=httpd state=running // 若是参数过长,可使用空格或者缩进分隔为多行 tasks: - name: copy ansible inventory file to client copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts owner=root group=root mode=0644 // 或者使用 yaml 的字典做为参数 tasks: - name: copy ansible inventory file to client copy: src: /etc/ansible/hosts dest: /etc/ansible/hosts owner: root group: root mode: 0644 // 大部分的模块都是使用 `key-value` 这种格式的,其中有两个比较特殊,command 和 shell 模块。 tasks: - name: disable selinux command: /sbin/setenforce 0 tasks: - name: run this command and ignore the result shell: /usr/bin/command || /bin/true tasks: - name: run some command and ignore the reslut shell: /usr/bin/somecommadn ignore_error: True
执行状态:
task中每一个action会调用一个module,在module中会去检查当前系统状态是否须要从新执行。
若是本次执行了,那么action会获得返回值changed
若是不须要执行,那么action获得返回值ok
三、响应事件
每一个主流的编程语言都会有event机制,那么handler就是playbook的event。
Handlers里面的每个handler,也是对module的一次调用。而handlers与tasks不一样,tasks会默认的按定义顺序执行每个task,handlers则不会,它须要在tasks中被调用,才有可能被执行。Tasks中的任务都是有状态的,changed或者ok。 在Ansible中,只在task的执行状态为changed的时候,才会执行该task调用的handler,这也是handler与普通的event机制不一样的地方。
什么状况下使用handlers呢?
若是你在tasks中修改了apache的配置文件。须要重起apache。此外还安装了apache的插件。那么还须要重起apache。像这样的应该场景中,重启apache就能够设计成一个handler。
特性:
在全部的任务里表执行以后执行,若是有多个task notify同一个handler,那么只执行一次。
在下面的例子里apache只执行一次
--- - hosts: lb remote_user: root vars: random_number1: "{{ 10000 | random }}" random_number2: "{{ 10000000000 | random }}" tasks: - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }} copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }} notify: - call in every action - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }} copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }} notify: - call in every action handlers: - name: call in every action debug: msg="call in every action, but execute only one time"
只有当TASKS种的action的执行状态是changed时,才会触发notify handler的执行。
下面的脚本执行两次,执行结果是不一样的:
第一次执行是,tasks的状态都是changed,会触发两个handler
第二次执行是,
第一个task的状态是OK,那么不会触发handlers"call by /tmp/hosts",
第二个task的状态是changed,触发了handler"call by /tmp/hosts.random_number"
//示例代码 --- - hosts: lb remote_user: root vars: random_number: "{{ 10000 | random }}" tasks: - name: Copy the /etc/hosts to /tmp/hosts copy: src=/etc/hosts dest=/tmp/hosts notify: - call by /tmp/hosts - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number }} copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number }} notify: - call by /tmp/hosts.random_number handlers: - name: call by /tmp/hosts debug: msg="call first time" - name: call by /tmp/hosts.random_number debug: msg="call by /tmp/hosts.random_number"
andlers是按照在handlers中定义个顺序执行的,而不是安装notify的顺序执行的。
下面的例子定义的顺序是1>2>3,notify的顺序是3>2>1,实际执行顺序:1>2>3
--- - hosts: lb remote_user: root gather_facts: no vars: random_number1: "{{ 10000 | random }}" random_number2: "{{ 10000000000 | random }}" tasks: - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }} copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }} notify: - define the 3nd handler - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }} copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }} notify: - define the 2nd handler - define the 1nd handler handlers: - name: define the 1nd handler debug: msg="define the 1nd handler" - name: define the 2nd handler debug: msg="define the 2nd handler" - name: define the 3nd handler debug: msg="define the 3nd handler"
四、变量
playbook中经常使用变量的几种状况:
用户能够在Playbook中,经过vars关键字自定义变量,使用时用{{ }}引用以来便可。
示例:
--- - hosts: web vars: http_port: 80 remote_user: root tasks: - name: insert firewalld rule for httpd firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
当变量比较多的时候,或者变量须要在多个playbook中重用的时候,能够把变量放到一个单独的文件中。经过关键字var_files把文件中定义的变量引入playbook中,使用变量的方法和在本文件中定义的变量相同。
- hosts: web remote_user: root vars_files: - vars/server_vars.yml tasks: - name: insert firewalld rule for httpd firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes #cat vars/server_vars.yml http_port: 80
定义复杂的变量
当变量的值不是简单的字符串或者数字,而是一个字典,语法以下
foo: field1: one field2: two
访问复杂变量中的子属性,能够利用中括号或者点号:
foo['field1'] 或 foo.field1
ansible会经过module setup来收集主机的系统信息,这些收集到的系统信息叫作facts,这些facts信息能够直接以变量的形式使用。
有哪些facts变量能够引用呢?在命令行上经过调用setup module命令能够查看:
$ ansible all -m setup -u root
系统变量在playbook中能够直接使用:
--- - hosts: all user: root tasks: - name: echo system shell: echo {{ ansible_os_family }} - name install ntp on Debian linux apt: name=git state=installed when: ansible_os_family == "Debian" - name install ntp on redhat linux yum: name=git state=present when: ansible_os_family == "RedHat"
对于复杂的、多层级的facts变量,能够经过下面的两种方式的任意一种访问复杂的变量中的子属性:
中括号:
{{ ansible_ens3["ipv4"]["address"] }}
点号:
{{ ansible_ens3.ipv4.address }}
关系facts变量,一旦关闭以后就不用调用了。关闭方法:
- hosts: webserver gather_facts: no
把task的执行结果也能够做为一个变量值。这个时候就须要用到“注册变量”,将执行结果注册到一个变量中,待后面的action使用。
--- - hosts: web tasks: - shell: ls register: result ignore_errors: True - shell: echo "{{ result.stdout }}" when: result.rc == 5 - debug: msg="{{ result.stdout }}"
注册变量常常和debug module一块儿使用,这样能够获得更多action的输出信息,帮助用户调试。
为了使Playbook更灵活、通用性更强,容许用户在执行的时候传入变量的值,这个时候就须要用到“额外变量”。
定义命令行变量
在test.yml文件里,hosts和user都定义为变量,须要从命令行传递变量值。
--- - hosts: '{{ hosts }}' remote_user: '{{ user }}' tasks: ...
在命令行里面传值得的方法:
ansible-playbook testyml --extra-vars "hosts=web user=root"
还能够用json格式传递参数:
ansible-playbook test.yml --extra-vars "{'hosts':'vm-rhel7-1', 'user':'root'}"
还能够将参数放在文件里面:
ansible-playbook test.yml --extra-vars "@vars.json"
五、如何执行playbook
语法:
$ ansible-playbook deploy.yml
查看输出的细节
$ ansible-playbook playbook.yml --verbose
查看该脚本影响哪些hosts
$ ansible-playbook playbook.yml --list-hosts
并行执行脚本
$ ansible-playbook playbook.yml -f 10
4、playbooks的高级用法
一、逻辑控制
主机为Debian Linux马上关机:
tasks: - name: "shutdown Debian flavored systems" command: /sbin/shutdown -t now when: ansible_os_family == "Debian"
根据action的执行结果,来决定接下来执行的action
tasks: - command: /bin/false register: result ignore_errors: True - command: /bin/something when: result|failed - command: /bin/something_else when: result|success - command: /bin/still/something_else when: result|skipped
远程中的系统变量facts变量做为when的条件,用“|int”还能够转换返回值的类型:
--- - hosts: web tasks: - debug: msg="only on Red Hat 7, derivatives, and later" when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6
条件表达式
基本:
tasks: - shell: echo "This certainly is epic!" when: epic
否认:
tasks: - shell: echo "This certainly isn't epic!" when: not epic
变量定义:
tasks: - shell: echo "I've got '{{ foo }}' and am not afraid to use it!" when: foo is defined - fail: msg="Bailing out. this play requires 'bar'" when: bar is not defined
数值表达:
tasks: - command: echo {{ item }} with_items: [ 0, 2, 4, 6, 8, 10 ] when: item > 5
与Include一块儿用:
- include: tasks/sometasks.yml when: "'reticulating splines' in output"
与Role一块儿用:
- hosts: webservers roles: - { role: debian_stock_config, when: ansible_os_family == 'Debian' }
标准循环
为了保持简洁,重复的任务能够用如下简写的方式:
- name: add several users user: name={{ item }} state=present groups=wheel with_items: - testuser1
若是你在变量文件中或者 'vars' 区域定义了一组YAML列表,你也能够这样作:
vars: somelist: ["testuser1", "testuser2"] tasks: -name: add several user user: name={{ item }} state=present groups=wheel with_items: "{{somelist}}"
使用 'with_items' 用于迭代的条目类型不只仅支持简单的字符串列表.若是你有一个哈希列表,那么你能够用如下方式来引用子项:
- name: add several users user: name={{ item.name }} state=present groups={{ item.groups }} with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' }
注意:若是同时使用 when 和 with_items (或其它循环声明),when声明会为每一个条目单独执行。
嵌套循环:
- name: give users access to multiple databases mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo with_nested: - [ 'alice', 'bob' ] - [ 'clientdb', 'employeedb', 'providerd'] 或者 - name: give users access to multiple databases mysql_user: name={{ item.0 }} priv={{ item.1 }}.*:ALL append_privs=yes password=foo with_nested: - [ 'alice', 'bob' ] - [ 'clientdb', 'employeedb', 'providerd']
对字典使用循环:
--- vars: users: alice: name: Alice Appleworth telephone: 123-456-7890 bob: name: Bob Bananarama telephone: 987-654-3210 tasks: - name: Print phone records debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})" with_dict: "{{users}}"
对文件列表使用循环
with_fileglob 能够以非递归的方式来模式匹配单个目录中的文件.以下面所示:
tasks: # first ensure our target directory exists - file: dest=/etc/fooapp state=directory # copy each file over that matches the given pattern - copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600 with_fileglob: - /playbooks/files/fooapp/*
多个action组装成块,能够根据不一样条件执行一段语句 :
tasks: - block: - yum: name={{ item }} state=installed with_items: - httpd - memcached - template: src=templates/src.j2 dest=/etc/foo.conf - service: name=bar state=started enabled=True when: ansible_distribution == 'CentOS' become: true become_user: root
组装成块处理异常更方便:
tasks: - block: - debug: msg='i execute normally' - command: /bin/false - debug: msg='i never execute, cause ERROR!' rescue: - debug: msg='I caught an error' - command: /bin/false - debug: msg='I also never execute :-(' always: - debug: msg="this always executes"
二、重用playbook
Playbook支持两种重用机制,一种是重用静态Playbook脚本,另一种是相似于编程语言中函数的机制。
Include语句的功能,基本的代码重用机制。主要重用tasks。同时Include可将tasks分割成多个文件,避免Playbook过于臃肿,使用户更关注于总体的架构,而不是实现的细节上。
普通用法
像其它语言的Include语句同样,直接Include:
--- # possibly saved as tasks/firewall_httpd_default.yml - name: insert firewalld rule for httpd firewalld: port=80/tcp permanent=true state=enabled immediate=yes main.yml文件中调用include的方法: tasks: - include: tasks/firewall_httpd_default.yml
高级用法-使用参数
include文件中还能够定义参数
被include的文件tasks/firewall_httpd_default.yml中,使用{{ port }}定义了一个名字为port的参数。
--- - name: insert firewalld rule for httpd firewalld: port={{ port }}/tcp permanent=true state=enabled immediate=yes
传参数的各类方法
在执行的playbook传参数,能够加在行尾,使用空格分隔:
tasks: - include: tasks/firewall.yml port=80 - include: tasks/firewall.yml port=3260 - include: tasks/firewall.yml port=423
还可使用yml的字典传参数:
tasks: - include: wordpress.yml vars: wp_user: timmy ssh_keys: - keys/one.txt - keys/two.txt
还能够把一条task简写成成一个类JSON的形式传参数:
- { include: wordpress.yml, wp_user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }
固然在playbook中已经定义了的参数,就不须要再显示传入值了,能够直接写成下面的:
--- - hosts: lb vars: port: 3206 remote_user: root tasks: - include: tasks/firewall.yml
Role是比include更强大灵活的代码重用和分享机制。Include相似于编程语言中的include,是重用单个文件的,功能有限。
而Role相似于编程语言中的“Package”,能够重用一组文件造成完整的功能。例如安装和配置apache,须要tasks实现安装包和拷贝模版等,httpd.conf和index.html的模版文件,和handler文件实现重起功能。这些文件均可以放在一个role里面,供不一样的playbook文件重用。
Ansible很是提倡在playbook中使用role,而且提供了一个分享role的平台Ansible Galaxy, https://galaxy.ansible.com/, 在galaxy上能够找到别人写好的role。
在ansible中,经过遵循特定的目录结构,就能够实现对role的定义:
下面的目录结构定义了一个role:名字为myrole。在site.yml,调用了这个role。
role的目录结构:
site.yml roles/ ├── myrole ├── tasks │ └── main.yml ├── handlers │ └── main.yml ├── defaults │ └── main.yml ├── vars │ └── main.yml ├── files ├── templates ├── README.md ├── meta │ └── main.yml └── tests ├── inventory └── test.yml
site.yml中调用role
--- - hosts: webservers roles: - myrole
ansible并不要求role包含上述全部的目录及文件,根据role的功能须要加入对应的目录和文件。下面是每一个目录和文件的功能。
若是 roles/x/tasks/main.yml 存在, 其中列出的 tasks 将被添加到 play 中,因此这个文件也能够视做role的入口文件,想看role作了什么操做,能够今后文件看起。
若是 roles/x/handlers/main.yml 存在, 其中列出的 handlers 将被添加到 play 中
若是 roles/x/vars/main.yml 存在, 其中列出的 variables 将被添加到 play 中
若是 roles/x/meta/main.yml 存在, 其中列出的 “角色依赖” 将被添加到 roles 列表中
roles/x/tasks/main.yml中全部tasks,能够引用 roles/x/{files,templates,tasks}中的文件,不须要指明文件的路径。
在写role的时候,通常都要包含role入口文件roles/x/tasks/main.yml,其它的文件和目录,能够根据需求选择加入。
参数在role中是如何定义的呢
定义一个带参数的role,名字是myrole,那么目录结构为:
main.yml roles role_with_var tasks main.yml
在roles/myrole/tasks/main.yml中,使用{{ }}定义的变量就能够了
--- - name: use param debug: msg="{{ param }}"
使用带参数的role
那么在main.yml就能够用以下的方法使用myrole
--- - hosts: webservers roles: - { role: myrole, param: 'Call some_role for the 1st time' } - { role: myrole, param: 'Call some_role for the 2nd time' }
或者写成YAML字典格式:
--- - hosts: webservers roles: - role: myrole param: 'Call some_role for the 1st time' - role: myrole param: 'Call some_role for the 2nd time'
role指定默认的参数
指定默认参数后,若是在调用时传参数了,那么就使用传入的参数值.若是调用的时候没有传参数,那么就使用默认的参数值.
指定默认参数很简单,以上面的role_with_var为例
main.yml roles: myrole tasks main.yml defaults main.yml
在roles/myrole/defaults/main.yml中,使用yml的字典定义语法定义param的值,以下:
param: "I am the default value"
这样在main.yml中,下面两种调用方法均可以
--- - hosts: webservers roles: - role_with_var - { role: role_with_var, param: 'I am the value from external' }
role与条件语句when一块儿执行,下面的例子中,my_role只有在RedHat系列的server上才执行。
--- - hosts: webservers roles: - { role: my_role, when: "ansible_os_family == 'RedHat'" } 一样也能够写成YAML字典格式 --- - hosts: webservers roles: - role: my_role when: "ansible_os_family == 'RedHat'" roles和tasks的执行顺序
若是一个playbook同时出现role和tasks,他们的调用顺序以下:
pre_tasks > role > tasks > post_tasks
--- - hosts: lb user: root pre_tasks: - name: pre shell: echo 'hello' roles: - { role: some_role } tasks: - name: task shell: echo 'still busy' post_tasks: - name: post shell: echo 'goodbye' 执行的结果为: PLAY [lb] ********************************************************************** TASK [setup] ******************************************************************* ok: [rhel7u3] TASK [pre] ********************************************************************* changed: [rhel7u3] TASK [some_role : some role] *************************************************** ok: [rhel7u3] => { "msg": "Im some role" } TASK [task] ******************************************************************** changed: [rhel7u3] TASK [post] ******************************************************************** changed: [rhel7u3] PLAY RECAP ********************************************************************* rhel7u3 : ok=5 changed=3 unreachable=0 failed=0
三、tags的用法
若是playbook文件比较大,在执行的时候只是想执行部分功能,这个时候没有有解决方案呢?Playbook提供了tags便签能够实现部分运行。
tags的基本用法
例如,文件example.yml如何所示,标记了两个tag:packages和configuration
tasks: - yum: name={{ item }} state=installed with_items: - httpd tags: - packages - name: copy httpd.conf template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf tags: - configuration - name: copy index.html template: src=templates/index.html.j2 dest=/var/www/html/index.html tags: - configuration
那么咱们在执行的时候,若是不加任何tag参数,那么会执行全部的tasks
$ ansible-playbook example.yml
指定执行安装部分的tasks,则能够利用关键字tags
$ ansible-playbook example.yml --tags "packages"
指定不执行packages部分的task,则能够利用关键字skip-tags
$ ansible-playbook example.yml --skip-tags "configuration"
特殊的Tags:“always”
tags的名字是用户自定义的,可是若是你把tags的名字定义为“always”,那么就有点特别了。只要在执行playbook时,没有明确指定不执行always tag,那么它就会被执行。
在下面的例子中,即便你只指定执行packages,那么always也会被执行。
tasks: - debug: msg="Always print this debug message" tags: - always - yum: name= state=installed with_items: - httpd tags: - packages - template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf tags: - configuration
指定运行packages时,仍是会执行always tag对应的tasks
$ ansible-playbook tags_always.yml --tags "packages" “tagged”,“untagged”和“all” tasks: - debug: msg="I am not tagged" tags: - tag1 - debug: msg="I am not tagged" 分别指定--tags为“tagged”,“untagged”和“all”试下效果吧: $ ansible-playbook tags_tagged_untagged_all.yml --tags tagged $ ansible-playbook tags_tagged_untagged_all.yml --tags untagged $ ansible-playbook tags_tagged_untagged_all.yml --tags all
在include中和role中使用tags
include语句指定执行的tags的语法:
- include: foo.yml tags: [web,foo]
调用role中的tags的语法为:
roles: - { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }
四、playbook文件加密
ansible-vault命令能够对配置文件进行基于密码的加密,防止敏感信息泄漏。
加密已存在文件
$ ansible-vault encrypt site.yml
加密并建立文件
$ ansible-vault create filename
执行加密后的playbook
$ ansible-playbook site.yml --ask-vault-pass
解密
$ ansible-vault decrypt site.yml
5、编译安装nginx的playbook的实例
playbooks目录结构:
$ tree playbooks playbooks ├── group_vars ├── roles │ └── nginx │ ├── files │ │ ├── nginx-1.8.0.tar.gz │ │ └── pcre-8.33.tar.gz │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ └── default.conf └── site.yml
yml文件内内容以下:
$ cat playbooks/site.yml --- - name: Install Nginx hosts: test user: admin roles: - nginx $ cat playbooks/roles/nginx/tasks/main.yml --- - name: Copy nginx software copy: src=nginx-1.8.0.tar.gz dest=/app/admin/soft/nginx-1.8.0.tar.gz - name: Copy pcre software copy: src=pcre-8.33.tar.gz dest=/app/admin/soft/pcre-8.33.tar.gz - name: Install Pcre shell: cd /app/admin/soft;tar zxf pcre-8.33.tar.gz;cd pcre-8.33;./configure;make;make install sudo: yes - name: Install Nginx shell: cd /app/admin/soft;tar zxf nginx-1.8.0.tar.gz;cd nginx-1.8.0;./configure --user=admin --group=app --prefix=/app/admin/nginx --with-http_stub_status_module --with-http_ssl_module;make;make install - name: Copy nginx configuration template: src=default.conf dest=/app/admin/nginx/conf/nginx.conf notify: restart nginx [admin@wts-vm-01 ~]$ cat playbooks/roles/nginx/handlers/main.yml --- - name: restart nginx shell: /app/admin/nginx/sbin/nginx sudo: yes
6、其它官方例子Ansible官方提供了一些比较经常使用的、通过测试的Playbook例子:https://github.com/ansible/ansible-examplesPlaybook分享平台:https://galaxy.ansible.com/