Ansible系列(三):YAML語法和playbook寫法


我寫了更完善的Ansible專欄文章:一步到位玩兒透Ansible

Ansible系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html


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"'


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM