Ansible進階

shijingjing02發表於2016-06-07

本章的目的是讓你能夠上手寫ansible的指令碼,並且能夠看懂一些高階一點的ansible playbook指令碼。

深入介紹一下幾個主題

  • Ansible的配置
  • Ansible的主機目錄管理(Host Inventory)
  • Ansible Playbook的進階語法
  • 配置Extra Modules

ansible的配置

ansible的配置

可以配置什麼?

從基本的,主機目錄檔案"inventory",extra module放置路徑"library",管理節點上臨時檔案的位置"remote_tmp",遠端主機的臨時檔案位置"local_tmp"

inventory      = /etc/ansible/hosts
library        = /usr/share/my_modules/
remote_tmp     = $HOME/.ansible/tmp
local_tmp      = $HOME/.ansible/tmp

到高階的,連線埠號"accelerate_port",超時時間等。

accelerate_port = 5099
accelerate_timeout = 30
accelerate_connect_timeout = 5.0

看一個完整的anbile配置檔案例子,就能基本瞭解到ansible都能配置什麼了:

https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg

對ansible配置檔案裡面的關鍵字不能完整理解,還可以參考關鍵詞解釋列表:

http://docs.ansible.com/ansible/intro_configuration.html#explanation-of-values-by-section

anbile配置檔案的優先順序

ansible的預設配置檔案是/etc/ansible/ansible.cfg。其實ansible會按照下面的順序查詢配置檔案,並使用第一個發現的配置檔案。

* ANSIBLE_CONFIG (an environment variable)
* ansible.cfg (in the current directory)
* .ansible.cfg (in the home directory)
* /etc/ansible/ansible.cfg

Ansible1.5以前的版本順序為:

* ansible.cfg (in the current directory)
* ANSIBLE_CONFIG (an environment variable)
* .ansible.cfg (in the home directory)
* /etc/ansible/ansible.cfg

主機目錄(Host Inventory)

什麼叫主機目錄管理,告訴ansible需要管理哪些server,和server的分類和分組資訊。可以根據你自己的需要根據地域分類,也可以按照功能的不同分類。

主機目錄的配置檔案

預設檔案

/etc/ansible/hosts

修改主機目錄的配置檔案

/etc/ansible/ansible.cfg

...  
inventory      = /etc/ansible/hosts  
...  

命令列中傳遞主機目錄配置檔案

$ ansible-playbook -i hosts site.yml
或者引數--inventory-file
$ ansible-playbook --inventory-file hosts site.yml

遠端主機的分組

遠端主機的分組

簡單的分組[]內是組名 mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

[webservers]
www[01:50].example.com

[databases]
db-[a:f].example.com

分組的子組[usa:children]

[atlanta]
host1
host2

[raleigh]
host2
host3

[southeast:children]
atlanta
raleigh


[usa:children]
southeast
northeast
southwest
northwest

指定連線的引數

指定連線的引數

引數

指定Server的連線引數,其中包括連線方法,使用者等。

[targets]

localhost              ansible_connection=local
other1.example.com     ansible_connection=ssh        ansible_user=mpdehaan
other2.example.com     ansible_connection=ssh        ansible_user=mdehaan


[atlanta]
host1 http_port=80 maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909

可以指定的引數在文件中 http://docs.ansible.com/ansible/intro_inventory.html#list-of-behavioral-inventory-parameters

變數

為一個組指定變數

[atlanta]
host1
host2

[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com

按目錄結構儲存變數

按目錄結構儲存變數

假設inventory檔案為/etc/ansible/hosts,那麼相關的hosts和group變數可以放在下面的目錄結構下

/etc/ansible/group_vars/raleigh # can optionally end in '.yml', '.yaml', or '.json'
/etc/ansible/group_vars/webservers
/etc/ansible/host_vars/foosball

/etc/ansible/group_vars/raleigh 檔案內容可以為

ntp_server: acme.example.org
database_server: storage.example.org

如果對應的名字為目錄名,ansible會讀取這個目錄下面所有檔案的內容 /etc/ansible/group_vars/raleigh/db_settings /etc/ansible/group_vars/raleigh/cluster_settings

group_vars/ 和 host_vars/ 目錄可放在 inventory 目錄下,或是 playbook 目錄下. 如果兩個目錄下都存在,那麼 playbook 目錄下的配置覆蓋 inventory 目錄的配置.

Ansible的指令碼Playbook

Playbook

YML

ansible的指令碼語言,yaml格式. 請參考YAML語法結構章節

別人的Playbook

能夠學會快速用別人的成果,高效的分享自己的成果,才是好碼農,才是能早下班的好碼農。在你動手從頭開始寫一個Playbook之前,不如先參考一下別人的成果吧。

官方例子

Ansible官方提供了一些比較常用的,經過測試的Playbook例子:

https://github.com/ansible/ansible-examples

Playbook分享平臺

此外,Ansible還提供了Playbook的分享平臺,上面的例子是Ansible使用者自己上傳的。你如果在沒有思路的情況下參考下吧,不過一定要再重新嚴謹的測試下。

https://galaxy.ansible.com/

Playbook基本語法

Playbook基本語法

本節列舉了寫第一個Playbook,你必須瞭解基本語法。

隨著你面臨的機器越多,配屬的需求越複雜,你可能需要了解後面介紹的一些稍微複雜邏輯語句。

執行Playbook語法

bash $ ansible-playbook deploy.yml

檢視輸出的細節
ansible-playbook playbook.yml --list-hosts

檢視該指令碼影響哪些hosts
ansible-playbook playbook.yml --list-hosts

並行執行指令碼
ansible-playbook playbook.yml -f 10

完整的playbook指令碼示例

最基本的playbook指令碼分為三個部分:

  1. 在什麼機器上以什麼身份執行
    • hosts
    • users
    • ...
  2. 執行的任務是都有什麼
    • tasks
  3. 善後的任務都有什麼
    • 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

Playbook中定義主機和使用者

主機和使用者

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

指令碼里用became的時候,執行的playbook的時候可以加引數--ask-become-pass

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

Tasks任務列表

Tasks任務列表

  • tasks是從上到下順序執行,如果中間發生錯誤,那麼整個playbook會中止。你可以改修檔案後,再重新執行。
  • 每一個task的對module的一次呼叫。使用不同的引數和變數而已。
  • 每一個task必須有一個name屬性,這個是供人讀的,然後會在命令列裡面輸出,提示使用者執行情況。

語法

task的基本寫法

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中會去檢查當前系統狀態是否需要重新執行. 具體的判斷規則由各個module自己實現.

  • 如果執行那麼action會得到返回值changed;
  • 如果不需要執行,那麼action得到返回值ok

"copy" module的判斷方法是比較檔案的checksum,程式碼如下:

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 enter image description here

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

TASK的狀態是ok,由於第一次執行copy_hosts.yml的時候,已經拷貝過檔案,那麼ansible目標檔案的狀態避免重複執行. enter image description here

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

enter image description here

響應事件(handler)

響應事件Handler

什麼是handler?

每個主流的程式語言都會有event機制,那麼handler就是playbook的event。

Handlers裡面的每一個handler,也是對module的一次呼叫。而handler與tasks不同的是,handlers不會預設的按順序執行。

Tasks中的任務都是有狀態的,changed或者ok。 Ansible提供了一種機制,只在task的執行狀態為changed的時候,才會觸發執行,這就是handler。

應用場景

什麼情況下使用handlers呢?

如果你在tasks中修改了apache的配置檔案。需要重起apache。此外還安裝了apache的外掛。那麼還需要重起apache。像這樣的應該場景中,重起apache就可以設計成一個handler.

一個handler最多隻執行一次

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

在下面的例子裡apache只執行一次

https://github.com/ansible-book/ansible-first-book-examples/blob/master/handlers_state.yml

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

測試程式碼見:

https://github.com/shijingjing1221/ansible-first-book-examples/blob/master/handlers_execution_time.yml

---
- 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(include語句)

Include語句

Include語句的功能,基本的程式碼重用機制。主要重用tasks。

普通用法

像其它語言的Include語句一樣,直接Include:

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

高階用法-加引數

加引數

tasks:
  - include: tasks/firewall.yml port=80
  - include: tasks/firewall.yml port=3260
  - include: tasks/firewall.yml port=423

還可以這樣加:

tasks:

  - include: wordpress.yml
    vars:
        wp_user: timmy
        ssh_keys:
          - keys/one.txt
          - keys/two.txt

還可以簡寫成:

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

在handlers裡面加include

handlers:
  - include: handlers/handlers.yml

在全域性加include時,tasks和handlers不能有include

- name: this is a play at the top level of a file
  hosts: all
  remote_user: root

  tasks:

  - name: say hi
    tags: foo
    shell: echo "hi..."

- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml

include裡面的handlers在外面呼叫不了

不知道為什麼有一處文件裡面寫可以呼叫。文件下面兩個地方提到include裡面的handlers,但是兩處是矛盾的:

  • hander的文件寫不能呼叫 http://docs.ansible.com/ansible/playbooks_intro.html
  • include的文件寫能呼叫 http://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse

通過下面的例子實測後,是不能呼叫include裡面的handler的。

---
- hosts: lb
  user: root
  gather_facts: no
  vars:
      random_number: "{{ 10000 | random }}"
  tasks:
  - name: Copy the /etc/hosts to /tmp/hosts.{{ random_number }}
    copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number }}
    notify:
      - restart apache
      - restart apache in handlers


  handlers:
    - include: handlers/handlers.yml
    - name: restart apache
      debug: msg="This is the handler restart apache"

ansible的“函式"(role語句)

Role語句

比include更強大的程式碼重用機制。一個role可以包含vars_files, tasks, and handlers等等. 通常一個role定義瞭如何完成一個特定的功能,比如安裝Webservers可以寫成一個role, 安裝Database可以寫成一個role.

Ansible提供了一個分享role的平臺, https://galaxy.ansible.com/, 在galaxy上可以找到別人寫好的role.

Role的目錄結構

在ansible中,通過遵循特定的目錄結構,就可以實現對role的定義.

下面的目錄結構定義了兩個role,一個是common,另外一個是webservers. 在site.yml,呼叫了這兩個role.

role的目錄結構

site.yml
roles/
   common/  
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/
   webservers/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/

site.yml中的使用

---
- hosts: webservers
  roles:
     - common
     - webservers

帶引數的Role

定義帶引數的role

定義一個帶引數的role,名字是role_with_var,那麼目錄結構為

 main.yml
 roles
   role_with_var
     tasks
       main.yml

在roles/rolw_with_var/tasks/main.yml中,直接使用定義的變數就可以了

 ---
 - name: use param
   debug: msg="{{ param }}"

使用帶引數的role

那麼在main.yml就可以用如下的方法使用role_with_var

---

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

指定預設的引數

指定預設引數後,如果在呼叫時傳引數了,那麼就使用傳入的引數值.如果呼叫的時候沒有傳引數,那麼就使用預設的引數值.

指定預設引數很簡單,以上面的role_with_var為例

main.yml
roles:
  role_with_var
    tasks
      main.yml
    vars
      main.yml

在roles/role_with_var/vars/main.yml中,使用yml的字典定義語法定義param的值,如下:
param: "I am the default value"

這樣在main.yml中,下面兩種呼叫方法都可以

---

- hosts: webservers
  roles:
    - role_with_var
    - { role: role_with_var, param: 'I am the value from external' }

更多的例子在https://github.com/shijingjing1221/ansible-first-book-examples/blob/master/role_vars.yml 中

與條件語句一起執行

下面的例子中,some_role只有在RedHat系列的server上才執行.

---

- hosts: webservers
  roles:
    - { role: some_role, when: "ansible_os_family == 'RedHat'" }

執行順序

pre_tasks > role > tasks > post_tasks

---

- hosts: vm-rhel7-1
  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'

看例子!!!

條件語句when

條件語句When

類似於程式語言的if

When語句

有時候使用者有可能需滿足特定條件才執行某一個特定的步驟。在某一個特定版本的系統上裝包,或者只在磁碟空間滿了的檔案系統上執行清理操作一樣。這些操作在Ansible上,使用when語句都非常簡單.

主機為Debian Linux立刻關機

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

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

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”還可以轉換返回值的型別:

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

條件表示式

vars:
  epic: true

基本款

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

否定款:

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

變數定義款

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

數值表達款

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

與Include一起用

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

與Role一起用

- hosts: webservers
  roles:
     - { role: debian_stock_config, when: ansible_os_family == 'Debian' }

迴圈語句loop

Loop迴圈

標準迴圈

為了保持簡潔,重複的任務可以用以下簡寫的方式:

- name: add several users
  user: name={{ item }} state=present groups=wheel
  with_items:
     - testuser1
     - testuser2

如果你在變數檔案中或者 ‘vars’ 區域定義了一組YAML列表,你也可以這樣做:

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

使用 ‘with_items’ 用於迭代的條目型別不僅僅支援簡單的字串列表.如果你有一個雜湊列表,那麼你可以用以下方式來引用子項:

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

請note如果同時使用 when 和 with_items (或其它迴圈宣告),when宣告會為每個條目單獨執行.請參見 the_when_statement 示例.

巢狀迴圈

迴圈也可以巢狀:

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

對雜湊表使用迴圈

---
vars:
  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 可以以非遞迴的方式來模式匹配單個目錄中的檔案.如下面所示:

  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

Block塊

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

 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

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

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"

變數

變數

定義

使用yml格式定義

foo:
  field1: one
  field2: two

使用變數

使用Python的template語言Jinja2的語法引用:利用中括號和點號來訪問子屬性

foo['field1']
foo.field1

Playbook中使用的變數

Playbook中使用的變數

在Playbook中使用,需要用{{ }}引用以來即可:

- hosts: webservers
  vars:
      apache_config: labs.conf
  tasks:
      - name: deploy haproxy config
        template: src={{ apache_config }} dest=/etc/httpd/conf.d/{{ apache_config }}

在Playbook中使用變數檔案定義變數

- hosts: webservers
  vars_files:
      - vars/server_vars.yml
  tasks:
      - name: deploy haproxy config
        template: src={{ apache_config }} dest=/etc/httpd/conf.d/{{ apache_config }}  

變數檔案vars/server_vars.yml的內容為:

apache_config: labs.conf

YAML的陷阱

YAML的陷阱是YAML和Ansible Playbook的變數語法不能在一起好好工作了。這裡特指冒號後面的值不能以{ 開頭。

下面的程式碼會報錯:

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

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

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

主機的系統變數(facts)

主機的系統變數(facts)

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

哪些facts變數可以引用呢?在命令列上通過呼叫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

把執行結果當做變數使用

把執行結果當做變數使用-註冊變數

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

---

- hosts: web

  tasks:

     - shell: ls
       register: result
       ignore_errors: True

     - shell: echo "{{ result.stdout }}"
       when: result.rc == 5

     - debug: msg="{{ result.stdout }}"

檔案模板中使用的變數

檔案模板中使用的變數

檔案模板即template。Ansible使用的檔案是python的一個j2的模板。

template變數的定義

在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變數的使用

在template index.html.j2中可以直接使用系統變數和使用者自定義的變數

  • 系統變數 {{ ansible_hostname }} , {{ ansible_default_ipv4.address }}

  • 使用者自定義的變數 {{ defined_name }}

index.html.j2檔案:

<html>
<title>#46 Demo</title>

<!--
http://stackoverflow.com/questions/22223270/vertically-and-horizontally-center-a-div-with-css
http://css-tricks.com/centering-in-the-unknown/
http://jsfiddle.net/6PaXB/
-->

<style>.block {text-align: center;margin-bottom:10px;}.block:before {content: '';display: inline-block;height: 100%;vertical-align: middle;margin-right: -0.25em;}.centered {display: inline-block;vertical-align: middle;width: 300px;}</style>

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

用命令列傳遞引數

用命令列傳遞引數

定義命令列變數

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

更多的Ansible模組

更多的Ansible模組

  • 介紹兩類Modules: Core Module和Extra module
  • Extra module的配置和使用方法
  • 通過命令列檢視modules的用法

Modules的分類

Modules的分類

Ansible Module文件上檢視單個Module的時候,每一個Module文件的底部都會標識, 這是一個"Core Module", 或者這是一個"Extra Module".

比如, yum就是一個core module, yum_repository就是一個extra module,

Core Module

  • 不需要格外下載和配置就可以直接使用的.
  • 比較常用的module
  • 經過嚴格測試的.

Extra module

  • 進行下載和格外的配置才能使用
  • 次常用的
  • 還有可能存在bug的

Extra module的使用方法

Extra module的使用方法

使用Exra module需要進行下面的配置,就可以在命令列或者是playbook中使用了。配置後extra module使用方法和core module的使用方法是一樣的。

1 下載ansible module extra專案

git clone https://github.com/ansible/ansible-modules-extras.git

我的一下在/home/jshi/software/目錄下了,後面會用到。

2 修改配置檔案或者環境變數

方法1 - 改ansible預設配置檔案/etc/ansible/ansible.cfg

修改ansible配置檔案/etc/ansible/ansible.cfg, 新增一行

library    = /home/jshi/software/ansible-modules-extras/

方法2 - 改ansible當前目錄下配置檔案ansible.cfg

改ansible playbook當前的目錄下的配置檔案ansible.cfg,那麼只對當前目錄的playbook生效。對所有其它目錄,包括父目錄和子目錄的playbook都不生效。

library/ansible-modules-extras
ansible.cfg
use_extra_module.yml
subfolder/use_extra_module_will_throw_error.yml

在當前目錄的ansible.cfg中,可以使用相對路徑:

library = library/ansible-modules-extras/

方法3 - 該環境變數

export ANSIBLE_LIBRARY=/project/demo/demoansible/library/ansible-module-extras

如果需要在重啟後生效,那麼放在~/.bashrc中宣告ANSIBLE_LIBRARY變數:

$ echo >>~/.bashrc <<EOF 
export ANSIBLE_LIBRARY=/project/demo/demoansible/library/ansible-module-extras  
EOF    
$ source ~/.bashrc

命令列檢視module的用法

命令列檢視module的用法

類似bash命令的man,ansible也可以通過命令列檢視module的用法。命令是ansible-doc,語法如下:
ansible-doc module_name

core module可以在任何目錄下執行。例如檢視yum的用法:
ansible-doc yum

extra module必須在配置了extra module的目錄下檢視用法(行為當前目錄下的playbook是一致的): ansible-doc yum_repository