zoukankan      html  css  js  c++  java
  • yaml语法和playbook写法

    ansible的playbook采用yaml语法,它简单地实现了json格式的事件描述。yaml之于json就像markdown之于html一样,极度简化了json的书写。在学习ansible playbook之前,很有必要把yaml的语法格式、引用方式做个梳理。
    TP

    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文件一样。但即使没有使用---开头,也不会有什么影响。

    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运行时的选项。

    - 班名: 初中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"'
    
  • 相关阅读:
    MFC Windows 程序设计>WinMain 简单Windows程序 命令行编译
    AT3949 [AGC022D] Shopping 题解
    CF643D Bearish Fanpages 题解
    CF643C Levels and Regions 题解
    CF241E Flights 题解
    CF671C Ultimate Weirdness of an Array 题解
    CF1592F Alice and Recoloring 题解
    GYM 102452E 题解
    CF494C Helping People 题解
    P5556 圣剑护符
  • 原文地址:https://www.cnblogs.com/syy1757528181/p/13113558.html
Copyright © 2011-2022 走看看