一、playbook介紹 playbook 是 Ansible 管理配置、部署應用的核心所在,一個playbook由有多“play組成”,而一個play實際就是一個task,每個task是由多個ansible基本模塊構成,這樣我們可以用 playbook 來描述想在遠程主機執行的一些列操作,包括安裝 ...
一、playbook介紹
playbook 是 Ansible 管理配置、部署應用的核心所在,一個playbook由有多“play組成”,而一個play實際就是一個task,每個task是由多個ansible基本模塊構成,這樣我們可以用 playbook 來描述想在遠程主機執行的一些列操作,包括安裝部署、配置管理、任務處理等等。 playbook是通過yaml格式來定義的,支持同步和非同步方式來運行,運行順序是從上到下運行每個我們定義的task,從而實現各種複雜任務。關於yaml語法可以參考這裡。二、核心元素
一個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切換
--- - 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
執行:
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.txt2.主機清單中定義變數 格式:
#單獨主機變數,優先順序高於公共變數 host varname=value #主機組變數 [groupname:vars] #為指定主機組自定義變數,vars為關鍵字 varname=value
列如:
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
示例:
[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:
3.playbook中定義變數
格式:
vars: - varname1: value1 - varname2: value2
示例:
--- - 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:
4.命令行中定義變數
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
三、playbook進階技巧
使用tag標記任務
為某個任務打一個標簽,可以使用ansible-playbook 命令選擇性的執行任務。打標簽可通過tags標簽指定,可以是與其相關的參數:- --list-tags :列出playbook中所有的tag
- --skip-tags:跳過playbook中被標記的tag任務
- -t 或 --tags:指定運行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模塊的var或msg參數觀察變數的結果。以上示例運行結果如下:
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
四、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
一、創建對應的目錄結構:
[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”
二、定義變數
[root@app52 ~]# vi roles/nginx/vars/main.yml pkg_name: nginx #安裝包名稱 listen_port: 80 #監聽埠
三、編寫任務
這裡可以把任務模塊化,最後在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
四、準備配置文件以及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; } }
五、編寫handlers
如果在task中使用了notify,則就需要寫對應的handlers,上述我使用了reload這個handler,所以這裡需要定義:
[root@app52 ~]# vi roles/nginx/handlers/main.yml - name: reload service: name=nginx state=reloaded
六、在角色同級目錄編寫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
七、運行playbook並測試
如紅色部分,curl測試nginx 安裝成功。