Lab 8 — Ansible Roles
Mục tiêu: Sau Lab này, người học hiểu được Ansible Role, biết cách tổ chức một project Ansible theo chuẩn production, tách
Tasks,Handlers,Templates,Variables,Defaultsthành các thành phần độc lập và có thể tái sử dụng.
1. Bối cảnh
Ở các Lab trước, chúng ta đã xây dựng Playbook như:
playbook.yml
├── tasks
├── handlers
├── variables
└── templates
Ví dụ:
---
- name: Configure nginx
hosts: webservers
become: true
tasks:
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
- name: Deploy nginx configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Reload nginx
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
Cách này hoàn toàn ổn khi project nhỏ.
Nhưng hãy tưởng tượng project có:
10 Playbook
20 server
5 environment
3 application
Nếu tất cả logic nằm trong playbook.yml, project sẽ rất nhanh trở nên khó quản lý.
Ví dụ:
playbook.yml
├── install nginx
├── configure nginx
├── install docker
├── configure docker
├── create users
├── deploy application
├── configure firewall
├── setup monitoring
├── configure logging
└── ...
Đây là lúc chúng ta cần Roles.
2. Role là gì?
Có thể hiểu đơn giản:
Role là một package chứa toàn bộ logic để quản lý một thành phần của hệ thống.
Ví dụ:
nginx role
có thể chứa:
Tasks
Handlers
Templates
Files
Defaults
Variables
Mô hình:
Ansible Role
│
┌────────────┼────────────┐
│ │ │
Tasks Templates Handlers
│ │ │
└────────────┼────────────┘
│
▼
Nginx
Thay vì viết:
tasks:
- install nginx
- configure nginx
- deploy config
- reload nginx
chúng ta có thể:
roles:
- nginx
Playbook trở nên rất ngắn và dễ đọc.
3. Tại sao cần Roles?
Roles giải quyết 4 vấn đề lớn.
1. Organization
Tách logic thành từng thành phần.
2. Reusability
Một Role có thể sử dụng lại nhiều lần.
3. Maintainability
Dễ sửa đổi khi project lớn.
4. Scalability
Có thể mở rộng từ:
1 server
thành:
100 servers
mà không biến Playbook thành một file khổng lồ.
4. Tư duy quan trọng
Trước Role:
Playbook
│
├── Install
├── Configure
├── Template
└── Restart
Sau Role:
Playbook
│
├── nginx role
├── docker role
├── nodejs role
└── monitoring role
Playbook trở thành orchestrator.
Role chịu trách nhiệm implementation.
Đây là một cách phân tách rất quan trọng.
5. Cấu trúc Role
Một Role thường có cấu trúc:
roles/
└── nginx/
├── defaults/
│ └── main.yml
├── handlers/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
│ └── nginx.conf.j2
├── files/
├── vars/
│ └── main.yml
├── meta/
│ └── main.yml
└── README.md
Không nhất thiết Role nào cũng phải sử dụng tất cả các thư mục.
6. Ý nghĩa từng thư mục
| Directory | Mục đích |
|---|---|
tasks/ |
Tasks chính |
handlers/ |
Handlers |
templates/ |
Jinja2 templates |
files/ |
Static files |
defaults/ |
Default variables |
vars/ |
Role variables |
meta/ |
Dependencies và metadata |
README.md |
Documentation |
Đây là cấu trúc mà người học cần nhớ.
7. Tạo Role đầu tiên
Từ project hiện tại:
mkdir -p roles
Sau đó:
ansible-galaxy role init roles/nginx
Ansible sẽ tạo:
roles/
└── nginx/
├── README.md
├── defaults/
│ └── main.yml
├── handlers/
│ └── main.yml
├── meta/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
├── tests/
├── vars/
│ └── main.yml
└── files/
ansible-galaxy role initlà cách nhanh để tạo skeleton Role chuẩn.
8. Project sau khi chuyển sang Role
Cấu trúc:
ansible-lab-08/
├── ansible.cfg
├── inventory.ini
├── site.yml
└── roles/
└── nginx/
├── defaults/
│ └── main.yml
├── handlers/
│ └── main.yml
├── tasks/
│ └── main.yml
├── templates/
│ └── nginx.conf.j2
└── vars/
└── main.yml
9. Role tasks/main.yml
File:
roles/nginx/tasks/main.yml
Nội dung:
---
- name: Install nginx
ansible.builtin.apt:
name: nginx
state: present
update_cache: true
- name: Deploy nginx configuration
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/sites-available/todo.conf
owner: root
group: root
mode: "0644"
validate: "nginx -t -c %s"
notify: Reload nginx
- name: Ensure nginx is running
ansible.builtin.service:
name: nginx
state: started
enabled: true
Lưu ý:
Trong Role, template:
src: nginx.conf.j2
sẽ được tìm trong:
roles/nginx/templates/
Không cần viết:
roles/nginx/templates/nginx.conf.j2
10. Role Template
Tạo:
roles/nginx/templates/nginx.conf.j2
Ví dụ:
server {
listen {{ nginx_port }};
server_name {{ server_name }};
location / {
proxy_pass http://{{ app_host }}:{{ app_port }};
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
11. Role Handler
File:
roles/nginx/handlers/main.yml
---
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
Chúng ta đã đưa kiến thức từ Lab 7 vào Role:
Template
↓
changed
↓
notify
↓
Handler
↓
Reload
12. Role Defaults
File:
roles/nginx/defaults/main.yml
---
nginx_port: 80
server_name: localhost
app_host: 127.0.0.1
app_port: 8080
Đây là một trong những phần quan trọng nhất của Role.
Role không nên hard-code:
80
localhost
127.0.0.1
8080
mà nên expose configuration thông qua Variables.
13. Tại sao dùng defaults/main.yml?
Ví dụ Role nginx có:
nginx_port: 80
Nhưng production muốn:
nginx_port: 8080
Thay vì sửa Role:
# roles/nginx/defaults/main.yml
nginx_port: 8080
chúng ta có thể override từ bên ngoài.
Đây là một nguyên tắc quan trọng:
Role nên reusable và configurable.
14. defaults vs vars
Hai thư mục này rất dễ gây nhầm.
defaults/main.yml
Dùng cho:
Default configuration có thể dễ dàng override.
Ví dụ:
nginx_port: 80
vars/main.yml
Dùng cho:
Variables nội bộ của Role, thường không muốn người dùng override dễ dàng.
Ví dụ:
nginx_config_path: /etc/nginx/sites-available/todo.conf
15. Quy tắc thực tế
Trong Role reusable:
defaults/
thường được sử dụng nhiều hơn:
vars/
Ví dụ:
# defaults/main.yml
nginx_port: 80
server_name: localhost
app_port: 8080
Trong khi:
# vars/main.yml
nginx_config_path: /etc/nginx/sites-available/todo.conf
Người mới nên nhớ:
Configuration mà user có thể muốn thay đổi → defaults.
16. Playbook sử dụng Role
Tạo:
site.yml
---
- name: Configure webservers
hosts: webservers
become: true
roles:
- nginx
Chỉ vậy thôi.
Playbook từ:
30–50 dòng
có thể trở thành:
roles:
- nginx
Đây chính là sức mạnh của Role.
17. Chạy Playbook
ansible-playbook site.yml
Ansible sẽ tự động thực hiện:
site.yml
│
▼
nginx role
│
├── tasks/main.yml
│
├── templates/nginx.conf.j2
│
├── handlers/main.yml
│
└── defaults/main.yml
18. Kiểm tra Idempotency
Chạy lần 1:
ansible-playbook site.yml
Có thể:
changed > 0
Chạy lần 2:
ansible-playbook site.yml
Mục tiêu:
changed=0
Role không làm mất tính Idempotency.
Đây là điểm rất quan trọng:
Role không thay thế Idempotency. Role giúp tổ chức các automation có tính Idempotent.
19. Override Variables
Ví dụ:
---
- name: Configure webservers
hosts: webservers
become: true
roles:
- role: nginx
vars:
nginx_port: 8080
server_name: todo.example.com
app_port: 9090
Bây giờ Role dùng:
nginx_port = 8080
server_name = todo.example.com
app_port = 9090
thay vì default.
20. Cách này có tốt không?
Có thể dùng:
roles:
- role: nginx
vars:
nginx_port: 8080
Nhưng khi project lớn, chúng ta thường muốn tách configuration ra khỏi Playbook.
Ví dụ:
group_vars/
├── webservers.yml
└── production.yml
Sau đó:
nginx_port: 80
server_name: todo.example.com
app_port: 8080
Role chỉ consume variables.
21. Role nên biết gì?
Một Role tốt:
Role
│
├── biết cách install
├── biết cách configure
├── biết cách restart/reload
└── biết cách validate
Nhưng không nên biết:
Production server IP
Developer username
Environment name
Company-specific secrets
Ví dụ không nên hard-code:
server_name: production.mycompany.com
trong Role.
Nên:
server_name: "{{ app_domain }}"
và app_domain được truyền từ environment.
22. Một Role nên làm một việc
Không nên tạo:
roles/
└── everything/
bên trong:
install nginx
install docker
install node
create users
configure firewall
deploy application
setup monitoring
Thay vào đó:
roles/
├── nginx/
├── docker/
├── nodejs/
├── users/
├── firewall/
├── application/
└── monitoring/
Đây là Separation of Concerns.
23. Nhiều Roles trong một Playbook
Ví dụ:
---
- name: Provision application servers
hosts: app_servers
become: true
roles:
- common
- docker
- nginx
- application
- monitoring
Flow:
Server
│
├── common
│
├── docker
│
├── nginx
│
├── application
│
└── monitoring
Playbook trở thành một bản mô tả kiến trúc rất rõ ràng.
24. Role common
Một Role phổ biến trong production là:
common
Nó có thể quản lý:
Timezone
Packages
Users
SSH configuration
NTP
Directories
Basic security
Ví dụ:
roles:
- common
- docker
- nginx
25. Role Dependencies
Một Role có thể phụ thuộc vào Role khác.
Ví dụ:
application
│
└── depends on
│
▼
docker
Trong:
roles/application/meta/main.yml
có thể khai báo dependency.
Ví dụ:
---
dependencies:
- role: docker
Khi chạy:
application
↓
docker
↓
application
Ansible sẽ xử lý dependency.
26. Tại sao Dependency hữu ích?
Giả sử:
application role
cần Docker.
Thay vì bắt người dùng nhớ:
roles:
- docker
- application
Role application có thể tự mô tả:
I depend on docker.
Điều này làm Role reusable hơn.
27. Nhưng Dependency không nên lạm dụng
Ví dụ:
application
↓
docker
↓
common
↓
security
↓
firewall
↓
monitoring
↓
...
Sau một thời gian rất khó hiểu:
Tại sao chạy Role A lại thay đổi 6 thứ khác?
Do đó dependency nên được thiết kế rõ ràng.
28. Exercise 1 — Create Nginx Role
Tạo:
roles/nginx/
Yêu cầu:
tasks/main.yml
handlers/main.yml
defaults/main.yml
templates/nginx.conf.j2
Role phải:
- [ ] Install Nginx.
- [ ] Enable Nginx.
- [ ] Start Nginx.
- [ ] Deploy configuration.
- [ ] Validate configuration.
- [ ] Reload khi config thay đổi.
29. Exercise 2 — Configurable Nginx
Tạo variables:
nginx_port: 80
server_name: localhost
app_host: 127.0.0.1
app_port: 8080
Sau đó override:
nginx_port: 8080
server_name: todo.example.com
app_port: 3000
Kiểm tra configuration thực tế trên server.
30. Exercise 3 — Multiple Roles
Tạo:
roles/
├── common/
├── nginx/
└── docker/
common
Cài:
curl
vim
git
htop
nginx
Cài và configure Nginx.
docker
Cài Docker.
Playbook:
---
- name: Provision server
hosts: webservers
become: true
roles:
- common
- docker
- nginx
31. Exercise 4 — Role Reusability
Có:
web-01
web-02
web-03
Tất cả cùng sử dụng:
nginx role
Nhưng:
web-01 → app_port 8080
web-02 → app_port 8081
web-03 → app_port 8082
Sử dụng variables để cấu hình khác nhau.
Mục tiêu:
Không sửa Role.
32. Exercise 5 — Role + Handler
Thay đổi:
app_port: 8080
thành:
app_port: 8081
Chạy:
ansible-playbook site.yml
Kỳ vọng:
Template → changed
↓
notify
↓
Reload nginx
Chạy lại:
ansible-playbook site.yml
Kỳ vọng:
Template → ok
Handler → không chạy
33. Exercise 6 — Tạo Role Docker
Tạo:
ansible-galaxy role init roles/docker
Cấu trúc:
roles/docker/
├── defaults/
├── handlers/
├── tasks/
├── templates/
└── vars/
Task cần thực hiện:
Install Docker
↓
Enable Docker
↓
Start Docker
Không cần copy logic từ Role Nginx.
34. Exercise 7 — Application Role
Tạo:
roles/application/
Role này chịu trách nhiệm:
Create application directory
↓
Deploy configuration
↓
Deploy Docker Compose
↓
Start application
Variables:
app_name: todo
app_dir: /opt/todo
app_port: 8080
35. Bài tập tổng hợp
Xây dựng:
roles/
├── common/
├── docker/
├── nginx/
└── application/
Architecture:
site.yml
│
┌────────────┼────────────┐
│ │ │
common docker nginx
│
│
application
Trong đó:
common
↓
Base server
docker
↓
Container runtime
application
↓
Todo application
nginx
↓
Reverse proxy
36. Project Structure hoàn chỉnh
Sau bài tập:
ansible-project/
│
├── ansible.cfg
├── inventory.ini
├── site.yml
│
├── group_vars/
│ └── webservers.yml
│
└── roles/
│
├── common/
│ ├── defaults/
│ ├── handlers/
│ ├── tasks/
│ ├── templates/
│ └── vars/
│
├── docker/
│ ├── defaults/
│ ├── handlers/
│ ├── tasks/
│ └── vars/
│
├── nginx/
│ ├── defaults/
│ ├── handlers/
│ ├── tasks/
│ ├── templates/
│ └── vars/
│
└── application/
├── defaults/
├── handlers/
├── tasks/
├── templates/
└── vars/
Đây đã bắt đầu giống một Ansible project thực tế.
37. Tư duy Senior #1 — Role là Boundary
Một Role nên có boundary rõ ràng.
Ví dụ:
nginx role
chịu trách nhiệm:
Nginx installation
Nginx configuration
Nginx service
Không nên tự ý:
install PostgreSQL
create application database
deploy backend
configure Kubernetes
Nếu Role làm quá nhiều thứ, nó sẽ trở thành một "God Role".
38. Tư duy Senior #2 — Role phải configurable
Bad:
server_name: todo.example.com
Good:
server_name: "{{ nginx_server_name }}"
Role:
Generic
↓
Variables
↓
Environment-specific configuration
Nhờ vậy cùng một Role có thể dùng cho:
Development
Staging
Production
39. Tư duy Senior #3 — Role không nên chứa Secret
Không nên:
db_password: "123456"
trong:
roles/database/defaults/main.yml
hoặc:
roles/database/vars/main.yml
Secrets sẽ được học kỹ hơn ở:
Lab 11 — Ansible Secrets & Vault
40. Tư duy Senior #4 — Role nên có README
Một Role tốt nên có:
README.md
Ví dụ:
# Nginx Role
## Description
Install and configure Nginx.
## Requirements
Ubuntu 22.04+
## Variables
nginx_port: 80
server_name: localhost
## Example
roles:
- role: nginx
Người khác có thể sử dụng Role mà không cần đọc toàn bộ code.
41. Tư duy Senior #5 — Role phải Test được
Một Role production không nên chỉ:
Viết xong
↓
Chạy thử
↓
OK
Mà cần nghĩ:
Role
↓
Test
↓
Expected State
↓
Verify
Ở level cao hơn, chúng ta có thể dùng:
Molecule
để test Role.
Phần này chưa cần đi sâu trong Lab 8, nhưng người học nên biết đây là hướng phát triển tiếp theo.
42. Role và Reusability
Giả sử bạn có:
nginx role
Hôm nay:
Project A
sử dụng.
Ngày mai:
Project B
cũng cần Nginx.
Không cần copy:
nginx-task.yml
nginx-handler.yml
nginx-template.yml
Chỉ cần:
roles:
- nginx
Đây chính là DRY — Don't Repeat Yourself.
43. Role không phải chỉ là "chia file"
Đây là điểm người học cần hiểu sâu.
Sai cách hiểu:
Role chỉ là cách chia Playbook thành nhiều folder.
Đúng hơn:
Role là một abstraction boundary cho một capability cụ thể của infrastructure.
Ví dụ:
Role
↓
"Server có khả năng chạy Nginx"
hoặc:
Role
↓
"Server có khả năng chạy Docker"
hoặc:
Role
↓
"Server có application Todo"
Đây là cách tư duy quan trọng để tiến lên Senior.
44. Role vs Playbook
| Playbook | Role | |
|---|---|---|
| Mục đích | Orchestration | Implementation |
| Scope | Workflow | Capability |
| Reusable | Có | Rất cao |
| Template | Có thể | Có |
| Handler | Có thể | Có |
| Variables | Có | Có |
| Project lớn | Khó quản lý nếu quá nhiều logic | Phù hợp |
Có thể nhớ:
Playbook = What should happen?
Role = How is a capability implemented?
45. Role vs Task
Ví dụ:
- name: Install nginx
ansible.builtin.apt:
name: nginx
đây là Task.
Còn:
roles/nginx/
là toàn bộ capability:
Install
Configure
Validate
Start
Reload
Do đó:
Task
⊂ Role
46. Workflow chuẩn sau Lab 8
Từ bây giờ, khi xây một automation mới, hãy suy nghĩ:
1. Thành phần nào cần quản lý?
↓
2. Có nên tạo Role không?
↓
3. Role cần Variables nào?
↓
4. Có Template không?
↓
5. Có Handler không?
↓
6. Có Dependency không?
↓
7. Có cần Validate không?
↓
8. Có Idempotent không?
↓
9. Có thể Reuse không?
Đây là checklist rất tốt cho người học.
47. Checklist hoàn thành Lab 8
Người học cần có khả năng:
- [ ] Hiểu Role là gì.
- [ ] Biết tại sao cần Role.
- [ ] Tạo Role bằng
ansible-galaxy role init. - [ ] Hiểu cấu trúc Role.
- [ ] Sử dụng
tasks/main.yml. - [ ] Sử dụng
handlers/main.yml. - [ ] Sử dụng
templates/. - [ ] Sử dụng
files/. - [ ] Sử dụng
defaults/main.yml. - [ ] Hiểu
vars/main.yml. - [ ] Hiểu
meta/main.yml. - [ ] Gọi Role từ Playbook.
- [ ] Override Role Variables.
- [ ] Sử dụng nhiều Role trong một Playbook.
- [ ] Hiểu Role Dependencies.
- [ ] Kết hợp Role + Jinja2.
- [ ] Kết hợp Role + Handler.
- [ ] Kết hợp Role + Idempotency.
- [ ] Biết thiết kế Role có boundary rõ ràng.
- [ ] Biết tránh hard-code configuration.
- [ ] Biết tại sao không nên để Secret trong Role.
48. Bài tập cuối Lab — Production Server
Hãy xây dựng một server theo kiến trúc:
site.yml
│
┌───────────────┼───────────────┐
│ │ │
▼ ▼ ▼
common docker nginx
│ │ │
│ │ │
└───────────────┼───────────────┘
│
▼
application
Server sau khi chạy Playbook phải đạt:
Ubuntu Server
│
├── Base packages
│
├── Docker
│ └── running
│
├── Nginx
│ ├── running
│ └── configured
│
└── Todo Application
├── configured
└── running
Và quan trọng nhất:
Run 1
↓
Infrastructure changes
↓
Run 2
↓
changed = 0
↓
Run 3
↓
changed = 0
Nếu thay đổi:
app_port
server_name
nginx configuration
application configuration
thì chỉ những thành phần liên quan mới được thay đổi/reload/restart.
49. Kết quả sau Lab 8
Sau Lab 1 → 8, người học đã đi qua một progression khá quan trọng:
Lab 1
Ansible Fundamentals
↓
Lab 2
Inventory & SSH
↓
Lab 3
Ad-Hoc Commands
↓
Lab 4
Playbook
↓
Lab 5
Variables & Facts
↓
Lab 6
Jinja2
↓
Lab 7
Handlers & Idempotency
↓
Lab 8
Roles
Tư duy cũng đã thay đổi:
BEGINNER
"Ansible chạy command trên server"
↓
"Ansible chạy Playbook"
↓
"Ansible quản lý Desired State"
↓
"Ansible automation phải Idempotent"
↓
"Ansible automation phải Reusable"
↓
INTERMEDIATE
"Ansible project được thiết kế bằng Roles"
Đây là mốc rất quan trọng trước khi bước sang Lab 9 — Ansible Server Provisioning, nơi chúng ta sẽ bắt đầu sử dụng các Role để tự động dựng một server gần với môi trường production: package, user, SSH, Docker, Docker Compose, directory structure và các thiết lập hệ thống cần thiết.
All rights reserved