Mở Rộng Ansible: Filter, Lookup và Callback Plugins
Bài 11 viết module — code chạy trên host để làm việc. Bài này là chiều mở rộng còn lại: plugin — code chạy trên control node để mở rộng chính Ansible. Hiểu rõ hai khái niệm này là bạn nắm trọn khả năng tùy biến Ansible.
Module vs plugin: khác biệt cốt lõi
Đây là phân biệt quan trọng và hay nhầm:
MODULE PLUGIN
─────────────────────────────────────────────────────────────
chạy TRÊN HOST đích (qua SSH) chạy TRÊN CONTROL NODE
làm việc: cài gói, sửa file... mở rộng bản thân Ansible
ship lên host (AnsiballZ — Bài 1) chạy local trong tiến trình ansible
gọi như task dùng trong Jinja2 / inventory / output...
Nói cách khác: module = "làm gì đó trên máy đích"; plugin = "thay đổi cách Ansible vận hành". Bạn đã dùng plugin mà chưa để ý: filter | default (Bài 6) là filter plugin, inventory động aws_ec2 (Bài 3) là inventory plugin, kết nối SSH là connection plugin.
Các loại plugin hay gặp
filter biến đổi dữ liệu trong Jinja2 ( {{ x | myfilter }} )
lookup lấy dữ liệu từ NGUỒN NGOÀI ( {{ lookup('file', '/path') }} )
callback tùy biến OUTPUT/logging (đổi cách hiển thị kết quả)
connection cách kết nối host (ssh, docker, local, winrm...)
inventory nguồn inventory (aws_ec2, k8s... — Bài 3)
test phép kiểm tra trong Jinja2 ( {{ x is myverб }} )
Ta đi vào ba loại bạn hay tự viết: filter, lookup, callback.
Filter plugin: biến đổi dữ liệu trong Jinja2
Filter (| something) biến đổi giá trị trong template/biểu thức (Bài 6). Ngoài filter sẵn (default, upper, length...), bạn tự viết. Filter plugin là một file Python trong thư mục filter_plugins/:
# filter_plugins/custom_filters.py
def shout(value):
return str(value).upper() + "!"
def env_prefix(value, env="dev"):
return f"{env}-{value}"
class FilterModule(object):
def filters(self):
return {
'shout': shout,
'env_prefix': env_prefix,
}
Mỗi filter chỉ là một hàm Python (nhận giá trị bên trái |, trả giá trị mới). Lớp FilterModule.filters() đăng ký tên filter → hàm. Dùng ngay trong playbook:
- hosts: localhost
vars:
name: "kkloud"
tasks:
- ansible.builtin.debug:
msg: "{{ name | shout }} / {{ name | env_prefix('prod') }}"
"msg": "KKLOUD! / prod-kkloud"
name | shout → KKLOUD!; name | env_prefix('prod') → prod-kkloud (tham số 'prod' truyền vào hàm). Filter chạy trên control node lúc render — đúng định nghĩa plugin. Filter tùy biến rất hữu ích để xử lý dữ liệu phức tạp gọn gàng trong template thay vì nhồi logic vào playbook.
Lookup plugin: lấy dữ liệu ngoài
Lookup lấy dữ liệu từ nguồn bên ngoài vào playbook, dùng qua lookup('tên', ...). Đây là cách chuẩn để đọc file, biến môi trường, secret từ vault ngoài... Vài lookup sẵn:
- ansible.builtin.debug:
msg: "{{ lookup('ansible.builtin.file', '/etc/hostname') }}" # nội dung file
- ansible.builtin.debug:
msg: "{{ lookup('ansible.builtin.env', 'HOME') }}" # biến môi trường
- ansible.builtin.debug:
msg: "{{ lookup('ansible.builtin.pipe', 'date +%F') }}" # output lệnh
Lookup đặc biệt mạnh để lấy secret từ secret manager (nhớ Bài 10 — thay cho Vault tĩnh ở quy mô lớn):
db_pass: "{{ lookup('community.hashi_vault.hashi_vault', 'secret/db:password') }}"
api_key: "{{ lookup('amazon.aws.aws_secret', 'prod/api_key') }}"
Bạn tự viết lookup plugin (trong lookup_plugins/) tương tự filter — kế thừa LookupBase, hiện thực run() trả về danh sách. Hữu ích khi cần tích hợp một nguồn dữ liệu nội bộ.
Lưu ý: lookup chạy trên control node, mỗi lần được tham chiếu (không cache mặc định). Đừng đặt lookup nặng (gọi API) trong vòng lặp lớn — sẽ chạy lại nhiều lần.
Callback plugin: tùy biến output
Callback plugin thay đổi cách Ansible hiển thị/ghi lại kết quả — phản ứng với các sự kiện (task bắt đầu, task xong, play kết thúc). Đây là cách đổi giao diện output hoặc tích hợp logging/monitoring. Bật một callback có sẵn trong ansible.cfg:
[defaults]
stdout_callback = yaml # output dạng YAML dễ đọc hơn mặc định
callbacks_enabled = profile_tasks, timer # đo thời gian từng task + tổng
stdout_callback = yaml— hiển thị kết quả dạng YAML (dễ đọc hơn JSON dồn một dòng).profile_tasks— in thời gian mỗi task (tìm task chậm — liên quan Bài 13).timer— in tổng thời gian playbook.
Callback tự viết hữu ích để: gửi kết quả lên Slack, ghi log JSON cho hệ thống giám sát, định dạng output cho CI. Bạn kế thừa CallbackBase và hiện thực các hook như v2_runner_on_ok, v2_runner_on_failed...
Đặt plugin ở đâu
Giống module (Bài 11), plugin được tìm theo quy ước thư mục cạnh playbook, mỗi loại một thư mục:
filter_plugins/ filter plugin
lookup_plugins/ lookup plugin
callback_plugins/ callback plugin
library/ module (Bài 11)
Hoặc khai báo đường dẫn trong ansible.cfg (filter_plugins = ...), hoặc — chuẩn nhất ở quy mô lớn — đóng gói vào một collection (Bài 9): collection có thư mục plugins/filter/, plugins/lookup/, plugins/modules/... gom tất cả phần mở rộng của bạn lại để chia sẻ.
Tổng kết
Plugin mở rộng bản thân Ansible (chạy trên control node), khác module (làm việc trên host). Ba loại hay tự viết: filter biến đổi dữ liệu trong Jinja2 (| myfilter, lớp FilterModule); lookup lấy dữ liệu ngoài (lookup('file'/'env'/secret-manager...)); callback tùy biến output/logging (stdout_callback, profile_tasks). Đặt plugin trong filter_plugins/, lookup_plugins/, callback_plugins/ cạnh playbook, hoặc đóng gói vào collection. Cùng module ở Bài 11, plugin cho bạn tùy biến Ansible ở mọi tầng.
Đã nắm cách viết và mở rộng, ta chuyển sang vận hành thực chiến ở quy mô lớn. Bài 13: tối ưu và chiến lược thực thi — chạy Ansible nhanh và an toàn trên hàng trăm host.