• 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"'
    
  • 相关阅读:
    我学的是设计模式的视频教程——辛格尔顿,生成器VS工厂方法
    android在广播接收器BroadcastReceiver里面再进行发送广播,造成当前广播接收器不断循环执行问题
    Android 出现警告Exported service does not require permission
    Android中如何像 360 一样优雅的杀死后台服务而不启动
    Android下写一个永远不会被KILL掉的进程/服务
    android 程序防止被360或者系统给kill掉
    android如何让service不被杀死
    如何让自己的Android程序永不被系统kill
    TextView的一些高级应用(自定义字体、显示多种颜色、添加阴影)
    Android ListView的背景和黑色边缘化的问题
  • 原文地址:https://www.cnblogs.com/syy1757528181/p/13113558.html
Copyright © 2020-2023  润新知