devops工具-Ansible進階playbook&roles

1、playbook介紹

    playbook 是 Ansible 管理配置、部署應用的核心所在,一個playbook由有多「play組成」,而一個play實際就是一個task,每一個task是由多個ansible基本模塊構成,這樣咱們能夠用 playbook 來描述想在遠程主機執行的一些列操做,包括安裝部署、配置管理、任務處理等等。
    playbook是經過yaml格式來定義的,支持同步和異步方式來運行,運行順序是從上到下運行每一個咱們定義的task,從而實現各類複雜任務。關於yaml語法能夠參考 這裏。 

2、核心元素

一個playbook中比較核心的組成由如下幾部分組成:
  • Hosts和User:主機列表和用戶,定義一個playbook操做的遠程主機以及登陸的用戶
  • Tasks:tasks表明任務集,定義遠程主機運行的任務列表,
  • Variables:變量,能夠在playbook中傳遞變量
  • Templates:模版,複用配置文件,jinja2模版引擎
  • Handlers和Notify:觸發器,用於在task任務結束後,能夠觸發其餘操做 

hosts和users

  hosts表明是playbook中的主機列表,能夠是主機組、單獨主機、多個主機(中間以冒號分隔開)、還可使用通配符,users表明登陸目標主機的用戶,使用remote_user指定,當須要使用sudo進行操做時候,須要制定become參數。
---
- hosts: test       #指定主機組
  remote_user: root #指定ssh登陸的用戶
  tasks:           
    - name: test server
      ping:

使用sudo切換html

---
- hosts: test
  remote_user: root
  become: yes             #become參數在2.6版本用於指定sudo
  become_user: admin       #sudo的用戶
  tasks:
    - name: test server
      ping:

tasks

  Tasks是playbook中核心組成成分部分,用於在定義遠程主機執行的一系列操做,這裏成爲任務集。每一個task運行順序按照playbook文件中從上到下依次運行,當某個task運行出錯時中止(可使用ignore_erros參數忽略錯誤改變)。每一個task須有個name用於標記這次的任務名稱,同時也能友好方便咱們閱讀結果。
定義語法: 
tasks:           
    - name: task_name   #任務名稱
      module: module_args #使用的模塊,以及模塊參數,列如yum: name=nginx state=present

示例:
安裝一個httpd服務並啓動: 
---
- hosts: 10.1.210.51
  remote_user: root
  tasks:
   - name: install package   #使用yum安裝包
     yum: name=httpd state=present
   - name: start httpd       #啓動該服務
     service: name=httpd state=started

執行:nginx

variables

  playbook可以使用變量來代替某些重複性的字符串,定義變量方式有多種,而引用變量只須要經過雙括號引用(Jinji2模版語法),例如:{{ var_name }}。 變量命名僅能由字母、數字和下劃線組成,且只能以字母開頭。
變量定義:
1.直接使用facts
在上一篇文章紅介紹了setup模塊收集facts信息,而這些信息能夠用來做爲咱們playbook的變量使用,例如ansible_all_ipv4_addresses這個facts變量保存的是主機的IP地址。
示例:
---
- hosts: 10.1.210.53
  remote_user: root
  tasks:
    - name: test facts var
      shell: echo "{{ ansible_all_ipv4_addresses }}" > host_ip.txt

2.主機清單中定義變量
格式: 
#單獨主機變量,優先級高於公共變量
host  varname=value
#主機組變量
[groupname:vars] #爲指定主機組自定義變量,vars爲關鍵字
varname=value

列如:web

vi /etc/ansible/hosts 
10.1.210.51 business=card   #變量名爲business值爲card
10.1.210.53

[dev]
10.1.210.33
10.1.210.32

[dev:vars]                  #組變量定義
myname=wd

示例:shell

[root@app52 ~]# cat test.yaml 
---
- hosts: 10.1.210.51
  remote_user: root
  tasks:
    - name: test facts from hosts
      debug: var=business  #使用debug打印變量的值,var參數測試變量不須要用{{ }}

運行playbook:json

3.playbook中定義變量vim

格式:服務器

vars:
  - varname1: value1
  - varname2: value2

示例:app

---
- hosts: 10.1.210.51
  remote_user: root
  vars:
    - pkg_name: httpd
    - pkg_cmd: /usr/sbin/httpd
  tasks:
    - name: restart httpd
      service: name={{ pkg_name }} state=restarted
    - name: copy cmd
      copy:  src={{ pkg_cmd }} dest=/tmp/

運行playbook:ssh

4.命令行中定義變量curl

ansible-playbook -e 'varname=value’  #該方式定義的變量優先級最高

5.經過文件引用變量。

[root@app52 ~]# cat /tmp/myvars.yml 
var1: value1
var2: value2

playbook中使用

---
- hosts: 10.1.210.51
  remote_user: root
  vars_files:
    - /tmp/myvars.yml      #變量文件
  tasks:
    - name: test myvar
      debug: msg="{{ var1 }}/{{ var2 }}" #這裏的var1變量和var2變量來自於文件/tmp/myvars.yml

運行結果:

6.在roles中定義變量,後續在介紹roles在講解。

Templates

  不一樣的遠程主機配置文件是通常不相同的,不可能每次都要修改配置文件,全部就出現了模板技術(魔模塊template實現)。經過定義一份模板,在進行配置文件推送時候,會根據咱們定義好的變量進行替換,從而達到不一樣的主機配置文件是不一樣且符合咱們預期的。 templates模版語法採用Jinja2模版語法,參考 這裏
使用方法:
1.定義模版,文件名稱必須以.j2結尾
vim /tmp/nginx.conf.j2

user  nginx;
worker_processes  {{ ansible_processor_vcpus }};  #使用cpu個數做爲woeker數量
error_log /var/log/nginx_error.log crit;
pid       /var/run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
worker_rlimit_nofile 65535;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    gzip  on;
 server
  {
    listen 80;  
    server_name   {{ ansible_fqdn }} #使用主機名做爲hostname
    access_log    /var/log/access.log;
    error_log    /var/log/error.log;
}
}

2.playbook中使用

---
- hosts: 10.1.210.53
  remote_user: root
  vars:
  - package_name: nginx
  - config_path: /etc/nginx/nginx.conf
  tasks:
    - name: install epel
      yum:  name=epel-release
    - name: install package
      yum: name={{ package_name }}
    - name: copy config file
      template: src=/tmp/nginx.conf.j2 dest={{ config_path }} backup=yes #backup用於備份
    - name: start service
      service: name={{ package_name }} state=started

運行playbook

Handlers和notify

  Handlers是task列表,這些task與tasks中的task並無本質上的不一樣,用於當關注的資源發生變化時,纔會採起必定的操做。而觸發的行爲則是由notify產生,能夠理解爲notify通知Handlers觸發某個task。
    須要注意的是,無論發生多少次notify行爲,等到play中的全部task執行完成以後,handlers中的task纔會被執行,而這些task只會被執行一次;
   notify能夠定義多個,多個notify用列表方式來表示。
示例:
- hosts: 10.1.210.53
  remote_user: root
  vars:
  - package_name: nginx
  - config_path: /etc/nginx/nginx.conf
  tasks:
    - name: install epel
      yum:  name=epel-release
    - name: install package
      yum: name={{ package_name }}
    - name: copy config file
      template: src=/tmp/nginx.conf.j2 dest={{ config_path }} backup=yes
      notify:  #當配置文件發生變化時候,通知hanler觸發task
        - stop service
        - start service
  handlers:
    - name: stop service
      service: name={{ package_name }} state=stopped
    - name: start service
      service: name={{ package_name }} state=started

運行playbook

3、playbook進階技巧 

使用tag標記任務

爲某個任務打一個標籤,可使用ansible-playbook 命令選擇性的執行任務。打標籤可經過tags標籤指定,能夠是與其相關的參數:
  • --list-tags :列出playbook中全部的tag
  • --skip-tags:跳過playbook中被標記的tag任務
  • -t 或 --tags:指定運行playbook中被標記的tag任務
 
如下playbook中分別定義了將兩個tag進行了標記:
---
- hosts: 10.1.210.51
  remote_user: root
  tasks:
    - name: exec tag1
      shell: cat /etc/passwd
      tags: tag1
    - name: exec tag2
      file: path=/tmp/b.txt state=touch
      tags: tag2
    - name: view password
      shell: cat /etc/passwd

列出tag

指定運行某個tag任務

跳過tag任務運行

從以上結果可看出,跳過了tag1和tag2任務,只運行了view password任務。

 

條件測試when

when關鍵字可實現根據某些條件判斷當前的task是否須要執行。when可根據變量、facts(setup)或此前任務的執行結果來做爲判斷依據,同時支持邏輯運算符操做,比較強大和實用。
須要注意的是:
  • when判斷的對象是task,因此和tas k在同一列表層次。它的判斷結果決定它所在task是否執行,而不是它下面 的task是否執行。
  • when中引用變量的時候不須要加{{ }}符號。

示例一:使用facts變量測試

tasks:
  - name: "shut down Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_facts['os_family'] == "Debian"     #當操做系統是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")

兩個條件使用and還能夠以下表示

tasks:
  - name: "shut down CentOS 6 systems"
    command: /sbin/shutdown -t now
    when:
      - ansible_facts['distribution'] == "CentOS"
      - ansible_facts['distribution_major_version'] == "6"  #同時知足系統是CentOS且版本是6

示例三:根據任務的執行結果狀態來判斷任務是否執行,這裏使用register來保存了任務執行結果,後續會介紹。

---
- hosts: 10.1.210.51
  name: test when
  remote_user: root
  tasks:
    - name: task1
      command: /bin/false
      register: result            #將本次任務結果保存在變量中
      ignore_errors: True          #忽略錯誤執行,可確保任務錯誤也能執行下一個任務

    - name: pre task failed
      file: path=/tmp/failed.txt state=touch    
      when: result is failed          #判斷result結果是不是failed 

    - name: pre task successed state=touch
      file: path=/tmp/success.txt
      when: result is succeeded       #判斷result結果是不是succeeded

    - name: pre task skipped  state=touch
      file: path=/tmp/skip.txt
      when: result is skipped          #判斷結果是不是skipped

運行一下:

如上圖,紅色的部分任務都跳過了未執行,這是由於result結果是failded,因此只執行了「pre task failed」這個任務。

示例4:使用變量進行測試

---
- hosts: 10.1.210.51
  name:  test var when
  remote_user: root
  vars:
    - flag: true        #變量定義
  tasks:
    - name: test server
      ping:
      when: flag        #判斷變量flag爲true執行

    - name: create file
      file: path=/tmp/myfile.txt state=touch
      when: not flag    #變量flag不爲true執行

執行結果:

一樣能夠看到create file 任務被跳過了。

使用register保存任務結果

  register註冊變量,用於保存任務結果,可用於保存配置或when關鍵字判斷,這是很是有用了,在上個示例中使用了任務結果做爲判斷條件。例如,你能夠將文件中的配置或者json內容保存在變量中,能夠供後續使用:

---
- hosts: 10.1.210.51
  name: test register
  remote_user: root
  tasks:
    - name: read conf
      shell: cat /tmp/nginx.conf
      register: nginx_conf  #註冊變量

    - name: copy conf
      copy: content={{ nginx_conf }} dest=/etc/nginx/nginx.conf #使用變量

還能夠配合循環使用,如下示例展現了批量建立軟連接:

- name: registered variable usage as a loop list
  hosts: all
  tasks:

    - name: retrieve the list of home directories
      command: ls /home
      register: home_dirs

    - name: add home dirs to the backup spooler
      file:
        path: /mnt/bkspool/{{ item }}
        src: /home/{{ item }}
        state: link
      loop: "{{ home_dirs.stdout_lines }}"

使用迭代進行重複性操做或任務

當有須要重複性執行的任務時,可使用迭代機制。其迭代的內容會保存在特殊變量item中,ansible提供不少迭代指令,大多都是以with_開頭,如下是幾種常見的循環。 
1.with_items迭代列表
示例一:建立多個用戶 
---
- hosts: localhost
  remote_user: root
  tasks:
   - name: create user
     user: name={{ item }} state=present
     with_items:
       - zabbix
       - admin

執行結果:

示例二:循環中使用register註冊變量

- hosts: localhost
  remote_user: root
  tasks:
     - command: echo {{ item }}
       with_items: [ 0, 2, 4, 6, 8, 10 ]
       register: num
     - debug: msg="{% for i in num.results %} {{i.stdout}} {% endfor %}"

注意,將with_items迭代後的結果註冊爲變量時,其註冊結果也是列表式的,且其key"results"。具體的結果比較長,可使用debug模塊的varmsg參數觀察變量的結果。以上示例運行結果以下:

2.with_dict迭代字典

使用"with_dict "能夠迭代字典項。迭代時,使用"item.key"表示字典的key"item.value"表示字典的值。
示例一:
---
- hosts: localhost
  remote_user: root
  tasks:
     - debug: msg="{{ item.key }} / {{ item.value }}"
       with_dict: { ip: 10.1.210.51, hostname: app52, gateway: 10.1.210.1}

以上示例中字典是已經存在了,除此以外字典能夠來源於變量、facts等。例如使用facts進行迭代

---
- hosts: localhost
  remote_user: root
  tasks:
    - debug: msg="{{item.key}} / {{item.value}}"
      with_dict: "{{ ansible_cmdline }}"

 

使用include簡化playbook

 若是將全部的play都寫在一個playbook中,很容易致使這個playbook文件變得臃腫龐大,且不易讀。所以,能夠將多個不一樣任務分別寫在不一樣的playbook中,而後使用include將其包含進去便可。include能夠導入兩種文件:導入task文件、導入playbook。

示例:建立task.yml任務列表

vim task.yml
- name: task1
  debug: msg="exec task1"
- name: task2
  debug: msg="exec task2"

在目標playbook中倒入任務

---
- hosts: 10.1.210.51
  remote_user: root
  tasks:
    - include: task.yml

執行playbook:

示例2:直接導入其餘playbook,不過在ansible2.8將移除,2.8中將使用import_playbook 

---
- hosts: 10.1.210.51
  remote_user: root
  tasks:
    - include: task.yml

- include: test_when.yml
- include: test_with.yml

4、roles(角色)介紹

簡介

  roles 就字面上來講有角色、做用的意思,但它的全名實際上是 Playbooks Roles,咱們可把它當成是 playbooks 的延伸使用,能夠下降 playbooks 的複雜性,更能夠增長 playbooks 的可用性。簡單來說,roles就是經過分別將變量、文件、任務、模板及處理器放置於單獨的目錄中,並能夠便捷地include它們的一種機制。

使用場景

  • 同時安裝多個不一樣的軟件如:LNMP環境
  • 不一樣服務器組須要安裝不一樣服務
  • 複雜的playbook,使用role能夠具備閱讀性 

目錄結構

一個角色中目錄包含如下目錄:
  • files:用來存放由copy模塊或script模塊調用的文件。
  • templates:用來存放jinjia2模板,template模塊會自動在此目錄中尋找jinjia2模板文件。
  • tasks:此目錄應當包含一個main.yml文件,用於定義此角色的任務列表,此文件可使用include包含其它的位於此目錄的task文件。
  • handlers:此目錄應當包含一個main.yml文件,用於定義此角色中觸發條件時執行的動做。
  • vars:此目錄應當包含一個main.yml文件,用於定義此角色用到的變量。
  • defaults:此目錄應當包含一個main.yml文件,用於爲當前角色設定默認變量。
  • meta:此目錄應當包含一個main.yml文件,用於定義此角色的元數據信息。

   例如:一個nginx角色的目錄結構能夠是:

.
└── nginx
    ├── default
    ├── files
    ├── handlers
    ├── meta
    ├── tasks
    ├── templates
    └── vars

多個role目錄:

├── httpd             #http role
│   ├── default
│   ├── files
│   ├── handlers
│   ├── meta
│   ├── tasks
│   ├── templates
│   └── vars
└── nginx            #nginx role
    ├── default
    ├── files
    ├── handlers
    ├── meta
    ├── tasks
    ├── templates
    └── vars

演示:使用role安裝nginx

1、建立對應的目錄結構:

[root@app52 ~]# mkdir -pv roles/nginx/{files,templates,vars,tasks,handlers,meta,default} 
mkdir: 已建立目錄 "roles"
mkdir: 已建立目錄 "roles/nginx"
mkdir: 已建立目錄 "roles/nginx/files"
mkdir: 已建立目錄 "roles/nginx/templates"
mkdir: 已建立目錄 "roles/nginx/vars"
mkdir: 已建立目錄 "roles/nginx/tasks"
mkdir: 已建立目錄 "roles/nginx/handlers"
mkdir: 已建立目錄 "roles/nginx/meta"
mkdir: 已建立目錄 "roles/nginx/default」

2、定義變量

[root@app52 ~]# vi roles/nginx/vars/main.yml
pkg_name: nginx  #安裝包名稱
listen_port: 80   #監聽端口

3、編寫任務

這裏能夠把任務模塊化,最後在main.yml包含它們

[root@app52 ~]# vi roles/nginx/tasks/yum.yml
- name: install epel
  yum: name=epel-release state=present

- name: install nginx pkg
  yum: name={{ pkg_name }} state=present

[root@app52 ~]# vi roles/nginx/tasks/copy.yml 
- name: copy nginx.conf
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

- name: copy index.html
  copy: src=index.html dest=/var/www/html/
  notify: reload

[root@app52 ~]# vi roles/nginx/tasks/start_service.yml 
- name: start nginx
  service: name=nginx state=restarted

[root@app52 ~]# vi roles/nginx/tasks/main.yml 
- include: yum.yml
- include: copy.yml
- include: start_service.yml

4、準備配置文件以及index.html

#index.html
[root@app52 ~]# vi roles/nginx/files/index.html 
<h1>Hello wd</h1>

#配置文件模版
[root@app52 ~]# vi roles/nginx/templates/nginx.conf.j2 
user  nginx;
worker_processes  {{ ansible_processor_vcpus }};  #使用cpu個數做爲woeker數量
error_log /var/log/nginx_error.log crit;
pid       /var/run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
worker_rlimit_nofile 65535;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    gzip  on;
 server
  {
    listen {{ listen_port }};
    server_name   {{ ansible_all_ipv4_addresses[0] }} ; #使用IP地址做爲server name
    root /var/www/html                ;
    access_log    /var/log/access.log;
    error_log    /var/log/error.log;
}
}

5、編寫handlers

若是在task中使用了notify,則就須要寫對應的handlers,上述我使用了reload這個handler,因此這裏須要定義:

[root@app52 ~]# vi roles/nginx/handlers/main.yml 
- name: reload
  service: name=nginx state=reloaded

6、在角色同級目錄編寫playbook引入角色

[root@app52 ~]# vi roles/install_nginx.yml 
- hosts: web             #指定使用role的主機或主機組
  remote_user: root      #指定用戶
  roles:                 #使用的role,能夠有多個
    - nginx

最後的目錄結構爲:

[root@app52 ~]# tree roles/
roles/
├── install_nginx.yml
└── nginx
    ├── default
    ├── files
    │   └── index.html
    ├── handlers
    │   └── main.yml
    ├── meta
    ├── tasks
    │   ├── copy.yml
    │   ├── main.yml
    │   ├── start_service.yml
    │   └── yum.yml
    ├── templates
    │   └── nginx.conf.j2
    └── vars
        └── main.yml

8 directories, 9 files

7、運行playbook並測試

如紅色部分,curl測試nginx 安裝成功。 

相關文章
相關標籤/搜索