iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0
DevOps

不爆肝學習 Ansible 的短暫30天系列 第 12

Day12 - 善用 Jinja2 template 製作動態設定檔

  • 分享至 

  • xImage
  •  

今日目標

  • 學會使用 Ansible Jinja2 template

為什麼要用 Jinja2 template?

大家可以思考一下,假設如果沒有 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 %}
}

其實看到這邊各位會注意到這邊使用了許多前幾天提到的變數概念,其實概念都大同小異,我們要能夠做到動態設定,必然會在一個地方挖洞,好讓我們可以動態將值丟進來。

Jinja2 的一些常見語法

變數與預設值

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 %}

舉個小例子

  • 首先新增 2 個檔案,一個 Playbook (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;
  }
}
  • 執行 Playbook,大家會看到多了一個檔案
# 透過 cat 指令查看內容
cat /tmp/nginx-demo.conf

一些進階技巧

變數的清洗

# server_name 透過 re 僅允許英數、點與連字號
server_name {{ server_name | regex_replace('[^a-zA-Z0-9.-]', '') }};

macro

{# 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 的文檔後才發現原來有這個酷酷的用法,筆者覺得是一個不錯的功能,所以就順便分享出來了。

作業練習時間

  • 練習 1:寫一個 Traefik config or Caddy config,根據自己常用的去練習即可。
  • 練習 2:用一個模板生成 .env 檔 (由 group_vars/all.yaml 提供值)。

明日預告

明天我們來學習 Facts 跟系統資訊的收集,看看要如何把主機的即時狀態帶入 Playbook 與模板邏輯中。


上一篇
Day11 - 我們都應該站在巨人的肩膀上:Ansible Galaxy
系列文
不爆肝學習 Ansible 的短暫30天12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言