Ansible入門筆記(3)之Playbook


Ansible-playbook

1.1、什么是playbook?

  • playbook 是由一個或多個play組成的列表
  • play的主要功能在於將直線歸並為一組的主機裝扮實現通過ansible中的task定義好的角色。從根本來講,所謂的task無非是調用ansible的一個module。將多個play組織在一個playbook內,即可以讓它們聯動起來按實現編排的機制唱一台大戲
  • playbook采用YAML語言編寫
    其工作流程圖如下:

1.2、playbook的核心組成

  • Hosts 執行的遠程主機列表

  • Tasks 任務集

  • Varniables 內置變量或自定義變量在playbook中調用

  • Templates 模板,可替換模板中的變量並實現一些簡單的邏輯的文件

  • Hanglers和notify結合使用,由特定條件觸發的操作,滿足條件方才執行,否則不執行

  • Tags 標簽 制定某條任務執行,用戶選擇運行playbook中的部分代碼,ansible具有冪等性,因此會自動跳過沒有辯護的部分,即便如此,有的代碼為測試其確實沒有發生變化的時間依然會非常的長,此時確信其沒有變化,就可以通過tags跳過這些代碼片段

    ansible-playbook -t tagsname useradd.yml

先來一個例子:

[root@ansible ansible]# cat hello.yml 
---
- hosts: web	#指定執行劇本的主機列表
  remote_user: root	#指定以什么用戶去執行playbook

  tasks:	#任務列表
    - name: create new file		#任務名稱
      file: name=/data state=directory	#任務模塊
    - name: create new user
      user: name=lilei shell=/sbin/nologin
    - name: install httpd
      yum: name=httpd
    - name: start service
      service: name=httpd state=started enabled=yes
[root@ansible ansible]# ansible-playbook -C hello.yml	#檢測playbook語法
[root@ansible ansible]# ansible-playbook hello.yml	#執行playbook
  • Hosts:playbook中的每一個play 的目的都是為了讓某個或某些主機以某個特定身份執行任務,hosts用於制定要執行執行任務的主機,須事先定義在主機清單內。可以是兩個組的並集,也可以是兩個組的交集,也支持模糊匹配。
  • remote_user:可用於Host和task中,也可以通過指定其通過sudo的方式在遠程主機上執行任務,其可用於play全局或某服務;次在,甚至可以在sudo時使用sudo_user制定sudo時切換的用戶
  • tasks:play的主題部分是task list。task list中的個任務按次序諸葛在hosts制定的所有主機下執行,即在所有主機上完成第一個任務后開始第二個。
#運行playbook的方式
ansible-playbook <filename.yml> ...[options]

#常見選項

-- check只檢測可能發生的改變,不真正執行等於-C

--list-hosts 列出運行任務的主機

--limit 主機列表指着對主機列表中的主機執行

-v 顯示過程 -vv -vvv更詳細

實例

ansible-playbook file.yml --check

ansible-playbook file.yml

ansible-playbook file.yml --limit web

ansible-playbook file.yml --list-tasks

當我們對服務的配置文件更改時,如果還是用以上的方法,服務是不會根據配置文件的修改后進行自動重啟而生效的,如下:

[root@ansible ansible]# cat hello.yml 
---
- hosts: web
  remote_user: root

  tasks:
    - name: create new file
      file: name=/data state=directory
    - name: create new user
      user: name=lilei shell=/sbin/nologin
    - name: install httpd
      yum: name=httpd
    - name: copy conf file
      copy: src=conf_files/httpd.conf dest=/etc/httpd/conf/httpd.conf
    - name: start service
      service: name=httpd state=started enabled=yes

當我們對上述的配置文件進行了修改監聽端口為81時,httpd服務是不會自動重啟而使配置生效的。此時就需要用到playbook的handers、notify結合來觸發服務的重啟。

1.3、playbook的handlers、notify觸發

[root@ansible ansible]# cat hello.yml 
---
- hosts: web
  remote_user: root

  tasks:
#創建文件  
    - name: create new file
      file: name=/data state=directory
#創建用戶
    - name: create new user
      user: name=lilei shell=/sbin/nologin
#安裝httpd服務
    - name: install httpd
      yum: name=httpd
#拷貝配置文件
    - name: copy conf file
      copy: src=conf_files/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart service    #notify主要用於檢測文件的變化,而通知handler對應的模塊
#啟動服務
    - name: start service
      service: name=httpd state=started enabled=yes
#如果配置文件發生變化則會調用handlers下面的模塊
  handlers:
    - name: restart service
      service: name=httpd state=restarted
     
[root@ansible ansible]# ansible web -m shell -a "netstat -tulnp |grep 82"

1.4、playbook的變量和標簽

  • 標簽(tags)

在眾多的playbook當中,我們為了更加方便地調用公共模塊的tasks,通常會給一些tasks進行打定標簽,在執行的過程中進行指定標簽運行,以實現我們的目標需求,如下:

[root@ansible ansible]# cat hello.yml 
---
- hosts: web
  remote_user: root

  tasks:
    - name: create new file
      file: name=/data state=directory
    - name: create new user
      user: name=lilei shell=/sbin/nologin
    - name: install httpd
      yum: name=httpd
      tags: install_httpd	#安裝httpd的標簽
    - name: copy conf file
      copy: src=conf_files/httpd.conf dest=/etc/httpd/conf/httpd.conf
      notify: restart service
      tags: restart_httpd	#重啟httpd服務的標簽
    - name: start service
      service: name=httpd state=started enabled=yes
      tags: start_httpd		#啟動httpd的標簽
      
  handlers:
    - name: restart service
      service: name=httpd state=restarted

# 直接調用了重啟httpd服務的標簽,-t為指定標簽執行,也可以指定多個標簽一起執行
[root@ansible ansible]# ansible-playbook -t restart_httpd hello.yml
  • 變量(vars)

在playbook當中,所有的任務都是固定的模式,在針對主機時,也是固定組別,服務端口等等也是固定的,寫過shell腳本的大佬都知道在一個腳本當中,對於常用的量以變量代替,從而增加腳本的靈活性,那么在playbook當中也是可以引入變量的。

變量名:只能由字母、數字和下划線組成,且只能字母開頭

變量的定義方式:

  • (1)ansible setup facts遠程主機所有的變量可直接調用,支持通配符。

下面可以通過setup模塊過濾出ip的變量名為:ansible_all_ipv4_addresses,那么在使用時,可以直接調用該變量名,以實現調用。

[root@ansible ~]# ansible web -m setup -a 'filter=*address*'
192.168.0.116 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.0.116"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fef3:ce94"
        ]
    }, 
    "changed": false
}
192.168.0.135 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.0.135"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fe4c:ef31"
        ]
    }, 
    "changed": false
}

(2)在/etc/ansible/hosts中進行定義,普通變量(主機組中主機單獨定義,優先級高於公共變量),公共變量(是在主機組中對所有主機定義的統一變量)

[root@ansible ansible]# cat vars.yml 
---
- hosts: web
  remote_user: root

  tasks:
    - name: set hostname
      hostname: name={{ nodename }}.{{ domainname }}	#playbook中的變量調用
      
[root@ansible ansible]# vim /etc/ansible/hosts 
[web]
192.168.0.135 nodename=node01	#普通變量的定義
192.168.0.116 nodename=node02
[web:vars]	#新增變量組,公共變量的定義
domainname=magedu.com
[root@ansible ansible]# ansible-playbook -C vars.yml
[root@ansible ansible]# ansible-playbook vars.yml
[root@ansible ansible]# ansible web -m shell -a "hostname"
192.168.0.135 | CHANGED | rc=0 >>
node01.magedu.com

192.168.0.116 | CHANGED | rc=0 >>
node02.magedu.com

(3)命令行指定變量,優先級是最高的。

# 在命令行用-e參數指定變量的值
[root@ansible ansible]# ansible-playbook -e 'domainname=baidu.com' vars.yml 
[root@ansible ansible]# ansible web -m shell -a "hostname"
192.168.0.116 | CHANGED | rc=0 >>
node02.baidu.com

192.168.0.135 | CHANGED | rc=0 >>
node01.baidu.com

(4)playbook中定義變量

#變量定義模式
vars:
- var1: value1
- var2: value2

#修改playbook,增加變量使用
[root@ansible ansible]# vim vars.yml 
---
- hosts: web
  remote_user: root
  #配置domainname變量
  vars:	
    - domainname: magedu.com

  tasks:
    - name: set hostname
      hostname: name={{ nodename }}.{{ domainname }}

#檢測語法后執行,並查看執行后效果
[root@ansible ansible]# ansible-playbook -C vars.yml 
[root@ansible ansible]# ansible-playbook vars.yml
[root@ansible ansible]# ansible web -m shell -a "hostname"
192.168.0.135 | CHANGED | rc=0 >>
node01.magedu.com

192.168.0.116 | CHANGED | rc=0 >>
node02.magedu.com

(5)獨立的YAML文件中定義

對於變量管理,由於變量有多重方式可以定義,不同人習慣會導致變量混亂等,故可以考慮吧變量放在同一文件內,使用的時候在文件內修改,劇本中調用該文件。

# 新建var.yml增加變量定義
[root@ansible ansible]# vim var.yml
domainname: hao123.com

#在playbook進行調用變量定義文件
[root@ansible ansible]# vim vars.yml 
---
- hosts: web
  remote_user: root
  #導入變量文件,這里使用的相對路徑,必須和playbook在同一目錄下,如果不在同一目錄,則需要寫全路徑
  vars_files:	
    - var.yml
  tasks:
    - name: set hostname
      hostname: name={{ nodename }}.{{ domainname }}
[root@ansible ansible]# ansible-playbook -C vars.yml 
[root@ansible ansible]# ansible-playbook  vars.yml
[root@ansible ansible]# ansible web -m shell -a "hostname"
192.168.0.135 | CHANGED | rc=0 >>
node01.hao123.com

192.168.0.116 | CHANGED | rc=0 >>
node02.hao123.com

1.5、playbook的模板

在生產服務器集群當中,每台服務器的配置都可能存在不同,在使用ansible進行自動化運維時,只是單純的查詢式操作,我們可以使用普通命令行ansible + hosts + -m + module + -a + "xxx"的模式進行進行批量查詢,如查詢負載,內存,cpu資源使用率等指標數據。而當我們需要批量化對目標主機進行批量任務操作時,如安裝服務,啟動服務,設置開機自啟等批量化任務時,我們采用了playbook的方式進行任務的批量化執行。而在針對配置更改,實現服務自動重啟,也采用了handlers+notify的方式進一步實現自動化的批量更改生效。與此同時,在使用ansible批量化自動運維時,還增加了變量和標簽,以提高playbook的靈活性,以上的種種都說明了ansible模塊化的強大功能。

而對於不同服務器的配置,以及不同的使用需求時,又改如何去更加靈活地去編寫playbook來提高實用性呢?而ansible就提供了這樣的一種模板(template)模塊。假設有這樣的一個需求,進行批量化部署httpd服務后,要求監聽的服務端口分別為87、88端口。那么可以來一場這樣的劇演:

# (1)創建模板配置目錄,拷貝httpd服務的配置文件為j2后綴文件
[root@ansible ansible]# mkdir templates
[root@ansible ansible]# cp conf_files/httpd.conf templates/httpd.conf.j2
[root@ansible ansible]# cd templates/
[root@ansible templates]# ll
total 12
-rw-r--r-- 1 root root 12026 Nov 19 13:51 httpd.conf.j2

# (2)修改j2文件的配置,更改監聽端口配置為http_port變量調用
[root@ansible templates]# vim httpd.conf.j2 
Listen {{ http_port }}

# (3)修改主機列表中的普通變量,增加每台主機分別監聽的端口變量
[root@ansible templates]# vim /etc/ansible/hosts 
[web]
192.168.0.135 nodename=node01 http_port=87
192.168.0.116 nodename=node02 http_port=88
[web:vars]
domainname=magedu.com

# (4)編寫playbook
[root@ansible ansible]# vim hello.yml 
---
- hosts: web
  remote_user: root

  tasks:
    - name: create new file
      file: name=/data state=directory
    - name: create new user
      user: name=lilei shell=/sbin/nologin
    - name: install httpd
      yum: name=httpd
      tags: install_httpd
    - name: copy conf template file
#這里要使用的是template模塊,使用方式和copy模塊類似
      template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
      notify: restart service
      tags: restart_httpd
    - name: start service
      service: name=httpd state=started enabled=yes
      tags: start_httpd

  handlers:
    - name: restart service
      service: name=httpd state=restarted

# (5)playbook測試與執行和查看執行結果
[root@ansible ansible]# ansible-playbook -C hello.yml
[root@ansible ansible]# ansible-playbook hello.yml 
[root@ansible ansible]# ansible web -m shell -a "netstat -tulnp |grep httpd"
192.168.0.135 | CHANGED | rc=0 >>
tcp6       0      0 :::87                   :::*                    LISTEN      32274/httpd         

192.168.0.116 | CHANGED | rc=0 >>
tcp6       0      0 :::88                   :::*                    LISTEN      28307/httpd         

1.6、playbook的條件語句--When

有時候我們希望對某些特定的主機執行某些特定的操作,比如對指定的系統版本,進行關機操作,如下:

tasks:
  - name: "shut down Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_facts['os_family'] == "Debian"
    
# 也可以進行分組多個條件組合進行判斷
tasks:
  - name: "shut down CentOS 6 and Debian 7 systems"
    command: /sbin/shutdown -t now
    when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
          (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")

# 當需要多個條件都必須具備時,可以使用列表的方式進行指定
tasks:
  - name: "shut down CentOS 6 systems"
    command: /sbin/shutdown -t now
    when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"

使用舉例:對判斷被控端的主機名為node02.hao123.com的主機進行更改httpd服務的端口

# 使用setup獲取目標主機的公共變量值
[root@ansible ansible]# ansible web -m setup -a "filter="*hostname*""
192.168.0.135 | SUCCESS => {
    "ansible_facts": {
        "ansible_hostname": "node01"
    }, 
    "changed": false
}
192.168.0.116 | SUCCESS => {
    "ansible_facts": {
        "ansible_hostname": "node02"
    }, 
    "changed": false
}

# 編寫playbook
[root@ansible ansible]# vim hello.yml 
---
- hosts: web
  remote_user: root
  vars:
    - http_port: 90

  tasks:
    - name: install httpd
      yum: name=httpd
      tags: install_httpd
    - name: copy conf template file
      template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
      when: ansible_hostname == "node02"	#增加判斷條件,當 hostname為node02才會更改配置文件,並重啟服務
      notify: restart service
      tags: restart_httpd
    - name: start service
      service: name=httpd state=started enabled=yes
      tags: start_httpd

  handlers:
    - name: restart service
      service: name=httpd state=restarted

[root@ansible ansible]# ansible-playbook -C hello.yml 
[root@ansible ansible]# ansible-playbook hello.yml 
[root@ansible ansible]# ansible 192.168.0.116 -m shell -a "netstat -tulnp |grep httpd"
192.168.0.116 | CHANGED | rc=0 >>
tcp6       0      0 :::90                   :::*                    LISTEN      30308/httpd         

1.7、playbook的循環迭代--Item

Item主要用於循環迭代多個重復的操作,比如批量創建用戶、批量創建文件等等。

# 編寫playbook,進行批量創建文件
[root@ansible ansible]# cat item.yml 
---
- hosts: web
  remote_user: root

  tasks:
    - name: create directory
      file: name=/data state=directory
    - name: create files
      file: name=/data/{{ item }} state=touch
      when: ansible_hostname == "node02"
      with_items:
        - file1
        - file2
        - file3

[root@ansible ansible]# ansible-playbook -C item.yml 
[root@ansible ansible]# ansible-playbook item.yml 
[root@ansible ansible]# ansible 192.168.0.116 -m shell -a "ls -l /data/file*"
192.168.0.116 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 0 Nov 19 15:34 /data/file1
-rw-r--r-- 1 root root 0 Nov 19 15:34 /data/file2
-rw-r--r-- 1 root root 0 Nov 19 15:34 /data/file3

# 創建3個組,3個用戶,並且對應每一個組
[root@ansible ansible]# cat item_user_group.yml 
---
- hosts: web
  remote_user: root
  
  tasks:
    - name: add some groups
      group: name={{ item }}
      with_items:
        - g1
        - g2
        - g3
    - name: add some users
      user: name={{ item.name }} group={{ item.group }}
      with_items:
        - { name: 'user1' , group: 'g1'}
        - { name: 'user2' , group: 'g2'}
        - { name: 'user3' , group: 'g3'}
[root@ansible ansible]# ansible-playbook -C item_user_group.yml
[root@ansible ansible]# ansible-playbook item_user_group.yml

1.8、playbook的循環語句--For

重復性執行一段代碼,生成一段配置信息。示例如下:

# for循環使用語法示例:
{% for vhost in nginx/-vhosts %}

server {

listen {{ vhost.listen| default('80 default_server') }}

{% endfor %}

# 通過jin2模板生成不同監聽端口的server標簽
[root@ansible ansible]# cat for.yml 
---
- hosts: web
  remote_user: root
  vars:
    ports:
      - 83
      - 84
  tasks:
    - name: copy conf file
      template: src=templates/nginx.conf.j2 dest=/data/nginx.conf

# 模板中通過定義循環,生成配置
[root@ansible ansible]# cat templates/nginx.conf.j2 
{% for port in ports %}
server {
     listen {{ port }}
}
{% endfor %}

[root@ansible ansible]# ansible-playbook for.yml
[root@ansible ansible]# ansible web -m shell -a 'cat /data/nginx.conf'
192.168.0.135 | CHANGED | rc=0 >>
server {
     listen 83
}
server {
     listen 84
}

192.168.0.116 | CHANGED | rc=0 >>
server {
     listen 83
}
server {
     listen 84
}

1.9、playbook的判斷語句--If

通過判斷去執行,和shell的語法類似:

{% if vhost.server_name is defined %}

server_name {{vhost.server_name }};

{% endif %}

{% if vhost.root is defined 80 %}

root {{ vhost.root }};

{% endif %}

# 創建3個不同的web站點
[root@ansible ansible]# vim testif1.yml 

---
- hosts: web
  remote_user: root
  vars:
    ports:
      - web1:
        port: 86
        #name: web1.hao123.com   #注釋
        rootdir: /data/website1
      - web2:
        port: 87
        name: web2.hao123.com
        rootdir: /data/website2
      - web3:
        port: 88
        #name: web3.hao123.com   #注釋
        rootdir: /data/website3

  tasks:
    - name: copy conf
      template: src=for3.conf.j2 dest=/data/for3.conf
#對p.name進行判斷,沒有則不生成servername
[root@ansible ansible]# vim templates/for3.conf.j2 
#只有87端口的server有servername  別的由於被注釋,if判斷不存在 就不創建servername
{% for p in ports %}
server {
        listen {{ p.port }}
{% if p.name is defined %}
        servername {{ p.name }}
{% endif %}
        documentroot {{ p.rootdir }}
}
{% endfor %}

[root@ansible ansible]# ansible-playbook testif1.yml 

[root@ansible ansible]# ansible web -m shell -a 'less /data/for3.conf'

1.10、playbook的異常處理

默認Playbook會檢查命令和模塊的返回狀態,如遇到錯誤就中斷playbook的執行加入參數: ignore_errors: yes 忽略錯誤

[root@ansible ansible]# vim ignore.yml
---
- hosts: web
  remote_user: root

  tasks:
    - name: Ignore False
      command: /bin/false	#直接執行返回false
      ignore_errors: yes	#忽略錯誤

# 當上一個任務遇到錯誤中斷時,后面的任務就不會執行了
    - name: touch files
      file: path=/tmp/bgx_ignore state=touch

[root@ansible ansible]# ansible-playbook -C ignore.yml 
[root@ansible ansible]# ansible-playbook ignore.yml 
[root@ansible ansible]# ansible web -m shell -a 'ls -l /tmp/bgx_ignore'
192.168.0.116 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 0 Nov 20 14:35 /tmp/bgx_ignore

192.168.0.135 | CHANGED | rc=0 >>
-rw-r--r-- 1 root root 0 Nov 20 14:35 /tmp/bgx_ignore


免責聲明!

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



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