Ansible第二篇:ansible-playbook

一、Playbook語(yǔ)法

Ansible-playbook采用YAML語(yǔ)法編寫捻爷。

示例:
[root@LOCALHOST ~]# cat yaml/httpd.yaml

---
- hosts: control-node    # 將要執(zhí)行任務(wù)的主機(jī)勒极,已經(jīng)在hosts文件中定義好了病梢,可是單個(gè)主機(jī)或主機(jī)組
  remote_user: root      # 在目標(biāo)主機(jī)上執(zhí)行任務(wù)時(shí)的用戶身份
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd     # 當(dāng)這個(gè)任務(wù)執(zhí)行狀態(tài)發(fā)生改變時(shí)仰迁,觸發(fā)handlers執(zhí)行.
    - name: "boot httpd service."
      service: name=httpd state=started
  handlers:      # handlers與tasks是同一級(jí)別
    - name: restart httpd
      service: name=httpd state=restarted

連續(xù)的項(xiàng)目(即列表)用 -減號(hào)來(lái)表示箕般,key/value(字典)用冒號(hào):分隔耐薯。

playbook語(yǔ)法有如下特性:

  1. --- (三個(gè)減號(hào))開始,必須頂行寫;
  2. 次行開始寫Playbook的內(nèi)容曲初,但是一般要求寫明該playbook的功能体谒;
  3. 嚴(yán)格縮進(jìn),并且不能用Tab鍵縮進(jìn)臼婆;
  4. 縮進(jìn)級(jí)別必須是一致的抒痒,同樣的縮進(jìn)代表同樣的級(jí)別,程序判別配置的級(jí)別是通過(guò)縮進(jìn)結(jié)合換行來(lái)實(shí)現(xiàn)的颁褂;
  5. K/V的值可同行寫评汰,也可換行寫。同行使用 :分隔,換行寫需要以 - 分隔;

執(zhí)行playbook:

[root@LOCALHOST ~]# ansible-playbook yaml/httpd.yaml 

PLAY [control-node] ********************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [openstack-control2]
ok: [openstack-control1]

TASK [install httpd package.] **********************************************************************************************
changed: [openstack-control2]
changed: [openstack-control1]

TASK [copy httpd configure file to remote host.] ***************************************************************************
changed: [openstack-control1]
changed: [openstack-control2]

TASK [boot httpd service.] *************************************************************************************************
changed: [openstack-control2]
changed: [openstack-control1]

PLAY RECAP *****************************************************************************************************************
openstack-control1         : ok=4    changed=3    unreachable=0    failed=0   
openstack-control2         : ok=4    changed=3    unreachable=0    failed=0 

二再膳、Playbook中的變量

2.1 yaml文件中定義變量

[root@LOCALHOST ~]# cat yaml/vars.yaml 
---
- hosts: compute-node
  remote_user: root
  vars:
    pkg: httpd        # 定義變量
  tasks:
    - name: install httpd service
      yum: name={{ pkg }} state=installed     # 引用變量

playbook中用vars關(guān)鍵字聲明變量旗芬,變量定義 變量名: 變量值
變量引用 :{{ 變量名 }}

2.2 引用外部變量

在命令行使用--extra-vars參數(shù)賦值變量;

[root@LOCALHOST ~]# cat yaml/extra_vars.yaml 
---
- hosts: control-node
  remote_user: root
  tasks:
    - name: create a file
      file:
        path: /tmp/{{ filename }}    # 引用外部變量
        mode: 0644
        state: touch

命令行使用--extra_vars傳入變量:

[root@LOCALHOST ~]# ansible-playbook yaml/extra_vars.yaml --extra-vars "filename=temp.txt"

--extra_vars "變量名=變量值"

2.3 資產(chǎn)清單(inventory)中定義的變量

也就是在/etc/ansible/hosts文件中定義的變量;

[root@LOCALHOST ~]# cat /etc/ansible/hosts
[load-node]
openstack-load1 
openstack-load2

[compute-node]
openstack-compute1 ansible_ssh_host=10.0.1.10 ansible_ssh_port=2002 ansible_ssh_user=stanley ansible_ssh_pass=etyfhzmweadf
openstack-compute2

[control-node]
openstack-control1 filename=control1.txt    # 主機(jī)變量
openstack-control2 filename=control2.txt

[openstack:children]
load-node
compute-node
control-node

[openstack:vars]
issue="Hello, World"    # 組變量

注意:組變量定義時(shí),不要落下關(guān)鍵字vars[組名:vars]
在playbook中引用{{ 變量名 }}即可坯墨。

ansible內(nèi)置了一些固定的主機(jī)變量名,在inventory中定義其值病往,如下:

ansible_ssh_host
      將要連接的遠(yuǎn)程主機(jī)名.與你想要設(shè)定的主機(jī)的別名不同的話,可通過(guò)此變量設(shè)置.

ansible_ssh_port
      ssh端口號(hào).如果不是默認(rèn)的端口號(hào),通過(guò)此變量設(shè)置.

ansible_ssh_user
      默認(rèn)的 ssh 用戶名

ansible_ssh_pass
      ssh 密碼(這種方式并不安全,我們強(qiáng)烈建議使用 --ask-pass 或 SSH 密鑰)

ansible_sudo_pass
      sudo 密碼(這種方式并不安全,我們強(qiáng)烈建議使用 --ask-sudo-pass)

ansible_sudo_exe (new in version 1.8)
      sudo 命令路徑(適用于1.8及以上版本)

ansible_connection
      與主機(jī)的連接類型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默認(rèn)使用 paramiko.1.2 以后默認(rèn)使用 'smart','smart' 方式會(huì)根據(jù)是否支持 ControlPersist, 來(lái)判斷'ssh' 方式是否可行.

ansible_ssh_private_key_file
      ssh 使用的私鑰文件.適用于有多個(gè)密鑰,而你不想使用 SSH 代理的情況.

ansible_shell_type
      目標(biāo)系統(tǒng)的shell類型.默認(rèn)情況下,命令的執(zhí)行使用 'sh' 語(yǔ)法,可設(shè)置為 'csh' 或 'fish'.

ansible_python_interpreter
      目標(biāo)主機(jī)的 python 路徑.適用于的情況: 系統(tǒng)中有多個(gè) Python, 或者命令路徑不是"/usr/bin/python",比如  \*BSD, 或者 /usr/bin/python
      不是 2.X 版本的 Python.我們不使用 "/usr/bin/env" 機(jī)制,因?yàn)檫@要求遠(yuǎn)程用戶的路徑設(shè)置正確,且要求 "python" 可執(zhí)行程序名不可為 python以外的名字(實(shí)際有可能名為python26).

      與 ansible_python_interpreter 的工作方式相同,可設(shè)定如 ruby 或 perl 的路徑....

參考Ansible中文權(quán)威指南捣染。

2.4 直接使用facts變量

使用命令ansible all -m setup獲取到的主機(jī)信息,其中的KEY都可以被當(dāng)作變量引用:

[root@LOCALHOST ~]# cat yaml/facts_vars.yaml 
---
- hosts: compute-node
  remote_user: root
  tasks:
    - name: write fqdn into fqdn.log
      shell: "echo {{ ansible_fqdn }} {{ ansible_eth1.ipv4.address }} > /tmp/fqdn.log"

上述代碼中{{ ansible_fqdn }}就是直接引用的facts變量停巷;
{{ ansible_eth1.ipv4.address }} 引用的值就相當(dāng)于下面這樣耍攘。

{ 
    "ansible_eth1": {
        "ipv4": {
            "address":10.0.1.10,
        },
    },
}

最終{{ ansible_eth1.ipv4.address }}=10.0.1.10

2.5 注冊(cè)變量

在playbook中用register關(guān)鍵字定義一個(gè)變量,這個(gè)變量的值就是當(dāng)前任務(wù)執(zhí)行的輸出結(jié)果畔勤;

[root@LOCALHOST ~]# cat yaml/reg_vars.yaml 
---
- hosts: load-node
  remote_user: root
  tasks:
    - name: show date
      shell: "/bin/date"
      register: date        # 注冊(cè)一個(gè)變量
    - name: Record time log
      shell: "echo {{ date.stdout }} > /tmp/date.log"

引用注冊(cè)變量要用 {{ date.stdout }}表示標(biāo)準(zhǔn)輸出蕾各。

[root@openstack-load1 ~]# cat /tmp/date.log 
2018年 03月 29日 星期四 15:52:01 CST

如果直接{{ date }}這樣引用,則文件中寫入的是如下內(nèi)容:

{stderr_lines: [], uchanged: True, uend: u2018-03-29 15:49:52.609894, failed: False, ustdout: u2018\u5e74 03\u6708 29\u65e5 \u661f\u671f\u56db 15:49:52 CST, ucmd: u/bin/date, urc: 0, ustart: u2018-03-29 15:49:52.602918, ustderr: u, udelta: u0:00:00.006976, stdout_lines: [u2018\u5e74 03\u6708 29\u65e5 \u661f\u671f\u56db 15:49:52 CST]}

2.6 變量?jī)?yōu)先級(jí)

上述這些變量定義的方法庆揪,它們的優(yōu)先級(jí)如下:

  1. 在命令行中定義的變量(即用-e--extra-vars定義的變量)式曲;
  2. 在Inventory中定義的連接變量(比如:ansible_ssh_user);
  3. 大多數(shù)的其他變量(命令行轉(zhuǎn)換、play中的變量缸榛、included的變量吝羞、role中的變量等);
  4. 在Inventory中定義的其他變量内颗;
  5. Facts變量钧排;
  6. “Role”默認(rèn)變量,這個(gè)是默認(rèn)的值起暮,很容易喪失優(yōu)先權(quán)卖氨。

三、Playbook中的方法

3.1 handlers

在需要被監(jiān)控的任務(wù)(tasks)中定義一個(gè)notify负懦,只有當(dāng)這個(gè)任務(wù)被執(zhí)行時(shí)筒捺,才會(huì)觸發(fā)notify對(duì)應(yīng)的handlers去執(zhí)行相應(yīng)操作。

[root@LOCALHOST ~]# cat yaml/httpd.yaml 
---
- hosts: control-node
  remote_user: root
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd
    - name: "boot httpd service."
      service: name=httpd state=started
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

在使用handlers的過(guò)程中纸厉,有以下幾點(diǎn)需要注意:

  1. handlers只有在其所在的任務(wù)被執(zhí)行時(shí)系吭,都會(huì)被運(yùn)行;
  2. handlers只會(huì)在Play的末尾運(yùn)行一次颗品;如果想在一個(gè)Playbook的中間運(yùn)行handlers肯尺,則需要使用meta模塊來(lái)實(shí)現(xiàn),例如:- meta: flush_handlers躯枢。
  3. 如果一個(gè)Play在運(yùn)行到調(diào)用handlers的語(yǔ)句之前失敗了则吟,那么這個(gè)handlers將不會(huì)被執(zhí)行。我們可以使用mega模塊的--force-handlers選項(xiàng)來(lái)強(qiáng)制執(zhí)行handlers锄蹂,即使在handlers所在Play中途運(yùn)行失敗也能執(zhí)行氓仲。

3.2 tags

tags用于讓用戶選擇運(yùn)行playbook中的部分代碼。ansible具有冪等性得糜,因此會(huì)自動(dòng)跳過(guò)沒(méi)有變化的部分敬扛,即便如此,有些代碼為測(cè)試其確實(shí)沒(méi)有發(fā)生變化的時(shí)間依然會(huì)非常地長(zhǎng)朝抖。此時(shí)啥箭,如果確信其沒(méi)有變化,就可以通過(guò)tags跳過(guò)此些代碼片斷治宣。

ansible的標(biāo)簽(Tags)功能可以給角色(Roles)急侥、文件、單獨(dú)的任務(wù)侮邀,甚至整個(gè)Playbook打上標(biāo)簽缆巧,然后利用這些標(biāo)簽來(lái)指定要運(yùn)行Playbook中的個(gè)別任務(wù),或不執(zhí)行指定的任務(wù)豌拙。

[root@LOCALHOST ~]# cat yaml/httpd.yaml 
---
- hosts: control-node
  remote_user: root
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd
    - name: "start httpd service."
      tags:
        - start_httpd     # 給“start httpd service”這個(gè)任務(wù)打個(gè)標(biāo)簽
      service: name=httpd state=started
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

在命令行中調(diào)用:

[root@LOCALHOST ~]# ansible-playbook yaml/httpd.yaml --tags start_httpd

PLAY [control-node] ********************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [openstack-control1]
ok: [openstack-control2]

TASK [start httpd service.] ************************************************************************************************
ok: [openstack-control1]
ok: [openstack-control2]

PLAY RECAP *****************************************************************************************************************
openstack-control1         : ok=2    changed=0    unreachable=0    failed=0   
openstack-control2         : ok=2    changed=0    unreachable=0    failed=0 

四陕悬、Playbook的流程控制

4.1 when 條件判斷

當(dāng)關(guān)鍵字when后面的條件滿足時(shí)(也就是通過(guò)運(yùn)算得到的結(jié)果為true時(shí)),才會(huì)執(zhí)行當(dāng)前任務(wù)按傅。

示例如下(yaml/when.yaml):

---
- hosts: load-node,img
  remote_user: root
  tasks:
  - name: "touch flag file"
    command: "touch /tmp/this_is_{{ ansible_distribution }}_system" 
    when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == '6') or
          (ansible_distribution == "Debian" and ansible_distribution_major_version == '7')

上述代碼中捉超,ansible_distributionansible_distribution_major_version都是是Facts變量,分別表示Linux發(fā)行版和版本號(hào)

執(zhí)行結(jié)果如下圖:

image.png

4.2 循環(huán)語(yǔ)句

Playbook中的循環(huán)類型唯绍,如下表:

循環(huán)類型 關(guān)鍵字
標(biāo)準(zhǔn)循環(huán) with_items
遍歷字典 with_dict
遍歷目錄文件 with_fileglob
遍歷文件列表的內(nèi)容 with_file
嵌套循環(huán) with_nested
并行遍歷列表 with_together
遍歷列表和索引 with_indexed_items
重試循環(huán) until
查找第一個(gè)匹配文件 with_first_found
隨機(jī)選擇 with_random_choice
在序列中循環(huán) with_sequence

注意:列表中前三個(gè)為常用的循環(huán)語(yǔ)句拼岳。

4.2.1 標(biāo)準(zhǔn)循環(huán) with_items

示例1(with_items_1.yaml):

---
- name: when and with_items
  hosts: load-node
  remote_user: root
  gather_facts: false            
  tasks:
  - name: Create thress groups
    group: name=testgroup6 state=present
    ignore_errors: yes
    register: excu_result
  - name: Append excu_result to tmp.txt
    shell: "echo {{ excu_result }} > /tmp/tmp.txt"
  - name: Create some users
    user: name={{ item }} group=testgroup6 state=present
    when: excu_result|success
    with_items:
      - testuser1
      - testuser2
      - testuser3

釋義:

  • gather_facts: false表示運(yùn)行此playbook時(shí)不收集目標(biāo)主機(jī)的系統(tǒng)信息。因?yàn)槟J(rèn)此項(xiàng)是開啟的况芒,每次運(yùn)行playbook都會(huì)收集主機(jī)facts惜纸,這會(huì)影響playbook的運(yùn)行速度。將gather_facts設(shè)為false即可關(guān)閉。

  • when:excu_result|success的意思為當(dāng)變量excu_result執(zhí)行結(jié)果為成功狀態(tài)耐版,則執(zhí)行當(dāng)前的任務(wù)祠够。其中success為Ansible內(nèi)部過(guò)濾器方法,返回True代表命令運(yùn)行成功粪牲。還有excu_result|failed表示excu_result執(zhí)行結(jié)果為失敗狀態(tài)古瓤;excu_result|skipped表示excu_result執(zhí)行被跳過(guò)。

  • with_items的值還可以寫成[testuser1, testuser2, testuser3]

示例2:
添加多個(gè)用戶腺阳,并將用戶加入不同的組內(nèi):

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

4.2.2 遍歷字典 with_dict

輸出用戶的姓名和電話:

---
- hosts: load-node
  remote_user: root
  tasks:
  - name: print phone records
    debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.phone }})"
    with_dict: {'alice':{'name':'Alice Appleworth', 'phone':'13243252136'}, 'bob':{'name':'Bob Banarama', 'phone': '18766761211'}}

注意:Playbook中對(duì)字典的循環(huán)落君,與python中對(duì)字典循環(huán)類似,取值方法也一樣亭引。

4.2.3 遍歷目錄文件 with_fileglob

遍歷/root/sh/目錄下的所有文件绎速,并將其拷貝至目標(biāo)主機(jī)。

[root@LOCALHOST ~]# cat yaml/with_fileglob.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: create a directory
    file: path=/root/script state=directory
    ignore_errors: yes
    register: result
  - name: copy some scripts
    copy: src={{ item }} dest=/root/script owner=root mode=600
    when: result|success
    with_fileglob:
      - /root/sh/*

注意:with_fileglob匹配單個(gè)目錄中的所有文件焙蚓,非遞歸匹配模式朝氓。
作者: Jeson老師
鏈接:http://www.imooc.com/article/22753

執(zhí)行結(jié)果如下圖:

image.png

4.2.4 其它循環(huán)

其它循環(huán)請(qǐng)參考 Jeson老師的手記

五、Playbook任務(wù)計(jì)時(shí)插件

Github地址: https://github.com/jlafon/ansible-profile

安裝這個(gè)插件后會(huì)顯示 ansible-playbook 執(zhí)行每一個(gè)任務(wù)所花費(fèi)的時(shí)間主届。

這個(gè)插件安裝很簡(jiǎn)單赵哲,只需要簡(jiǎn)單的三個(gè)命令即可完成安裝。在你的 playbook 文件的目錄下創(chuàng)建一個(gè)目錄君丁,目錄名 callback_plugins 然后將下載的 profile_tasks.py 文件放到該目錄下即可枫夺。

# mkdir callback_plugins
# cd callback_plugins
# wget https://raw.githubusercontent.com/jlafon/ansible-profile/master/callback_plugins/profile_tasks.py
image.png

六、Playbook異常處理

6.1 ignore_errors

在有些情況下绘闷,一些必須運(yùn)行的命令或腳本會(huì)報(bào)一些錯(cuò)誤橡庞,而這些錯(cuò)誤并不一定真的說(shuō)明有問(wèn)題,但是經(jīng)常會(huì)給接下來(lái)要運(yùn)行的任務(wù)造成困擾印蔗,甚至直接導(dǎo)致playbook運(yùn)行中斷扒最。

這時(shí)候,我們可以在相關(guān)任務(wù)中添加ignore_errors: true來(lái)屏蔽當(dāng)前任務(wù)的報(bào)錯(cuò)信息华嘹。ansible也將視該任務(wù)運(yùn)行成功吧趣,不再報(bào)錯(cuò),這樣就不會(huì)對(duì)接下來(lái)要運(yùn)行的任務(wù)造成額外困擾耙厚。但是要注意的是强挫,我們不應(yīng)過(guò)度依賴ignore_errors,因?yàn)樗鼤?huì)隱藏所有的報(bào)錯(cuò)信息薛躬,而應(yīng)該把精力集中在尋找報(bào)錯(cuò)的原因上面俯渤,這樣才能從根本上解決問(wèn)題。

例如:

[root@LOCALHOST ~]# cat yaml/httpd.yaml 
---
- hosts: load-node
  remote_user: root
  vars:
    - pkg: httpd
  tasks:
    - name: "install httpd package."
      yum: name={{ pkg }}  state=installed
    - name: "copy httpd configure file to remote host."
      copy: src=/root/config/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart httpd
      ignore_errors: true         # 忽略錯(cuò)誤
    - name: "start httpd service."
      tags:
        - start_httpd
      service: name=httpd state=started
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted

上述代碼中型宝,任務(wù)"copy httpd configure file to remote host."中的src是一個(gè)不存在的路徑八匠,所以此任務(wù)運(yùn)行一定會(huì)出錯(cuò)絮爷。這個(gè)playbook運(yùn)行結(jié)果如下圖:

image.png

從運(yùn)行結(jié)果圖中可以看到有兩個(gè)...ignoring,表示忽略了這些錯(cuò)誤梨树,并繼續(xù)執(zhí)行下面的任務(wù)坑夯。

6.2 failed_when

當(dāng)滿足一定的條件時(shí),主動(dòng)拋出錯(cuò)誤劝萤。

[root@LOCALHOST ~]# cat yaml/failed_when.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: get process
    shell: ps aux | wc -l 
    register: process_count
    failed_when: process_count > 3
  - name: touch a file
    file: path=/tmp/test3.txt state=touch owner=root mode=0700

failed_when: process_count > 3當(dāng)進(jìn)程數(shù)大于3時(shí)主動(dòng)拋出錯(cuò)誤渊涝,后續(xù)任務(wù)就不會(huì)執(zhí)行了慎璧。如果不滿足條件床嫌,則不會(huì)拋出錯(cuò)誤。

6.3 changed_when

[root@LOCALHOST ~]# cat yaml/changed_when.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: touch a file
    file: path=/tmp/changed_test state=touch

上述Playbook執(zhí)行結(jié)果如下圖:

image.png

如果想要關(guān)閉狀態(tài)改變的提示胸私,則可以添加changed_when: false厌处,如下:

[root@LOCALHOST ~]# cat yaml/changed_when.yaml 
---
- hosts: DH-TEST
  remote_user: root
  gather_facts: false
  tasks:
  - name: touch a file
    file: path=/tmp/changed_testi2 state=touch
    changed_when: false       # 關(guān)閉狀態(tài)改變提示

執(zhí)行結(jié)果如下圖:

image.png

七、Roles

Roles是一種利用在大型Playbook中的劇本配置模式岁疼,它有著自己特定的結(jié)構(gòu)阔涉。用于層次性、結(jié)構(gòu)化地組織playbook捷绒。roles能夠根據(jù)層次型結(jié)構(gòu)自動(dòng)裝載變量文件瑰排、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可暖侨。簡(jiǎn)單來(lái)講椭住,roles就是通過(guò)分別將變量、文件字逗、任務(wù)京郑、模板及處理器放置于單獨(dú)的目錄中,并可以便捷地include它們的一種機(jī)制葫掉。角色一般用于基于主機(jī)構(gòu)建服務(wù)的場(chǎng)景中些举,但也可以是用于構(gòu)建守護(hù)進(jìn)程等場(chǎng)景中。

劇本roles設(shè)計(jì)思路:將公共任務(wù)俭厚、資源户魏、變量等對(duì)象盡可能獨(dú)立。

一個(gè)roles的案例如下所示:

site.yml            # 主入口文件
webservers.yml      # webserver類型服務(wù)所用的劇本
dbservers.yml       # 數(shù)據(jù)庫(kù)類型的服務(wù)所用的劇本
files/              # 存放通用的將要被上傳的文件
templates/          # 存放通用的模板文件
roles/              # roles目錄名稱是固定的
   common/          # 此目錄下的各個(gè)組件是所有角色共用的
     tasks/         # 存放通用的任務(wù)文件
     handlers/      # 存放通用的處理器文件
     vars/          # 存放通用的變量文件 
     meta/          # 存放通用的角色依賴文件
   webservers/      # 存放webserver類型的服務(wù)的各個(gè)組件  
     files/         # 存放webserver角色需要的上傳文件
     templates/     # 存放webserver角色需要的模板文件
     tasks/
     handlers/
     vars/
     meta/

而在playbook中挪挤,可以這樣使用roles:

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

也可以向roles傳遞參數(shù)绪抛,例如:

---
- hosts: webservers
  roles:
    - common
    - { role: foo_app_instance, dir: '/opt/a',  port: 5000 }
    - { role: foo_app_instance, dir: '/opt/b',  port: 5001 }

甚至也可以條件式地使用roles,例如:

---

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

7.1 創(chuàng)建role的步驟

(1) 創(chuàng)建以roles命名的目錄电禀;

(2) 在roles目錄中分別創(chuàng)建以各角色名稱命名的目錄幢码,如webservers等;

(3) 在每個(gè)角色命名的目錄中分別創(chuàng)建files尖飞、handlers症副、meta店雅、tasks、templates和vars目錄贞铣;用不到的目錄可以創(chuàng)建為空目錄闹啦,也可以不創(chuàng)建;

(4) 在roles目錄的同級(jí)目錄下創(chuàng)建一個(gè)yaml文件辕坝,如:site.yml 窍奋,在此文件中調(diào)用各角色;

7.2 role內(nèi)各目錄中可用的文件

tasks目錄:至少應(yīng)該包含一個(gè)名為main.yml的文件酱畅,其定義了此角色的任務(wù)列表琳袄;此文件可以使用include包含其它的位于此目錄中的task文件;

files目錄:存放由copy或script等模塊調(diào)用的文件纺酸;

templates目錄:template模塊會(huì)自動(dòng)在此目錄中尋找Jinja2模板文件窖逗;

handlers目錄:此目錄中應(yīng)當(dāng)包含一個(gè)main.yml文件,用于定義此角色用到的各handler餐蔬;在handler中使用include包含的其它的handler文件也應(yīng)該位于此目錄中碎紊;

vars目錄:應(yīng)當(dāng)包含一個(gè)main.yml文件,用于定義此角色用到的變量樊诺;

meta目錄:應(yīng)當(dāng)包含一個(gè)main.yml文件仗考,用于定義此角色的特殊設(shè)定及其依賴關(guān)系;ansible 1.3及其以后的版本才支持词爬;

default目錄:為當(dāng)前角色設(shè)定默認(rèn)變量時(shí)使用此目錄秃嗜;應(yīng)當(dāng)包含一個(gè)main.yml文件;

7.3 簡(jiǎn)單部署LAMP的Roles案例

先看一下這個(gè)roles的目錄結(jié)構(gòu):

[root@LOCALHOST ansible-examples-master]# tree [root@LOCALHOST ansible-examples-master]# tree lamp_simple
lamp_simple
├── group_vars
│   ├── all
│   └── dbservers
├── hosts
├── LICENSE.md
├── README.md
├── roles
│   ├── common
│   │   ├── handlers
│   │   │   └── main.yml
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── ntp.conf.j2
│   ├── db
│   │   ├── handlers
│   │   │   └── main.yml
│   │   ├── tasks
│   │   │   └── main.yml
│   │   └── templates
│   │       └── my.cnf.j2
│   └── web
│       ├── handlers
│       │   └── main.yml
│       ├── tasks
│       │   ├── copy_code.yml
│       │   ├── install_httpd.yml
│       │   └── main.yml
│       └── templates
│           └── index.php.j2
└── site.yml

14 directories, 17 files

查看各個(gè)playbook的內(nèi)容:

  • 查看主機(jī)清單文件
    # cat lamp_simple/hosts
[webservers]
web3

[dbservers]
web2
  • 查看主入口文件
    # cat lamp_simeple/site.yml
---
# This playbook deploys the whole application stack in this site.

- name: apply common configuration to all nodes
  hosts: all
  remote_user: root

  roles:
    - common

- name: configure and deploy the webservers and application code
  hosts: webservers
  remote_user: root

  roles:
    - web

- name: deploy MySQL and configure the databases
  hosts: dbservers
  remote_user: root

  roles:
    - db
  • 查看變量文件:

# cat lamp_simple/group_vars/all

---
# Variables listed here are applicable to all host groups

httpd_port: 80
ntpserver: 192.168.1.2
repository: https://github.com/bennojoy/mywebapp.git

# cat lamp_simple/group_vars/dbservers

---
# The variables file used by the playbooks in the dbservers group.
# These don't have to be explicitly imported by vars_files: they are autopopulated.

mysqlservice: mysqld
mysql_port: 3306
dbuser: foouser
dbname: foodb
upassword: abc
  • 查看通用hanlder文件:

# cat lamp_simple/roles/common/handlers/main.yml

---
# Handler to handle common notifications. Handlers are called by other plays.
# See http://docs.ansible.com/playbooks_intro.html for more information about handlers.

- name: restart ntp
  service: name=ntpd state=restarted

- name: restart iptables
  service: name=iptables state=restarted
  • 查看通用tasks文件:

# cat lamp_simple/roles/common/tasks/main.yml

---
# This playbook contains common plays that will be run on all nodes.

- name: Install ntp
  yum: name=ntp state=present
  tags: ntp

- name: Configure ntp file
  template: src=ntp.conf.j2 dest=/etc/ntp.conf
  tags: ntp
  notify: restart ntp

- name: Start the ntp service
  service: name=ntpd state=started enabled=yes
  tags: ntp

- name: test to see if selinux is running
  command: getenforce
  register: sestatus
  changed_when: false
  • 查看通用模板文件:

# cat lamp_simple/roles/common/templates/ntp.conf.j2

driftfile /var/lib/ntp/drift

restrict 127.0.0.1 
restrict -6 ::1

server {{ ntpserver }}

includefile /etc/ntp/crypto/pw

keys /etc/ntp/keys
  • 查看db角色的handlers文件:

# cat lamp_simple/roles/db/handlers/main.yml

---
# Handler to handle DB tier notifications

- name: restart mysql
  service: name=mysqld state=restarted

- name: restart iptables
  service: name=iptables state=restarted[root@LOCALHOST ansible-examples-master]# cat lamp_simple/roles/db/handlers/main.yml 
---
# Handler to handle DB tier notifications

- name: restart mysql
  service: name=mysqld state=restarted

- name: restart iptables
  service: name=iptables state=restarted
  • 查看db角色的tasks文件:

# cat lamp_simple/roles/db/tasks/main.yml

---
# This playbook will install mysql and create db user and give permissions.

- name: Install Mysql package
  yum: name={{ item }} state=installed
  with_items:
   - mysql-server
   - MySQL-python
   - libselinux-python
   - libsemanage-python

- name: Configure SELinux to start mysql on any port
  seboolean: name=mysql_connect_any state=true persistent=yes
  when: sestatus.rc != 0

- name: Create Mysql configuration file
  template: src=my.cnf.j2 dest=/etc/my.cnf
  notify:
  - restart mysql

- name: Start Mysql Service
  service: name=mysqld state=started enabled=yes

- name: insert iptables rule
  lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}"
              insertafter="^:OUTPUT " line="-A INPUT -p tcp  --dport {{ mysql_port }} -j  ACCEPT"
  notify: restart iptables

- name: Create Application Database
  mysql_db: name={{ dbname }} state=present

- name: Create Application DB User
  mysql_user: name={{ dbuser }} password={{ upassword }} priv=*.*:ALL host='%' state=present
  • 查看db角色的模板文件:

# cat lamp_simple/roles/db/templates/my.cnf.j2

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
port={{ mysql_port }}

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
  • 查看web角色的handlers文件:

# cat lamp_simple/roles/web/handlers/main.yml

---
# Handler for the webtier: handlers are called by other plays.
# See http://docs.ansible.com/playbooks_intro.html for more information about handlers.

- name: restart iptables
  service: name=iptables state=restarted
  • 查看web角色的tasks文件:

# cat lamp_simple/roles/web/tasks/main.yml

---
- include: install_httpd.yml
- include: copy_code.yml

# cat lamp_simple/roles/web/tasks/install_httpd.yml

---
# These tasks install http and the php modules.

- name: Install http and php etc
  yum: name={{ item }} state=present
  with_items:
   - httpd
   - php
   - php-mysql
   - git
   - libsemanage-python
   - libselinux-python

- name: insert iptables rule for httpd
  lineinfile: dest=/etc/sysconfig/iptables create=yes state=present regexp="{{ httpd_port }}" insertafter="^:OUTPUT "
              line="-A INPUT -p tcp  --dport {{ httpd_port }} -j  ACCEPT"
  notify: restart iptables

- name: http service state
  service: name=httpd state=started enabled=yes

- name: Configure SELinux to allow httpd to connect to remote database
  seboolean: name=httpd_can_network_connect_db state=true persistent=yes
  when: sestatus.rc != 0`

# cat lamp_simple/roles/web/tasks/copy_code.yml

---
# These tasks are responsible for copying the latest dev/production code from
# the version control system.

- name: Copy the code from repository
  git: repo={{ repository }} dest=/var/www/html/

- name: Creates the index.php file
  template: src=index.php.j2 dest=/var/www/html/index.php

查看web角色的模板文件:

# cat lamp_simple/roles/web/templates/index.php.j2

<html>
 <head>
  <title>Ansible Application</title>
 </head>
 <body>
 </br>
  <a href=http://{{ ansible_default_ipv4.address }}/index.html>Homepage</a>
 </br>
<?php 
 Print "Hello, World! I am a web server configured using Ansible and I am : ";
 echo exec('hostname');
 Print  "</BR>";
echo  "List of Databases: </BR>";
        {% for host in groups['dbservers'] %}
                $link = mysqli_connect('{{ hostvars[host].ansible_default_ipv4.address }}', '{{ hostvars[host].dbuser }}', '{{ hostvars[host].upassword }}') or die(mysqli_connect_error($link));
        {% endfor %}
        $res = mysqli_query($link, "SHOW DATABASES;");
        while ($row = mysqli_fetch_assoc($res)) {
                echo $row['Database'] . "\n";
        }
?>
</body>
</html>

執(zhí)行這個(gè)roles

# ansile-playbook -i lamp_simple/hosts lamp_simple/site.yml

八缸夹、Ansible Galaxy

Ansible Galaxy是Ansible官方Roles分享平臺(tái)(galaxy.ansible.com)痪寻,在Galaxy平臺(tái)上所有人可以免費(fèi)上傳或下載Roles,在這里好的技巧虽惭、思想橡类、架構(gòu)得以積累和傳播。

8.1 ansible-galaxy命令的用法:

[root@LOCALHOST tasks]# ansible-galaxy --help
Usage: ansible-galaxy [delete|import|info|init|install|list|login|remove|search|setup] [--help] [options] ...

Options:
  -h, --help            show this help message and exit
  -c, --ignore-certs    Ignore SSL certificate validation errors.
  -s API_SERVER, --server=API_SERVER
                        The API server destination
  -v, --verbose         verbose mode (-vvv for more, -vvvv to enable
                        connection debugging)
  --version             show program's version number and exit

 See 'ansible-galaxy <command> --help' for more information on a specific command.

搜索lamp相關(guān)的roles:

命令:ansible-galaxy search lamp
結(jié)果如下圖:

image.png

來(lái)安裝一個(gè)搜索到的角色:
命令:ansible-galaxy install vivaserver.lamp

[root@LOCALHOST ~]# ansible-galaxy install vivaserver.lamp
- downloading role 'lamp', owned by vivaserver
- downloading role from https://github.com/vivaserver/ansible-lamp/archive/master.tar.gz
- extracting vivaserver.lamp to /root/.ansible/roles/vivaserver.lamp
- vivaserver.lamp (master) was installed successfully

根據(jù)輸出提示芽唇,安裝的role放在/root/.ansible/roles/vivaserver.lamp

查看這個(gè)role目錄結(jié)構(gòu):

[root@LOCALHOST vivaserver.lamp]# tree /root/.ansible/roles/vivaserver.lamp/
/root/.ansible/roles/vivaserver.lamp/
├── files
│   ├── favicon.ico
│   └── www.html
├── LICENSE
├── meta
│   └── main.yml
├── README.md
├── screenshot.png
└── tasks
    └── main.yml

3 directories, 7 files

按作者搜索roles:

[root@LOCALHOST ~]# ansible-galaxy search zabbix --author dj-wasabi

Found 5 roles matching your search:

 Name                         Description
 ----                         -----------
 dj-wasabi.zabbix-javagateway Installing and maintaining zabbix-javagateway for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-server      Installing and maintaining zabbix-server for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-web         Installing and maintaining zabbix-web for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-proxy       Installing and maintaining zabbix-proxy for RedHat/Debian/Ubuntu.
 dj-wasabi.zabbix-agent       Installing and maintaining zabbix-agent for RedHat/Debian/Ubuntu.

上述命令中 dj-wasabi是作者的用戶名

8.2 批量安裝多個(gè)roles

# cat roles.txt
user1.role1,v1.0.0
user2.role2,v0.5
user2.role3 

安裝:

# ansible-galaxy install -r roles.txt
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顾画,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子匆笤,更是在濱河造成了極大的恐慌研侣,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件炮捧,死亡現(xiàn)場(chǎng)離奇詭異庶诡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)咆课,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門末誓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扯俱,“玉大人,你說(shuō)我怎么就攤上這事喇澡⊙刚ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵晴玖,是天一觀的道長(zhǎng)读存。 經(jīng)常有香客問(wèn)我,道長(zhǎng)呕屎,這世上最難降的妖魔是什么让簿? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮榨惰,結(jié)果婚禮上拜英,老公的妹妹穿的比我還像新娘静汤。我一直安慰自己琅催,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布虫给。 她就那樣靜靜地躺著藤抡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抹估。 梳的紋絲不亂的頭發(fā)上缠黍,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音药蜻,去河邊找鬼瓷式。 笑死,一個(gè)胖子當(dāng)著我的面吹牛语泽,可吹牛的內(nèi)容都是我干的贸典。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼踱卵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼廊驼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起惋砂,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤妒挎,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后西饵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酝掩,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年眷柔,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了期虾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片积蜻。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖彻消,靈堂內(nèi)的尸體忽然破棺而出竿拆,到底是詐尸還是另有隱情,我是刑警寧澤宾尚,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布丙笋,位于F島的核電站,受9級(jí)特大地震影響煌贴,放射性物質(zhì)發(fā)生泄漏御板。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一牛郑、第九天 我趴在偏房一處隱蔽的房頂上張望怠肋。 院中可真熱鬧,春花似錦淹朋、人聲如沸笙各。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)杈抢。三九已至,卻和暖如春仑性,著一層夾襖步出監(jiān)牢的瞬間惶楼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工诊杆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留歼捐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓晨汹,卻偏偏與公主長(zhǎng)得像豹储,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宰缤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容