1. 程式人生 > 程式設計 >python中Ansible模組的Playbook的具體使用

python中Ansible模組的Playbook的具體使用

Playbook

在上一節中,我們詳細介紹了Ansible提供的一些常用模組。可以看到,Ansible中的每個模組專注於某一方面的功能。雖然每個模組實現的功能都比較簡單,但是,將各個模組組合起來就可以實現比較複雜的功能。在Ansible中,將各個模組組合起來的檔案是一個YAML格式的配置檔案。這個配置檔案,在Ansible中稱為Playbook。

在這一節中,我們將循序漸進地介紹Ansible中的Playbook,我們將首先介紹Playbook的定義,然後介紹如何使用Playbook完成遠端伺服器部署,之後詳細介紹Playbook的基本語法,使用Playbook的基本講法就能夠完成大部分的部署任務。

在這一節中,找們將介紹如何使用Playbook的基本語法完成nginx與MongoDB的部署,最後,我們介紹了部分Playbook的高階語法。

1、Playbook的定義

Playbook不同於其他使用單個模組操作遠端伺服器,Playbook的功能更加強大。如果只使用Playbook的基本功能,那麼,Playbook是一個非常簡單的配、管理和部署系統。此外,Playbook也可以實現各種高階功能,如指定任務的執行順序,委派其他主機來執行某一個任務,與監控伺服器和負載均衡元件進行互動等。

有一個非常恰當的比喻,,Ansible中的模組類似於Linux下的命令,Ansible中的Playbook類似於Linux下的Shell指令碼檔案。Shell指令碼檔案將各個Linux命令組合起來,以此實現複雜的功能,Playbook將各個模組組合起來也可以實現複雜的部署功能。在shell指令碼中,除了呼叫Linux命令以外,還有一些基本的語法,如變數定義、if語句、for迴圈等。在Playbook中,一方面通過YAML格式進行定義提高Playbook的可讀性、可維護性,降低工程師的學習負擔;另一方面,Ansible提供了若干可以應用在Playbook中的選項,以便工程師實現更加高階的功能。

一個Playbook可以包含一到多個Play,每一個Play是一個完整的部署任務。在Play中,我們需要指定對哪些遠端伺服器執行操作,以及對這些遠端伺服器執行哪些操作。

下面是一個名為first_playbook.yml的Playbook。在這個Playbook中,我們定義了兩個Play,前者用來在資料庫伺服器上部署MongoDB,後者用來在web伺服器上部署“應用”。這裡只是為了對Playbook進行演示,並沒有真的部署應用。

[root@python ~]# vim first_playbook.yml

---
- hosts: dbservers
 become: yes
 become_method: sudo
 tasks:
 - name: install mongodb
  yum: name=mongodb-server state=present

- hosts: webservers
 tasks:
 - name: copy file
  copy: src=/tmp/data.txt dest=/tmp/data.txt

 - name: change mode
  file: dest=/tmp/data.txt mode=655 owner=root group=root

這個Playbook中包含了兩個Play。一個Playbook可以包含一到多個Play,所以即使Playbook中值包含一個Play,也需要使用列表的形式進行定義。在YAML語法中,“- hosts”前面的“-”表示定義列表。

在Ansible中,一個Play必須包含以下兩項:

1. hosts:需要對哪些遠端伺服器執行操作
2. tasks:需要在這些伺服器上執行的任務列表

例如,對web伺服器進行部署時,我們僅僅使用了hosts和tasks兩個選項。前者表示對哪些伺服器執行操作,後者表示對伺服器執行哪些操作。在部署資料庫伺服器時需要安裝軟體,因此使用了become與become_method兩個選項,用來表示使用管理員的身份去安裝MongoDB資料庫。

一個Play可以包含一到多個task,因此task也必須以YAML的列表形式進行定義。可以看到,在這個例子中,對資料庫伺服器進行操作時僅包含了一個task,對web伺服器進行部署時包含了兩個task。

在Ansible中,task有兩種定義形式:

1. action:module options

2. module:options

前一種形式是Ansible的舊版本語法,第2種形式是新版本的語法,直接使用模組的名稱作為鍵,使用模組的引數作為值。如下所示:

- name: install httpd
 yum: name=httpd update_cache=yes state=present

在安裝Apache的例子中,“name=httpd update_cache=yes state=present”是一個完整的字串,而不是一個字典。只是字串的值是一個“key=value”形式的引數。

在引數較多時,為了增加Playbook的可讀性,我們也可以像下面這樣定義一個task:

- name: install httpd
 yum: >
  name=httpd
  update_cache=yes
  state=present

在Ansible中,當引數較長時,除了使用“>”進行摺疊換行以外,也可以使用縮排字塊的形式:

- name: install httpd
 yum: 
  name: httpd
  update_cache: yes
  state: present

雖然從字面來看,這兩種指定引數的方式相差不大。但是,從YAML的語法來說,這是完全不同的兩個方法。前者是一個比較長的字串,後者是一個字典。

task的定義中,name是可選的。所以,像下面這樣定義task也是完全合法的:

- yum: name=httpd update_cache=yes state=present

name的作用在於,執行Playbook時作為註釋進行顯示,以便使用者知道當前執行到哪一步。因此,在定義task時,一般都會定義name欄位。

在實際工作中,雖然一個Playbook可以包含多個Play,但是為了Playbook的可讀性和可維護性,我們一般只會在Playbook中編寫一個Play。例如,對於這裡的例子,我們可以將first_playbook.yml這個Playbook拆分成兩個Playbook,分別名為db.yml與web.yml。其中,db.yml檔案包含了與資料庫伺服器相關的部署任務,web.yml檔案包含了與web伺服器相關的部署任務。

當我們需要部署資料庫伺服器和web伺服器時,可以先執行db.yml檔案,再執行web.yml檔案。除此之外,Ansible還提供了一種便捷方式來處理這種情況。例如,我們可以編寫一個名為all.yml的Playbook,它的內容如下:

---
- include: db.yml
- include: web.yml

include選項是Ansible提供的,用於在一個Playbook中匯入其他Playbook。在Ansible中,只需要使用include選項匯入其他Playbook檔案,執行這個Playbook時,被匯入的Playbook便會依次執行。

上面詳細介紹了Ansible的Playbook定義,這個Playbook定義雖然比較簡單,但是,是一個比較完整的Playbook例子。在實際工作中使用的Playbook也不會比這個Playbook複雜很多。

我們接下來將介紹如何使用ansible-playbook命令執行Playbook,然後再介紹Playbook的其他語法。

2、ansible拆分playbook.yml

檢視一下所需檔案是否正確

[root@python ~]# cat hosts 
127.0.0.1
[webservers]
192.168.1.60
[dbservers]
192.168.1.80
[common:children]
dbservers
webservers
[root@python ~]# cat /etc/ansible/ansible.cfg 
[defaults]
remote_user = root
remote_port = 22
inventory = /root/hosts

拆分playbook.yml

[root@python ~]# cat db.yml 
---
- hosts: dbservers
 become: yes
 become_method: sudo
 tasks:
 - name: install mongodb
  yum: name=mongodb-server state=present #mongodb-server 可歡成其他服務如(git)
[root@python ~]# cat web.yml 
---
- hosts: webservers
 tasks:
 - name: copy file
  copy: src=/tmp/data.txt dest=/opt/data.txt 
 - name: change mode
  file: dest=/opt/data.txt mode=655 owner=root group=root
[root@python ~]# cat all.yml 
---
- include: db.yml
- include: web.yml
[root@python ~]# touch /tmp/data.txt
[root@python ~]# touch /opt/data.txt

3、使用Ansible-playbook執行Playbook

上一小節中,我們簡單地介紹了Playbook的定義。那麼,當我們有了一個Playbook檔案以後,如何執行這個檔案完成應用部署呢?我們知道,Ansible安裝完成以後存在多個可執行的命令列工具,其中,ansible-playbook便是用於執行Playbook的命令列工具。

ansible-playbook的執行方式如下:

ansible-playbook first_playbook.yml

ansible-playbook命令也有若干命令列選項,其中,有部分選項與ansible命令相同。Ansible中也存在一些ansible-playbook特有的命令列選項。

ansible-playbook命令與ansible命令相同的命令列選項:

-T --timeout:建立SSH連線的超時時間
--key-file --private-key:建立SSH連線的私鑰檔案
-i --inventory-file:指定Inventory檔案,預設是/etc/ansible/hosts
-f --forks:併發執行的程序數,預設為5
--list-hosts:playbooks匹配的伺服器列表。

ansible-playbook也有一個名為--list-hosts的選項,該選項的作用是列出匹配的伺服器列表。例如,在我們這個 Playbook的例子中,hosts檔案的內容如下:

127.0.0.1
[webservers]
192.168.1.60
[dbservers]
192.168.1.80
[common:children]
dbservers
webservers

我們知道,Ansible中的Play定義了需要對哪些伺服器執行哪些操作,也就是說,每一個Play都可以指定匹配的遠端伺服器。在我們這個Playbook的例子中,對資料庫伺服器安裝MongoDB,對web伺服器部署“應用“。因此,ansible-playbook命令與ansible命令的--list-hosts選項輸出的結果將會大不相同。ansible-playbook命令的--list-hosts選項輸出的結果如下:

[root@python ~]# ansible-playbook all.yml --list-hosts

python中Ansible模組的Playbook的具體使用

ansible-playbook命令有一些特有的選項,如下所示:

--list-tasks:列出任務列表
--step:每執行一個任務後停止,等待使用者確認
--syntax-check:檢查Playbook語法
-C --check:檢查當前這個Playbook是否會修改遠端伺服器,相當於預測Playbook的執行結果。

這裡的幾個選項,除了--step以外,其他幾個選項都不會執行Playbook中的任務。這些選項存在主要是為了便於除錯Playbook。例如,--list-tasks選項,該選項用來顯示當前Playbook中的任務列表。當Playbook比較大時,可以通過這個方式快速檢視任務列表。如下所示:

[root@python ~]# ansible-playbook all.yml --list-tasks
playbook: all.yml

 play #1 (dbservers): dbservers  TAGS: []
  tasks:
   install mongodb  TAGS: []

 play #2 (webservers): webservers TAGS: []
  tasks:
   copy file TAGS: []
   change mode  TAGS: []

當我們檢視任務列表時,任務的名稱就是task的name欄位。因此,name的定義需要具有較好的描述性,讓使用者通過名字就能知道該任務需要做什麼事情。

--step選項類似於程式語言中的單步除錯。當我們使--step選項執行Playbook時,ansible-playbook在每一個任務之前都會停住,等侍使用者輸入yes,、no或continue。如下所示:

[root@python ~]# ansible-playbook all.yml --step
[DEPRECATION WARNING]: 'include' for playbook includes. You should use 
'import_playbook' instead. This feature will be removed in version 2.12. 
Deprecation warnings can be disabled by setting deprecation_warnings=False in 
ansible.cfg.

PLAY [dbservers] ***************************************************************
Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: y

Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: *********************

TASK [Gathering Facts] *********************************************************
ok: [192.168.1.80]
Perform task: TASK: install mongodb (N)o/(y)es/(c)ontinue: y

Perform task: TASK: install mongodb (N)o/(y)es/(c)ontinue: *********************

TASK [install mongodb] *********************************************************
changed: [192.168.1.80]

PLAY [webservers] **************************************************************
Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: y

Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: *********************

TASK [Gathering Facts] *********************************************************
ok: [192.168.1.60]
Perform task: TASK: copy file (N)o/(y)es/(c)ontinue: y

Perform task: TASK: copy file (N)o/(y)es/(c)ontinue: ***************************

TASK [copy file] ***************************************************************
changed: [192.168.1.60]
Perform task: TASK: change mode (N)o/(y)es/(c)ontinue: y

Perform task: TASK: change mode (N)o/(y)es/(c)ontinue: *************************

TASK [change mode] *************************************************************
changed: [192.168.1.60]

PLAY RECAP *********************************************************************
192.168.1.60        : ok=3  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0  
192.168.1.80        : ok=2  changed=1  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0 

輸入yes以後,任務將會繼續執行,並在下一個任務時停止,等待使用者繼續輸入。當我們輸入continue時,Ansible會執行完當前這個Play,當執行到下一個Play時再停止,並等待使用者輸入。

二、Playbook的詳細語法

到目前為止,我們已經學習瞭如何編寫Playbook以及如何執行Playbook。但是,我們僅僅介紹了最簡單的Playbook。在這一節中,我們將會介紹Playbook是如何通過不同的選項提供豐富多樣的功能。靈活使用這些選項,能夠編寫出形式各異的Playbook,以此應對自動部署中的各種情況。

在定義Play時,只有hosts與tasks是必選選項,其他選項都是根據需要新增的。在這一小節中。我們將介紹Playbook提供的不同功能,以Playbook的功能為線索,介紹Play與task中可以使用的選項。

(1)許可權

在Ansible中,預設使用當前使用者連線遠端伺服器執行操作。我們也可以在anaible.cfg檔案中配置連線遠端伺服器的預設使用者。此外,如果是不同的使用者使用不同型別的遠端伺服器,那麼也可以在Playbook的Play定義中指定連線遠端伺服器的使用者。例如,我們可以指定執行Play的使用者:

---
- hosts: webservers
 remote_user: root

使用者可以細分每一個task,如下所示:

---
- hosts: werbservers
 remote_user: root
 tasks:
  - name: test connection
   ping:
    remote_user: yourname

很多時候,我們需要的不是以某個特定使用者連線遠端伺服器,而是在需要更高級別的許可權時,使用管理員身份去執行操作。在ansible中,可以通過become與become_ method選項實現:

---
- hosts: werbservers
 remote_user: root
 become: yes

與remote_user選項類似,我們也可以為單個任務使用管理員許可權,如下所示:

---
- hosts: werbservers
 remote_user: yourname
 tasks:
  - name: installed nginx
   service: name=nginx state=started
   become: yes
   become_method: sudo

例項

先修改遠端伺服器中test的許可權為(0:0)

[root@192 ~]# vim /etc/passwd
test:x:0:0::/home/test:/bin/bash
[root@python ~]# chown test:root /etc/ansible/*
[root@python ~]# su test
[test@python root]$ cd 
[test@python ~]$ vim hosts
[db]
192.168.1.60

[test@python ~]$ vim db.yml

---
- hosts: db
 remote_user: root       #遠端伺服器登陸的使用者
 tasks:
  - name: installed nginx
   become: yes
   become_method: sudo
   ping:
    remote_user: root     #遠端伺服器登陸的使用者
[test@python ~]$ vim /etc/ansible/ansible.cfg 

[defaults]
inventory = /home/test/hosts

執行一下

[test@python ~]$ sudo ansible-playbook db.yml --step

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

  #1) Respect the privacy of others.
  #2) Think before you type.
  #3) With great power comes great responsibility.

[sudo] password for test: 

PLAY [db] **********************************************************************
Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: y

Perform task: TASK: Gathering Facts (N)o/(y)es/(c)ontinue: *********************

TASK [Gathering Facts] *********************************************************
Enter passphrase for key '/root/.ssh/id_rsa': 
ok: [192.168.1.60]
Perform task: TASK: installed nginx (N)o/(y)es/(c)ontinue: 

Perform task: TASK: installed nginx (N)o/(y)es/(c)ontinue: *********************

PLAY RECAP *********************************************************************
192.168.1.60        : ok=1  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0 

(2)通知

在Ansible中,模組是冪等的。例如,我們要在遠端伺服器上建立一個使用者,如果該使用者已經存在,那麼Ansible不會將該使用者刪除以後重新建立,而是直接返回成功,並通過changed欄位表示是否對遠端伺服器進行了修改。

考慮這樣一種需求:我們要通過Ansible修改Apache的配置檔案,並重啟Apache服務,使得新的配置檔案生效。由於Ansible的模組是冪等的,當我們修改Apache的配置檔案時,如果配置檔案的內容已經與我們想要修改成的內容一樣(例如,不小心將Ansible執行了兩次的情況),那麼,Ansible就什麼也不做。既然Apache的配置檔案並沒有真的被修改,那麼我們也不應該去重啟Apache的伺服器。在Ansible中,通過notify與handler機制來實現這裡的功能。

在下面的例子中,我們首先嚐試安裝Apache,然後修改Apache的配置檔案。如果配置檔案被修改,則通過notify選項通知handler進行後續處理。

handler是Ansible提供的條件機制,與tasks比較類似,都是去執行某些操作。但是,handler只有在被notify觸發以後才會執行,如果沒有被觸發則不會執行。在Playbook中,如果task後面存在notify選項,那麼,當Ansible識別到task改變了系統的狀態,就會通過notify去觸發handler。

Ansibie是通過什麼條件判斷notify觸發的是哪一個handler呢?很簡單,在Ansible中,task使用handler的名字作為引數,以此來觸發特定的handler。例如,在我們這裡的例子中,notify觸發的是“restart apache"這個handler,handlers中也存在一個名為" restart apache“的handler。

---
- hosts: webservers
 tasks:
 - name: ensure apache is at the latest version
  yum: name=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

需要注意的是,handler只會在所有task執行完後執行。並且,即便一個handler被觸發多次,它也只會執行一次。handler並不是在被觸發時立即執行,而是按照Play中定義的順序執行。一般情況下,handler都位於Play的最後,即在所有任務執行完成以後再執行。

Ansible官方文件提到handler的唯一用途,就是重啟服務與伺服器,正如找們這個例子所演示的。

在這個例子中,我們還用到T了template模組。template模組用以渲染Jinja模板。

(3)變數

在Inventory管理章節,我們已經介紹瞭如何定義變數。在Ansible中,還有其他幾種定義變數的方式。對於簡單的Playbook,最直接的方式是將變數定義在Playbook的vars選項中。如下所示:

- hosts: dbservers
 vars:
  mysql_port: 3307

在Playbook中定義變數,可以在模板渲染時使用。例如:Ansible官方給出的例子中,MySQL配置檔案的部分模板如下:

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
port={{ mysql_port }}

當變數較少的時候,定義在vars選項中完全沒有問題。當變數較多時,可以將變數儲存在一個獨立的檔案中,並通過vars_files選項引用該檔案。如下所示:

---
- hosts: all
 vars:
  favcolor: blue
 vars_files:
  - /vars/external_vars.yml

 tasks:
 - name: this is just a placeholer
  command: /bin/echo foo

儲存變數的檔案是一個簡單的YAML格式的字典,如下所示:

---
# in th above example,this would be vars/external_vars.yml
somevar: somevalue
password: magic

在shell指令碼中,我們可以通過獲取上一條命令的返回碼判斷命令是否執行成功。在Ansible中,我們也可以獲取任務的執行結果,將任務的執行結果儲存在一個變最中,並在之後引用這個變數。這樣的變數在Ansible中使用register選項獲取,也稱為註冊變數。

例如,在下面這個例子中,我們首先執行/usr/bin/foo命令,並通過register選項獲取命令的執行結果,將結果儲存在foo_result中。在之後的task中,使用這個變數名引用/usr/bin/foo命令的執行結果。

- hosts: web_servers
 tasks:
 - shell: /usr/bin/foo
  register: foo_result
  ignore_errors: True

 - shell: /usr/bin/bar
  when: foo_result == 5

這個例子還涉及了兩個新的選項,分別是ignore_errors與when。前者表示忽略當前task中的錯誤,後者是一個條件語句,只有條件為真時才會執行這個task。

(4)Facts變數

在Ansible中,還有一些特殊的變數,這些變數不需要我們進行任何設定就可以直接使用,這樣的變數稱為Facts變數。Facts變數是Ansible執行遠端部署之前從遠端伺服器中獲取的系統資訊,包括伺服器的名稱、IP地址、作業系統、分割槽資訊、硬體資訊等。Facts變數可以配合Playbook實現更加個性化的功能需求。例如,將MongoDB資料庫的資料儲存在/var/mongo-\<hostname>/目錄下。

我們可以通過setup模組檢視Facts變數的列表,如下所示:

ansible all -m setup

有了Facts變數以後,如何在Ansible中使用它們呢?答案是直接使用。我們可以在Playbook中直接通過變數的名字引用變數,也可以在Jinja2模板中通過變數的名字引用變數。下面是一個名為test_facts.yml的Playbook。在這個Playbook中,我們輸出了作業系統的型別,並且只有在作業系統為“RedHat"類作業系統時才會執行安裝操作。

---
- hosts: dbservers
 tasks:
  - shell: echo {{ ansible_os_family }}
   register: myecho

  - debug: var=myecho.stdout_lines

  - name: install git on Red Hat Linux
   yum: name=git state=installed
   when: ansible_os_family == "RedHat"

setup模組為了輸出結果的可讀性,對模組的輸出進行了歸類和整理。因此,當我們要訪問複雜變數的子屬性時,需要使用巢狀結構。例如,我們可以通過下面兩種方式訪問Ansible中的ipv4地址:

ansible_ens33['ipv4']['address']
ansible_ens33.ipv4.address

訪問複雜的變數的Playbook:

---
- hosts: dbservers
 gather_facts: yes
 tasks:
  - shell: echo {{ ansible_ens33['ipv4']['address'] }}
   register: myecho

  - debug: var=myecho.stdout_lines

  - shell: echo {{ ansible_ens33.ipv4.address }}
   register: myecho

  - debug: var=myecho.stdout_lines

在實際工作中,我們一般會在Jinja2模板中引用Facts變數。使用方式與這裡的例子一樣,為了節省篇幅就不再贅述了。

在Playbook中,可以通過gather_ facts選項控制是否收集遠端伺服器的資訊。該選項預設取值為yes,如果確定不需要用到遠端伺服器的資訊,可以將該選項設定為no,以此提高Ansible部署的效率。如下所示:

---
- hosts: dbservers
 gather_factes: no
 tasks:

(5)迴圈

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

(6)條件

有時候,一個任務是否執行取決於一個變數的取值,或者上一個任務的執行結果,這個時候找們就需要條件語句。再或者說,在迴圈的時候想要跳過一些特定的元素,在伺服器部署時只對某些特定的作業系統進行操作。所有這些行為都可以使用條件語句解決。Ansible的Playbook不是一門程式語言,因此沒有相應的條件語句,不過Ansible提供了一個類似的選項。

在Playbook中可以通過when選項執行條件語句,when就類似於程式語言中的if語句。

下面是一個簡單的when選項使用示例:

# 檢視Linux系統版本:cat /etc/redhat-release
tasks:
 - name: "系統關機"
  command: /sbin/shutdown -t now
  when: ansible_os_family == "RedHat"  

when選項也支援多個條件語句,下面是一個YAML格式的多條件:

tasks:
 - name: "shutdown CentOS 7 systems"
  command: /sbin/shutdown -t now
  when:
   - ansible_distribution == "CentOS"
   - ansible_distribution_major_version == "7"

對於更復雜的條件可以使用and、or與括號進行定義:

tasks:
 - name: "shutdown CentOS 7 and Debian 6 systems"
  command: /sbin/shutdown -t now
  when: (ansible_distribution == "CentOS" and ansible_distribution_major_version == "7") or (ansible_distribution == "Debian" and ansible_distribution_major_version == "6")

在when選項中可以讀取變數的取值,例如:

vars:
 epic: true

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

when選項可以與迴圈一起使用,以實現過濾的功能:

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

(7)任務執行策略

在Ansible中,Playbook的執行是以task為單位進行的。Ansible預設使用5個程序對遠端伺服器執行任務。在預設情況的任務執行策略( linear)中,Ansible首先執行task1,並且等到所有伺服器執行完task1以後再開始執行task2,以此類推。從Ansible 2.0開始,Ansible支援名為free的任務執行策略,允許執行較快的遠端伺服器提前完成Play的部署,不用等待其他遠端伺服器一起執行task。如下所示:

- hosts: all
 strategy: free
 tasks:
  ……

在這一節中,我們比較詳細地介紹了Ansible中的Playbook選項。在Ansible中,Play與task都有很多選項,每個選項可以實現不同的功能。Ansibie官方並沒有通過功能的形式介紹不同的選項給出一個完整的選項列表。我們也可以參考https://github.com/lorin/ansible-quickref快速瞭解Play與task中的選項,以及各個選項的含義。

4、案例:使用Playbook部署nginx

wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
//下載源

在這個例子中,我們使用Ansible配置一臺伺服器執行nginx程序。部署nginx的Playbook如下:

---
- hosts: webservers
 become: yes
 become_method: sudo
 vars:
  worker_prosess: 4
  worker_connections: 768
  max_open_files: 65506
 tasks:
  - name: install nginx
   yum: name=nginx update_cache=yes state=present

  - name: copy nginx config file
   template: src=/root/test_ansible/nginx.conf.j2 dest=/etc/nginx/nginx.conf
   notify: restart nginx

  - name: copy index.html
   template:
    src: /root/test_ansible/index.html.j2
    dest: /usr/share/nginx/www/index.html
    mode: 0644
   notify: restart nginx
 handlers:
  - name: restart nginx
   service: name=nginx state=restarted

在這個Playbook中,我們首先通過hosts選項指定了要對哪些遠端伺服器執行操作。隨後,我們通過become與become_method選項聲明瞭部署時使用sudo許可權。接下來,我們在vars欄位中定義了三個變數,這三個變數將用在nginx的配置檔案中。我們在tasks選項下定義了部署nginx服務的任務列表,包括軟體安裝、模板渲染、定製s首頁和重啟nginx程序。

為了避免配置檔案在沒有任何修改的情況下重啟了nginx程序,這裡使用了Ansible的handler機制。在這個Playbook中,存在兩個notify選項,以及一個handler選項。無論是nginx的配置檔案,還是定製首頁發生了修改,我們都會重啟nginx程序。由於我們使用了Ansible的handlers機制,因此,在沒有任何修改的情況下,Ansible並不會重啟nginx程序。使用handler機制還有一個好處,notify多次,handler也只會執行一次,避免了反覆多次重啟nginx程序。

在這個部署nginx服務的Playbook中,我們用到了nginx.conf.j2這個配置模板。這個模板使用的是Jinja2的語法,所以後綴名為j2。模板的內容如下:

[root@python ~]# mkdir test_ansible
[root@python ~]# vim /root/test_ansible/nginx.conf.j2
worker_processes {{ worker_prosess }};
worker_rlimit_nofile {{ max_open_files }};

events {
  worker_connections {{ worker_connections }};
}

http {
  server {
    listen    80;

    listen 443 ssl;

    server_name localhost;

    location / {
      root  /usr/share/nginx/www;
      index index.html index.htm;

      tr_files $uri $uri/ =404;
    }
  }
}

Ansible會使用我們在Playbook的vars欄位中定義的變數,將Jinja2模板渲染成真實的配置檔案。

我們的Playbook還用到了一個名為index.html.j2的模板,該模板用於渲染網站首頁。index.html.j2的內容如下:

[root@python ~]# vim /root/test_ansible/index.html.j2
<html>
  <meta charset="utf-8">
  <head>
    <title>wellcome to ansible</title>
  </head>
  <body>
    <h1>nginx,configured by ansible</h1>
    <p>如果你能看到這個頁面,說明ansible自動部署nginx成功了!</p>

    <p>{{ ansible_hostname }}<p>
  </body>
</html>

在index.html.j2中,我們用到了一個名為ansible_hostname的變數。這個變數是Facts變數,是Ansible在執行Playbook之前從遠端伺服器獲取到的資訊。因此,我們不需要定義,直接使用即可。

有了Playbook以後,使用ansible-playbook命令進行部署。如下所示:

[root@python ~]# pip install Jinja2
[root@python ~]# vim /etc/ansible/ansible.cfg 

[defaults]
inventory = /root/hosts

[root@bogon ~]# ansible-playbook nginx.yml

PLAY [webservers] **********************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************
ok: [127.0.0.1]

TASK [install nginx] *******************************************************************************************************
ok: [127.0.0.1]

TASK [copy nginx config file] **********************************************************************************************
ok: [127.0.0.1]

TASK [copy index.html] *****************************************************************************************************
ok: [127.0.0.1]

PLAY RECAP *****************************************************************************************************************
127.0.0.1         : ok=4  changed=0  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0  

[root@bogon ~]# 

如果安裝失敗,可能是埠被佔用,可以停止使用該埠的服務,或者更改nginx埠。

[root@bogon ~]# netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address      Foreign Address     State    PID/Program name  
tcp    0   0 0.0.0.0:111       0.0.0.0:*        LISTEN   1/systemd      
tcp    0   0 0.0.0.0:6000      0.0.0.0:*        LISTEN   7470/X       
tcp    0   0 192.168.122.1:53    0.0.0.0:*        LISTEN   7654/dnsmasq    
tcp    0   0 0.0.0.0:22       0.0.0.0:*        LISTEN   7337/sshd      
tcp    0   0 127.0.0.1:631      0.0.0.0:*        LISTEN   7340/cupsd     
tcp    0   0 127.0.0.1:6010     0.0.0.0:*        LISTEN   31653/sshd: root@pt 
tcp    0   0 127.0.0.1:6011     0.0.0.0:*        LISTEN   31653/sshd: root@pt 
tcp    0   0 127.0.0.1:6012     0.0.0.0:*        LISTEN   31653/sshd: root@pt 
tcp6    0   0 :::111         :::*          LISTEN   1/systemd      
tcp6    0   0 :::80          :::*          LISTEN   17867/httpd     
tcp6    0   0 :::6000         :::*          LISTEN   7470/X       
tcp6    0   0 :::22          :::*          LISTEN   7337/sshd      
tcp6    0   0 ::1:631         :::*          LISTEN   7340/cupsd     
tcp6    0   0 ::1:6010        :::*          LISTEN   31653/sshd: root@pt 
tcp6    0   0 ::1:6011        :::*          LISTEN   31653/sshd: root@pt 
tcp6    0   0 ::1:6012        :::*          LISTEN   31653/sshd: root@pt 
[root@bogon ~]# systemctl stop httpd.service

第二臺伺服器啟動一下nginx

[root@192 ~]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@192 ~]# nginx
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()

瀏覽器訪問一下

python中Ansible模組的Playbook的具體使用

到此這篇關於python中Ansible模組的Playbook的具體使用的文章就介紹到這了,更多相關python Ansible Playbook內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!