MyException - 我的异常网
当前位置:我的异常网» Linux/Unix » Ansible系列(3):YAML语法和playbook写法

Ansible系列(3):YAML语法和playbook写法

www.MyException.Cn  网友分享于:2013-09-28  浏览:0次
Ansible系列(三):YAML语法和playbook写法

本文目录:
1.1 初步说明
1.2 列表
1.3 字典
1.4 分行写
1.5 向模块传递参数
1.6 playbook和play的关系
1.7 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 }}"
  1. yaml文件以---开头,以表明这是一个yaml文件,就像xml文件在开头使用<?xml version="1.0" encoding="utf-8"?>宣称它是xml文件一样。但即使没有使用---开头,也不会有什么影响。

  2. yaml中使用"#"作为注释符,可以注释整行,也可以注释行内从"#"开始的内容。

  3. yaml中的字符串通常不用加任何引号,即使它包含了某些特殊字符。但有些情况下,必须加引号,最常见的是在引用变量的时候。具体见后文。

  4. 关于布尔值的书写格式,即true/false的表达方式。其实playbook中的布尔值类型非常灵活,可分为两种情况:

  5. 模块的参数: 这时布尔值作为字符串被ansible解析。接受yes/on/1/true/no/off/0/false,这时被ansible解析。例如上面示例中的update_cache=yes
  6. 非模块的参数: 这时布尔值被yaml解释器解析,完全遵循yaml语法。接受不区分大小写的true/yes/on/y/false/no/off/n。例如上面的gpgcheck=noenabled=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"'

回到系列文章大纲:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

转载请注明出处:http://www.cnblogs.com/f-ck-need-u/p/7113610.html

注:若您觉得这篇文章还不错请点击下右下角的推荐,有了您的支持才能激发作者更大的写作热情,非常感谢!

文章评论

程序员都该阅读的书
程序员都该阅读的书
如何成为一名黑客
如何成为一名黑客
那些争议最大的编程观点
那些争议最大的编程观点
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
 程序员的样子
程序员的样子
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
Java程序员必看电影
Java程序员必看电影
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
一个程序员的时间管理
一个程序员的时间管理
中美印日四国程序员比较
中美印日四国程序员比较
程序员的鄙视链
程序员的鄙视链
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员应该关注的一些事儿
程序员应该关注的一些事儿
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
老程序员的下场
老程序员的下场
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
编程语言是女人
编程语言是女人
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
代码女神横空出世
代码女神横空出世
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
旅行,写作,编程
旅行,写作,编程
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
10个调试和排错的小建议
10个调试和排错的小建议
总结2014中国互联网十大段子
总结2014中国互联网十大段子
程序员和编码员之间的区别
程序员和编码员之间的区别
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
漫画:程序员的工作
漫画:程序员的工作
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有