一 變量的命名規范
變量的命名應該符如下合兩個規范:
- 變量應該由字母、數字、下划線組成
- 變量應該以字母開頭
例如:host_port、HOST_PORT、var5是符合命名規范的,foo-port、 foo port、foo.port 、12都不符合命名規范。
變量的定義通常是YAML形式,在inventory host文件中也可以使用INI形式。
ansible變量不僅可以支持簡單的key=value格式,而且也支持更復雜數據結構,例如字典類型等。
二 變量的作用域
變量的作用域可以分為四種:
- 作用於全局的變量
- 作用於play的變量
- 作用於task的變量
- 作用於host的變量
接下來我們根據變量的作用域,詳細分析下ansible變量的定義、使用和調用順序。
三 作用於全局的變量
3.1 配置文件變量
ansible配置文件會定義一些變量信息,主要是對執行環境、連接信息變量的定義。
例如inventory目錄、library目錄、與目的主機連接方式、越權信息、連接超時時間等等。
3.2 系統環境變量
在ansible連接到目的主機時,會以non-login shell登陸到目的主機,此時目的主機的/etc/bashrc和~/.basrc的環境變量會被加載,所以這兩個文件中設置的環境變量會作用於playbook全局。
3.3 命令行變量
我們可以在執行playbook的命令行指定變量,需要注意的是,命令行指定的變量在所有其他變量中優先級是最高的。也就是說如果命令行指定的變量和其他地方指定的變量有沖突時,那么ansible最終會采用命令行定義的變量。
命令行指定變量示例如下:
ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"
四 作用於play的變量
4.1 playbook中的變量
vars語句定義全局變量
我們可以在playbook中使用「vars」語句定義變量,該變量作用於整個play。
例如:
---
# inventory/playbooks/test.yaml - hosts: node1 vars: http_port: 80
上面示例中中「http_port」是一個作用於整個play的變量,對這個play里的tasks、roles、import、include等等之下定義的task均生效。
引用變量文件
除了將變量寫在playbook中,我們也可以將變量放在一個單獨的YAML文件中,通過「vars_files」語句來導入。
「vars_files」變量只能作用於play全局,不能在某個task中單獨被引用。「vars_files」參數可以使用系統絕對路徑或playbook文件的相對路徑。
舉個🌰:
我們在playbooks目錄下創建一個vars-files.yaml文件:
--- # playbooks/vars-files.yaml age: 100
在playbook中使用vars_files語句引用該變量文件:
--- # playbooks/test.yaml - hosts: node1 vars_files: ./vars-files.yaml tasks: - debug: msg: "My age is {{ age }}"
4.2 roles中的變量
4.2.1 default變量
default變量位於roles/defaults/main.yml文件中,該變量作用於role里的所有play,通常作為模版或模塊里的默認參數。
default變量與ansible filter變量 「{{ some_variable | default("some_value") }}」具有同樣的作用,在所有ansible變量中優先級最低。
4.2.2 dependencies變量
dependencies變量位於roles/meta/main.yaml文件中,該變量與「role」語句同級縮進,作用於本身的role和dependencies role。
舉個🌰:
role_A 和 role_B定義了相同的task,debug出「age」變量:
--- # tasks file for role_B - debug: var: age
--- # tasks file for role_B - debug: var: age
role_A/meta/main.yaml定義role_A依賴role_B,並指定「age」變量等於26:
--- dependencies: - role: role_B vars: age: 27
寫playbook,引用role_A:
--- # playbooks/test.yaml - hosts: node1 roles: - role: role_A
執行結果如下:
➜ lab-ansible ansible-playbook playbooks/test.yaml PLAY [node1] ******************************************************************************************** TASK [Gathering Facts] ********************************************************************************** ok: [node1] TASK [role_B : debug] *********************************************************************************** ok: [node1] => { "age": 27 } TASK [role_A : debug] *********************************************************************************** ok: [node1] => { "age": 27 } PLAY RECAP ********************************************************************************************** node1 : ok=3 changed=0 unreachable=0 failed=0
輸出結果顯示,dependencise變量「age」在role_A和role_B均生效。
4.2.3 vars變量
vars變量位於roles/vars/main.yml,該變量作用於role里的所有模塊。通常將除了默認變量的其他的變量放在這個文件內。
4.3 register變量
register方法能夠將一個task的執行結果注冊為一個變量。書寫格式要與模塊名稱對齊,該變量作用於整個play。
通常register變量和when語句聯合使用,以達到滿足某些條件才運行task的目的。
五 作用於task的變量
5.1 playbook中的變量
with modules
我們可以為某個模塊定義變量,該變量作用於這個task。
舉個🌰,示例中為「debug」模塊定義了「name」和「age」變量並在「msg」參數后使用了這兩個變量:
--- # playbooks/test.yaml - hosts: node1 tasks: - debug: msg: "My name is {{ name }} and I'm {{ age }} years old" vars: name: Maurice age: 27
with import*/include*
在使用import_playbook、import_tasks、include_tasks、import_role、include_role時可以在import*/include*的同級位置指定變量,該變量作用於導入的所有play。
使用import_role舉個🌰:
playbook導入role_A,並定義變量「age」,這樣role_A內的play就可以使用「age」變量了:
--- # playbooks/test.yaml - hosts: node1 tasks: - import_role: name: role_A vars: age: 1000
其他import*/include*的語句使用方法類似,只要記住縮進與import*/include*語句保持一致即可。
with roles
在playbook中使用roles語句來導入role時也可以定義變量,該變量作用於role包含的所有play。
舉個🌰:
playbook使用roles語句導入role_A,並定義變量「age」:
--- # playbooks/test.yaml - hosts: node1 roles: - role: role_A vars: age: 1000
通過上面兩個示例我們發現,roles和import_role語句定義變量寫法上很相似,其實import_role和include_role是新版本的語法,功能上完全可以代替roles語句。如果你使用的ansible版本>=2.4,建議使用include_role和import_role語句。
5.2 roles中的變量
指的是在tasks/main.yaml或handlers/main.yaml內書寫task時指定的變量,該變量作用於某個task,這個變量類型和上述章節中「5.1playbook中的變量—with modules」類似,這里就不再舉例說明。
六 作用於host的變量
6.1 系統變量Facts
6.1.1 facts變量
ansible中有個特殊的變量,這些變量不是開發者定義的,而是ansible根據目的主機環境信息自動收集的,稱之為fact變量。
fact變量很實用,和「when」語句配合使用會讓你的代碼更加健壯。
舉個🌰,如果當前的操作系統為「RedHat」類型,則通過yum安裝需要的軟件包:
--- # playbooks/test.yaml - hosts: node1 tasks: - yum: name: firewalld state: present when: ansible_os_family == 'RedHat'
6.1.2 facts緩存
在執行playbook時,我們發現在「Gathering Facts」步驟時總會卡住一會,如果定義的play多了,會非常耗時。其實這步就是ansible在收集目的主機的facts信息。
如果我們定義的playbook中並沒有使用到fact變量,那么我們可以選擇將其關閉,只需添加「gather_facts: false」即可。
如果必須要使用facts信息,我們可以將fact信息緩存到redis服務或本地json文件中,這樣當我們第二次執行playbook時,ansible就會讀取緩存信息,從而加快運行速度。
假設本地redis服務正常運行,我們只需更改ansible配置文件即可達到緩存fact的目的。
redis緩存:
[defaults] gathering = smart fact_caching = redis fact_caching_timeout = 86400
json文件緩存:
[defaults] gathering = smart fact_caching = jsonfile fact_caching_connection = /path/to/cachedir fact_caching_timeout = 86400
6.2 inventory中的變量
6.2.1 主機變量
關於主機和主機組變量,我們在「ansible基礎-安裝與配置:3.1 主機與主機組」中有介紹,當時介紹了組和主機變量的定義方法、變量的分離、優先級等知識點。其實主機變量的知識點不復雜,這里做下總結。主機變量是指作用在某一台主機上的變量。位置可以與主機定義寫在一起也可以寫在inventory/host_vars/a_host_name.yaml文件里。通常前者使用使用INI格式,后者使用YAML格式。這里要注意一下YAML的語法,在「:」后面要留有一個空格。如果組變量和主機變量都對同一個主機定義了相同的變量,那么ansible最終會采用主機變量而放棄組變量。主機變量示例:INI格式:
# inventory/hosts [nodes] node1 node2 node3 [nodes:vars] http_port=80 database_port=3306
轉換為YAML格式:
--- # inventory/hosts nodes: hosts: node1: node2: node3: vars: http_port: \'80\'
database_port: \'3306\'
不含節點定義的主機變量定義:
--- # inventory/host_vars/node1.yaml http_port: \'80\'
database_port: \'3306\'
6.2.2 組變量
和主機變量類似,組變量作用於主機組,即多個主機。位置可以與主機組定義寫在一起也可以寫在inventory/group_vars/a_group_name.yaml文件里。通常前者使用使用INI格式,后者使用YAML格式。
INI格式:
# inventory/hosts [nodes] node1 node2 node3 [nodes:vars] http_port=80 database_port=3306
轉換為YAML格式:
--- # inventory/hosts nodes: hosts: node1: node2: node3: vars: http_port: \'80\'
database_port: \'3306\'
定義一個字典變量,位於inventory/group_vars/nodes.yaml:
--- # 一位職工記錄 name: Maurice job: Developer skill: Develop program employed: True foods: - Apple - Orange languages: shell: Elite python: Elite c++: Lame
將上面示例轉換為一行:
--- # 一位職工記錄 {name: Maurice,job: Developer,skill: Develop program,employed: True,foods: [\'Apple\',\'Orange\'],languages: {shell: Elite,python: Elite,c++: Lame}}
很顯然,YANL格式分行來寫會更加直觀和美觀。
七 變量的調用順序
通過上面描述,我們發現ansible能夠定義變量的地方真的是太多太多了,我個人覺得ansible變量這塊的設計有點復雜和冗余。
在生產中,我們要讀懂別人的代碼或者讓自己的代碼更加健壯,就必須清楚的知道ansible最終會使用哪個變量。這里我總結下ansible的調用變量的順序,當小伙伴迷茫時可以回來看下這個列表(越靠后變量優先級越高,越會被ansible采用)
- 命令行參數(非-e指定的參數,eg: "-u user -b yes")
- roles defaults目錄下的變量
- 組變量:inventory 文件
- 組變量:inventory/group_vars/all
- 組變量:playbook/group_vars/all
- 組變量:inventory/group_vars/*
- 組變量:playbook/group_vars/*
- 主機變量:inventory 文件
- 主機變量:inventory/group_vars/*
- 主機變量:playbook/group_vars/*
- facts變量
- play變量:vars定義的
- play變量:vars_prompt定義的
- play變量:vars_files導入的
- roles vars目錄下的變量
- block中task定義的變量
- playbook中task定義的變量
- include_vars導入的變量
- set_facts/register注冊的變量
- 使用roles/include_role/import_role語句時定義的變量
- 使用include語句(ansible舊版本)時定義的變量
- 命令行-e參數指定的額外變量(優先級最高)
八 變量的使用
8.1 模塊使用變量
一個變量被定義后,在它的作用域內的play可以直接調用,例如:
我們定義了整個play作用域的變量「name」和「age」,那么在之后的兩個debug模塊內可以直接調用。
--- # playbooks/test.yaml - hosts: node1 vars: name: Maurice age: 27 tasks: - debug: msg: "My name is {{ name }} and I'm {{ age }} years old"
- debug: msg: "Hello Maurice" when: name == 'Maurice'
輸出結果展示:
➜ lab-ansible ansible-playbook playbooks/test.yaml PLAY [node1] ******************************************************************************************** TASK [Gathering Facts] ********************************************************************************** ok: [node1] TASK [debug] ******************************************************************************************** ok: [node1] => { "msg": "My name is Maurice and I'm 27 years old" } TASK [debug] ******************************************************************************************** ok: [node1] => { "msg": "Hello Maurice" } PLAY RECAP ********************************************************************************************** node1 : ok=3 changed=0 unreachable=0 failed=0
8.2 模版使用變量
變量被頻繁使用的還有roles里的模版,位於roles/template/xxx.j2,該模版使用python的Jinja2模版語法。
roles模版多被用於生成服務的配置文件,所以會調用很多的變量。
示例如下:
"customerMonthInfo": "{{ cmp_server_customerMonthInfo }}", "type_black_list": [{% for type_black in cmp_server_type_black_list %}"{{ type_black }}"{{ '' if loop.last else ',' }}{% endfor %}],
上述示例中「customerMonthInfo」的參數比較簡單,就是變量「cmp_server_customerMonthInfo」的值
「type_black_list」參數從列表變量「cmp_server_type_black_list」中獲取,執行結果是個字符串,字符串由該列表的元素以逗號為間隔組成,最后一個參數后沒有逗號。
九 本節應該掌握的技能
- 掌握變量的命名規范
- 掌握變量定義的方法
- 掌握變量的作用域及調用順序
- 會在模塊和模版里使用變量
- 熟悉Jinja2模版的語法規則
十 參考鏈接
- https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#passing-variables-on-the-command-line
- https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html
- https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html
- 紅帽DO407 Automation with Ansible 教材
歡迎大家關注我的公眾號:

