天天看點

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

Playbooks是一種簡單的配置管理系統與多機器部署系統的基礎,非常适合于複雜應用的部署。Playbooks可用于聲明配置,可以編排有序的執行過程,甚至可以做到在多組機器間來回有序的執行指定的步驟,并且可以同步或異步的發起任務。playbooks可以稱為劇本,也可以稱為作業,我更習慣将其稱之為作業。

系統環境:

伺服器 IP位址 作業系統 所需軟體
ansible主機 192.168.2.203 Centos 7 64位 ansible
遠端主機1 192.168.2.205 httpd
遠端主機2 192.168.2.208
遠端主機3 192.168.2.214
遠端主機4 192.168.2.215

一、YAML文法簡單介紹

Playbooks遵循YAML文法,是playbook的配置管理語言。YAML跟XML或JSON一樣,都是一種利于人們讀寫的資料格式。每一個YAML檔案都是從一個清單開始,清單中的每一項都是一個鍵值對,通常它們被稱為一個“哈希”或“字典”,我們需要知道如何在YAML中編寫清單和字典。

YAML基本規則:

1、區分大小寫

2、使用縮進表示層級關系

3、禁止使用tab健縮進,隻能使用空格鍵

4、縮進長度沒有限制,隻要元素對齊就表示這些元素屬于一個層級。

5、使用#表示注釋

6、字元串可以不用引号括起來,但有特殊符号的字元串必須用引号括起來

YAML檔案的第一行是“---”,聲明一個檔案的開始,但實際上也可以省略此行。

YAML有三種資料結構:1、清單,2、字典,3、常量。

清單中的所有成員都要以”- ”作為開頭(一個橫杠和一個空格)。舉個例子,下面是一個水果的清單

---
- Apple
- Orange
- Strawberry
- Mango           

一個字典是由簡單的“鍵: 值”的形式組成(冒号後面必須有一個空格):

name: Example Developer
job: Developer
skill: Elite           

字典也可以使用類似json的形式來表示:

{name: Example Developer, job: Developer, skill: Elite}

YAML中提供了多種常量結構,包括:整數、浮點數、字元串、NULL、日期、布爾值、時間等。例如:

boolean:
    Knows_agent: TRUE   # true, True都可以
    users_cvs: FALSE   # false, False都可以
float:
    - 3.14
    - 6.856351e+5   # 可以使用科學計數法
int:
    - 123
    - 0b1010_0111_1010_1110   # 二進制表示
null:
    parent: ~   # 使用~表示null
string:
    - newline
    - "he&lo x$%d"   # 包含特殊字元的字元串要用雙引号括起來
date:
    - 2018-02-17   # 日期必須使用ISO 8601格式,即yyyy-MM-dd
datetime:
    - 2018-02-17T15:02:31+08:00   # 時間使用ISO 8601格式,時間和日期之間使用T連接配接,最後使用+代表時區           

資料格式嵌套,把清單、字典和常量嵌套使用,例如:

---
name: Example Developer
job: Developer
skill: Elite
employed: True
foods:
    - Apple
    - Orange
languages:
    ruby: Elite
    python: Elite
    dotnet: yes           

此外,ansible使用“{{ var }}”來引用變量,一個“{}”将認為是一個字典,兩個“{{}}”則是變量。例如:

Foo: "{{ variable }}"

二、playbook的構成

我們先來看一個官網的例子

執行個體1:httpd服務版本更新、配置推送

---   # 聲明這是YAML檔案
- hosts: webservers   # 指定要操作的主機或主機組
  vars:   # 定義變量
    http_port: 80   # 定義了一個http_port變量,http_port變量的内容是80
    max_clients: 200   # 定義了一個max_clients變量,max_clients變量的内容是200
  remote_user: root  # 指定執行的使用者
  tasks:   # tasks段指定要執行的任務操作
  - name: ensure apache is at the latest version   # 任務名稱
    yum: pkg=httpd state=latest   # 通過yum子產品確定httpd服務為最新版本
  - name: write the apache config file   # 任務名稱
    template: src=/srv/httpd.j2 dest=/etc/httpd/conf/httpd.conf   # 使用httpd.j2做為配置模闆,可實作配置推送
    notify:   # 配置有修改的話将會觸發handlers,重新開機httpd服務
    - restart apache   # 重新開機任務的名稱
  - name: ensure apache is running   # 任務名稱
    service: name=httpd state=started   # 確定httpd服務是啟動狀态
  handlers:   # 跟上面的notify是配對的,由notify來進行觸發
    - name: restart apache   # 任務名稱
      service: name=httpd state=restarted   # 重新開機httpd服務           

這個playbook作業的功能如下:

1、確定apache是最新版本,如果不是則更新到最新版本。

2、通過httpd.j2子產品來進行配置推送,如果httpd.j2和httpd.conf檔案内容一樣,則什麼都不做;如果httpd.j2檔案内容有改動,則将改動的配置更新到httpd.conf,并且重新開機apache。

3、確定apache是啟動狀态,如果不是則啟動apache。

首先将httpd服務的配置拷貝到ansible本機的/svr/目錄下,并命名為httpd.j2,将此檔案作為httpd服務的配置模闆檔案。

scp -P 22 [email protected]:/etc/httpd/conf/httpd.conf /srv/httpd.j2

然後運作這個playbook作業,確定兩台機器httpd的版本、配置都一樣。

ansible-playbook httpd_config.yml

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

傳回資訊的最後兩行詳解:

205這台主機共修改了3個地方,更新了版本、更新了配置、重新開機了服務,是以是changed=3;共确認了5個地方,連通情況、版本更新、配置推送、運作狀态、重新開機,是以ok=5。

208這台主機共修改了1個地方,更新了版本,是以是changed=1;共确認了4個地方,比205少了一個重新開機,是以是ok=4。

接下來我們來測試下這個playbook作業,修改httpd.j2模闆檔案,添加status配置内容

ExtendedStatus On
<Location /server-status>
    SetHandler server-status
    Order deny,allow
    Allow from all
</Location>           

然後再執行這個playbook作業,将配置推送到兩台主機上

ansible-playbook httpd_config.yml

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

我們再到兩台主機上檢視一下配置檔案内容,確定status配置已經推送成功。

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

打開浏覽器通路http://192.168.2.205/server-status和http://192.168.2.208/server-status

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

status頁面通路OK,以後即使遠端主機的httpd配置被人改動了,可以很友善的通過這個playbook作業輕松恢複;要修改httpd的配置,也可以通過它來更新配置,是不是很友善啊。

上面這個例子沒看懂的話也不要緊,接下來我會一步一步來講解如何寫一個playbook,而且後面會有更多的執行個體。

上面這個playbook作業主要是由以下四部分組成:

Target section:定義将要執行 playbook 的遠端主機組或主機

Variable section:定義 playbook 運作時需要使用的變量

Task section:定義将要在遠端主機上執行的任務清單

Handler section:定義 task 執行完成以後需要調用的任務

即然如此,那麼要寫一個playbook,首先得指定需要操作的主機或主機組,以及執行playbook作業的使用者。

---
- hosts: web_server   # 定義要操作的主機或主機組
  remote_user: root   # 指定執行任務的使用者           

也支援用sudo指令執行playbook作業

- hosts: web_server
  remote_user: root
  sudo: yes   # 使用sudo執行任務           

如果要sudo到其它使用者,而不是sudo到root,隻需要添加

sudo_user: xuad

如果sudo設定了密碼的話,可在運作ansible-playbook指令時添加--ask-sudo-pass或者-k選項。建議使用sudo的話,不要設定sudo密碼。

ansible-playbook httpd_config.yml --ask-sudo-pass
ansible-playbook httpd_config.yml -k           

需要定義變量使用vars

vars:
    filename: xuad.txt   # 定義了一個檔案名稱變量
    pathname: "/etc/ansible/"   # 定義了一個檔案路徑變量           

需要執行的任務通過tasks來定義

tasks:
  - name: ensure apache is at the latest version   # 任務名稱
    service: name=httpd state=started   # 定義要執行的操作           

也可以在tasks中指定使用sudo執行指令

tasks:
  - name: ensure apache is at the latest version
    service: name=httpd state=started
    sudo: yes   # 使用sudo指令啟動httpd服務
    sudo_user: xuad   # 指定sudo到的使用者           

如果要中止一個playbook作業的運作,可以使用ctrl+c組合鍵中止運作。

playbook按從上到下的順序執行,每個task的目标在于執行一個moudle(子產品),通常是帶有特定的參數來執行,在參數中可以使用變量。

可以通過{{ 變量名 }}的方式擷取vars裡定義的變量,如果參數太多,可以使用縮進的方式隔開連續的一行

tasks:
  - name: Copy ansible inventory file to client
    copy: src={{ pathname }}{{ filename }} dest={{ pathname }}{{ filename }} 
                  owner=root group=root mode=0644   # 使用縮進的方式隔開連續的行           

通過前面ansible指令的熟悉後,我們知道command和shell子產品不使用key=value格式,在playbook中可以這樣定義

tasks:
  - name: disable selinux
    command: /sbin/setenforce 0   # 直接使用command指令
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand || /bin/true   # 直接使用shell指令           

notify和handlers組合,當上一條任務發生改動,在每一個task結束時會觸發notify去執行handlers,即使有多個任務指出因為有改動需要執行handlers,notify也隻會被觸發一次,handlers也隻會執行一次。

例如上面那個例子,當httpd配置檔案發生改變會觸發notify,我們在傳回的資訊明顯看到handlers是在playbook作業的最後一步才執行。

ansible還可以通過ansible-pull從節點主機上拉取配置,具體的使用可參考官網,這裡我就不講了。

檢視一個playbook是對哪些主機執行任務,可使用下面語句

ansible-playbook httpd_config.yml --list-hosts

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

ansible預設隻會建立5個并發程序,也就是說一個任務5台機器同時執行,然後再同時執行5台,直到所有機器都執行完成為止。當我們有大量的機器時,需要同時執行更多的機器,可以使用-f參數,指定并發程序數量。

ansible-playbook httpd_config.yml -f 10

如果要并發執行單個任務,比如說某一個任務我需要所有機器同時執行,可以使用async和poll,async觸發ansible并發執行任務,async的值是這個任務執行的最長時間,而poll是檢查這個任務是否完成的頻率時間。

- hosts: all
   tasks:
     - name: Shell script
       shell: sh /tmp/script.sh
     - name: run updatedb
       command: /usr/bin/updatedb
       async: 300
       poll: 10           

上面這個例子shell子產品是同時在5台機器上執行,而command子產品是同時在所有機器上執行,并且逾時時間是300秒,檢查任務完成的頻率時間是10秒。

如果playbook是放到背景去執行,背景執行加-B參數,那就不需要檢查了,可以把poll設定為0。如果一個任務需要運作很長時間,需要一直等待這個任務完成,不需要設定逾時時間,可以把async的值設定為0。

三、條件選擇語句

(1)通過when語句實作條件選擇

有時我們可能希望某一個任務在滿足一個條件的情況下執行,如果不滿足這個條件就不執行,可以使用when語句,可以按下面方式定義

tasks:
  - name: "Shutdown RedHat flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_os_family == "RedHat"   # 當節點主機的作業系統是redhat時關機           

setup子產品擷取到的主機系統資訊可以直接在playbook裡面調用,檢視遠端節點主機是什麼系統可使用下面語句

ansible 192.168.2.205 -m setup -a 'filter=ansible_os_family'

如果不滿足此條件可以用not,有多個條件需要同時滿足可以用and,滿足其中一個條件用or。

when: not ansible_os_family == "RedHat"
when: ansible_os_family == "RedHat" and ansible_os_family == "Debian"
when: ansible_os_family == "RedHat" or ansible_os_family == "Debian"           

也可以對數值變量進行判斷,如下

when: ansible_lsb.major_release|int >= 6
when: ansible_lsb.major_release|int != 6           

我們都知道在shell中判斷一條指令執行成功與否,可以echo $?,0表示成功,1表示失敗。在playbook中我們可以使用register語句來判斷一條指令執行成功與否,成功傳回succeeded或者success,失敗傳回failed,然後将傳回資訊儲存在一個變量裡,通過這個變量内容來做相應的處理。

執行個體2:判斷httpd服務是否running狀态,是則列印“httpd is running, OK”,不是則列印“httpd is faild, Faild”

- hosts: web_server
  remote_user: root
  tasks:
  - name: httpd status
    shell: systemctl status httpd | grep running
    register: status_result
    ignore_errors: True
  - name: Httpd is Running
    debug: msg="httpd is running, OK"
    when: status_result is success
  - name: Httpd is Faild
    debug: msg="httpd is faild, Faild"
    when: status_result is failed           
通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

register需要配置使用ignore_errors,并且設定成True,否則如果任務執行失敗,即echo $0不為0,則會導緻程式中止,後面的任務不會執行。

dedug子產品有兩個參數,msg和fail,msg就是列印資訊,而當fail=yes時,會發送失敗通知給ansible,然後ansible會停止運作任務。

debug: msg="OS Family {{ ansible_os_family }} is not supported" fail=yes

不管是在inventory中還是playbooks中定義的變量都可以給when使用,下面的例子是基于“鍵: 值”來決定一個任務是否被執行

vars:
  xuad: true
tasks:
  - shell: sh /tmp/abcd.sh
    when: xuad
tasks:
  - shell: sh /tmp/efgh.sh
    when: not xuad           

如果一個變量不存在,可以使用defined跳過執行此任務

tasks:
  - shell: sh /tmp/abcd.sh   # 此任務不會執行
    when: foo is defined

  - shell: sh /tmp/efgh.sh   # 此任務會執行
    when: bar is not defined           

還可以同時對多個變量進行判斷,可通過items定義多個變量,然後循環代入到任務當中,通過when還判斷變量内容,符合條件的變量才會代入到任務中執行。關于items的使用,後面循環内容會進行講解。

執行個體3:對多個變量進行判斷,符合條件的才會列印變量内容

- hosts: 192.168.2.205
  remote_user: root
  tasks:
    - command: echo {{ item }}
      with_items: [ 0, 2, 4, 6, 8, 10 ]
      when: item > 5           
通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

when語句還可以應用在tasks的includes上面,如下

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

when語句也可以應用在roles上面,如下

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

關于roles(角色)部分,後面會進行講解,這裡隻需要知道如何在roles上面定義when即可。

(2)通過變量實作條件選擇

通過setup系統資訊息變量擷取變量檔案,再通過擷取變量檔案裡定義的變量實作條件選擇

執行個體4:不同的系統版本主機擷取對應的變量,實作條件選擇

首先建立一個變量檔案RedHat.yml,内容如下:

apache: httpd
somethingelse: 42           

再建立一個playbook檔案httpd_running.yml,内容如下:

- hosts: all
  remote_user: root
  vars_files:
    - [ "/root/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure apache is running
    service: name={{ apache }} state=started           

此執行個體是不同的系統版本的主機擷取到自己的apache變量。先通過ansible_os_family變量擷取到RedHat.yml檔案或者其它系統版本的變量檔案,再通過RedHat.yml擷取apache變量,如果擷取不到,就通過os_defaults.yml預設的變量檔案擷取apache變量,然後将apache變量内容代入到service子產品裡,進而實作確定httpd服務是運作狀态。

通過with_first_found實作檔案比對,第一個檔案比對不到,才會去比對第二個檔案,依此類推。

- name: template a file
   template: src={{ item }} dest=/etc/myapp/foo.conf
   with_first_found:
     - files:
         - {{ ansible_distribution }}.conf
         - default.conf
       paths:
         - search_location_one/somedir/
         - /opt/other_location/somedir/           

先通過{{ ansible_distribution }}.conf檔案比對,不同的系統版本主機會比對到自己的配置檔案,比對到的話就推送這個檔案的配置;比對不到的主機會推送default.conf檔案的配置。

四、變量的定義和引用

變量可以在四個地方進行定義:1、在Inventory中定義變量,這個在我上一篇博文已經講過,這裡不再重複。2、在playbook中定義變量。3、在檔案中定義變量。4、在role中定義變量。

通過前面的一些例子,大家應該已經發現在playbook中定義變量其實很簡單

vars:
    http_port: 80           

引用變量也很簡單,隻需使用{{}}兩個大括号即可,例如:{{ http_port }}

下面來看一個例子,我們現在要批量更新一個應用,那首先是将更新檔案打包壓縮,然後将更新包分發到各個遠端主機上,然後再将更新包解壓到應用所在目錄,最後重新開機應用。我們有個以目前日期為名稱的yml_20180730.tar.gz壓縮包,此壓縮包包含兩個檔案docker.yml和nginx.yml,那我現在需要将它分發到web_server組裡所有主機的/tmp目錄下并解壓,如下執行個體。

執行個體5:将tar.gz格式的壓縮包拷貝到遠端主機上并解壓

- name: copy and unzip file
  hosts: web_server
  remote_user: root
  vars:
      file: yml_{{ansible_date_time.year}}{{ansible_date_time.month}}{{ansible_date_time.day}}.tar.gz
  tasks:
     - name: 分發更新包
       copy: src=/etc/ansible/{{ file }} dest=/tmp
     - name: 解壓更新包
       shell: tar -zxvf /tmp/{{ file }} -C /tmp           

ansible-playbook tarcopy.yml

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

ansible web_server -m shell -a "ls -lh /tmp"

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

通過facts擷取系統相關的變量,也就是前面所說的setup子產品,例如:擷取遠端系統的目前日期

{{ ansible_date_time.date }}

在playbook中也可以關閉facts資料的擷取,這樣有利于加快ansible操作大批量伺服器的速度

- hosts: all
  gather_facts: no           

要想指定擷取哪台主機的系統變量,可以按如下方式引用

{{ hostvars['192.168.2.205']['ansible_os_family'] }}

在檔案中定義變量也很簡單,我們首先寫一個變量檔案vars.yml

somevar: somevalue
password: magic           

在playbook中可以按以下方式引用,使用vars_files來擷取檔案中的變量

- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/vars.yml
  tasks:
  - name: this is just a placeholder
    command: /bin/echo foo           

在指令行中傳遞變量,使用--extra-vars,例如我在playbook中引用了兩個變量,如下

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

在執行playbook的指令中可以按如下方式傳遞變量到playbook中

ansible-playbook release.yml --extra-vars "hosts=web_server user=root"

也可以傳遞json格式的變量

--extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

使用“@”傳遞json檔案

--extra-vars "@some_file.json"

在roles中可以按如下方式定義變量

roles:
   - { role: app_user, name: Ian }
   - { role: app_user, name: Terry }           

如果在不同的地方定義了一個相同的變量,擷取變量的優先級如下:

extra vars (在指令行中使用 -e)優先級最高

然後是在inventory中定義的連接配接變量(比如ansible_ssh_user)

接着是大多數的其它變量(指令行轉換,play中的變量,included的變量,role中的變量等)

然後是在inventory定義的其它變量

然後是由系統發現的facts

然後是 "role預設變量", 這個是最預設的值,很容易喪失優先權

五、循環語句

使用with_items定義多個變量,以循環的方式引用變量

執行個體6:copy兩個檔案到遠端主機的/tmp下

- name: copy file
  hosts: web_server
  remote_user: root
  tasks:
     - name: cp file
       copy: src=/etc/ansible/{{ item }} dest=/tmp
       with_items:
         - ansible.cfg
         - hosts           

ansible-playbook copyf.yml

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

ansible web_server -m shell -a "ls /tmp"

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

同時引用兩個變量,可以按如下方式定義

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

使用with_nested實作循環嵌套,按如下方式定義

注:item[0]循環alice和bob兩個變量,item[1]循環clientdb、employeedb和providerdb三個變量,以下例子是實作在三個資料庫上建立兩個使用者。

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

使用with_dict實作對哈希表循環,按如下方式定義

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實作對檔案清單進行循環,如下執行個體

執行個體7:copy一個目錄下的所有檔案到遠端主機上

- hosts: web_server
  remote_user: root
  tasks:
    - name: 建立目錄
      file: dest=/tmp/ansible state=directory
    - name: 拷貝目錄下的所有檔案
      copy: src={{ item }} dest=/tmp/ansible/ owner=root mode=600
      with_fileglob:
        - /etc/ansible/*           

ansible-playbook files.yml

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

使用with_together實作并行循環,一次擷取多個變量

vars:
  alpha: [ 'a', 'b', 'c', 'd' ]
  numbers: [ 1, 2, 3, 4 ]
tasks:
  - debug: msg="{{ item.0 }} and {{ item.1 }}"
    with_together:
      - "{{alpha}}"
      - "{{numbers}}"           

使用with_subelements實作對子元素使用循環,假設我們有一份按以下方式定義的檔案

users:
  - name: alice
    authorized:
      - /tmp/alice/onekey.pub
      - /tmp/alice/twokey.pub
    mysql:
        password: mysql-password
        hosts:
          - "%"
          - "127.0.0.1"
          - "::1"
          - "localhost"
        privs:
          - "*.*:SELECT"
          - "DB1.*:ALL"
  - name: bob
    authorized:
      - /tmp/bob/id_rsa.pub
    mysql:
        password: other-mysql-password
        hosts:
          - "db1"
        privs:
          - "*.*:SELECT"
          - "DB2.*:ALL"           

要擷取以上檔案裡定義的資料,可以按以下方式引用變量

- user: name={{ item.name }} state=present generate_ssh_key=yes
  with_items: "{{users}}"

- authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'"
  with_subelements:
     - users
     - authorized           

以嵌套的方式擷取變量,如下

- name: Setup MySQL users
  mysql_user: name={{ item.0.user }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join('/') }}
  with_subelements:
    - users
- mysql.hosts           

使用with_sequence對整數序列使用循環,如下

- hosts: all

  tasks:

    # create groups
    - group: name=evens state=present
    - group: name=odds state=present

    # create some test users
    - user: name={{ item }} state=present groups=evens
      with_sequence: start=0 end=32 format=testuser%02x

    # create a series of directories with even numbers for some reason
    - file: dest=/var/stuff/{{ item }} state=directory
      with_sequence: start=4 end=16 stride=2

    # a simpler way to use the sequence plugin
    # create 4 groups
    - group: name=group{{ item }} state=present
      with_sequence: count=4           

使用with_random_choice實作随機選擇一個變量

- debug: msg={{ item }}
  with_random_choice:
     - "go through the door"
     - "drink from the goblet"
     - "press the red button"
     - "do nothing"           

使用Do-Until循環,實作循環執行某個任務直到指定的條件成立後停止

- action: shell /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") != -1
  retries: 5
  delay: 10           

上面的例子遞歸運作shell子產品,直到運作結果中的stdout輸出中包含“all systems go”字元串時停止運作,或者該任務按照10秒的延遲重試超過5次時停止運作。

六、角色(Roles)使用

ansible支援将一個playbook進行拆解,比如将vars、tasks和需要操作的主機或執行的使用者分别寫在不同的檔案裡,然後通過一個簡單的playbook檔案去調用這些檔案,那麼最好的辦法就是使用角色(roles),也可以将roles稱之為各個項目的集合。

一個角色必須包含如下目錄:

role_name/

files/:存儲由copy或script等子產品調用的檔案;

tasks/:此目錄中至少應該有一個名為main.yml的檔案,用于定義各個task;其它的檔案需要由main.yml進行"包含"調用;

handlers/:此目錄中至少應該有一個名為main.yml的檔案,用于定義各個handler;其它的檔案需要由main.yml進行"包含"調用;

vars/:此目錄中至少應該有一個名為main.yml的檔案,用于定義各個variable;其它的檔案需要由main.yml進行“包含”調用;

templates/:存儲由template子產品調用的模闆文本;

meta/:此目錄中至少應該有一個名為main.yml的檔案,定義目前角色的特殊設定及其依賴關系;其它的檔案需要由main.yml進行"包含"調用;

default/:此目錄中至少應該有一個名為main.yml的檔案,用于設定預設變量;

執行個體8:yum方式批量安裝部署mysql

首先建立ssh-key授權,将公鑰檔案copy到兩台主機上

ssh-copy-id 192.168.2.214
ssh-copy-id 192.168.2.215           

然後hosts檔案添加如下内容

[db_server]
192.168.2.214
192.168.2.215           

确定2台主機都能正常連接配接

ansible db_server -m ping

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

建立mysql項目的角色目錄

cd /etc/ansible/roles/
mkdir -p mysql/{default,files,handlers,meta,tasks,templates,vars}           

由于我們修改了mysql的資料存儲路徑、日志路徑和socket檔案路徑,是以需要關閉selinux,mysql才能正常啟動。

建立關閉selinux的配置模闆檔案,将此模闆檔案放到/etc/ansible/roles/mysql/templates目錄下

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled - No SELinux policy is loaded.
#SELINUX=enforcing
SELINUX=disabled
# SELINUXTYPE= can take one of three two values:
#     targeted - Targeted processes are protected,
#     minimum - Modification of targeted policy. Only selected processes are protected. 
#     mls - Multi Level Security protection.
#SELINUXTYPE=targeted            

建立mysql的配置模闆檔案,将此模闆檔案放到/etc/ansible/roles/mysql/templates目錄下

[mysqld]
datadir=/data/mysql/data
socket=/data/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0
# Settings user and group are ignored when systemd is used.
# If you need to run mysqld under a different user or group,
# customize your systemd unit file for mariadb according to the
# instructions in http://fedoraproject.org/wiki/Systemd

[mysqld_safe]
log-error=/data/mysql/mariadb.log
pid-file=/data/mysql/mariadb.pid
socket=/data/mysql/mysql.sock

#
# include all files from the config directory
#
!includedir /etc/my.cnf.d           

建立調用roles的playbook作業

vim mysql.yml

- hosts: db_server
  remote_user: root
  roles:
    - mysql           

建立關閉selinux的tasks檔案

vim mysql/tasks/selinux.yml

- name: Modify the configuration file for SELinux
  template: src=config.j2 dest=/etc/selinux/config
- name: close selinux
  shell: setenforce 0           

建立變量vars定義檔案

vim mysql/vars/main.yml

mysql_name: "mariadb"
mysql_path: "/data/mysql"
mysql_port: 3306
root_pass: "123456"           

yum安裝mariadb需要安裝mariadb-devel、mariadb和mariadb-server三個包,那麼我們現在來建立tasks檔案

vim mysql/tasks/mysql_install.yml

- name: ensure {{ mysql_name }}-devel is at the latest version
  yum: pkg={{ mysql_name}}-devel state=latest
- name: ensure {{ mysql_name }} is at the latest version
  yum: pkg={{ mysql_name }} state=latest
- name: ensure {{ mysql_name }}-server is at the latest version
  yum: pkg={{ mysql_name }}-server state=latest
- name: Create a data directory
  file: dest={{ mysql_path }}/data mode=755 owner=mysql group=mysql state=directory
- name: write the mysql config file
  template: src=my.cnf.j2 dest=/etc/my.cnf
  notify:
  - restart mysql
- name: ensure mysql is running
  service: name={{ mysql_name }} state=started enabled=yes
- name: Creating a soft link file
  file: src={{ mysql_path }}/mysql.sock dest=/var/lib/mysql/mysql.sock state=link
- name: Setting the root password
  shell: mysqladmin -u root -h localhost password {{ root_pass }}           

建立tasks的main.yml檔案

vim mysql/tasks/main.yml

- include: selinux.yml
- include: mysql_install.yml           

建立handlers檔案

vim mysql/handlers/main.yml

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

最終mysql的角色路徑如下

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

運作mysql.yml作業

ansible-playbook mysql.yml

登陸到遠端主機上看一下結果吧

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

七、playbook執行個體

執行個體9:通過unarchive将tar.gz的壓縮包解壓到遠端主機的指定目錄下

前面的執行個體5是通過tar指令解壓的,是以會抛出一個警告,意思是建議使用unarchive來完成此任務。

- name: copy and unzip file
  hosts: web_server
  remote_user: root
  tasks:
     - name: copy your folder from control machine to remote host
       unarchive: src=yml_{{ansible_date_time.year}}{{ansible_date_time.month}}{{ansible_date_time.day}}.tar.gz dest=/tmp           

ansible-playbook untar.yml

通過ansible批量管理Linux伺服器:playbook作業一、YAML文法簡單介紹二、playbook的構成三、條件選擇語句四、變量的定義和引用五、循環語句六、角色(Roles)使用七、playbook執行個體

執行個體10:批量啟動、重新開機、停止httpd服務

注:tags可實作單獨運作一個playbook作業中的指定的任務

- hosts: web_server
  remote_user: root
  tasks:
     - name: start httpd
       service: name=httpd state=started
       tags: start_httpd
     - name: stop httpd
       service: name=httpd state=stopped
       tags: stop_httpd
     - name: restart httpd
       service: name=httpd state=restarted
       tags: restart_httpd           
- hosts: web_server
  remote_user: root
  tasks:
    - name: 建立使用者
      user: name={{ item }} password="$6$rounds=656000$O//XUuLIX35/oB2V$yuLC/9TUUvCBb/aCtN0N.xhjBA1ui3t0kPcK2PWP0Hp0eLuThZzx904v3ZoOhAxj/pS6GIHM4RudAzNfnGbxq0"
      with_items:
        - xiaozh
        - wangbs
        - yangdl           
- hosts: db_server
  remote_user: root
  vars_files:
    - /etc/ansible/roles/mysql/vars/main.yml
  vars:
    zauser: zabbix_user
    user_pass: "user123456"
  tasks:
  - name: 安裝MySQL-python子產品
    yum: pkg=MySQL-python state=latest
  - name: 建立zabbix資料庫
    mysql_db: login_user=root login_password={{ root_pass }} name=zabbix encoding=utf8 state=present
  - name: 建立zabbix使用者
    mysql_user: login_user=root login_password={{ root_pass }} name={{ zauser }} password={{ user_pass }} priv='zabbix.*:ALL' state=present
  - name: 分發sql腳本
    copy: src={{ item }} dest=/tmp/
    with_fileglob:
      - /etc/ansible/roles/mysql/files/*
  - name: 導入sql腳本到zabbix資料庫
    mysql_db: login_user=root login_password={{ root_pass }} name=zabbix state=import target=/tmp/{{ item }}
    with_items:
      - schema.sql
      - images.sql
      - data.sql           

繼續閱讀