Ansible template module ใช้สร้างไฟล์บน remote server จาก template ที่มี Jinja2 syntax โดยแทนค่า variables และ expressions ก่อน deploy ต่างจาก copy module ที่ส่งไฟล์ static ตรงๆ template ช่วยให้ config file เดียวสามารถปรับเปลี่ยนได้ตามค่า inventory, facts, หรือ variables ของแต่ละ host
บทความนี้อธิบายการใช้ template module ตั้งแต่ Jinja2 syntax พื้นฐาน, variables และ filters, conditionals, loops, ไปจนถึง pattern สำหรับ multi-environment deployment ที่ใช้ template เดียวรองรับหลาย environment
Template พื้นฐานและ Jinja2 Syntax
ไฟล์ template ใช้นามสกุล .j2 โดย convention และเก็บไว้ใน templates/ directory ของ Playbook หรือ role ใช้ {{ variable }} แทนค่า, {% %} สำหรับ control flow, {# #} สำหรับ comment
# templates/nginx.conf.j2
server {
listen {{ nginx_port | default(80) }};
server_name {{ server_name }};
root {{ web_root }};
index index.html;
{# เพิ่ม SSL ถ้ากำหนดไว้ #}
{% if ssl_enabled %}
listen {{ ssl_port | default(443) }} ssl;
ssl_certificate {{ ssl_cert_path }};
ssl_certificate_key {{ ssl_key_path }};
{% endif %}
access_log /var/log/nginx/{{ server_name }}.access.log;
error_log /var/log/nginx/{{ server_name }}.error.log;
}
---
- name: Deploy nginx config from template
hosts: webservers
become: true
vars:
server_name: "myapp.example.com"
web_root: "/var/www/html"
nginx_port: 80
ssl_enabled: true
ssl_port: 443
ssl_cert_path: "/etc/ssl/certs/myapp.crt"
ssl_key_path: "/etc/ssl/private/myapp.key"
tasks:
- name: Deploy nginx virtualhost config
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/myapp
owner: root
mode: '0644'
notify: reload nginx
handlers:
- name: reload nginx
service:
name: nginx
state: reloaded
Variables และ Jinja2 Filters
Jinja2 มี filters สำหรับแปลงค่า variables ใน template ใช้ | (pipe) ต่อกับชื่อ filter เช่น upper, lower, default, join
# templates/app.conf.j2
[application]
name = {{ app_name | upper }}
version = {{ app_version | default('1.0.0') }}
debug = {{ debug_mode | lower }}
[database]
host = {{ db_host }}
port = {{ db_port | default(5432) }}
name = {{ db_name }}
# password ไม่แสดงใน template ให้ใช้ vault แทน
[server]
workers = {{ ansible_processor_vcpus * 2 }}
hostname = {{ inventory_hostname }}
ip = {{ ansible_default_ipv4.address }}
[logging]
level = {{ log_level | default('INFO') | upper }}
path = /var/log/{{ app_name | lower }}/app.log
Ansible facts เช่น ansible_processor_vcpus และ ansible_default_ipv4.address ใช้ได้ตรงใน template เหมาะสำหรับ config ที่ขึ้นอยู่กับ spec ของ server จริง
Conditionals ใน Template
{% if %} ... {% elif %} ... {% else %} ... {% endif %} ใช้สำหรับเนื้อหาที่แสดงเฉพาะเมื่อเงื่อนไขตรง เหมาะสำหรับ config ที่แตกต่างกันตาม environment หรือ OS
# templates/php-fpm.conf.j2
[www]
user = {{ web_user | default('www-data') }}
group = {{ web_user | default('www-data') }}
{% if php_fpm_socket_type == 'unix' %}
listen = /run/php/php{{ php_version }}-fpm.sock
listen.owner = {{ web_user | default('www-data') }}
listen.group = {{ web_user | default('www-data') }}
listen.mode = 0660
{% else %}
listen = 127.0.0.1:{{ php_fpm_port | default(9000) }}
{% endif %}
pm = dynamic
pm.max_children = {{ php_fpm_max_children | default(10) }}
pm.start_servers = {{ php_fpm_start_servers | default(3) }}
{% if environment == 'production' %}
php_admin_value[error_log] = /var/log/php-fpm/error.log
php_admin_flag[log_errors] = on
php_admin_value[display_errors] = Off
{% else %}
php_admin_value[display_errors] = On
php_admin_flag[log_errors] = on
{% endif %}
Loops ใน Template
{% for item in list %} ... {% endfor %} ใช้ generate เนื้อหาซ้ำๆ เช่น list ของ virtual hosts, database users, หรือ firewall rules
# templates/hosts.j2
# Managed by Ansible — do not edit manually
127.0.0.1 localhost
{% for host in groups['all'] %}
{{ hostvars[host]['ansible_default_ipv4']['address'] }} {{ host }}
{% endfor %}
{% for entry in custom_hosts | default([]) %}
{{ entry.ip }} {{ entry.hostname }}
{% endfor %}
# templates/nginx-upstreams.conf.j2
upstream {{ upstream_name }} {
{% for server in backend_servers %}
server {{ server.host }}:{{ server.port | default(8080) }}{% if server.weight is defined %} weight={{ server.weight }}{% endif %};
{% endfor %}
keepalive 32;
}
Multi-Environment Deployment
pattern ที่พบบ่อยคือใช้ template เดียวกับ variables ที่แตกต่างกันตาม environment (dev/staging/production) โดยแยก variables ไว้ใน group_vars/ หรือ host_vars/
# group_vars/production/app.yml
environment: production
app_name: myapp
db_host: db-prod-01.internal
db_port: 5432
debug_mode: false
log_level: WARNING
php_fpm_max_children: 50
ssl_enabled: true
# group_vars/staging/app.yml
environment: staging
app_name: myapp
db_host: db-staging.internal
db_port: 5432
debug_mode: true
log_level: DEBUG
php_fpm_max_children: 5
ssl_enabled: false
---
- name: Deploy application config
hosts: appservers
become: true
tasks:
- name: Deploy app config from template
template:
src: templates/app.conf.j2
dest: /etc/myapp/app.conf
owner: "{{ app_user }}"
mode: '0640'
backup: yes
notify: restart app
- name: Deploy PHP-FPM config
template:
src: templates/php-fpm.conf.j2
dest: /etc/php/{{ php_version }}/fpm/pool.d/www.conf
owner: root
mode: '0644'
notify: restart php-fpm
handlers:
- name: restart app
service:
name: myapp
state: restarted
- name: restart php-fpm
service:
name: "php{{ php_version }}-fpm"
state: restarted
Validate Config หลัง Template
เหมือนกับ copy module template รองรับ validate parameter เพื่อทดสอบ syntax ก่อน commit ไฟล์จริง ลด risk จากการ deploy config ที่มีข้อผิดพลาด
---
- name: Deploy configs with validation
hosts: webservers
become: true
tasks:
- name: Deploy nginx config with validation
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/myapp
validate: nginx -t -c %s
backup: yes
- name: Deploy Apache config with validation
template:
src: templates/apache.conf.j2
dest: /etc/apache2/sites-available/myapp.conf
validate: apache2ctl -t -f %s
- name: Deploy sshd config with validation
template:
src: templates/sshd_config.j2
dest: /etc/ssh/sshd_config
validate: sshd -t -f %s
mode: '0600'
notify: restart sshd
สรุป
template module เป็นเครื่องมือหลักสำหรับ deploy configuration files ที่ต้องปรับค่าตาม environment ใน Ansible Pattern ที่ควรจำ: เก็บ template ไว้ใน templates/ directory พร้อมนามสกุล .j2, แยก variables ตาม environment ด้วย group_vars/, ใช้ default() filter สำหรับค่า fallback, และใช้ validate parameter เสมอสำหรับ config ที่มี syntax checker
ความแตกต่างหลักระหว่าง template และ copy คือ template ประมวลผล Jinja2 ก่อน deploy ทำให้ config file เดียวรองรับหลาย host และหลาย environment ได้ ส่วน copy ส่งไฟล์ตามที่เป็นโดยไม่แปลงค่าใดๆ

