一、Playbook基本語法
執行Playbook語法
$ansible-playbook deploy.yml
查看輸出的細節
ansblie-playbook playbook.yml --verbose
查看該腳本影響哪些hosts
ansible-playbook playbook.yml --list-hosts
並行執行腳本
ansible-playbook playbook.yml -f 10
完整的playbook腳本示例
最基本的playbook腳本分為三個部分:
在什么機器上以什么身份執行
-
hosts
-
users
-
…
執行的任務是都有什么 -
tasks
善后的任務都有什么 -
handlers
deploy.yml文件
---
- hosts: webservers
vars:
http_port: 80
max_clients: 200
user: root
tasks:
- name: ensure apache is at the latest version
yum: pkg=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
二、主機和用戶
- hosts: 為主機的IP,或者主機組名,或者關鍵字all
- user: 在遠程以那個用戶身份執行
- become: 切換成其他用戶身份執行,值為yes或者no
- become_method: 與became一起用,指可以為‘sudo’/’su’/’pbrun’/’pfexec’/’doas’
- become_user: 與become_user一起用,可以是root或者其他用戶名
腳本里用became的時候,執行的playbook的時候可以加參數—ask-become-pass
ansible-playbook deploy.yml --ask-become-pass
Tasks任務列表
- tasks是從上到下順序執行,如果中間發生錯誤,那么整個playbook會中止。你可以修改文件后,再重新執行。
- 每一個task的對module一次調用。使用不同的參數和變量而已。
- 每一個task最好有name屬性,這個是供人讀的,沒有實際的操作。然后會在命令行里面輸出,提示用戶執行情況。
語法:
task的基本語法:
tasks:
- name: make sure apache is running
service: name=httpd state=running
其中name是可選的,也可以簡寫成下面的例子:
tasks:
- service: name=httpd state=running
寫name的task在playbook執行時,會顯示對應的名字,信息更友好、豐富。寫name是個好習慣!
TASK: [make sure apache is running] *************************************************************
changed: [yourhost]
沒有寫name的task在playbook執行時,直接顯示對應的task語法。在調用同樣的module多次后,不同意分辨執行到哪步了。
TASK: [service name=httpd state=running] **************************************
changed: [yourhost]
參數的不同寫法
最上的代碼展示了最基本的傳入module的參數的方法:key=value
tasks:
- name: make sure apache is running
service: name=httpd state=running
當需要傳入參數列表太長時,可以分隔到多行:
tasks:
- name: Copy ansible inventory file to client
copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
owner=root group=root mode=0644
或者用yml的字典傳入參數
tasks:
- name: Copy ansible inventory file to client
copy:
src: /etc/ansible/hosts
dest: /etc/ansible/hosts
owner: root
group: root
mode: 0644
TASK的執行狀態
task中每個action會調用一個module,在module中會去檢查當前系統狀態是否需要重新執行。
- 如果本次執行了,那么action會得到返回值changed;
- 如果不需要執行,那么action得到返回值ok
module的執行狀態的具體判斷規則由各個module自己決定和實現的。例如,”copy” module的判斷方法是比較文件的checksum,代碼如下:
狀態示例
以一個copy文件的task為例子:
tasks:
- name: Copy the /etc/hosts
copy: src=/etc/hosts dest=/etc/hosts
第一次執行,它的結果是這個樣子的:
TASK的狀態是changed
第二次執行是下面這個樣子的:
TASK的狀態是ok,由於第一次執行copy_hosts.yml的時候,已經拷貝過文件,那么ansible目標文件的狀態避免重復執行.
下面我更改vm-rhel7-1的/etc/hosts, 再次執行看看:
三、響應事件(Handler)
什么是handler
每個主流的編程語言都會有event機制,那么handler就是playbook的event。
Handlers里面的每一個handler,也是對module的一次調用。而handlers與tasks不同,tasks會默認的按定義順序執行每一個task,handlers則不會,它需要在tasks中被調用,才有可能被執行。
Tasks中的任務都是有狀態的,changed或者ok。在Ansible中,只在task的執行狀態為changed的時候,才會執行該task調用的handler,這也是handler與普通的event機制不同的地方。
應用場景
什么情況下使用handlers呢?
如果你在tasks中修改了apache的配置文件。需要重啟apache。此外還安裝了apache的插件。那么還需要重啟apache。像這樣的應用場景中,重啟apoache就可以設計成一個handler
一個handler最多只執行一次
在所有的任務里表執行之后執行,如果有多個task notify同一個handler,那么只執行一次。
---
- hosts: lb
remote_user: root
vars:
random_number1: "{{ 10000 | random }}"
random_number2: "{{ 10000000000 | random }}"
tasks:
- name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }}
copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }}
notify:
- call in every action
- name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }}
copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }}
notify:
- call in every action
handlers:
- name: call in every action
debug: msg="call in every action, but execute only one time"
action是Changed ,才會執行handler
只有當TASKS種的action的執行狀態是changed時,才會觸發notify handler的執行。
下面的腳本執行兩次,執行結果是不同的:
-
第一次執行是,tasks的狀態都是changed,會觸發兩個handler
-
第二次執行是,
- 第一個task的狀態是OK,那么不會觸發handlers”call by /tmp/hosts”, - 第二個task的狀態是changed,觸發了handler”call by /tmp/hosts.random_number”
測試代碼見:
---
- hosts: lb
remote_user: root
vars:
random_number: "{{ 10000 | random }}"
tasks:
- name: Copy the /etc/hosts to /tmp/hosts
copy: src=/etc/hosts dest=/tmp/hosts
notify:
- call by /tmp/hosts
- name: Copy the /etc/hosts to /tmp/hosts.{{ random_number }}
copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number }}
notify:
- call by /tmp/hosts.random_number
handlers:
- name: call by /tmp/hosts
debug: msg="call first time"
- name: call by /tmp/hosts.random_number
debug: msg="call by /tmp/hosts.random_number"
按Handler的定義順序執行
handlers是按照在handlers中定義個順序執行的,而不是安裝notify的順序執行的。
下面的例子定義的順序是1>2>3,notify的順序是3>2>1,實際執行順序:1>2>3.
---
- hosts: lb
remote_user: root
gather_facts: no
vars:
random_number1: "{{ 10000 | random }}"
random_number2: "{{ 10000000000 | random }}"
tasks:
- name: Copy the /etc/hosts to /tmp/hosts.{{ random_number1 }}
copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number1 }}
notify:
- define the 3nd handler
- name: Copy the /etc/hosts to /tmp/hosts.{{ random_number2 }}
copy: src=/etc/hosts dest=/tmp/hosts.{{ random_number2 }}
notify:
- define the 2nd handler
- define the 1nd handler
handlers:
- name: define the 1nd handler
debug: msg="define the 1nd handler"
- name: define the 2nd handler
debug: msg="define the 2nd handler"
- name: define the 3nd handler
debug: msg="define the 3nd handler"
四、變量
在Playbook中用戶自定義的變量
用戶可以在Playbook中,通過vars關鍵字自定義變量,使用時用{{}}引用以來即可。
Playbook中定義和使用的變量的方法
例如下面的例子中,用戶定義變量名為http_port,其值為80.在tasks firewalld中,通過{{http_port}}引用。
---
- hosts: web
vars:
http_port: 80
remote_user: root
tasks:
- name: insert firewalld rule for httpd
firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
把變量放在單獨的文件中
當變量比較多的時候,或者變量需要在多個playbook中重用的時候,可以把變量放到一個單獨的文件中。通過關鍵字var_files把文件中定義的變量引起playbook中,使用變量的方法和在本文中定義的變量相同。
- hosts: web
remote_user: root
vars_files:
- vars/server_vars.yml
tasks:
- name: insert firewalld rule for httpd
firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
變量文件/vars/server_vars.yml的內容為:
http_port: 80
定義和使用復雜變量
有時候我們使用的變量不是簡單的字符串或者數字,而是一個對象。這時候定義的語法如下,格式為YAML的字典格式:
foo:
field1: one
field2: two
訪問復雜變量中的子屬性,可以利用中括號或者點號:
foo['field1']
foo.field1
YAML的陷阱
YAML的陷阱是某些時候YAML和Ansible Playbook的變量語法不能在一起好好工作了。這里僅發生在指冒號后面的值不能以{開頭的時候,如果有必要以{開頭,必須加上因號。總之在YAML值的定義中,如果提示YMAL語法錯誤,都可以嘗試下加入引號來解決。
下面的代碼會報錯:
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
解決辦法:要在”{ “開始的值加上引號:
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
五、主機的系統變量(facts)
ansible 會通過module setup來收集主機的系統信息,這些收集到的系統信息叫做facts,這些facts信息可以直接以變量的形式使用。
哪些fact變量可以引用呢?在命令行上通過調用setup module命令可以查看
$ ansible all -m setup -u root
怎樣在playbook中使用facts變量呢,答案是直接使用:
---
- hosts: all
user: root
tasks:
- name: echo system
shell: echo {{ ansible_os_family }}
- name install ntp on Debian linux
apt: name=git state=installed
when: ansible_os_family == "Debian"
- name install ntp on redhat linux
yum: name=git state=present
when: ansible_os_family == "RedHat"
使用復雜facts變量
一般在系統中收集到如下的信息,復雜的、多層級的facts變量如何使用呢?
...
"ansible_ens3": {
"active": true,
"device": "ens3",
"ipv4": {
"address": "10.66.192.234",
"netmask": "255.255.254.0",
"network": "10.66.192.0"
},
"ipv6": [
{
"address": "2620:52:0:42c0:5054:ff:fef2:e2a3",
"prefix": "64",
"scope": "global"
},
{
"address": "fe80::5054:ff:fef2:e2a3",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "52:54:00:f2:e2:a3",
"module": "8139cp",
"mtu": 1500,
"promisc": false,
"type": "ether"
},
...
那么可以通過下面的兩種方式訪問復雜的變量中的子屬性:
中括號:
{{ ansible_ens3["ipv4"]["address"] }}
點號:
{{ ansible_ens3.ipv4.address }}
關閉facts
在Playbook中,如果寫gather_facts來控制是否收集遠程系統的信息.如果不收集系統信息,那么上面的變量就不能在該playybook中使用了.
- hosts: whatever
gather_facts: no
六、文件模板中使用的變量
template module 在Ansible中非常常用,而它在使用的時候又沒有顯示的指定template文件中的值,所以有時候用戶會對template文件中使用的變量感到困惑,所以在這里又重新強調下。
template變量的定義
在playbook中定義的變量,可以直接在template中使用,同時facts變量也可以直接在template中使用,當然也包含在inventory里面定義的host和group變量。只要是在playbook中可以訪問的變量,都可以在template文件中使用。
下面的playbook腳本中使用了template module來拷貝文件index.html.j2,並且替換了index.html.j2中的變量為playbook中定義變量值。
---
- hosts: web
vars:
http_port: 80
defined_name: "Hello My name is Jingjng"
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: pkg=httpd state=latest
- name: Write the configuration file
template: src=templates/httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
notify:
- restart apache
- name: Write the default index.html file
template: src=templates/index2.html.j2 dest=/var/www/html/index.html
- name: ensure apache is running
service: name=httpd state=started
- name: insert firewalld rule for httpd
firewalld: port={{ http_port }}/tcp permanent=true state=enabled immediate=yes
handlers:
- name: restart apache
service: name=httpd state=restarted
template變量的使用
Ansible模版文件中使用變量的語法是Python的template語言Jinja2
Ansible模版文件使用變量的語法是Python的template語言Jinja2。
在下面的例子template index.html.j2中,直接使用了以下變量:
系統變量 {{ ansible_hostname }} , {{ ansible_default_ipv4.address }}
用戶自定義的變量 {{ defined_name }}
ndex.html.j2文件:
<html>
<title>Demo</title>
<body>
<div class="block" style="height: 99%;">
<div class="centered">
<h1>#46 Demo {{ defined_name }}</h1>
<p>Served by {{ ansible_hostname }} ({{ ansible_default_ipv4.address }}).</p>
</div>
</div>
</body>
</html>
七、把運行結果當做變量使用-注冊變量
把task的執行結果當作是一個變量的值也是可以的。這個時候就需要用到“注冊變量”,將執行結果注冊到一個變量中,待后面的action使用:
---
- hosts: web
tasks:
- shell: ls
register: result
ignore_errors: True
- shell: echo "{{ result.stdout }}"
when: result.rc == 5
- debug: msg="{{ result.stdout }}"
注冊變量經常和debug module一起使用,這樣可以得到更多的關於執行錯誤的信息,幫助用戶調試。
八、用命令行中傳遞變量
用命令行傳遞參數
為了使Playbook更靈活、通用性更強,允許用戶在執行的時候傳入變量的值,這個時候就需要用到“額外變量”。
定義命令行變量
在release.yml文件里,hosts和user都定義為變量,需要從命令行傳遞變量值。
---
- hosts: '{{ hosts }}'
remote_user: '{{ user }}'
tasks:
- ...
使用命令行變量
在命令行里面傳值得的方法:
ansible-playbook e33_var_in_command.yml --extra-vars "hosts=web user=root"
還可以用json格式傳遞參數:
ansible-playbook e33_var_in_command.yml --extra-vars "{'hosts':'vm-rhel7-1', 'user':'root'}"
還可以將參數放在文件里面:
ansible-playbook e33_var_in_command.yml --extra-vars "@vars.json"