Playbooks与Ad-Hoc相比,是一种彻底不一样的运用Ansible的方式,并且是很是之强大的;也是系统ansible命令的集合,其利用yaml语言编写,运行过程,ansbile-playbook命令根据自上而下的顺序依次执行。
简单来讲,Playbooks 是一种简单的配置管理系统与多机器部署系统的基础。与现有的其余系统有不一样之处,且很是适合于复杂应用的部署。html
同时,Playbooks开创了不少特性,它能够容许你传输某个命令的状态到后面的指令,如你能够从一台机器的文件中抓取内容并附为变量,而后在另外一台机器中使用,这使得你能够实现一些复杂的部署机制,这是ansible命令没法实现的。python
Playbooks可用于声明配置,更强大的地方在于,在Playbooks中能够编排有序的执行过程,甚至于作到在多组机器间,来回有序的执行特别指定的步骤。而且能够同步或异步的发起任务。mysql
咱们使用Ad-Hoc时,主要是使用 /usr/bin/ansible 程序执行任务.而使用Playbooks时,更可能是将之放入源码控制之中,用之推送你的配置或是用于确认你的远程系统的配置是否符合配置规范。linux
在如右的连接中:ansible-examples repository,有一些整套的Playbooks,它们阐明了上述的这些技巧。git
playbooks 的格式是yaml,语法作到最小化,意在避免 playbooks 成为一种编程语言或是脚本,但它也并非一个配置模型或过程的模型。github
playbook是由一个或多个“play”组成的列表。play的主要功能在于将事先归并为一组的主机装扮成事先经过Ansible中的tasks定义好的角色(play的内容被称为tasks,即任务)。从根本上来说所谓tasks无非是调用Ansible的一个module。将多个“play”组织在一个playbook中便可以让它们联同起来按事先编排的机制一同工做。web
“plays”算是一个类比,能够经过多个plays告诉系统作不一样的事情,不只是定义一种特定的状态或模型。也能够在不一样时间运行不一样的plays。正则表达式
对于初学者,这里有一个playbook,其中仅包含一个play:sql
--- - hosts: webservers vars: http_port: 80 max_clients: 200 remote_user: root 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
第一行中,文件开头为 ---;这是YAML将文件解释为正确的文档的要求。YAML容许多个“文档”存在于一个文件中,每一个“文档”由 --- 符号分割,但Ansible只须要一个文件存在一个文档便可,所以这里须要存在于文件的开始行第一行。shell
YAML对空格很是敏感,并使用空格来将不一样的信息分组在一块儿,在整个文件中应该只使用空格而不使用制表符,而且必须使用一致的间距,才能正确读取文件。相同缩进级别的项目被视为同级元素。
以 - 开头的项目被视为列表项目。做为散列或字典操做,它具备key:value格式的项。YAML文档基本上定义了一个分层的树结构,其中位于左侧是包含的元素。YAML文件扩展名一般为.yaml或者.yml。
接下来,咱们将分别讲解 playbook 语言的多个特性
Playbook主要有如下四部分构成:
而Playbook对应的目录层有五个,分别以下:
通常所需的目录层有:(视状况可变化)
接下来,咱们将分别介绍构成playbook的四层结构。
咱们能够为playbook中的每个play,个别的选择操做的目标机器是哪些,以哪一个用户身份去完成要执行的步骤(called tasks)
--- - hosts: webservers remote_user: root tasks: - name: test connection remote_user: yourname sudo: yes
playbook中的每个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。
hosts:用于指定要执行指定任务的主机其能够是一个或多个,由逗号为分隔符分隔主机组
remote_user:用于指定远程主机上的执行任务的用户。不过remote_user也可用于各tasks中。也能够经过指定其经过sudo的方式在远程主机上执行任务其可用于play全局或某任务。此外甚至能够在sudo时使用sudo_user指定sudo时切换的用户
user:与remote_user相同
sudo:若是设置为yes,执行该任务组的用户在执行任务的时候,获取root权限
sudo_user:若是设置user为bob,sudo为yes,sudo_user为tom时,则bob用户在执行任务时会得到tom用户的权限
connection:经过什么方式链接到远程主机,默认为ssh
gather_facts:除非明确说明不须要在远程主机上执行setup模块,不然默认自动执行。若是确实不须要setup模块传递过来的变量,则能够将该选项设置为False
说明:
每个play包含了一个tasks列表(任务列表)。
任务列表中的各任务按次序逐个在hosts中指定的全部主机上执行即在全部主机上完成第一个任务后再开始第二个。在自上而下运行某playbook时若是中途发生错误,全部已执行任务都将回滚,所以在更正playbook后从新执行便可。
每个tasks必须有一个名称name,这样在运行playbook时,从其输出的任务执行信息中能够很好的辨别出是属于哪个tasks的。若是没有定义name,“action”的值将会用做输出信息中标记特定的tasks。
tasks的目的是使用指定的参数执行模块,而在模块参数中可使用变量。模块执行是幂等的,这意味着屡次执行是安全的,由于其结果均一致。每一个tassk都应该有其name用于playbook的执行结果输出,建议其内容尽量清晰地描述任务执行步骤。若是未提供name则action的结果将用于输出。
若是要声明一个tasks,之前有一种格式:“action: module options”(可能在一些老的playbooks中还能见到)。如今推荐使用更常见的格式:“module: options”。
下面是一种基本的taska的定义,service moudle使用key=value格式的参数,这也是大多数module使用的参数格式:
tasks: - name: make sure apache is running service: name=httpd state=running # 在众多模块中,只有command和shell模块仅须要给定一个列表而无需使用“key=value”格式以下: tasks: - name: disable selinux command: /sbin/setenforce 0 # 使用command module和shell module时,咱们须要关心返回码信息,若是有一条命令,它的成功执行的返回码不是0,咱们能够这样作: tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand || /bin/true # 或者使用ignore_errors来忽略错误信息 tasks: - name: run this command and ignore the result shell: /usr/bin/somecommand ignore_errors: True # 若是action行看起来太长,可使用space(空格)或者indent(缩进)隔开连续的一行: tasks: - name: Copy ansible inventory file to client copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts owner=root group=root mode=0644
上面咱们曾提到过,module具备“幂等”性,因此当远程主机被人改动时,能够重放playbooks达到恢复的目的。playbooks自己能够识别这种改动,而且有一个基本的event system(事件系统),能够响应这种改动。
(当发生改动时)“notify”这个actions会在playbook的每个tasks结束时被触发,并且即便有多个不一样的tasks通知改动的发生,“notify” actions只会被触发一次。这样能够避免屡次有改变发生时每次都执行指定的操做,取而代之仅在全部的变化发生完成后一次性地执行指定操做。
在“notify”中列出的操做称为handlers,即“notify”中调用handlers中定义的操做。
说明:
在“notify”中定义内容必定要和tasks中定义的 - name 内容同样,这样才能达到触发的效果,不然会不生效。
举例来讲,好比多个resources指出由于一个配置文件被改动,因此apache须要从新启动,可是从新启动的操做只会被执行一次。
这里有一个例子,当一个文件的内容被改动时,重启两个services:
- name: template configuration file template: src=template.j2 dest=/etc/foo.conf notify: - restart memcached - restart apache
“notify”下列出的便是 handlers。
handlers也是一些tasks的列表,经过名字来引用,它们和通常的tasks并无什么区别。handlers是由通知者进行notify,若是没有被notify,handlers不会执行。无论有多少个通知者进行了notify,等到play中的全部tasks执行完成以后,handlers也只会被执行一次。
这里是一个 handlers 的示例:
handlers: - name: restart memcached service: name=memcached state=restarted - name: restart apache service: name=apache state=restarted
Handlers最佳的应用场景是用来重启服务,或者触发系统重启操做。除此之外不多用到了。
说明:handlers会按照声明的顺序执行。
tags用于让用户选择运行或略过playbook中的部分代码。ansible具备幂等性,所以会自动跳过没有变化的部分,即使如此,有些代码为测试其确实没有发生变化的时间依然会很是的长。此时若是确信其没有变化就能够经过tags跳过这些代码片段。
经过playbook添加用户示例
(1)给远程主机添加用户test
[root@Ansible ~]# vim user.yml --- - name: create user hosts: all user: root gather_facts: False vars: - user: test tasks: - name: create user user: name={{ user }}
上面的playbook 实现的功能是新增一个用户:
name:对该playbook实现的功能作一个概述,后面执行过程当中,会打印name变量的值
hosts:指定了对哪些主机进行参做
user:指定了使用什么用户登陆远程主机操做
gather_facts:指定了在如下任务部分执行前,是否先执行setup模块获取主机相关信息,这在后面的task会使用到setup获取的信息时用到
vars:指定了变量,这里指定了一个user变量,其值为test,须要注意的是,变量值必定要用引号引发来
tasks:指定了一个任务,其下面的name参数一样是对任务的描述,在执行过程当中会打印出来。user提定了调用user模块,name是user模块里的一个参数,而增长的用户名字调用了上面user变量的值
查看执行结果以下:
[root@Ansible ~]# ansible-playbook user.yml
(2)删除远程主机test的帐号
只需user: name="{{ user }}" state=absent remove=yes 便可
[root@Ansible ~]# vim user.yml --- - name: create user hosts: all user: root gather_facts: False vars: - user: test tasks: - name: create user user: name={{ user }} state=absent remove=yes [root@Ansible ~]# ansible all -m command -a "id test"
(3)经过playbook 安装apache
安装 httpd 服务,将本地准备好的配置文件 copy 过去,而且启动服务
[root@Ansible ~]# vim apache.yml --- - hosts: all remote_user: root gather_facts: False tasks: - name: install apache on CentOS 7 yum: name=httpd state=present - name: copy httpd conf copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf - name: start apache service service: name=httpd state=started [root@Ansible ~]# ansible-playbook apache.yml [root@Ansible ~]# ansible all -m shell -a "lsof -i:80"
(4)经过playbook 安装apache(修改端口,并带有vars变量)
将httpd.conf监听的端口改成8080,而后从新覆盖配置文件,当这个配置文件发生改变时,就触发handler进行服务重启
notify 这个 action可用于在每一个play的最后被触发,这样能够避免屡次有改变发生时每次都执行指定的操做,notify中列出的操做称为handler
[root@Ansible ~]# vim apache.yml --- - hosts: all remote_user: root gather_facts: False vars: src_http_dir: /etc/httpd/conf dest_http_dir: /etc/httpd/conf tasks: - name: install apache on CentOS 7 yum: name=httpd state=present - name: copy httpd conf copy: src={{ src_http_dir }}/httpd.conf dest={{ dest_http_dir }}/httpd.conf notify: - systemctl restart httpd - name: start apache service service: name=httpd state=restarted handlers: - name: restart apache service: name=httpd state=restarted [root@Ansible ~]# vim /etc/httpd/conf/httpd.conf 42 Listen 80 --》 修改成8080 [root@Ansible ~]# ansible-playbook apache.yml [root@Ansible ~]# ansible all -m shell -a "lsof -i:8080"
Playbook的模块与在Ansible命令行下使用的模块有一些不一样。这主要是由于在playbook中会使用到一些facts变量和一些经过setup模块从远程主机上获取到的变量。有些模块无法在命令行下运行,就是由于它们须要这些变量。并且即便那些能够在命令行下工做的模块也能够经过playbook的模块获取一些更高级的功能。
具体模块可参考官网(http://docs.ansible.com/ansible/latest/list_of_all_modules.html)。
这里从官方分类的模块里选择最经常使用的一些模块进行介绍。
(1)简介
(2)参数
attributes(2.3后新增):文件或目录的属性
backup:若是原目标文件存在,则先备份目标文件
block_end_string(2.4后新增):标记块结束的字符串
block_start_string(2.4后新增):标记块的开始的字符串
dest:目标文件路径
follow(2.4后新增):是否遵循目标中的文件连接
force:是否强制覆盖,默认为yes
group:目标文件或目录的所属组
owner:目标文件或目录的所属主
mode:目标文件的权限。对于那些习惯于/usr/bin/chmod的记住,模式其实是八进制数字(如0644or 01777)。离开前导零可能会有意想不到的结果。从版本1.8开始,能够将模式指定为符号模式(例如u+rwx或u=rw,g=r,o=r)
newline_sequence(2.4后新增):指定用于模板文件的换行符序列(\n、\r、\r\n)
src:源模板文件路径
trim_blocks(2.4后新增):若是这设置为True,则删除块后的第一个换行符
validate:在复制以前经过命令验证目标文件,若是验证经过则复制
variable_end_string(2.4后新增):标记打印语句结束的字符串
variable_start_string(2.4后新增):标记打印语句开头的字符串
(3)示例
# 官方简单示例 - template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode=0644 - template: src=/mytemplates/foo.j2 dest=/etc/file.conf owner=bin group=wheel mode="u=rw,g=r,o=r" - template: src=/mine/sudoers dest=/etc/sudoers validate='visudo -cf %s' playbook的引用该模板配置文件的方法示例: - name: Setup BIND host: all tasks: - name: configure BIND template: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf owner=root group=root mode=0644 # 或者是这样 - template: src: /etc/httpd/conf/httpd.conf dest: /etc/file.conf owner: root group: root mode: 0644 # 建立DOS样式文本文件 - template: src: config.ini.j2 dest: /share/windows/config.ini newline_sequence: '\r\n'
(1)简介
(2)参数
cacheable(2.4后新增):可缓存
key_value:该set_fact模块将key=value做为变量,设置在剧本范围中
(3)示例
配置mysql innodb buffer size的示例
[root@Ansible ~]# echo "# Configure the buffer pool" >> /etc/my.cnf [root@Ansible ~]# echo "innodb_buffer_pool_size = {{ innodb_buffer_pool_size_mb|int }}M" >> /etc/my.cnf [root@Ansible ~]# vim set_fact.yml --- - name: Configure Mariadb hosts: all tasks: - name: install Mariadb yum: name=mariadb-server state=installed - name: Calculate InnoDB buffer pool size set_fact: innodb_buffer_pool_size_mb={{ ansible_memtotal_mb / 2 }} - name: Configure Mariadb template: src=/etc/my.cnf dest=/etc/my.cnf owner=root group=root mode=0644 - name: Start Mariadb service: name=mariadb state=started enabled=yes handlers: - name: restart Mariadb service: name=mariadb state=restarted [root@Ansible ~]# ansible-playbook set_fact.yml
在远程主机上查看该配置
[root@Client ~]# vim /etc/my.cnf
(1)简介
(2)参数
echo(2.5后增长):控制键入时是否显示键盘输入。若是设置了“秒”或“分钟”,则不起做用
minutes:暂停多少分钟
seconds:暂停多少秒
prompt:打印一串信息提示用户操做
(3)示例
# 暂停5分钟以创建应用程序缓存。 - pause: minutes: 5 # 有用的提醒,提醒您注意更新后的内容。 - pause: prompt: "Make sure org.foo.FooOverload exception is not present" # 暂停之后得到一些信息。 - pause: prompt: "Enter a secret" echo: no
(1)简介
(2)参数
active_connection_states(2.3后新增) :被计为活动链接的TCP链接状态列表
connect_timeout:在下一个任务执行以前等待链接的超时时间
delay:等待一个端口或者文件或者链接到指定的状态时,默认超时时间为300秒,在这等待的300s的时间里,wait_for模块会一直轮询指定的对象是否到达指定的状态,delay即为多长时间轮询一次状态
exclude_hosts(1.8后新增):在查找状态的活动TCP链接时要忽略的主机或IP的列表drained
host:wait_for模块等待的主机的地址,默认为127.0.0.1
msg(2.4后新增):这会覆盖正常的错误消息,使其不符合所需的条件
port:wait_for模块等待的主机的端口
path:文件路径,只有当这个文件存在时,下一任务才开始执行,即等待该文件建立完成
search_regex(1.4后新增):能够用来匹配文件或套接字链接中的字符串。默认为多行正则表达式
sleep(2.3后新增):检查之间睡眠的秒数,在2.3以前,这被硬编码为1秒
state:等待的状态,即等待的文件或端口或者链接状态达到指定的状态时,下一个任务开始执行。当等的对象为端口时,状态有started,stoped,即端口已经监听或者端口已经关闭;当等待的对象为文件时,状态有present或者started,absent,即文件已建立或者删除;当等待的对象为一个链接时,状态有drained,即链接已创建。默认为started
timeout:wait_for的等待的超时时间,默认为300秒
(3)示例
- name: create task hosts: all tasks: # 等待8080端口已正常监听,才开始下一个任务,直到超时 - wait_for: port=8080 state=started # 等待8000端口正常监听,每隔10s检查一次,直至等待超时 - wait_for: port=8081 delay=10 # 等待8000端口直至有链接创建 - wait_for: host=0.0.0.0 port=8000 delay=10 state=drained # 等待8000端口有链接创建,若是链接来自192.168.8.66或者192.168.8.8,则忽略 - wait_for: host=0.0.0.0 port=8000 state=drained exclude_hosts=192.168.8.66,192.168.8.8 # 等待/tmp/foo文件已建立 - wait_for: path=/tmp/foo # 等待/tmp/foo文件已建立,并且该文件中须要包含completed字符串 - wait_for: path=/tmp/foo search_regex=completed # 等待/var/lock/file.lock被删除 - wait_for: path=/var/lock/file.lock state=absent # 等待指定的进程被销毁 - wait_for: path=/proc/3466/status state=absent # 等待openssh启动,10s检查一次 - local_action: wait_for port=22 host="{{ ansible_ssh_host | default(inventory_hostname) }}" search_regex=OpenSSH delay=10
(1)简介
(2)参数
attributes(2.3后新增):文件或目录应的属性
backup:建立一个备份文件(若是yes),包括时间戳信息
decrypt(2.4后新增):控制使用保管库对源文件进行自动解密
delimiter(1.4后新增):分隔文件内容的分隔符
dest:使用全部源文件的链接建立的文件,合并后的大文件路径
group:合并后的大文件的所属组
owner:合并后的大文件的所属主
ignore_hidden(2.0后新增):组装时,是否忽略隐藏文件,默认为no
mode:合并后的大文件的权限。对于那些习惯于/usr/bin/chmod的记住,模式其实是八进制数字(如0644or 01777)。离开前导零可能会有意想不到的结果。从版本1.8开始,能够将模式指定为符号模式(例如u+rwx或u=rw,g=r,o=r)
regexp:在regex匹配文件名时汇编文件。若是未设置,则全部文件都被组合。全部“”(反斜杠)必须转义为“”才能符合yaml语法
remote_src(1.4后新增):若是为False,它将在本地主机上搜索src,若是为True,它将转到src的远程主机。默认值为True
src:源文件(即零散文件)的路径
validate(2.0后新增):与template的validate相同,指定命令验证文件
(3)示例
# 指定源文件(即零散文件)的路径,合并一个目标文件someapp.conf - assemble: src: /etc/someapp/fragments dest: /etc/someapp/someapp.conf # 指定一个分隔符,将在每一个片断之间插入 - assemble: src: /etc/someapp/fragments dest: /etc/someapp/someapp.conf delimiter: '### START FRAGMENT ###' # 在SSHD传递验证后,将新的“sshd_config”文件复制到目标地址 - assemble: src: /etc/ssh/conf.d/ dest: /etc/ssh/sshd_config validate: '/usr/sbin/sshd -t -f %s'
一、简介
二、参数
groups:要添加主机至指定的组,以逗号分隔
name:要添加的主机名或IP地址,包含冒号和端口号
三、示例
# 添加主机到webservers组中,主机的变量foo的值为42 - name: add host to group 'just_created' with variable foo=42 add_host: name: "{{ ip_from_ec2 }}" groups: just_created foo: 42 # 将主机添加到多个组 - name: add host to multiple groups add_host: hostname: "{{ new_ip }}" groups: - group1 - group2 # 向主机添加一个非本地端口的主机 - name: add a host with a non-standard port local to your machines add_host: name: "{{ new_ip }}:{{ new_port }}" # 添加一个经过隧道到达的主机别名(Ansible <= 1.9) - name: add a host alias that we reach through a tunnel (Ansible <= 1.9) add_host: hostname: "{{ new_ip }}" ansible_ssh_host: "{{ inventory_hostname }}" ansible_ssh_port: "{{ new_port }}" # 添加一个经过隧道到达的主机别名(Ansible >= 2.0) - name: add a host alias that we reach through a tunnel (Ansible >= 2.0) add_host: hostname: "{{ new_ip }}" ansible_host: "{{ inventory_hostname }}" ansible_port: "{{ new_port }}"
(1)简介
(2)参数
key:其值将被用做组的变量
parents(2.4后新增):父组的列表
(3)示例
# 建立主机组 - group_by: key: machine_{{ ansible_machine }} # 建立相似“kvm-host”的主机组 - group_by: key: virt_{{ ansible_virtualization_type }}_{{ ansible_virtualization_role }} # 建立嵌套主机组 - group_by: key: el{{ ansible_distribution_major_version }}-{{ ansible_architecture }} parents: - el{{ ansible_distribution_major_version }}
(1)简介
(2)参数
msg:调试输出的消息,打印自定义消息
var:将某个任务执行的输出做为变量传递给debug模块,debug会直接将其打印输出
verbosity(2.1后新增):debug的运行调试级别
(3)示例
# 为每一个主机打印IP地址和网关 - debug: msg: "System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}" - debug: msg: "System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}" when: ansible_default_ipv4.gateway is defined - shell: /usr/bin/uptime register: result - debug: var: result # 直接将上一条指令的结果做为变量传递给var,由debug打印出result的值 verbosity: 2 - name: Display all variables/facts known for a host debug: var: hostvars[inventory_hostname] verbosity: 4
(1)简介
(2)参数
msg:终止前打印出信息,用于执行失败的自定义消息
(3)示例
# 执行失败钱打印出自定义信息 - fail: msg: "The system may not be provisioned according to the CMDB status." when: cmdb_status != "to-be-staged"
在使用Ansible作自动化运维的时候,免不了的要重复执行某些操做,如:添加几个用户,建立几个MySQL用户并为之赋予权限,操做某个目录下全部文件等等。好在playbook支持循环语句,可使得某些需求很容易并且很规范的实现。
如下主要来自他人分享。
with_items是playbooks中最基本也是最经常使用的循环语句。
示例:
tasks: - name:Secure config files file: path=/etc/{{ item }} mode=0600 owner=root group=root with_items: - my.cnf - shadow - fstab # 或 with_items:"{{ somelist }}"
上面的例子说明在/etc下建立权限级别为0600,属主属组都是root三个文件,分别为my.cnf、shadow、fstab
使用with_items迭代循环的变量能够是个单纯的列表,也能够是一个较为复杂 的数据结果,如字典类型:
tasks: - name: add several users user: name={{ item.name }} state=present groups={{ item.groups }} with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' }
示例:
tasks: - 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', 'providerdb' ]
item[0]是循环的第一个列表的值['alice','bob']。
item[1]是第二个列表的值。表示循环建立alice和bob两个用户,而且为其赋予在三个数据库上的全部权限。
也能够将用户列表事先赋值给一个变量:
tasks: - name: here, 'users' contains the above list of employees mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo with_nested: - "{{users}}" - [ 'clientdb', 'employeedb', 'providerdb' ]
with_dict能够遍历更复杂的数据结构:
假若有以下变量内容:
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: - name:Make key directory file: path=/root/.sshkeys ensure=directory mode=0700 owner=root group=root - name:Upload public keys copy: src={{ item }} dest=/root/.sshkeys mode=0600 owner=root group=root with_fileglob: - keys/*.pub - name:Assemble keys into authorized_keys file assemble: src=/root/.sshkeys dest=/root/.ssh/authorized_keysmode=0600 owner=root group=root
假如如今须要遍历一个用户列表,并建立每一个用户,并且还须要为每一个用户配置以特定的SSH key登陆。变量文件内容以下:
users: - name: alice authorized: - /tmp/alice/onekey.pub - /tmp/alice/twokey.pub mysql: password: mysql-password hosts: - "%" - "127.0.0.1" - "::1" - "localhost" privs: - "*.*:SELECT" - "DB1.*:ALL" - name: bob authorized: - /tmp/bob/id_rsa.pub mysql: password: other-mysql-password hosts: - "db1" privs: - "*.*:SELECT" - "DB2.*:ALL" # playbook中定义以下: - user: name={{ item.name }} state=present generate_ssh_key=yes with_items: "`users`" - authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'" with_subelements: - users - authorized # 也能够遍历嵌套的子列表: - name: Setup MySQL users mysql_user: name={{ item.0.name }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join('/') }} with_subelements: - users - mysql.hosts
with_sequence能够生成一个自增的整数序列,能够指定起始值和结束值,也能够指定增加步长。 参数以key=value的形式指定,format指定输出的格式。数字能够是十进制、十六进制、八进制:
- hosts: all tasks: # create groups - group: name=evens state=present - group: name=odds state=present # create some test users - user: name={{ item }} state=present groups=evens with_sequence: start=0 end=32 format=testuser%02d # create a series of directories with even numbers for some reason - file: dest=/var/stuff/{{ item }} state=directory with_sequence: start=4 end=16 stride=2 # stride用于指定步长 # a simpler way to use the sequence plugin # create 4 groups - group: name=group{{ item }} state=present with_sequence: count=4
从列表中随机取一个值:
- debug: msg={{ item }} with_random_choice: - "go through the door" - "drink from the goblet" - "press the red button" - "do nothing"
示例:
- action: shell /usr/bin/foo register: result until: result.stdout.find("all systems go") != -1 retries: 5 delay: 10
重复执行shell模块,当shell模块执行的命令输出内容包含"all systems go"的时候中止。重试5次,延迟时间10秒。retries默认值为3,delay默认值为5。任务的返回值为最后一次循环的返回结果。
在循环中使用register时,保存的结果中包含results关键字,该关键字保存模块执行结果的列表。
- shell: echo "{{ item }}" with_items: - one - two register: echo
变量echo内容以下:
{ "changed": true, "msg": "All items completed", "results": [ { "changed": true, "cmd": "echo \"one\" ", "delta": "0:00:00.003110", "end": "2013-12-19 12:00:05.187153", "invocation": { "module_args": "echo \"one\"", "module_name": "shell" }, "item": "one", "rc": 0, "start": "2013-12-19 12:00:05.184043", "stderr": "", "stdout": "one" }, { "changed": true, "cmd": "echo \"two\" ", "delta": "0:00:00.002920", "end": "2013-12-19 12:00:05.245502", "invocation": { "module_args": "echo \"two\"", "module_name": "shell" }, "item": "two", "rc": 0, "start": "2013-12-19 12:00:05.242582", "stderr": "", "stdout": "two" } ] }
遍历注册变量的结果:
- name: Fail if return code is not 0 fail: msg: "The command ({{ item.cmd }}) did not have a 0 return code" when: item.rc != 0 with_items: "`echo`.`results`"
示例:
- hosts: webservers remote_user: root vars: alpha: [ 'a','b','c','d'] numbers: [ 1,2,3,4 ] tasks: - debug: msg="{{ item.0 }} and {{ item.1 }}" with_together: - "{{ alpha }}" - "{{ numbers }}"
输出的结果为:
ok: [192.168.1.65] => (item=['a', 1]) => { "item": [ "a", 1 ], "msg": "a and 1" } ok: [192.168.1.65] => (item=['b', 2]) => { "item": [ "b", 2 ], "msg": "b and 2" } ok: [192.168.1.65] => (item=['c', 3]) => { "item": [ "c", 3 ], "msg": "c and 3" } ok: [192.168.1.65] => (item=['d', 4]) => { "item": [ "d", 4 ], "msg": "d and 4" }
loop模块通常在下面的场景中使用:
在有的时候play的结果依赖于变量、fact或者是前一个任务的执行结果,从而须要使用到条件语句。
如下主要来自他人分享。
有的时候在特定的主机须要跳过特定的步骤,例如在安装包的时候,须要指定主机的操做系统类型,或者是当操做系统的硬盘满了以后,须要清空文件等,可使用when语句来作判断 。when关键字后面跟着的是python的表达式,在表达式中你可以使用任何的变量或者fact,当表达式的结果返回的是false,便会跳过本次的任务。
(1)基本用法
--- - name: Install VIM hosts: all tasks: - name:Install VIM via yum yum: name=vim-enhanced state=installed when: ansible_os_family =="RedHat" - name:Install VIM via apt apt: name=vim state=installed when: ansible_os_family =="Debian" - name: Unexpected OS family debug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yes when: not ansible_os_family =="RedHat" or ansible_os_family =="Debian"
条件语句还有一种用法,它还可让你当达到必定的条件的时候暂停下来,等待你的输入确认。通常状况下,当ansible遭遇到error时,它会直接结束运行。那其实你能够当遭遇到不是预期的状况的时候给使用pause模块,这样可让用户本身决定是否继续运行任务:
- name: pause for unexpected conditions pause: prompt="Unexpected OS" when: ansible_os_family !="RedHat"
(2)在when中使用jinja2的语法
tasks: - command: /bin/false register: result # 将命令执行的结果传递给result变量 ignore_errors: True # 忽略错误 - command: /bin/something when: result|failed # 若是注册变量的值 是任务failed则返回true - command: /bin/something_else when: result|success # 若是注册变量的值是任务success则返回true - command: /bin/still/something_else when: result|skipped # 若是注册变量的值是任务skipped则返回true - command: /bin/foo when: result|changed # 若是注册变量的值是任务changed则返回true - hosts: all user: root vars: epic: true tasks: - shell: echo "This certainly is epic!" when: epic - shell: echo "This certainly is not epic!" when: not epic
(3)若是变量不存在,则能够经过jinja2的'defined'命令跳过
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
(4)when在循环语句中的使用方法
tasks: - command: echo {{ item }} with_items: [ 0, 2, 4, 6, 8, 10 ] when: item > 5六、在include和roles中使用when: # 在include中使用的示例:- include: tasks/sometasks.yml when: "'reticulating splines' in output" # 在roles中使用的示例:- hosts: webservers roles: - { role: debian_stock_config, when: ansible_os_family == 'Debian' }
有些时候,你也许想在一个Playbook中以不一样的方式作事,好比说在debian和centos上安装apache,apache的包名不一样,除了when语句,还可使用下面的示例来解决:
--- - hosts: all remote_user: root vars_files: - "vars/common.yml" - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ] tasks: - name: make sure apache is running service: name={{ apache }} state=running
不少不一样的yml文件只是包含键和值,以下:
[root@Ansible ~]# for vars/CentOS.yml apache: httpd somethingelse: 42
若是操做系统是“CentOS”,Ansible导入的第一个文件将是“vars/CentOS.yml”,紧接着 是“/var/os_defaults.yml”,若是这个文件不存在。并且在列表中没有找到,就会报错。 在Debian系统中,最早查看的将是“vars/Debian.yml”而不是“vars/CentOS.yml”,若是没找到,则寻找默认文件“vars/os_defaults.yml”。
有些时候,咱们想基于不一样的操做系统,选择不一样的配置文件,及配置文件的存放路径,能够借助with_first_found来解决:
- name: template a file template: src={{ item }} dest=/etc/myapp/foo.conf with_first_found: - files: - {{ ansible_distribution }}.conf - default.conf paths: - search_location_one/somedir/ - /opt/other_location/somedir/
failed_when实际上是ansible的一种错误处理机制,是由fail模块使用了when条件语句的组合效果。示例以下:
- name: this command prints FAILED when it fails command: /usr/bin/example-command -x -y -z register: command_result failed_when: "'FAILED' in command_result.stderr"
咱们也能够直接经过fail模块和when条件语句,写成以下:
- name: this command prints FAILED when it fails command: /usr/bin/example-command -x -y -z register: command_result ignore_errors: True - name: fail the play if the previous command did not succeed fail: msg="the command failed" when: "'FAILED' in command_result.stderr"
当咱们控制一些远程主机执行某些任务时,当任务在远程主机上成功执行,状态发生更改时,会返回changed状态响应,状态未发生更改时,会返回OK状态响应,当任务被跳过期,会返回skipped状态响应。咱们能够经过changed_when来手动更改changed响应状态。示例以下:
- shell: /usr/bin/billybass --mode="take me to the river" register: bass_result changed_when: "bass_result.rc != 2" # 只有该条task执行之后,bass_result.rc的值不为2时,才会返回changed状态 # 永远不会报告“改变”的状态 - shell: wall 'beep' changed_when: False # 当changed_when为false时,该条task在执行之后,永远不会返回changed状态