Ansible Jinja2 模板使用、語法與使用案例 主機規劃 添加用戶賬號 說明: 1、 運維人員使用的登錄賬號; 2、 所有的業務都放在 /app/ 下「yun用戶的家目錄」,避免業務數據亂放; 3、 該用戶也被 ansible 使用,因為幾乎所有的生產環境都是禁止 root 遠程登錄的(因此 ...
Ansible Jinja2 模板使用、語法與使用案例
主機規劃
添加用戶賬號
說明:
1、 運維人員使用的登錄賬號;
2、 所有的業務都放在 /app/ 下「yun用戶的家目錄」,避免業務數據亂放;
3、 該用戶也被 ansible 使用,因為幾乎所有的生產環境都是禁止 root 遠程登錄的(因此該 yun 用戶也進行了 sudo 提權)。
1 # 使用一個專門的用戶,避免直接使用root用戶 2 # 添加用戶、指定家目錄並指定用戶密碼 3 # sudo提權 4 # 讓其它普通用戶可以進入該目錄查看信息 5 useradd -u 1050 -d /app yun && echo '123456' | /usr/bin/passwd --stdin yun 6 echo "yun ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 7 chmod 755 /app/
Ansible 配置清單Inventory
之後文章都是如下主機配置清單
1 [yun@ansi-manager ansible_info]$ pwd 2 /app/ansible_info 3 [yun@ansi-manager ansible_info]$ cat hosts_key 4 # 方式1、主機 + 埠 + 密鑰 5 [manageservers] 6 172.16.1.180:22 7 8 [proxyservers] 9 172.16.1.18[1:2]:22 10 11 # 方式2:別名 + 主機 + 埠 + 密碼 12 [webservers] 13 web01 ansible_ssh_host=172.16.1.183 ansible_ssh_port=22 14 web02 ansible_ssh_host=172.16.1.184 ansible_ssh_port=22 15 web03 ansible_ssh_host=172.16.1.185 ansible_ssh_port=22
Jinja2 模板概述
官網地址
http://docs.jinkan.org/docs/jinja2/
Jinja2 是一個現代的,設計者友好的,仿照 Django 模板的 Python 模板語言。 它速度快,被廣泛使用,並且提供了可選的沙箱模板執行環境保證安全。
Ansible 如何使用 jinja2 模板
Ansible 使用 jinja2 模板,也就是 template 模板。該模塊和 copy 模塊一樣,都是將文件複製到目標機器,但是區別在於 template 模塊可以獲取要複製文件中的變數的值,而 copy 則是原封不動的把文件內容複製過去。
實際運用,比如:針對不同的主機定義不同的變數,template 會在將文件分發前讀取變數到 jinja2 模板,之後再然後分發到不同的被管理主機上。
Jinja2 常用語法
賦值
為變數賦值,優先順序高於 playbook 中的優先順序。
1 {% set username = 'zhang' %} 2 {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
註釋
{# ... #}:要把模板中一行或多行註釋掉,預設的註釋語法。
變數
{{ ... }}:把表達式的結果列印到模板上。
你可以使用點( . )來訪問變數的屬性,作為替代,也可以使用所謂的“下標”語 法( [] )。如下示例:
1 {{ foo.bar }} 2 {{ foo['bar'] }}
示例:{{ a_variable }}、{{ foo.bar }}、{{ foo['bar'] }}
花括弧不是變數的一部分,而是列印語句的重要一部分。
條件判斷
Jinja 中的 if 語句可比 Python 中的 if 語句。
在最簡單的形式中,你可以測試一個變數是否未定義,為空或 false:
簡單形式:
1 {% if 條件表達式 %} 2 …… 3 {% endif %}
多分支形式:
1 {% if 條件表達式 %} 2 …… 3 {% elif 條件表達式 %} 4 …… 5 {% else %} 6 …… 7 {% endif %}
迴圈語句
for 迴圈語句
1 {% for user in users %} 2 {{ user.username }} 3 {% endfor %}
空白控制
預設配置中,模板引擎不會對空白做進一步修改,所以每個空白(空格、製表符、換行符 等等)都會原封不動返回。
此外,你也可以手動剝離模板中的空白。當你在塊(比如一個 for 標簽、一段註釋或變數表達式)的開始或結束放置一個減號( - ),可以移除塊前或塊後的空白。如下:
1 {% for item in range(1,9) -%} 2 {{ item }} 3 {%- endfor %}
輸出的所有元素前後不會有任何空白,輸出會是 123456789 。
轉義
有時想要或甚至必要讓 Jinja 忽略部分,而不會把它作為變數或塊來處理。那麼有如下兩種方式:
單行轉義:簡單方式
需求:把 “{ {“ 作為原始字元串使用,而不是一個變數的開始部分。
{{ '{{' }}
多行轉義:
需求:將如下一塊代碼不進行任何處理,直接列印輸出。
1 {% raw %} 2 <ul> 3 {% for item in seq %} 4 <li>{{ item }}</li> 5 {% endfor %} 6 </ul> 7 {% endraw %}
HTML 手動轉義
如果你有一個可能包含 >、<、& 或 " 字元的變數,那麼你需要轉義它;否則會被 HTML 使用。
轉義通過用管道傳遞到過濾器 |e 來實現,如:
{{ user.username|e }}
巨集定義
巨集類似常規編程語言中的函數。它們用於把常用行為作為可重用的函數,取代手動重覆的工作。
如果巨集在不同的模板中定義,你需要首先使用 import 。
示例:
1 ## 巨集變數順序和具體內容變數順序一致「推薦寫法」 2 {% macro input(name, value='', type='text', size=20) -%} 3 <input name="{{ name }}" value="{{ value|e }}" type="{{ type }}" size="{{ size }}"> 4 {%- endmacro %} 5 ## 巨集變數順序和具體內容變數的順序可以交錯「不推薦寫法」 6 {% macro input(name, value='', type='text', size=20) -%} 7 <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}"> 8 {%- endmacro %}
巨集變數解釋:
name:預設為空,引用時必填
value:預設為空字元串
type:預設為 text
size:預設為 20
在命名空間中,巨集之後可以像函數一樣調用:
1 <p>{{ input('username') }}</p> ## 結果為:<p><input name="username" value="" type="text" size="20"></p> 2 <p>{{ input('password', type='password') }}</p> ## 結果為:<p><input name="password" value="" type="password" size="20"></p>
import 導入
導入巨集,併在不同的模板中使用。
1 # 示例:導入 nginx 變數 2 {% from 'nginx_var.info' import nginx_package_path, nginx_version %}
過濾器
變數可以通過 過濾器 修改。過濾器與變數用管道符號( | )分割,並且也可以用圓括弧傳遞可選參數。多個過濾器可以鏈式調用,前一個過濾器的輸出會被作為後一個過濾器的輸入。
例如 {{ name|striptags|title }}、{{ list|join(', ') }}
內置過濾器清單
http://docs.jinkan.org/docs/jinja2/templates.html#builtin-filters
ansible 自帶過濾器
https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html?highlight=filter
Tests 測驗
除了過濾器,所謂的「Tests」也是可用的。要測驗一個變數或表達式,你要在變數後加上一個 is 和 Tests 的名稱。當然 Tests 也可以接受參數。
內置測驗清單
http://docs.jinkan.org/docs/jinja2/templates.html#builtin-tests
ansible 自帶測驗
https://docs.ansible.com/ansible/latest/user_guide/playbooks_tests.html
算術
Jinja 允許用計算值。這在模板中很少用到,但是為了完整性允許其存在。
支持如下運算符:
1 +:把兩個對象加到一起。如:{{ 1 + 1 }} 等於 2。但是如果兩者是字元串或列表,你可以用這種方式來銜接它們【連接字元串推薦使用 ~ 運算符】。 2 3 -:用第一個數減去第二個數。如:{{ 3 - 2 }} 等於 1 。 4 5 /:對兩個數做除法。返回值會是一個浮點數。如:{{ 1 / 2 }} 等於 {{ 0.5 }} 。 6 7 //:對兩個數做除法,返回整數商。如:{{ 20 // 7 }} 等於 2 。 8 9 %:計算整數除法的餘數。如:{{ 11 % 7 }} 等於 4 。 10 11 *:用右邊的數乘左邊的操作數。如:{{ 2 * 2 }} 會返回 4。也可以用於重覆一個字元串多次。如:{{ '=' * 80 }} 會列印 80 個等號的橫條。 12 13 **:取左操作數的右操作數次冪。如:{{ 2 ** 3 }} 會返回 8。
比較
==:比較兩個對象是否相等。
!=:比較兩個對象是否不等。
>:如果左邊大於右邊,返回 true。
>=:如果左邊大於等於右邊,返回 true。
<:如果左邊小於右邊,返回 true。
<=:如果左邊小於等於右邊,返回 true。
邏輯
對於邏輯判斷,在 for 過濾或 if 表達式中,它可以用於聯合多個表達式:
and:如果左操作數和右操作數同為真,返回 true。
or:如果左操作數或右操作數有一個為真,返回 true。
not:對一個表達式取反(見下)。
(expr):表達式組。
提示:is 和 in 運算符同樣支持使用中綴記法:foo is not bar 和 foo not in bar。所有的其它表達式需要首碼記法:not (foo and bar) 。
其它運算符
1 in:運行序列/映射包含檢查。如果左操作數 包含於 右操作數,返回 true 。比如 {{ 1 in [1,2,3] }} 會返回 true。 2 3 is:運行一個 測驗。參見上述 4 5 |:應用一個 過濾器。參見上述 6 7 ~:把所有的操作數轉換為字元串,並且連接它們。 {{ "Hello " ~ name ~ "!" }} 會返回(假設 name 值為 'John' ) Hello John!。
全局函數
range([start], stop[, step]):返回一個包含整等差級數的列表。
Ansible Jinja2 使用案例-常見功能
本例包含:註釋、賦值、變數、條件判斷、迴圈、空白控制、轉義。
目錄結構
1 [yun@ansi-manager jinja]$ pwd 2 /app/ansible_info/jinja 3 [yun@ansi-manager jinja]$ ll 4 total 4 5 drwxrwxr-x 2 yun yun 65 Sep 5 19:34 file 6 -rw-rw-r-- 1 yun yun 188 Sep 5 16:45 test_jinja2_01.yml 7 [yun@ansi-manager jinja]$ ll file/ 8 total 16 9 -rw-rw-r-- 1 yun yun 1562 Sep 5 19:36 test_jinja2_01.conf.j2
配置文件
1 [yun@ansi-manager jinja]$ cat file/test_jinja2_01.conf.j2 # 涉及的文件 2 ##### 註釋、賦值、變數 示例 3 # 為變數賦值,優先順序高於 playbook 中的變數賦值 4 {# 註釋 支持單行或多行 不會在受控機顯示任何註釋信息 #} 5 {# 簡單賦值 #} 6 {% set username = 'zhangsan' %} 7 {# 賦值一個數組 #} 8 {% set info = ('name', 'age', 'address') %} {# 或者 {% set info = ['name', 'age', 'address'] %} #} 9 {# 賦值一個二維數組 #} 10 {% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %} 11 {# 或者如下寫法 12 {% set navigation = (('index.html', 'Index'), ('about.html', 'About')) %} 13 #} 14 15 username = {{ username }} 16 17 info[1] = {{ info[1] }} 18 info = {{ info }} 19 20 navigation[0][1] = {{ navigation[0][1] }} 21 navigation = {{ navigation }} 22 23 # 變數 列印 DNS 信息 信息來自於 Ansible Facts 24 host_DNS1 = {{ ansible_dns['nameservers'][0] }} 25 host_DNS2 = {{ ansible_dns.nameservers[0] }} 26 27 ##### 條件判斷、迴圈、空白控制、轉義 示例 28 # 條件判斷 29 {# {% if username == 'zhangsan' %} #} 30 {% if username == 'zhangsan1' %} 31 username1 32 {# {% elif username == 'zhangsan' %} #} 33 {% elif username == 'zhangsan2' %} 34 username2 35 {% else %} 36 username_other 37 {% endif %} 38 39 # 迴圈 40 {% for host_dns in ansible_dns['nameservers'] %} 41 {{ host_dns }} 42 {% endfor %} 43 44 # 去掉前後空白 45 {% for host_dns in ansible_dns['nameservers'] -%} 46 {{ host_dns }} 47 {%- endfor %} 48 49 # 單行轉義 50 {{ '{{' }} 51 52 # 多行轉義 53 ## 塊中的所有代碼不做任何處理,直接原樣輸出 54 {% raw %} 55 <ul> 56 {% for item in range(1,5) %} 57 <li>{{ item }}</li> 58 {% endfor %} 59 </ul> 60 {% endraw %}
playbook 文件
1 [yun@ansi-manager jinja]$ cat test_jinja2_01.yml # playbook 文件 2 --- 3 # ansible jinja2 測試案例1 4 - hosts: proxyservers 5 vars: 6 - username: coco 7 8 tasks: 9 - name: "test jinja2 01" 10 template: 11 src: ./file/test_jinja2_01.conf.j2 12 dest: /tmp/test_jinja2_01.conf
文件執行
1 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key --syntax-check test_jinja2_01.yml # 語法檢測 2 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key -C test_jinja2_01.yml # 預執行,測試執行 3 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key test_jinja2_01.yml # 執行
Ansible Jinja2 使用案例-巨集與導入
本例包含:巨集、from 導入
目錄結構
1 [yun@ansi-manager jinja]$ pwd 2 /app/ansible_info/jinja 3 [yun@ansi-manager jinja]$ ll 4 total 8 5 drwxrwxr-x 2 yun yun 95 Sep 5 20:00 file 6 -rw-rw-r-- 1 yun yun 196 Sep 5 20:08 test_jinja2_02.yml 7 [yun@ansi-manager jinja]$ ll file/ 8 total 12 9 -rw-rw-r-- 1 yun yun 300 Sep 5 20:00 test_jinja2_02.conf.j2 10 -rw-rw-r-- 1 yun yun 209 Sep 5 19:58 test_jinja2_macro.info
巨集文件和配置文件
1 [yun@ansi-manager jinja]$ cat file/test_jinja2_macro.info 2 {# 配置文件中使用巨集 #} 3 4 {% macro nginx_package_path(nginx_path="/tmp/package/nginx") -%} 5 {{ nginx_path }} 6 {%- endmacro %} 7 8 {% macro nginx_version(version='1.14.2') -%} 9 {{ version }} 10 {%- endmacro %} 11 12 [yun@ansi-manager jinja]$ cat file/test_jinja2_02.conf.j2 13 {# from 導入 nginx 變數 #} 14 {% from 'test_jinja2_macro.info' import nginx_package_path, nginx_version %} 15 16 {# 沒有指定,那麼就是巨集定義的預設值 #} 17 nginx_package_path = {{ nginx_package_path() }} 18 19 {# 有給定值,那麼會覆蓋預設值 #} 20 nginx_version = {{ nginx_version("1.12.4") }}
playbook 文件
1 [yun@ansi-manager jinja]$ cat test_jinja2_02.yml 2 --- 3 # ansible jinja2 測試案例2 4 - hosts: proxyservers 5 6 tasks: 7 - name: "test jinja2 02" 8 template: 9 src: ./file/test_jinja2_02.conf.j2 10 dest: /tmp/test_jinja2_02.conf
文件執行
1 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key --syntax-check test_jinja2_02.yml # 語法檢測 2 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key -C test_jinja2_02.yml # 預執行,測試執行 3 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key test_jinja2_02.yml # 執行
Ansible Jinja2 使用案例-算術、比較、邏輯
本例包含:算術、比較、邏輯與其它運算符
目錄結構
1 [yun@ansi-manager jinja]$ pwd 2 /app/ansible_info/jinja 3 [yun@ansi-manager jinja]$ ll 4 total 8 5 drwxrwxr-x 2 yun yun 95 Sep 5 20:00 file 6 -rw-rw-r-- 1 yun yun 196 Sep 5 20:08 test_jinja2_03.yml 7 [yun@ansi-manager jinja]$ ll file/ 8 total 12 9 -rw-rw-r-- 1 yun yun 300 Sep 5 20:00 test_jinja2_03.conf.j2
配置文件
1 [yun@ansi-manager jinja]$ cat file/test_jinja2_03.conf.j2 2 ### 算術 3 加法運算: 3.2 + 5.3 = {{ 3.2 + 5.3 }} 4 減法運算: 5.3 - 3.2 = {{ 5.3 - 3.2 }} 5 除法運算: 5.3 / 3.2 = {{ 5.3 / 3.2 }} 6 取商運算: 5.3 // 3.2 = {{ 5.3 // 3.2 }} 7 取餘運算: 5.3 % 3.2 = {{ 5.3 % 3.2 }} 8 乘法運算: 3.2 * 3 = {{ 3.2 * 3 }} 9 次冪運算: 3.2 ** 3 = {{ 3.2 ** 3 }} 10 11 重覆一個字元多次:'$' * 20 = {{ '$' * 20 }} 12 13 14 ### 比較 15 比較是否相等: 'zhang' == "zhang" 為:{{ 'zhang' == "zhang" }} 16 比較是否相等: 'zhang' == "zhang1" 為:{{ 'zhang' == "zhang1" }} 17 比較是否不等: 'zhang' != "zhang1" 為:{{ 'zhang' != "zhang1" }} 18 比較是否不等: 'zhang' != "zhang" 為:{{ 'zhang' != "zhang" }} 19 左邊大於右邊: 5.3 > 3.2 為:{{ 5.3 > 3.2 }} 20 左邊大於右邊: 3.2 > 5.3 為:{{ 3.2 > 5.3 }} 21 左邊大於等於右邊: 5.3 >= 5.3 為:{{ 5.3 >= 5.3 }} 22 左邊大於等於右邊: 3.2 >= 5.3 為:{{ 3.2 >= 5.3 }} 23 左邊小於右邊: 5.3 < 3.2 為:{{ 5.3 < 3.2 }} 24 左邊小於右邊: 3.2 < 5.3 為:{{ 3.2 < 5.3 }} 25 左邊小於等於右邊: 5.3 <= 3.2 為:{{ 5.3 <= 3.2 }} 26 左邊小於等於右邊: 5.3 <= 5.3 為:{{ 5.3 <= 5.3 }} 27 28 29 ### 邏輯 30 {% set name1 = 'zhang' %} 31 {% set name2 = "zhang" %} 32 {% set name3 = "li" %} 33 34 ##### and 測驗 35 {% if name1 == 'zhang' and name2 == 'zhang' %} 36 結果:True. 37 {% else %} 38 結果:False. 39 {% endif %} 40 41 {% if name1 == 'zhang' and name3 == 'zhang' %} 42 結果:True. 43 {% else %} 44 結果:False. 45 {% endif %} 46 47 ##### or 測驗 48 {% if name1 == 'zhang' or name2 == 'zhang' %} 49 結果:True. 50 {% else %} 51 結果:False. 52 {% endif %} 53 54 {% if name1 == 'zhang' or name3 == 'zhang' %} 55 結果:True. 56 {% else %} 57 結果:False. 58 {% endif %} 59 60 ##### (expr) 測驗 61 {% if name3 == 'zhang' or ( name1 == 'zhang' and name2 == 'zhang' ) %} 62 結果:True. 63 {% else %} 64 結果:False. 65 {% endif %} 66 67 ##### not 測驗 68 {% if name3 == 'zhang' or not (name1 == 'zhang' and name2 == 'zhang') %} 69 結果:True. 70 {% else %} 71 結果:False. 72 {% endif %}
playbook 文件
1 [yun@ansi-manager jinja]$ cat test_jinja2_03.yml 2 --- 3 4 # ansible jinja2 測試案例3 5 - hosts: proxyservers 6 7 tasks: 8 - name: "test jinja2 03" 9 template: 10 src: ./file/test_jinja2_03.conf.j2 11 dest: /tmp/test_jinja2_03.conf
文件執行
1 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key --syntax-check test_jinja2_03.yml # 語法檢測 2 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key -C test_jinja2_03.yml # 預執行,測試執行 3 [yun@ansi-manager jinja]$ ansible-playbook -b -i ../hosts_key test_jinja2_03.yml # 執行
Ansible Jinja2 使用案例-其它運算
本例包含:其它運算符
目錄結構
1 [yun@ansi-manager jinja]$ pwd 2 /app/ansible_info/jinja 3 [yun@ansi-manager jinja]$ ll 4 total 8 5 drwxrwxr-x 2 yun yun 95 Sep 5 20:00 file 6 -rw-rw-r-- 1 yun yun 196 Sep 5 20:08 test_jinja2_04.yml 7 [yun@ansi-manager jinja]$ ll file/ 8 total 12 9 -rw-rw-r-- 1 yun yun 300 Sep 5 20:00 test_jinja2_04.conf.j2
配置文件
1 [yun@ansi-manager jinja]$ cat file/test_jinja2_04.conf.j2 2 # 其他運算符 3 4 ## in 5 左操作數包含於右操作數: {{ 1 in [1,2,3] }} 6 左操作數包含於右操作數: {{ 10 in [1,2,3] }} 7 8 ## is 9 {% set pawd = '123abc' %} 10 Test: {{ pawd is defined }} 11 12 ## ~ 13 {% set name = 'zhang' %} 14 字元串連接: "Hello" ~ name ~ '!' ~ 123 = {{ "Hello" ~ name ~ '!' ~ 123 }}
playbook 文件
1 [yun@ansi-manager jinja]$ cat test_jinja2_04.yml