YAML_01 YAML语法和playbook写法
ansible的playbook采用yaml语法,它简单地实现了json格式的事件描述。yaml之于json就像markdown之于html一样,极度简化了json的书写。在学习ansible playbook之前,很有必要把yaml的语法格式、引用方式做个梳理。
1.1 初步说明
以一个简单的playbook为例,说明yaml的基本语法。
---
    - hosts: 192.168.100.59,192.168.100.65
      remote_user: root
      pre_tasks:
        - name: set epel repo for Centos 7
          yum_repository:
            name: epel7
            description: epel7 on CentOS 7
            baseurl: http://mirrors.aliyun.com/epel/7/$basearch/
            gpgcheck: no
            enabled: True
      tasks:
# install nginx and run it
        - name: install nginx
          yum: name=nginx state=installed update_cache=yes
        - name: start nginx
          service: name=nginx state=started
      post_tasks:
        - shell: echo "deploy nginx over"
          register: ok_var
        - debug: msg="{{ ok_var.stdout }}"
yaml文件以
---开头,以表明这是一个yaml文件,就像xml文件在开头使用<?xml version="1.0" encoding="utf-8"?>宣称它是xml文件一样。但即使没有使用---开头,也不会有什么影响。yaml中使用"#"作为注释符,可以注释整行,也可以注释行内从"#"开始的内容。
yaml中的字符串通常不用加任何引号,即使它包含了某些特殊字符。但有些情况下,必须加引号,最常见的是在引用变量的时候。具体见后文。
关于布尔值的书写格式,即true/false的表达方式。其实playbook中的布尔值类型非常灵活,可分为两种情况:
- 模块的参数: 这时布尔值作为字符串被ansible解析。接受yes/on/1/true/no/off/0/false,这时被ansible解析。例如上面示例中的
update_cache=yes。 非模块的参数: 这时布尔值被yaml解释器解析,完全遵循yaml语法。接受不区分大小写的true/yes/on/y/false/no/off/n。例如上面的
gpgcheck=no和enabled=True。建议遵循ansible的官方规范,模块的布尔参数采用yes/no,非模块的布尔参数采用True/False。
1.2 列表
使用"- "(减号加一个或多个空格)作为列表项,也就是json中的数组。yaml的列表在playbook中极重要,必须得搞清楚它的写法。
例如:
 - zhangsan
 - lisi
 - wangwu
还支持内联写法:使用中括号。
[zhangsan,lisi,wangwu]
它们等价于json格式的:
[
    "zhangsan",
    "lisi",
    "wangwu"
]
再例如:
- 班名: 初中1班
  人数: 35
  班主任: 隔壁老张
  今天的任务: 扫操场
- 班名: 初中2班
  人数: 38
  班主任: 隔壁老王
  今天的任务: 搬桌子
具体在ansible playbook中,列表所描述的是局部环境,它不一定要有名称,不一定要从同一个属性开始,只要使用"- ",它就表示圈定一个范围,范围内的项都属于该列表。例如:
---
    - name: list1              # 列表1,同时给了个名称
      hosts: localhost         # 指出了hosts是列表1的一个对象
      remote_user: root        # 列表1的属性
      tasks:                   # 还是列表1的属性
    - hosts: 192.168.100.65    # 列表2,但是没有为列表命名,而是直入主题
      remote_user: root
      sudo: yes
      tasks:
唯一要注意的是,每一个playbook中必须包含"hosts"和"tasks"项。更严格地说,是每个play的顶级列表必须包含这两项。就像上面的例子中,就表示该playbook中包含了两个play,每个play的顶级列表都包含了hosts和tasks。其实绝大多数情况下,一个playbook中都只定义一个play,所以只有一个顶级列表项。顶级列表的各项,其实可以将其看作是ansible-playbook运行时的选项。
另外,playbook中某项是一个动作、一个对象或一个实体时,一般都定义成列表的形式。见下文。
1.3 字典
官方手册上这么称呼,其实就是key=value的另一种写法。使用"冒号+空格"分隔,即key: value。它一般当作列表项的属性。
例如:
- 班名: 初中1班
  人数:
    总数: 35
    男: 19
    女: 16
  班主任:
    大名: 隔壁老张
    这厮多大: 39
    这厮任教多少年: 15
  今天的任务: 扫操场
- 班名: 初中2班
  人数:
    总数: 38
    男: 19
    女: 19
  班主任:
    大名: 隔壁老王
    这厮多大: 30
    喜调戏女老师: True
  今天的任务: 搬桌子
     未完成任务怎么办:
        - 继续搬,直到完成
        - 写检讨
具体到playbook中,一般"虚拟性"的内容都可以通过字典的方式书写,而实体化的、动作性的、对象性的内容则应该定义为列表形式。
---
    - hosts: localhost              # 列表1
      remote_user: root
      tasks:
        - name: test1               # 子列表,下面是shell模块,是一个动作,所以定义为列表,只不过加了个name
          shell: echo /tmp/a.txt
          register: hi_var
        - debug: var=hi_var.stdout  # 调用模块,这是动作,所以也是列表
        - include: /tmp/nginx.yml   # 同样是动作,包含文件
        - include: /tmp/mysql.yml
        - copy:                     # 调用模块,定义为列表。但模块参数是虚拟性内容,应定义为字典而非列表
            src: /etc/resolv.conf   # 模块参数1
            dest: /tmp              # 模块参数2
    - hosts: 192.168.100.65           # 列表2
      remote_user: root
      vars:
        nginx_port: 80                # 定义变量,是虚拟性的内容,应定义为字典而非列表
        mysql_port: 3306
      vars_files:
        - nginx_port.yml              # 无法写成key/value格式,且是实体文件,因此定义为列表
      tasks:
        - name: test2
          shell: echo /tmp/a.txt
          register: hi_var            # register是和最近一个动作绑定的
        - debug: var=hi_var.stdout
从上面示例的copy模块可以得出,模块的参数是虚拟性内容,也能使用字典的方式定义。
字典格式的key/value,也支持内联格式写法:使用大括号。
{大名: 隔壁老王,这厮多大: 30,喜调戏女老师: True}
{nginx_port: 80,mysql_port: 3306}
这等价于json格式的:
{
    "大名": "隔壁老王",
    "这厮多大": 30,
    "喜调戏女老师": "True"
}
{
    "nginx_port": 80,
    "mysql_port": 3306
}
再结合其父项,于是转换成json格式的内容:
"班主任": {
    "大名": "隔壁老王",
    "这厮多大": 30,
    "喜调戏女老师": "True"
}
"vars": {
    "nginx_port": 80,
    "mysql_port": 3306
}
再加上列表项(使用中括号),于是:
[
  {
    "hosts": "192.168.100.65",
    "remote_user": "root",
    "vars": {
      "nginx_port": 80,
      "mysql_port": 3306
    },
    "vars_files": [
      "nginx_port.yml"
    ],
    "tasks": [
      {
        "name": "test2",
        "shell": "echo /tmp/a.txt",
        "register": "hi_var"
      },
      {
        "debug": "var=hi_var.stdout"
      }
    ]
  }
]
1.4 分行写
playbook中有3种方式进行续行。
- 在"key: "的后面使用大于号。
 - 在"key: "的后面使用竖线。这种方式可以像脚本一样写很多行语句。
 - 多层缩进。
 
例如,下面的3中方法。
---
    - hosts: localhost
      tasks:
        - shell: echo 2 >>/tmp/test.txt
            creates=/tmp/haha.txt          # 比模块shell缩进更多
        - shell: >                         # 在"key: "后使用大于号
            echo 2 >>/tmp/test.txt
            creates=/tmp/haha.txt
        - shell: |                         # 指定多行命令
            echo 2 >>/tmp/test.txt
            echo 3 >>/tmp/test.txt
          args:
            creates: /tmp/haha.txt
1.5 向模块传递参数
模块的参数一般来说是key=value格式的,有3种传递的方式:
- 直接写在模块后,此时要求使用"key=value"格式。这是让ansible内部去解析字符串。因为可分行写,所以有多种写法。
 - 写成字典型,即"key: value"。此时要求多层缩进。这是让yaml去解析字典。
 - 使用内置属性args,然后多层缩进定义参数列表。这是让ansible明确指定用yaml来解析。
 
例如:
---
    - hosts: localhost
      tasks:
        - yum: name=unix2dos state=installed    # key=value直接传递
        - yum:
            name: unxi2dos
            state: installed            # "key: value"字典格式传递
        - yum:
          args:                               # 使用args传递
            name: unix2dos
            state:installed
但要注意,当模块的参数是free_form时,即格式不定,例如shell和command模块指定要执行的命令,它无法写成key/value格式,此时不能使用上面的第二种方式。也就是说,下面第一个模块是正确的,第二个模块是错误的,因为shell模块的命令"echo haha"是自由格式的,无法写成key/value格式。
---
    - hosts: localhost
      tasks:
        - yum:
            name: unxi2dos
            state: installed
        - shell:
            echo haha
            creates: /tmp/haha.txt
所以,调用一个模块的方式就有了多种形式。例如:
---
    - hosts: localhost
      tasks:
        - shell: echo 1 >/tmp/test.txt creates=/tmp/haha.txt
        - shell: echo 2 >>/tmp/test.txt
            creates=/tmp/haha.txt
        - shell: echo 3 >>/tmp/test.txt
          args:
             creates: /tmp/haha.txt
        - shell: >
            echo 4 >>/tmp/test.txt
            creates=/tmp/haha.txt
        - shell: |
            echo 5.1 >>/tmp/test.txt
            echo 5.2 >>/tmp/test.txt
          args:
            creates: /tmp/haha.txt
        - yum:
            name: dos2unix
            state: installed
1.6 playbook和play的关系
一个playbook中可以包含多个play。每个play都至少包含有tasks和hosts这两项,还可以包含其他非必须项,如vars,vars_files,remote_user等。tasks中可以通过模块调用定义一系列的action。只不过,绝大多数时候,一个playbook都只定义一个play。
所以,大致关系为:
- playbook: [play1,play2,play3]
 - play: [hosts,tasks,vars,remote_user...]
 - tasks: [module1,module2,...]
 
也就是说,每个顶级列表都是一个play。例如,下面的playbook中包含了两个play。
---
    - name: list1
      hosts: localhost
      remote_user: root
      tasks:
    - hosts: 192.168.100.65
      remote_user: root
      sudo: yes
      tasks:
需要注意,有些时候play中使用了role,可能看上去没有tasks,这是因为role本身就是整合playbook的,所以没有也没关系。但没有使用role的时候,必须得包含hosts和tasks。例如:
---
  - hosts: centos
    remote_user: root
    pre_tasks:
        - name: config the yum repo for centos 7
          yum_repository:
              name: epel
              description: epel
              baseurl: http://mirrors.aliyun.com/epel/7/$basearch/
              gpgcheck: no
          when: ansible_distribution_major_version == "7"
        - name: config the yum repo for centos 6
          yum_repository:
              name: epel
              description: epel
              baseurl: http://mirrors.aliyun.com/epel/6/$basearch/
              gpgcheck: no
          when: ansible_distribution_major_version == "6"
    roles:
        - nginx
    post_tasks:
      - shell: echo 'deploy nginx/mysql over'
        register: ok_var
      - debug: msg='{{ ok_var.stdout }}'
1.7 playbook中什么时候使用引号
playbook中定义的都是些列表和字典。绝大多数时候,都不需要使用引号,但有两个特殊情况需要考虑使用引号。
- 出现大括号"{}"。
 - 出现冒号加空格时": "。
 
大括号要使用引号包围,是因为不使用引号时会被yaml解析成内联字典。例如要使用大括号引用变量的时候,以及想输出大括号符号的时候。
---
    - hosts: localhost
      tasks:
        - shell: echo "{{inventory_hostname}}:haha"
冒号尾随空格时要使用引号包围,是因为它会被解析为"key: value"的形式。而且包围冒号的引号还更严格。例如下面的debug模块中即使使用了引号也是错误的。
---
    - hosts: localhost
      tasks:
        - shell: echo "{{inventory_hostname}}:haha"
          register: hello
        - debug: msg="{{hello.stdout}}: heihei"
因为它把{{...}}当成key,heihei当成value了。因此,必须将整个debug模块的参数都包围起来,显式指定这一段是模块的参数。但这样会和原来的双引号冲突,因此使用单引号。
---
    - hosts: localhost
      tasks:
        - shell: echo "{{inventory_hostname}}:haha"
          register: hello
        - debug: 'msg="{{hello.stdout}}: heihei"'
但是,如果将shell模块中的冒号后也尾随上空格,即写成echo "{{inventory_hostname}}: haha",那么shell模块也会报错。因此也要使用多个引号,正确的如下:
---
    - hosts: localhost
      tasks:
        - shell: 'echo "{{inventory_hostname}}: haha"'
          register: hello
        - debug: 'msg="{{hello.stdout}}: heihei"'
												
											YAML_01 YAML语法和playbook写法的更多相关文章
- Ansible系列(三):YAML语法和playbook写法
		
html { font-family: sans-serif } body { margin: 0 } article,aside,details,figcaption,figure,footer,h ...
 - OC第五节 ——点语法和@property
		
一.setter和getter函数 1.回忆:如何访问对象中的成员变量 2.setter和getter函数的作用 setter 方法: 修改对象的字段/实例变 ...
 - iOS开发-OC语言 (六)点语法和@property
		
点语法和@property 知识点 1.setter/getter函数 2.点语法 3.@property语法和属性 ======================================== ...
 - Markdown 语法和 MWeb 写作使用说明
		
---恢复内容开始--- # Markdown 语法和 MWeb 写作使用说明 Markdown 的设计哲学 Markdown 的目標是實現「易讀易寫」. 不過最需要強調的便是它的可讀性.一份使用 M ...
 - Markdown语法和MWeb使用说明
		
Markdown 语法和 MWeb 写作使用说明 开始写博客,首先熟悉一下Markdown,以前过看GitHub里的README.MD,感受到了这种文字排版的简洁美观. 写博客是一种有效的学习总结和分 ...
 - Webstorm添加新建.vue文件功能并支持高亮vue语法和es6语法
		
转载:https://blog.csdn.net/qq_33008701/article/details/56486893 Webstorm 添加新建.vue文件功能并支持高亮vue语法和es6语法 ...
 - 好习惯: 用controller as 语法和$inject数组注入
		
angular好习惯1: 用controller as 语法和$inject数组注入 1) 像普通的JS类一样实现controller,摆脱$scope 2) 用.$inject数组注入相关模块,便于 ...
 - [golang] go的typeswitch guard(类型区别)语法和type assertion(类型断言)语法
		
最近在实现golang,看到个go的特性语法: typeswitch guard. typeswitch guard语法如下: package main import "fmt" ...
 - Webstorm 添加新建.vue文件功能并支持高亮vue语法和es6语法
		
添加新建.vue文件功能 ①Webstorm 右上角File-Plugins 搜索vue如果没有就去下载 点击serch in repositories ②点击安装vue.js ③安装成功后点击右下角 ...
 
随机推荐
- python 操作redis集群
			
一.连接redis集群 python的redis库是不支持集群操作的,推荐库:redis-py-cluster,一直在维护.还有一个rediscluster库,看GitHub上已经很久没更新了. 安装 ...
 - Qt5 源代码自动跳转
			
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/nixiaoxianggong/articl ...
 - redis客户端介绍及php客户端的下载安装
			
一.PHP客户端1.官方提供了几款PHP客户端,包括amphp/redis.phpredis.Predis.Rediska.推荐使用官方推荐的两款客户端,phpredis.Predis2.phpred ...
 - idea 中激活 JRebel
			
JRebel介绍: JRebel是一款JVM插件,它使得Java代码修改后不用重启系统,立即生效.IDEA上原生是不支持热部署的,一般更新了 Java 文件后要手动重启 Tomcat 服务器,修改才能 ...
 - Centos7部署开源聊天软件rocket.chat
			
一.部署rocket.chat 1.看官方文档部署,很简单,一步一步跟着部署即可 注意:需要部署节点需要联网主要是yum方式 https://rocket.chat/docs/installation ...
 - Java调用WebService方法总结(7)--CXF调用WebService
			
CXF = Celtix + XFire,继承了Celtix和XFire两大开源项目的精华,是一个开源的,全功能的,容易使用的WebService框架.文中所使用到的软件版本:Java 1.8.0_1 ...
 - 【洛谷 P4302】 [SCOI2003]字符串折叠(DP)
			
题目链接 简单区间dp 令\(f[i][j]\)表示\([i,j]\)的最短长度 先枚举区间,然后在区间中枚举长度\(k\),看这个区间能不能折叠成几个长度为\(k\)的,如果能就更新答案. #inc ...
 - 深入理解JVM(一) -- 自动内存管理机制
			
Java运行时数据区域分为:程序计数器,虚拟机栈,本地方法栈,Java堆,方法区,运行时常量池,直接内存,结构如下: 1.程序计数器: 是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示 ...
 - python3基础之“函数(2)”
			
1.def:定义一个函数 def f(x): return x+1 #返回函数值 a=f(2) print(a) >>3 def even_odd(x): if x%2==0: " ...
 - JavaScript 继承 封装 多态实现及原理详解
			
面向对象的三大特性 封装 所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏.封装是面向对象的特征之一,是对象和类概念的主要特性. ...