三、Ansible的腳本(Playbook)


一、Playbook基本語法

執行Playbook語法

$ansible-playbook  deploy.yml

查看輸出的細節

ansblie-playbook playbook.yml --verbose

查看該腳本影響哪些hosts

ansible-playbook  playbook.yml  --list-hosts

並行執行腳本

ansible-playbook  playbook.yml  -f 10

完整的playbook腳本示例

最基本的playbook腳本分為三個部分:

在什么機器上以什么身份執行

  • hosts

  • users


  • 執行的任務是都有什么

  • tasks
    善后的任務都有什么

  • handlers

deploy.yml文件

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  user: root
  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

二、主機和用戶

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

腳本里用became的時候,執行的playbook的時候可以加參數—ask-become-pass

ansible-playbook deploy.yml --ask-become-pass

Tasks任務列表

  • tasks是從上到下順序執行,如果中間發生錯誤,那么整個playbook會中止。你可以修改文件后,再重新執行。
  • 每一個task的對module一次調用。使用不同的參數和變量而已。
  • 每一個task最好有name屬性,這個是供人讀的,沒有實際的操作。然后會在命令行里面輸出,提示用戶執行情況。

語法:

task的基本語法:

tasks:
  - name: make sure apache is running
    service: name=httpd state=running

其中name是可選的,也可以簡寫成下面的例子:

tasks:
  - service: name=httpd state=running

寫name的task在playbook執行時,會顯示對應的名字,信息更友好、豐富。寫name是個好習慣!

TASK: [make sure apache is running] *************************************************************
changed: [yourhost]

沒有寫name的task在playbook執行時,直接顯示對應的task語法。在調用同樣的module多次后,不同意分辨執行到哪步了。

TASK: [service name=httpd state=running] **************************************
changed: [yourhost]

參數的不同寫法

最上的代碼展示了最基本的傳入module的參數的方法:key=value

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

或者用yml的字典傳入參數

 tasks:
  - name: Copy ansible inventory file to client
    copy:
      src: /etc/ansible/hosts
      dest: /etc/ansible/hosts
      owner: root
      group: root
      mode: 0644

TASK的執行狀態

task中每個action會調用一個module,在module中會去檢查當前系統狀態是否需要重新執行。

  • 如果本次執行了,那么action會得到返回值changed;
  • 如果不需要執行,那么action得到返回值ok
    module的執行狀態的具體判斷規則由各個module自己決定和實現的。例如,”copy” module的判斷方法是比較文件的checksum,代碼如下:

[https://github.com/ansible/ansible-modules-core/blob/devel/files/copy.py(https://github.com/ansible/ansible-modules-core/blob/devel/files/copy.py)

狀態示例

以一個copy文件的task為例子:

  tasks:
  - name: Copy the /etc/hosts
    copy: src=/etc/hosts dest=/etc/hosts

第一次執行,它的結果是這個樣子的:

TASK的狀態是changed

第二次執行是下面這個樣子的:

TASK的狀態是ok,由於第一次執行copy_hosts.yml的時候,已經拷貝過文件,那么ansible目標文件的狀態避免重復執行.

下面我更改vm-rhel7-1的/etc/hosts, 再次執行看看:

三、響應事件(Handler)

什么是handler

每個主流的編程語言都會有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。像這樣的應用場景中,重啟apoache就可以設計成一個handler

一個handler最多只執行一次

在所有的任務里表執行之后執行,如果有多個task notify同一個handler,那么只執行一次。

---
- 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”
    

測試代碼見:

---
- 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的定義順序執行

handlers是按照在handlers中定義個順序執行的,而不是安裝notify的順序執行的。

下面的例子定義的順序是1>2>3,notify的順序是3>2>1,實際執行順序:1>2>3.

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

四、變量

在Playbook中用戶自定義的變量

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

Playbook中定義和使用的變量的方法

例如下面的例子中,用戶定義變量名為http_port,其值為80.在tasks firewalld中,通過{{http_port}}引用。

---
- 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中,使用變量的方法和在本文中定義的變量相同。

- 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

變量文件/vars/server_vars.yml的內容為:

http_port: 80

定義和使用復雜變量

有時候我們使用的變量不是簡單的字符串或者數字,而是一個對象。這時候定義的語法如下,格式為YAML的字典格式:

foo:
  field1: one
  field2: two

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

foo['field1']
foo.field1

YAML的陷阱

YAML的陷阱是某些時候YAML和Ansible Playbook的變量語法不能在一起好好工作了。這里僅發生在指冒號后面的值不能以{開頭的時候,如果有必要以{開頭,必須加上因號。總之在YAML值的定義中,如果提示YMAL語法錯誤,都可以嘗試下加入引號來解決。

下面的代碼會報錯:

- hosts: app_servers
  vars:
      app_path: {{ base_path }}/22

解決辦法:要在”{ “開始的值加上引號:

- hosts: app_servers
  vars:
       app_path: "{{ base_path }}/22"

五、主機的系統變量(facts)

ansible 會通過module setup來收集主機的系統信息,這些收集到的系統信息叫做facts,這些facts信息可以直接以變量的形式使用。

哪些fact變量可以引用呢?在命令行上通過調用setup module命令可以查看

$ ansible all -m setup -u root

怎樣在playbook中使用facts變量呢,答案是直接使用:

---
- 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變量

一般在系統中收集到如下的信息,復雜的、多層級的facts變量如何使用呢?

...
        "ansible_ens3": {
            "active": true, 
            "device": "ens3", 
            "ipv4": {
                "address": "10.66.192.234", 
                "netmask": "255.255.254.0", 
                "network": "10.66.192.0"
            }, 
            "ipv6": [
                {
                    "address": "2620:52:0:42c0:5054:ff:fef2:e2a3", 
                    "prefix": "64", 
                    "scope": "global"
                }, 
                {
                    "address": "fe80::5054:ff:fef2:e2a3", 
                    "prefix": "64", 
                    "scope": "link"
                }
            ], 
            "macaddress": "52:54:00:f2:e2:a3", 
            "module": "8139cp", 
            "mtu": 1500, 
            "promisc": false, 
            "type": "ether"
        }, 
...

那么可以通過下面的兩種方式訪問復雜的變量中的子屬性:

中括號:

{{ ansible_ens3["ipv4"]["address"] }}
點號:

{{ ansible_ens3.ipv4.address }}

關閉facts

在Playbook中,如果寫gather_facts來控制是否收集遠程系統的信息.如果不收集系統信息,那么上面的變量就不能在該playybook中使用了.

- hosts: whatever
  gather_facts: no

六、文件模板中使用的變量

template module 在Ansible中非常常用,而它在使用的時候又沒有顯示的指定template文件中的值,所以有時候用戶會對template文件中使用的變量感到困惑,所以在這里又重新強調下。

template變量的定義

在playbook中定義的變量,可以直接在template中使用,同時facts變量也可以直接在template中使用,當然也包含在inventory里面定義的host和group變量。只要是在playbook中可以訪問的變量,都可以在template文件中使用。

下面的playbook腳本中使用了template module來拷貝文件index.html.j2,並且替換了index.html.j2中的變量為playbook中定義變量值。

---
- hosts: web
  vars:
    http_port: 80
    defined_name: "Hello My name is Jingjng"
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: Write the configuration file
    template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
    notify:
    - restart apache
  - name: Write the default index.html file
    template: src=templates/index2.html.j2 dest=/var/www/html/index.html
  - name: ensure apache is running
    service: name=httpd state=started
  - name: insert firewalld rule for httpd
    firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
  handlers:
    - name: restart apache
      service: name=httpd state=restarted

template變量的使用

Ansible模版文件中使用變量的語法是Python的template語言Jinja2

Ansible模版文件使用變量的語法是Python的template語言Jinja2。

在下面的例子template index.html.j2中,直接使用了以下變量:

系統變量 {{ ansible_hostname }} , {{ ansible_default_ipv4.address }}

用戶自定義的變量 {{ defined_name }}

ndex.html.j2文件:

<html>
<title>Demo</title>
<body>
<div class="block" style="height: 99%;">
    <div class="centered">
        <h1>#46 Demo {{ defined_name }}</h1>
        <p>Served by {{ ansible_hostname }} ({{ ansible_default_ipv4.address }}).</p>
    </div>
</div>
</body>
</html>

七、把運行結果當做變量使用-注冊變量

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

---
- hosts: web
  tasks:
     - shell: ls
       register: result
       ignore_errors: True
     - shell: echo "{{ result.stdout }}"
       when: result.rc == 5
     - debug: msg="{{ result.stdout }}"

注冊變量經常和debug module一起使用,這樣可以得到更多的關於執行錯誤的信息,幫助用戶調試。

八、用命令行中傳遞變量

用命令行傳遞參數

為了使Playbook更靈活、通用性更強,允許用戶在執行的時候傳入變量的值,這個時候就需要用到“額外變量”。

定義命令行變量

在release.yml文件里,hosts和user都定義為變量,需要從命令行傳遞變量值。

---
- hosts: '{{ hosts }}'
  remote_user: '{{ user }}'
  tasks:
     - ...

使用命令行變量

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

ansible-playbook e33_var_in_command.yml --extra-vars "hosts=web user=root"
還可以用json格式傳遞參數:

ansible-playbook e33_var_in_command.yml --extra-vars "{'hosts':'vm-rhel7-1', 'user':'root'}"
還可以將參數放在文件里面:

ansible-playbook e33_var_in_command.yml --extra-vars "@vars.json"


免責聲明!

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



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