Biến, Facts và Template (Jinja2)
Playbook ở Bài 4 hard-code mọi giá trị. Bài này làm chúng linh hoạt với ba thứ gắn bó: biến (giá trị tùy biến), facts (thông tin host Ansible tự biết), và template (sinh file động từ biến + facts). Đây là chỗ playbook chuyển từ "chạy được" sang "tái dùng được cho nhiều môi trường".
Biến: khai báo ở đâu
Biến cho phép cùng một playbook chạy với giá trị khác nhau (dev vs prod, port khác nhau...). Có nhiều nơi khai báo:
# 1. Ngay trong play
- hosts: web
vars:
site_name: "KKloud Lab"
http_port: 8080
# 2. Từ file riêng
- hosts: web
vars_files:
- vars/main.yml
Ngoài ra (nhớ Bài 3): group_vars/<nhóm>.yml và host_vars/<host>.yml. Và truyền lúc chạy bằng -e (extra-vars):
ansible-playbook site.yml -e "http_port=9090 app_env=staging"
Dùng biến bằng cú pháp Jinja2 {{ ten_bien }}:
- name: Mở port
ansible.builtin.debug:
msg: "App chạy ở môi trường {{ app_env }}, port {{ http_port }}"
Thứ tự ưu tiên (precedence) — chỗ hay rối
Khi cùng một biến được khai báo ở nhiều nơi, nơi nào thắng? Ansible có thứ tự ưu tiên rõ ràng. Đơn giản hóa từ thấp → cao:
THẤP (dễ bị ghi đè) ─────────────────────────────► CAO (thắng)
role defaults < group_vars/all < group_vars/<nhóm> < host_vars
< play vars < task vars < ... < -e extra-vars (LUÔN thắng)
Hai điều cần nhớ:
-e(extra-vars) luôn thắng tất cả — tiện để ghi đè lúc chạy ("chạy với port 9090 cho lần này"), nhưng đừng lạm dụng vì nó vượt mọi thứ.- role defaults thấp nhất — đặt giá trị mặc định của role ở
defaults/để dễ bị ghi đè bởi group_vars/host_vars (Bài 8).
Quy tắc thực dụng: giá trị chung đặt ở nơi thấp (defaults, group_vars/all), giá trị riêng đặt ở nơi cao hơn (host_vars, hoặc -e khi cần). (Tài liệu Ansible có bảng precedence đầy đủ ~22 mức; bạn không cần thuộc hết, chỉ cần nắm "cụ thể hơn / muộn hơn thì thắng, -e thắng tất".)
Facts: thông tin host Ansible tự biết
Nhớ Bài 4 — task Gathering Facts đầu mỗi play. Nó thu thập facts: hàng trăm thông tin về host (OS, IP, RAM, CPU, disk, hostname...), đặt vào các biến ansible_*. Xem tất cả:
ansible all -m setup
Vài fact hay dùng:
ansible_hostname tên host
ansible_distribution "Amazon", "Ubuntu", "CentOS"...
ansible_distribution_version "2023", "24.04"...
ansible_default_ipv4.address IP chính
ansible_processor_vcpus số vCPU
ansible_memtotal_mb tổng RAM (MB)
Facts cho phép playbook thích nghi với host: cài gói khác nhau theo ansible_distribution, cấu hình theo số CPU... (kết hợp when: ở Bài 7). Nếu không cần facts, đặt gather_facts: false để chạy nhanh hơn.
Template Jinja2: sinh file động
Module copy đặt file tĩnh. Module template mạnh hơn: nó render một file Jinja2 (.j2) — thay biến và facts vào — trước khi đặt lên host. Đây là cách sinh file cấu hình động (nginx.conf, .env, app config...).
Tạo template templates/index.html.j2:
<h1>{{ site_name }}</h1>
<p>Host: {{ ansible_hostname }} ({{ ansible_distribution }} {{ ansible_distribution_version }})</p>
<p>Môi trường: {{ app_env | default('dev') }}</p>
Playbook dùng nó:
- name: Demo biến, facts, template
hosts: web
become: true
vars:
site_name: "KKloud Lab"
app_env: "production"
tasks:
- name: Render template ra index.html
ansible.builtin.template:
src: templates/index.html.j2
dest: /usr/share/nginx/html/index.html
Chạy rồi curl xem kết quả render thật trên host:
<h1>KKloud Lab</h1>
<p>Host: ip-172-31-31-60 (Amazon 2023)</p>
<p>Môi trường: production</p>
Để ý: {{ site_name }} và {{ app_env }} thay từ biến; {{ ansible_hostname }}, {{ ansible_distribution }} thay từ facts — Ansible biết host tên gì, OS gì mà bạn không phải nhập tay. Một template, áp lên trăm host, mỗi host ra file đúng thông tin của nó.
Jinja2: filter, điều kiện, vòng lặp trong template
Jinja2 (cũng dùng trong {{ }} của playbook) có nhiều công cụ:
{{ app_env | default('dev') }} {# filter: giá trị mặc định nếu chưa đặt #}
{{ name | upper }} {# filter: viết hoa #}
{{ items | length }} {# filter: đếm phần tử #}
{% if app_env == 'production' %} {# điều kiện #}
log_level = warning
{% else %}
log_level = debug
{% endif %}
{% for host in groups['web'] %} {# vòng lặp — liệt kê các host nhóm web #}
server {{ host }};
{% endfor %}
Vòng lặp {% for %} trong template đặc biệt mạnh: ví dụ sinh cấu hình load balancer liệt kê mọi host nhóm web (qua biến groups). Filter (| default, | upper...) biến đổi giá trị — và bạn tự viết filter riêng được (Bài 12).
registered variables: bắt kết quả task
Bạn còn lưu kết quả của một task vào biến bằng register, để task sau dùng:
- name: Kiểm tra phiên bản nginx
ansible.builtin.command: nginx -v
register: nginx_out
changed_when: false # lệnh chỉ đọc, không coi là changed
- name: In ra
ansible.builtin.debug:
var: nginx_out.stdout
register bắt JSON trả về của module (gồm stdout, rc, changed...) — hữu ích để dựa vào kết quả mà quyết định task sau (kết hợp when: — Bài 7).
🧹 Dọn dẹp
Code mẫu (template, vars) ở repo nghiadaulau/ansible-series, thư mục 06-vars-facts-template. EC2 lab vẫn giữ tới Bài 16.
Tổng kết
Biến làm playbook tái dùng được, khai báo ở nhiều nơi với thứ tự ưu tiên (cụ thể hơn/muộn hơn thắng; -e thắng tất). Facts là thông tin host Ansible tự thu thập (ansible_*) cho playbook thích nghi theo từng máy. Template Jinja2 (module template) render file động từ biến + facts (kèm filter, if, for) — sinh cấu hình đúng cho mỗi host. register bắt kết quả task để dùng tiếp.
Có biến và facts rồi, ta cần điều khiển luồng: chạy task khi nào, lặp lại ra sao, phản ứng với thay đổi thế nào. Bài 7: handlers, loops và conditionals.