大家可以思考一下,假設如果沒有 template 的情況下,假設我們要維護大量機器的設定檔案,我們要怎麼做一份份複製貼上嗎?
這不僅僅是一個非常沒有效率的事,也非常難以擴展跟容易發生錯誤。
舉個例子:
# 每一台機器都有自己的設定檔
nginx-web01.conf
nginx-web02.conf
nginx-web03.conf
...
但是我們只需要善用 Jinja2 template,僅需一份檔案,就可以動態產生各機器的設定檔案了
# templates/nginx.conf.j2
server {
listen {{ http_port | default(80) }};
server_name {{ inventory_hostname }}.example.com;
root /var/www/{{ app_name }};
{% if ssl_enabled %}
listen 443 ssl;
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
{% endif %}
}
其實看到這邊各位會注意到這邊使用了許多前幾天提到的變數概念,其實概念都大同小異,我們要能夠做到動態設定,必然會在一個地方挖洞,好讓我們可以動態將值丟進來。
app: {{ app_name }}
port: {{ http_port | default(80) }}
db_host: {{ db.host }}
first_server: {{ servers[0] }}
upper: {{ env | upper }}
json: {{ config | to_json }}
yaml: |
{{ config | to_yaml }}
safe_name: {{ server_name | regex_replace('[^a-zA-Z0-9.-]', '') }}
unique_hosts: {{ hosts | unique | join(',') }}
看到這裡大家一定會發現,竟然可以使用一些程式語法像是 if 或是 for。
沒錯!其實大家在第一天應該就會發現到 Ansible 其實跟 Python 脫離不了關係,換句話說 Ansible 本質上就是基於 Python 出生的。
所以大家這邊看到的 jinja2 也就是 Python 中很常見的 jinja2,兩個指的是同樣的東西 ~
{% if environment == 'production' %}
log_level: warn
{% else %}
log_level: info
{% endif %}
upstreams:
{% for s in backends %}
- {{ s.host }}:{{ s.port }}
{% endfor %}
{% for k, v in envs -%}
{{ k }}={{ v }}
{%- endfor %}
template-demo.yml
),一個模板 (templates/nginx.conf.j2
)---
# template-demo.yml
- name: Day12 template demo
hosts: all
gather_facts: false
vars:
app_name: demo
http_port: 80
ssl_enabled: false
server_name: "{{ inventory_hostname | default('example.local') }}"
tasks:
- name: Render nginx conf to /tmp
template:
src: templates/nginx.conf.j2
dest: /tmp/nginx-demo.conf
mode: '0644'
# templates/nginx.conf.j2
# Managed by Ansible – DO NOT EDIT
server {
listen {{ http_port | default(80) }};
server_name {{ server_name }};
root /var/www/{{ app_name }};
{% if ssl_enabled %}
listen 443 ssl;
ssl_certificate {{ ssl_cert_path | default('/etc/ssl/certs/example.crt') }};
ssl_certificate_key {{ ssl_key_path | default('/etc/ssl/private/example.key') }};
{% endif %}
location / {
try_files $uri $uri/ /index.html;
}
}
# 透過 cat 指令查看內容
cat /tmp/nginx-demo.conf
# server_name 透過 re 僅允許英數、點與連字號
server_name {{ server_name | regex_replace('[^a-zA-Z0-9.-]', '') }};
{# templates/macros.j2 #}
{% macro loc(path, proxy_pass=none) -%}
location {{ path }} {
{% if proxy_pass %}proxy_pass {{ proxy_pass }};{% endif %}
}
{%- endmacro %}
{# templates/site.conf.j2 #}
{% from 'macros.j2' import loc %}
server {
{{ loc('/', 'http://127.0.0.1:3000') }}
}
題外話!這個用法其實是筆者在寫這篇文章跑去讀了 jinja2 的文檔後才發現原來有這個酷酷的用法,筆者覺得是一個不錯的功能,所以就順便分享出來了。
.env
檔 (由 group_vars/all.yaml
提供值)。明天我們來學習 Facts 跟系統資訊的收集,看看要如何把主機的即時狀態帶入 Playbook 與模板邏輯中。