Best Practices: Cấu Trúc Dự Án và Thiết Kế

K
Kai··4 min read

Bạn đã học mọi mảnh ghép. Bài này gom chúng thành best practices — cách tổ chức và viết Ansible để dự án dễ bảo trì, an toàn, và làm việc nhóm tốt. Đây là khác biệt giữa "playbook chạy được" và "codebase Ansible chuyên nghiệp".

Cấu trúc dự án

Một dự án Ansible nghiêm túc nên có cấu trúc rõ ràng, tách môi trường:

   project/
   ├── ansible.cfg                 # cấu hình
   ├── requirements.yml            # collection/role phụ thuộc (Bài 9)
   ├── inventories/
   │   ├── production/
   │   │   ├── hosts.yml           # inventory prod
   │   │   └── group_vars/         # biến RIÊNG prod
   │   └── staging/
   │       ├── hosts.yml
   │       └── group_vars/         # biến riêng staging
   ├── roles/
   │   ├── common/                 # role nền dùng chung
   │   └── webserver/              # role theo chức năng
   ├── playbooks/
   │   └── site.yml                # playbook gọi các role
   └── group_vars/ host_vars/      # biến chung mọi môi trường

Hai nguyên tắc quan trọng:

  • Tách môi trường bằng inventory riêng (inventories/production/, inventories/staging/), mỗi cái có group_vars/ riêng. Cùng playbook + role, chạy với inventory khác → môi trường khác. Tránh trộn biến prod/staging.
  • Playbook mỏng, role dày: site.yml chỉ gọi role; logic nằm trong role (Bài 8). Playbook trở thành "bản nhạc" điều phối, dễ đọc.

Thiết kế role tốt

Role là đơn vị tái dùng — thiết kế tốt thì dùng lại được khắp nơi:

  • Một role một trách nhiệm: role nginx lo nginx, role postgres lo postgres. Đừng nhồi mọi thứ vào một role.
  • Tham số hóa qua defaults/: mọi thứ có thể tùy biến (port, version, đường dẫn) đặt làm biến trong defaults/main.yml (ưu tiên thấp, dễ ghi đè — Bài 6). Role không nên hard-code.
  • Idempotent tuyệt đối (Bài 5): mọi task phải an toàn chạy lại.
  • meta/main.yml đầy đủ: khai báo galaxy_info (author, license, platform hỗ trợ). Nhớ Bài 14 — molecule/galaxy bắt buộc điều này.
  • Kèm READMEmolecule/: tài liệu cách dùng + test (Bài 14).

Quy ước đặt tên

Đặt tên nhất quán giúp đọc và gỡ lỗi:

  • Đặt name: cho MỌI task và play — log mới rõ ràng (nhớ PLAY RECAP/task name ở Bài 4). Task không tên hiện ra dạng module khó hiểu.
  • Dùng FQCN (Bài 9): ansible.builtin.copy thay vì copy. Rõ ràng, tránh trùng, và ansible-lint khuyến nghị.
  • Tiền tố biến của role: biến của role webserver nên đặt webserver_port, webserver_user... thay vì port, user. Tránh đụng biến giữa các role (biến Ansible là global trong một play).
  • snake_case cho tên biến.

Bảo mật

Tổng hợp từ các bài:

  • Bí mật qua Vault (Bài 10) hoặc secret manager (lookup — Bài 12). Không bao giờ để mật khẩu trần trong playbook/Git.
  • become đúng mức: chỉ nâng quyền task cần root, không bật become cả play nếu không cần (series Linux Bài 12).
  • Module thay vì command/shell (Bài 5): an toàn và idempotent hơn.
  • Không log secret: thêm no_log: true cho task xử lý dữ liệu nhạy cảm để Ansible không in giá trị ra log.

ansible-lint: tự động bắt lỗi

Đừng dựa vào trí nhớ để giữ best practice — dùng ansible-lint kiểm tra tự động:

ansible-lint site.yml

Chạy trên playbook site.yml (Bài 4) cho kết quả thật:

# Rule Violation Summary
  1 risky-file-permissions  profile:safety

risky-file-permissions: File permissions unset or incorrect.
site.yml:17 Task/Handler: Triển khai trang index

Failed: 1 failure(s) ... Rating: 2/5 star

ansible-lint phát hiện task copy ở dòng 17 không đặt mode: — rủi ro vì quyền file phụ thuộc umask, không tường minh (nhớ series Linux Bài 7). Cách sửa: thêm mode: "0644":

- name: Triển khai trang index
  ansible.builtin.copy:
    content: "..."
    dest: /usr/share/nginx/html/index.html
    mode: "0644"        # ← tường minh, lint hết phàn nàn

ansible-lint có nhiều ruleprofile (min, basic, safety, production...) với độ nghiêm khác nhau. Exit code khác 0 khi có vi phạm — nên đưa vào CI (cùng molecule — Bài 14) để chặn code không đạt chuẩn trước khi merge:

# .github/workflows/lint.yml (ý tưởng)
- run: ansible-lint
- run: molecule test

Vài quy ước khác đáng theo

  • Pin phiên bản trong requirements.yml (Bài 9): collection/role pin version để build tái lập.
  • --check --diff trước khi áp prod (Bài 5, 13): luôn dry-run.
  • Tags cho playbook dài (Bài 13): chạy chọn lọc.
  • Đừng dùng ignore_errors bừa: che lỗi thật. Dùng failed_when/block-rescue (Bài 7) có chủ đích.
  • Giữ playbook khai báo, không mệnh lệnh: mô tả trạng thái (state:), tránh chuỗi command thủ tục.

🧹 Dọn dẹp

Cấu trúc dự án mẫu ở repo nghiadaulau/ansible-series, thư mục 15-best-practices.

Tổng kết

Best practices: cấu trúc dự án rõ ràng (tách môi trường bằng inventory riêng, playbook mỏng gọi role dày); thiết kế role một-trách-nhiệm, tham số hóa qua defaults/, idempotent, có meta/README/molecule; đặt tên nhất quán (mọi task có name, dùng FQCN, tiền tố biến theo role); bảo mật qua Vault/secret manager, become tối thiểu, no_log; và ansible-lint tự động bắt lỗi (như risky-file-permissions thiếu mode) — đưa vào CI cùng molecule. Đây là chuẩn của codebase Ansible chuyên nghiệp.

Bài cuối (16) ghép tất cả thành một dự án hoàn chỉnh — deploy một ứng dụng thật bằng role, áp best practices — rồi dọn dẹp EC2 và tổng kết series.