Ansible--playbook的使用

一、什麼是playbooks
playbooks是ansible的腳本、如同shell腳本一樣,它是控制遠程主機的一系列命令的集合,通過YAML語言編寫。執行一些簡單的任務,我們可以使用ad-hoc命令就可以解決,對於一些較複雜的任務,ad-hoc就不能勝任了,這時候playbooks就派上用場了,在playbooks中可以編排有序的執行過程,甚至可以在多組機器間來回有序的執行特定的步驟,並且可以同步或異步發起任務。

二、YAML語法
1、文件開始符

1
---

2、數組

1
2
3
- name
- hosts
- user

3、字典

1
2
name: restart apache
service: name=httpd state=restarted

字典與字典的嵌套:

1
2
3
vars:
  http_port: 80
  max_clients: 200

字典與數組的嵌套:

1
2
3
4
5
6
7
8
9
tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    service: name=httpd state=started

此外,Ansible 使用 "{{ var }}"來引用變量.,如果一個值以 "{" 開頭, YAML 將認爲它是一個字典, 所以我們必須引用它, 像這樣:foo: "{{ variable }}"

三、playbooks的基本用法
最基本的playbook分爲四部分:

  • 定義主機和用戶

1
2
hosts
users
  • 定義 playbook 執行需要的變量

1
variable
  • 定義任務

1
tasks
  • 定義響應事件

1
handlers

簡單示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---
- name: Install apache
  hosts: webservers
  user: root
  gather_facts: false
  vars:
    http_port: 80
    max_clients: 200
  tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    service: name=httpd state=started
  handlers:
    - name: restart apache
      service: name=httpd state=restarted

說明:

  • name參數對此playbook實現的功能的概述,執行時會輸出name變量值。

  • hosts參數指定了在哪些主機上執行playbook

  • user參數指定在遠程主機上使用什麼用戶執行任務

  • gather_facts參數指定了下面任務執行前,是否先執行setup模塊獲取遠程主機相關信息,這些信息在task中可以作爲變量使用

  • vars參數指定了變量

  • task參數指定了任務,這裏有3個任務。name參數是對具體任務的描述,在執行過程中會輸出

  • handlers參數指定一個響應事件,當template: src=/srv/httpd.j2 dest=/etc/httpd.conf這個任務執行狀態是changed時纔會觸發。

 1、主機和用戶

key含 義
hosts爲主機的IP,或者主機組名,或者關鍵字all
user在遠程以哪個用戶身份執行。
become切換成其它用戶身份執行,值爲yes或者no
become_method與became一起用,指可以爲‘sudo’/’su’/’pbrun’/’pfexec’/’doas’
become_user與bacome一起用,可以是root或者其它用戶名

一般用法:

1
2
3
---
- hosts: webserver, [all]
  user: root

還可以在每個 task 中,定義遠程執行用戶

1
2
3
4
5
6
7
---
- hosts: webserver
  user: root
  tasks:
  - name: test connection
    ping:
    remote_user: root

也支持 sudo 方法,在 task中同樣支持,在sudo需要密碼時,可以加上選項 –ask-sudo-pass

1
2
3
4
5
6
7
8
---
- hosts: all
  user: test
  sudo: yes
  task:
    - service: name=nginx state=started
      sudo: yes
      sudo_user: root

2、任務列表
tasks 是從上到下順序執行,如果中間發生錯誤,整個 playbook 便會中斷。
每一個 task 是對module的一次調用,通常會帶有特定參數,參數可以使用變量。
每一個 task 有一個 name 屬性,name 值會在命令行中輸出,以提示用戶,如果沒有定義,aciton 的值會作爲輸出信息來標記task。

語法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
tasks:
  - name: make sure apache is running
    service: name=httpd state=running
// 如果參數過長,可以使用空格或者縮進分隔爲多行
tasks:
  - name: copy ansible inventory file to client
    copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
          owner=root group=root mode=0644
// 或者使用 yaml 的字典作爲參數
tasks:
  - name: copy ansible inventory file to client
    copy:
      src: /etc/ansible/hosts
      dest: /etc/ansible/hosts
      owner: root
      group: root
      mode: 0644
// 大部分的模塊都是使用 `key-value` 這種格式的,其中有兩個比較特殊,command 和 shell 模塊。
tasks:
  - name: disable selinux
    command: /sbin/setenforce 0
tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/command || /bin/true
tasks:
  - name: run some command and ignore the reslut
    shell: /usr/bin/somecommadn
    ignore_error: True

執行狀態:
task中每個action會調用一個module,在module中會去檢查當前系統狀態是否需要重新執行。
如果本次執行了,那麼action會得到返回值changed
如果不需要執行,那麼action得到返回值ok

3、響應事件
每個主流的編程語言都會有event機制,那麼handler就是playbook的event。
Handlers裏面的每一個handler,也是對module的一次調用。而handlers與tasks不同,tasks會默認的按定義順序執行每一個task,handlers則不會,它需要在tasks中被調用,纔有可能被執行。Tasks中的任務都是有狀態的,changed或者ok。 在Ansible中,只在task的執行狀態爲changed的時候,纔會執行該task調用的handler,這也是handler與普通的event機制不同的地方。
什麼情況下使用handlers呢?
如果你在tasks中修改了apache的配置文件。需要重起apache。此外還安裝了apache的插件。那麼還需要重起apache。像這樣的應該場景中,重啓apache就可以設計成一個handler。

特性:

  • 一個handler最多隻執行一次

在所有的任務裏表執行之後執行,如果有多個task notify同一個handler,那麼只執行一次。
在下面的例子裏apache只執行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
- hosts: lb
  remote_user: root
  vars:
      random_number1: "{{ 10000 | random }}"
      random_number2: "{{ 10000000000 | random }}"
  tasks:
  - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }}
    copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }}
    notify:
      - call in every action
  - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }}
    copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }}
    notify:
      - call in every action
 
  handlers:
  - name: call in every action
    debug: msg="call in every action, but execute only one time"
  • action是Changed ,纔會執行handler

只有當TASKS種的action的執行狀態是changed時,纔會觸發notify handler的執行。
下面的腳本執行兩次,執行結果是不同的:
第一次執行是,tasks的狀態都是changed,會觸發兩個handler
第二次執行是,
第一個task的狀態是OK,那麼不會觸發handlers"call by /tmp/hosts",
第二個task的狀態是changed,觸發了handler"call by /tmp/hosts.random_number"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//示例代碼
---
- hosts: lb
  remote_user: root
  vars:
      random_number: "{{ 10000 | random }}"
  tasks:
  - name: Copy the /etc/hosts to /tmp/hosts
    copy: src=/etc/hosts dest=/tmp/hosts
    notify:
      - call by /tmp/hosts
  - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number }}
    copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number }}
    notify:
      - call by /tmp/hosts.random_number
 
  handlers:
  - name: call by /tmp/hosts
    debug: msg="call first time"
  - name: call by /tmp/hosts.random_number
    debug: msg="call by /tmp/hosts.random_number"
  • 按Handler的定義順序執行

andlers是按照在handlers中定義個順序執行的,而不是安裝notify的順序執行的。
下面的例子定義的順序是1>2>3,notify的順序是3>2>1,實際執行順序:1>2>3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---
- hosts: lb
  remote_user: root
  gather_facts: no
  vars:
      random_number1: "{{ 10000 | random }}"
      random_number2: "{{ 10000000000 | random }}"
  tasks:
  - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }}
    copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }}
    notify:
      - define the 3nd handler
  - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }}
    copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }}
    notify:
      - define the 2nd handler
      - define the 1nd handler
 
  handlers:
  - name: define the 1nd handler
    debug: msg="define the 1nd handler"
  - name: define the 2nd handler
    debug: msg="define the 2nd handler"
  - name: define the 3nd handler
    debug: msg="define the 3nd handler"

4、變量
playbook中常用變量的幾種情況:

  • 用戶自定義的變量

用戶可以在Playbook中,通過vars關鍵字自定義變量,使用時用{{ }}引用以來即可。
示例:

1
2
3
4
5
6
7
8
---
- hosts: web
  vars:
    http_port: 80
  remote_user: root
  tasks:
  - name: insert firewalld rule for httpd
    firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
  • 把變量放在單獨的文件中

當變量比較多的時候,或者變量需要在多個playbook中重用的時候,可以把變量放到一個單獨的文件中。通過關鍵字var_files把文件中定義的變量引入playbook中,使用變量的方法和在本文件中定義的變量相同。

1
2
3
4
5
6
7
8
9
10
- hosts: web
  remote_user: root
  vars_files:
      - vars/server_vars.yml
  tasks:
  - name: insert firewalld rule for httpd
    firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
 
#cat vars/server_vars.yml
http_port: 80

定義複雜的變量
當變量的值不是簡單的字符串或者數字,而是一個字典,語法如下

1
2
3
foo:
  field1: one
  field2: two

訪問複雜變量中的子屬性,可以利用中括號或者點號:

1
2
3
foo['field1']
foo.field1
  • 系統變量(facts)

ansible會通過module setup來收集主機的系統信息,這些收集到的系統信息叫做facts,這些facts信息可以直接以變量的形式使用。
有哪些facts變量可以引用呢?在命令行上通過調用setup module命令可以查看:

1
$ ansible all -m setup -u root

系統變量在playbook中可以直接使用:

1
2
3
4
5
6
7
8
9
10
11
12
---
- hosts: all
  user: root
  tasks:
  - name: echo system
    shell: echo {{ ansible_os_family }}
  - name install ntp on Debian linux
    apt: name=git state=installed
    when: ansible_os_family == "Debian"
  - name install ntp on redhat linux
    yum: name=git state=present
    when: ansible_os_family == "RedHat"

對於複雜的、多層級的facts變量,可以通過下面的兩種方式的任意一種訪問複雜的變量中的子屬性:
中括號:

1
{{ ansible_ens3["ipv4"]["address"] }}

點號:

1
{{ ansible_ens3.ipv4.address }}

關係facts變量,一旦關閉之後就不用調用了。關閉方法:

1
2
- hosts: webserver
  gather_facts: no
  • 註冊變量

把task的執行結果也可以作爲一個變量值。這個時候就需要用到“註冊變量”,將執行結果註冊到一個變量中,待後面的action使用。

1
2
3
4
5
6
7
8
9
---
- hosts: web
  tasks:
     - shell: ls
       register: result
       ignore_errors: True
     - shell: echo "{{ result.stdout }}"
       when: result.rc == 5
     - debug: msg="{{ result.stdout }}"

註冊變量經常和debug module一起使用,這樣可以得到更多action的輸出信息,幫助用戶調試。

  • 命令行中傳遞的變量

爲了使Playbook更靈活、通用性更強,允許用戶在執行的時候傳入變量的值,這個時候就需要用到“額外變量”。
定義命令行變量
在test.yml文件裏,hosts和user都定義爲變量,需要從命令行傳遞變量值。

1
2
3
4
5
---
- hosts: '{{ hosts }}'
  remote_user: '{{ user }}'
  tasks:
  ...

在命令行裏面傳值得的方法:

1
ansible-playbook testyml --extra-vars "hosts=web user=root"

還可以用json格式傳遞參數:

1
ansible-playbook test.yml --extra-vars "{'hosts':'vm-rhel7-1', 'user':'root'}"

還可以將參數放在文件裏面:

1
ansible-playbook test.yml --extra-vars "@vars.json"

5、如何執行playbook

語法:

1
$ ansible-playbook deploy.yml

查看輸出的細節

1
$ ansible-playbook playbook.yml --verbose

查看該腳本影響哪些hosts

1
$ ansible-playbook playbook.yml --list-hosts

並行執行腳本

1
$ ansible-playbook playbook.yml -f 10

四、playbooks的高級用法
1、邏輯控制

  • when: 條件判斷語句,類似於變成語言中的if

主機爲Debian Linux立刻關機:

1
2
3
4
tasks:
  - name: "shutdown Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_os_family == "Debian"

根據action的執行結果,來決定接下來執行的action

1
2
3
4
5
6
7
8
9
10
tasks:
  - command: /bin/false
    register: result
    ignore_errors: True
  - command: /bin/something
    when: result|failed
  - command: /bin/something_else
    when: result|success
  - command: /bin/still/something_else
    when: result|skipped

遠程中的系統變量facts變量作爲when的條件,用“|int”還可以轉換返回值的類型:

1
2
3
4
5
---
- hosts: web
  tasks:
    - debug: msg="only on Red Hat 7, derivatives, and later"
      when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6

條件表達式

基本:

1
2
3
tasks:
    - shell: echo "This certainly is epic!"
      when: epic

否定:

1
2
3
tasks:
    - shell: echo "This certainly isn't epic!"
      when: not epic

變量定義:

1
2
3
4
5
tasks:
    - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
      when: foo is defined
    - fail: msg="Bailing out. this play requires 'bar'"
      when: bar is not defined

數值表達:

1
2
3
4
tasks:
    - command: echo {{ item }}
      with_items: [ 0, 2, 4, 6, 8, 10 ]
      when: item > 5

與Include一起用:

1
2
- include: tasks/sometasks.yml
  when: "'reticulating splines' in output"

與Role一起用:

1
2
3
- hosts: webservers
  roles:
     - { role: debian_stock_config, when: ansible_os_family == 'Debian' }
  • loop: 循環語句,類似於編程語言的中的while

標準循環
爲了保持簡潔,重複的任務可以用以下簡寫的方式:

1
2
3
4
- name: add several users
  user: name={{ item }} state=present groups=wheel
  with_items:
     - testuser1

如果你在變量文件中或者 'vars' 區域定義了一組YAML列表,你也可以這樣做:

1
2
3
4
5
6
vars:
  somelist: ["testuser1", "testuser2"]
tasks:
  -name: add several user
   user: name={{ item }} state=present groups=wheel
   with_items: "{{somelist}}"

使用 'with_items' 用於迭代的條目類型不僅僅支持簡單的字符串列表.如果你有一個哈希列表,那麼你可以用以下方式來引用子項:

1
2
3
4
5
- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

注意:如果同時使用 when 和 with_items (或其它循環聲明),when聲明會爲每個條目單獨執行。

嵌套循環:

1
2
3
4
5
6
7
8
9
10
11
- name: give users access to multiple databases
  mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
  with_nested:
    - [ 'alice', 'bob' ]
    - [ 'clientdb', 'employeedb', 'providerd']
或者
- name: give users access to multiple databases
  mysql_user: name={{ item.0 }} priv={{ item.1 }}.*:ALL append_privs=yes password=foo
  with_nested:
    - [ 'alice', 'bob' ]
    - [ 'clientdb', 'employeedb', 'providerd']

對字典使用循環:

1
2
3
4
5
6
7
8
9
10
11
12
13
---
vars:
  users:
    alice:
      name: Alice Appleworth
      telephone: 123-456-7890
    bob:
      name: Bob Bananarama
      telephone: 987-654-3210
tasks:
  - name: Print phone records
    debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
    with_dict: "{{users}}"

對文件列表使用循環
with_fileglob 可以以非遞歸的方式來模式匹配單個目錄中的文件.如下面所示:

1
2
3
4
5
6
7
tasks:
    # first ensure our target directory exists
    - file: dest=/etc/fooapp state=directory
    # copy each file over that matches the given pattern
    - copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
      with_fileglob:
        - /playbooks/files/fooapp/*
  • block: 把幾個tasks組成一塊代碼,便於針對一組操作的異常處理等操作。

多個action組裝成塊,可以根據不同條件執行一段語句 :

1
2
3
4
5
6
7
8
9
10
11
tasks:
     - block:
         - yum: name={{ item }} state=installed
           with_items:
             - httpd
             - memcached
         - template: src=templates/src.j2 dest=/etc/foo.conf
         - service: name=bar state=started enabled=True
       when: ansible_distribution == 'CentOS'
       become: true
       become_user: root

組裝成塊處理異常更方便:

1
2
3
4
5
6
7
8
9
10
11
tasks:
  - block:
      - debug: msg='i execute normally'
      - command: /bin/false
      - debug: msg='i never execute, cause ERROR!'
    rescue:
      - debug: msg='I caught an error'
      - command: /bin/false
      - debug: msg='I also never execute :-('
    always:
      - debug: msg="this always executes"

2、重用playbook

Playbook支持兩種重用機制,一種是重用靜態Playbook腳本,另外一種是類似於編程語言中函數的機制。

  • include語句 - 重用靜態的Playbook腳本,使用起來簡單、直接。

Include語句的功能,基本的代碼重用機制。主要重用tasks。同時Include可將tasks分割成多個文件,避免Playbook過於臃腫,使用戶更關注於整體的架構,而不是實現的細節上。
普通用法
像其它語言的Include語句一樣,直接Include:

1
2
3
4
5
6
7
8
---
# possibly saved as tasks/firewall_httpd_default.yml
 
  - name: insert firewalld rule for httpd
    firewalld: port=80/tcp permanent=true state=enabled immediate=yes
main.yml文件中調用include的方法:
tasks:
    - include: tasks/firewall_httpd_default.yml

高級用法-使用參數
include文件中還可以定義參數
被include的文件tasks/firewall_httpd_default.yml中,使用{{ port }}定義了一個名字爲port的參數。

1
2
3
---
  - name: insert firewalld rule for httpd
    firewalld: port={{ port }}/tcp permanent=true state=enabled immediate=yes

傳參數的各種方法
在執行的playbook傳參數,可以加在行尾,使用空格分隔:

1
2
3
4
tasks:
  - include: tasks/firewall.yml port=80
  - include: tasks/firewall.yml port=3260
  - include: tasks/firewall.yml port=423

還可以使用yml的字典傳參數:

1
2
3
4
5
6
7
tasks:
  - include: wordpress.yml
    vars:
        wp_user: timmy
        ssh_keys:
          - keys/one.txt
          - keys/two.txt

還可以把一條task簡寫成成一個類JSON的形式傳參數:

1
- { include: wordpress.yml, wp_user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }

當然在playbook中已經定義了的參數,就不需要再顯示傳入值了,可以直接寫成下面的:

1
2
3
4
5
6
7
---
- hosts: lb
  vars:
    port: 3206
  remote_user: root
  tasks:
    - include: tasks/firewall.yml
  • role語言 - Playbook的"函數機制",使用方法稍複雜、功能強大。是Playbook腳本的共享平臺ansible galaxy主要的分享方式

Role是比include更強大靈活的代碼重用和分享機制。Include類似於編程語言中的include,是重用單個文件的,功能有限。
而Role類似於編程語言中的“Package”,可以重用一組文件形成完整的功能。例如安裝和配置apache,需要tasks實現安裝包和拷貝模版等,httpd.conf和index.html的模版文件,和handler文件實現重起功能。這些文件都可以放在一個role裏面,供不同的playbook文件重用。
Ansible非常提倡在playbook中使用role,並且提供了一個分享role的平臺Ansible Galaxy, https://galaxy.ansible.com/, 在galaxy上可以找到別人寫好的role。
在ansible中,通過遵循特定的目錄結構,就可以實現對role的定義:
下面的目錄結構定義了一個role:名字爲myrole。在site.yml,調用了這個role。
role的目錄結構:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
site.yml
roles/
├── myrole
    ├── tasks
    │   └── main.yml
    ├── handlers
    │   └── main.yml
    ├── defaults
    │   └── main.yml
    ├── vars
    │   └── main.yml
    ├── files
    ├── templates
    ├── README.md
    ├── meta
    │   └── main.yml
    └── tests
        ├── inventory
        └── test.yml

site.yml中調用role

1
2
3
4
---
- hosts: webservers
  roles:
     - myrole

ansible並不要求role包含上述所有的目錄及文件,根據role的功能需要加入對應的目錄和文件。下面是每個目錄和文件的功能。
如果 roles/x/tasks/main.yml 存在, 其中列出的 tasks 將被添加到 play 中,所以這個文件也可以視作role的入口文件,想看role做了什麼操作,可以從此文件看起。
如果 roles/x/handlers/main.yml 存在, 其中列出的 handlers 將被添加到 play 中
如果 roles/x/vars/main.yml 存在, 其中列出的 variables 將被添加到 play 中
如果 roles/x/meta/main.yml 存在, 其中列出的 “角色依賴” 將被添加到 roles 列表中
roles/x/tasks/main.yml中所有tasks,可以引用 roles/x/{files,templates,tasks}中的文件,不需要指明文件的路徑。
在寫role的時候,一般都要包含role入口文件roles/x/tasks/main.yml,其它的文件和目錄,可以根據需求選擇加入。
參數在role中是如何定義的呢
定義一個帶參數的role,名字是myrole,那麼目錄結構爲:

1
2
3
4
5
main.yml
roles
  role_with_var
    tasks
      main.yml

在roles/myrole/tasks/main.yml中,使用{{ }}定義的變量就可以了

1
2
3
---
- name: use param
  debug: msg="{{ param }}"

使用帶參數的role
那麼在main.yml就可以用如下的方法使用myrole

1
2
3
4
5
---
- hosts: webservers
  roles:
    - { role: myrole, param: 'Call some_role for the 1st time' }
    - { role: myrole, param: 'Call some_role for the 2nd time' }

或者寫成YAML字典格式:

1
2
3
4
5
6
7
---
- hosts: webservers
  roles:
    - role: myrole
      param: 'Call some_role for the 1st time'
    - role: myrole
      param: 'Call some_role for the 2nd time'

role指定默認的參數
指定默認參數後,如果在調用時傳參數了,那麼就使用傳入的參數值.如果調用的時候沒有傳參數,那麼就使用默認的參數值.
指定默認參數很簡單,以上面的role_with_var爲例

1
2
3
4
5
6
7
main.yml
roles:
  myrole
    tasks
      main.yml
    defaults
      main.yml

在roles/myrole/defaults/main.yml中,使用yml的字典定義語法定義param的值,如下:
param: "I am the default value"
這樣在main.yml中,下面兩種調用方法都可以

1
2
3
4
5
---
- hosts: webservers
  roles:
    - role_with_var
    - { role: role_with_var, param: 'I am the value from external' }

role與條件語句when一起執行,下面的例子中,my_role只有在RedHat系列的server上才執行。

1
2
3
4
5
6
7
8
9
10
11
---
- hosts: webservers
  roles:
    - { role: my_role, when: "ansible_os_family == 'RedHat'" }
同樣也可以寫成YAML字典格式
---
- hosts: webservers
  roles:
    - role: my_role
      when: "ansible_os_family == 'RedHat'"
roles和tasks的執行順序

如果一個playbook同時出現role和tasks,他們的調用順序如下:
pre_tasks > role > tasks > post_tasks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
---
- hosts: lb
  user: root
  pre_tasks:
    - name: pre
      shell: echo 'hello'
  roles:
    - { role: some_role }
 
  tasks:
    - name: task
      shell: echo 'still busy'
 
  post_tasks:
    - name: post
      shell: echo 'goodbye'
執行的結果爲:
PLAY [lb] **********************************************************************
 
 
TASK [setup] *******************************************************************
ok: [rhel7u3]
 
TASK [pre] *********************************************************************
changed: [rhel7u3]
 
TASK [some_role : some role] ***************************************************
ok: [rhel7u3] => {
    "msg": "Im some role"
}
 
TASK [task] ********************************************************************
changed: [rhel7u3]
 
TASK [post] ********************************************************************
changed: [rhel7u3]
 
PLAY RECAP *********************************************************************
rhel7u3                    : ok=5    changed=3    unreachable=0    failed=0

3、tags的用法
如果playbook文件比較大,在執行的時候只是想執行部分功能,這個時候沒有有解決方案呢?Playbook提供了tags便籤可以實現部分運行。
tags的基本用法
例如,文件example.yml如何所示,標記了兩個tag:packages和configuration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
tasks:
  - yum: name={{ item }} state=installed
    with_items:
       - httpd
    tags:
       - packages
  - name: copy httpd.conf
    template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
    tags:
       - configuration
  - name: copy index.html
    template: src=templates/index.html.j2 dest=/var/www/html/index.html
    tags:
       - configuration

那麼我們在執行的時候,如果不加任何tag參數,那麼會執行所有的tasks

1
$ ansible-playbook example.yml

指定執行安裝部分的tasks,則可以利用關鍵字tags

1
$ ansible-playbook example.yml --tags "packages"

指定不執行packages部分的task,則可以利用關鍵字skip-tags

1
$ ansible-playbook example.yml --skip-tags "configuration"

特殊的Tags:“always”
tags的名字是用戶自定義的,但是如果你把tags的名字定義爲“always”,那麼就有點特別了。只要在執行playbook時,沒有明確指定不執行always tag,那麼它就會被執行。
在下面的例子中,即使你只指定執行packages,那麼always也會被執行。

1
2
3
4
5
6
7
8
9
10
11
12
tasks:
  - debug: msg="Always print this debug message"
    tags:
      - always
  - yum: name= state=installed
    with_items:
       - httpd
    tags:
       - packages
  - template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
    tags:
       - configuration

指定運行packages時,還是會執行always tag對應的tasks

1
2
3
4
5
6
7
8
9
10
11
$ ansible-playbook tags_always.yml --tags "packages"
“tagged”,“untagged”和“all”
tasks:
  - debug: msg="I am not tagged"
    tags:
      - tag1
  - debug: msg="I am not tagged"
分別指定--tags爲“tagged”,“untagged”和“all”試下效果吧:
$ ansible-playbook tags_tagged_untagged_all.yml --tags tagged
$ ansible-playbook tags_tagged_untagged_all.yml --tags untagged
$ ansible-playbook tags_tagged_untagged_all.yml --tags all

在include中和role中使用tags
include語句指定執行的tags的語法:

1
2
- include: foo.yml
  tags: [web,foo]

調用role中的tags的語法爲:

1
2
roles:
  - { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }

4、playbook文件加密

ansible-vault命令可以對配置文件進行基於密碼的加密,防止敏感信息泄漏。

加密已存在文件

1
$ ansible-vault encrypt site.yml

加密並創建文件

1
$ ansible-vault create filename

執行加密後的playbook

1
$ ansible-playbook site.yml --ask-vault-pass

解密

1
$ ansible-vault decrypt site.yml

五、編譯安裝nginx的playbook的實例

playbooks目錄結構:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ tree playbooks
playbooks
├── group_vars
├── roles
│   └── nginx
│       ├── files
│       │   ├── nginx-1.8.0.tar.gz
│       │   └── pcre-8.33.tar.gz
│       ├── handlers
│       │   └── main.yml
│       ├── tasks
│       │   └── main.yml
│       └── templates
│           └── default.conf
└── site.yml

yml文件內內容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ cat playbooks/site.yml
---
- name: Install Nginx
  hosts: test
  user: admin
 
  roles:
    - nginx
 
$ cat playbooks/roles/nginx/tasks/main.yml
---
- name: Copy nginx software
  copy: src=nginx-1.8.0.tar.gz dest=/app/admin/soft/nginx-1.8.0.tar.gz
 
- name: Copy pcre software
  copy: src=pcre-8.33.tar.gz dest=/app/admin/soft/pcre-8.33.tar.gz
 
- name: Install Pcre
  shell: cd /app/admin/soft;tar zxf pcre-8.33.tar.gz;cd pcre-8.33;./configure;make;make install
  sudo: yes
 
- name: Install Nginx
  shell: cd /app/admin/soft;tar zxf nginx-1.8.0.tar.gz;cd nginx-1.8.0;./configure --user=admin --group=app --prefix=/app/admin/nginx --with-http_stub_status_module --with-http_ssl_module;make;make install
 
- name: Copy nginx configuration
  template: src=default.conf dest=/app/admin/nginx/conf/nginx.conf
  notify: restart nginx
[admin@wts-vm-01 ~]$ cat playbooks/roles/nginx/handlers/main.yml
---
- name: restart nginx
  shell: /app/admin/nginx/sbin/nginx
  sudo: yes

六、其它
官方例子
Ansible官方提供了一些比較常用的、經過測試的Playbook例子:
https://github.com/ansible/ansible-examples
Playbook分享平臺:
https://galaxy.ansible.com/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章