Lab 2 — Ansible Inventory & SSH
Mục tiêu: Hiểu sâu cách Ansible xác định và kết nối tới Managed Node thông qua Inventory và SSH; biết cách tổ chức nhiều server, sử dụng SSH Key, thiết lập user, group, variables và kiểm tra kết nối trước khi chạy automation.
1. Bối cảnh thực tế
Ở Lab 1, chúng ta có một server:
MacBook
Control Node
│
│ SSH
▼
Ubuntu Server
Managed Node
Inventory rất đơn giản:
[webservers]
web-01 ansible_host=192.168.56.101
Nhưng trong môi trường thực tế, hệ thống sẽ nhanh chóng lớn hơn:
Ansible
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Web Tier App Tier DB Tier
│ │ │
┌────┴────┐ ┌────┴────┐ │
▼ ▼ ▼ ▼ ▼
web-01 web-02 app-01 app-02 db-01
Lúc này chúng ta cần trả lời:
- Ansible quản lý những server nào?
- Server nào là Web Server?
- Server nào là Application Server?
- Server nào là Database Server?
- Server nào thuộc Production?
- Server nào thuộc Staging?
- SSH bằng user nào?
- SSH bằng key nào?
- Server sử dụng port SSH nào?
- Làm sao kiểm tra toàn bộ server trước khi deployment?
Đây chính là nhiệm vụ của Inventory + SSH.
2. Inventory là gì?
Inventory là danh sách các Managed Node mà Ansible có thể quản lý.
Có thể hiểu đơn giản:
Inventory
│
├── Server 1
├── Server 2
├── Server 3
└── Server 4
Nhưng Inventory không chỉ là danh sách IP.
Nó còn cho phép chúng ta mô tả:
Server
│
├── Hostname
├── IP
├── SSH User
├── SSH Port
├── SSH Key
├── Group
└── Variables
Ví dụ:
[webservers]
web-01 ansible_host=192.168.56.101
web-02 ansible_host=192.168.56.102
Ở đây:
web-01
web-02
là tên mà Ansible sử dụng để tham chiếu tới server.
Còn:
192.168.56.101
192.168.56.102
là địa chỉ thật để Ansible kết nối.
3. Inventory không nhất thiết phải dùng IP làm hostname
Đây là một điểm người mới thường nhầm.
Ví dụ:
[webservers]
192.168.56.101
192.168.56.102
vẫn hoạt động.
Nhưng cách này kém rõ ràng khi infrastructure lớn.
Tốt hơn:
[webservers]
web-01 ansible_host=192.168.56.101
web-02 ansible_host=192.168.56.102
Bây giờ chúng ta có:
Logical Name Real Address
web-01 ───► 192.168.56.101
web-02 ───► 192.168.56.102
Điều này rất hữu ích khi đọc log:
web-01 | CHANGED
web-02 | SUCCESS
thay vì:
192.168.56.101 | CHANGED
192.168.56.102 | SUCCESS
4. Inventory Group
Khi có nhiều server, chúng ta không muốn chạy command từng server.
Ví dụ:
[webservers]
web-01 ansible_host=192.168.56.101
web-02 ansible_host=192.168.56.102
[appservers]
app-01 ansible_host=192.168.56.103
app-02 ansible_host=192.168.56.104
[dbservers]
db-01 ansible_host=192.168.56.105
Ta có:
Infrastructure
│
├── webservers
│ ├── web-01
│ └── web-02
│
├── appservers
│ ├── app-01
│ └── app-02
│
└── dbservers
└── db-01
Sau đó:
ansible webservers -m ansible.builtin.ping
chỉ chạy trên:
web-01
web-02
Còn:
ansible dbservers -m ansible.builtin.ping
chỉ chạy trên:
db-01
5. Chuẩn bị môi trường Lab
Trong Lab này chúng ta sẽ sử dụng 3 Managed Nodes:
MacBook
Control Node
│
│ SSH
┌───────────┼───────────┐
▼ ▼ ▼
web-01 app-01 db-01
Ubuntu Ubuntu Ubuntu
Ví dụ IP:
web-01 192.168.56.101
app-01 192.168.56.102
db-01 192.168.56.103
Nếu bạn chỉ có một server, vẫn có thể làm Lab bằng cách tạo nhiều hostname trỏ tới cùng server để hiểu cấu trúc Inventory.
6. Tạo Inventory
Tạo project:
mkdir ansible-lab-02
cd ansible-lab-02
Tạo:
ansible-lab-02/
└── inventory.ini
Nội dung:
[webservers]
web-01 ansible_host=192.168.56.101
[appservers]
app-01 ansible_host=192.168.56.102
[dbservers]
db-01 ansible_host=192.168.56.103
Kiểm tra:
ansible-inventory -i inventory.ini --graph
Kết quả:
@all:
|--@ungrouped:
|--@webservers:
| |--web-01
|--@appservers:
| |--app-01
|--@dbservers:
| |--db-01
7. Group là cách tổ chức Infrastructure
Inventory group không chỉ giúp chạy command.
Nó giúp chúng ta mô hình hóa infrastructure.
Ví dụ:
Production
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Web Servers App Servers DB Servers
│ │ │
web-01 app-01 db-01
web-02 app-02 db-02
Sau này chúng ta có thể viết:
hosts: webservers
hoặc:
hosts: appservers
hoặc:
hosts: dbservers
Đây là nền tảng để xây dựng automation có quy mô lớn.
8. SSH — nền tảng kết nối của Ansible
Ansible cần kết nối tới Managed Node.
Với Linux, phương thức phổ biến là SSH:
Control Node
│
│ SSH
▼
Managed Node
Trước khi chạy Ansible, hãy kiểm tra SSH trực tiếp:
ssh ubuntu@192.168.56.101
Nếu thành công:
ubuntu@web-01:~$
hãy thoát:
exit
Lặp lại với:
ssh ubuntu@192.168.56.102
ssh ubuntu@192.168.56.103
Quy tắc quan trọng:
Nếu SSH chưa hoạt động bình thường, hãy sửa SSH trước khi debug Ansible.
9. SSH Authentication
SSH có thể sử dụng nhiều phương thức authentication.
Trong môi trường DevOps, chúng ta thường sử dụng:
SSH Key Authentication
Thay vì:
Password Authentication
Flow:
Control Node
│
Private Key
│
▼
SSH Server
│
Public Key
│
▼
Authentication
Hai thành phần:
Private Key
Public Key
Ví dụ:
~/.ssh/
├── id_ed25519
└── id_ed25519.pub
Private Key không được chia sẻ.
Public Key có thể được cài lên server.
10. Kiểm tra SSH Key
Kiểm tra:
ls -la ~/.ssh
Có thể thấy:
id_ed25519
id_ed25519.pub
Nếu chưa có SSH key:
ssh-keygen -t ed25519
Sau đó:
ls -la ~/.ssh
Bạn sẽ thấy:
id_ed25519
id_ed25519.pub
11. Private Key và Public Key
Có thể hình dung:
Private Key
│
│ giữ bí mật
▼
Your Computer
Public Key
│
│ có thể copy
▼
Server
Không được làm:
❌ Upload private key lên GitHub
❌ Gửi private key qua Slack
❌ Copy private key vào server
❌ Commit private key vào Git
Nếu private key bị lộ, attacker có thể sử dụng nó để authenticate vào những server tin tưởng key đó.
12. Cài Public Key lên Server
Nếu server cho phép password authentication, có thể sử dụng:
ssh-copy-id ubuntu@192.168.56.101
Hoặc trên hệ thống không có ssh-copy-id, có thể thêm nội dung public key vào:
~/.ssh/authorized_keys
trên server.
Sau đó thử:
ssh ubuntu@192.168.56.101
Nếu không yêu cầu password của user server nữa, SSH key authentication đã hoạt động.
13. Cấu hình SSH trong Inventory
Giả sử user SSH là:
ubuntu
Có thể khai báo:
[webservers]
web-01 ansible_host=192.168.56.101 ansible_user=ubuntu
Nếu sử dụng SSH key cụ thể:
[webservers]
web-01 ansible_host=192.168.56.101 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_ed25519
Nếu SSH sử dụng port khác:
[webservers]
web-01 ansible_host=192.168.56.101 ansible_port=2222
Có thể kết hợp:
[webservers]
web-01 \
ansible_host=192.168.56.101 \
ansible_user=ubuntu \
ansible_port=22 \
ansible_ssh_private_key_file=~/.ssh/id_ed25519
Tuy nhiên trong thực tế, không nên nhồi quá nhiều configuration vào một dòng Inventory nếu project bắt đầu lớn.
Sau này chúng ta sẽ học cách tách variables ra.
14. Test SSH bằng Ansible
Sau khi SSH hoạt động:
ansible webservers \
-i inventory.ini \
-m ansible.builtin.ping
Kết quả:
web-01 | SUCCESS => {
"changed": false,
"ping": "pong"
}
Với toàn bộ server:
ansible all \
-i inventory.ini \
-m ansible.builtin.ping
Kết quả:
web-01 | SUCCESS
app-01 | SUCCESS
db-01 | SUCCESS
Đây nên là bước kiểm tra đầu tiên trước khi deployment.
15. all là gì?
Ansible tự tạo group:
all
Group này chứa tất cả host trong Inventory.
Ví dụ:
all
│
├── web-01
├── app-01
└── db-01
Do đó:
ansible all -m ansible.builtin.ping
sẽ kiểm tra tất cả server.
16. Chạy command trên một server
Có thể target trực tiếp:
ansible web-01 \
-i inventory.ini \
-m ansible.builtin.command \
-a "hostname"
17. Chạy command trên một group
Web:
ansible webservers \
-i inventory.ini \
-m ansible.builtin.command \
-a "hostname"
App:
ansible appservers \
-i inventory.ini \
-m ansible.builtin.command \
-a "hostname"
Database:
ansible dbservers \
-i inventory.ini \
-m ansible.builtin.command \
-a "hostname"
18. Ansible Pattern
Ansible cho phép target rất linh hoạt.
Ví dụ:
ansible webservers
ansible appservers
ansible dbservers
Hoặc:
ansible all
Có thể loại trừ:
ansible 'all:!dbservers'
Nghĩa là:
all
│
├── web-01 ✓
├── app-01 ✓
└── db-01 ✗
Có thể kết hợp group:
ansible 'webservers:appservers'
Nghĩa là target:
webservers
+
appservers
Pattern sẽ trở nên rất quan trọng khi infrastructure lớn.
19. Nested Groups
Inventory có thể tạo group lớn hơn.
Ví dụ:
[webservers]
web-01 ansible_host=192.168.56.101
web-02 ansible_host=192.168.56.102
[appservers]
app-01 ansible_host=192.168.56.103
app-02 ansible_host=192.168.56.104
[production:children]
webservers
appservers
Khi đó:
production
│
├── webservers
│ ├── web-01
│ └── web-02
│
└── appservers
├── app-01
└── app-02
Chạy:
ansible production \
-i inventory.ini \
-m ansible.builtin.ping
sẽ target toàn bộ:
web-01
web-02
app-01
app-02
20. Inventory Variables
Inventory cũng có thể chứa variables.
Ví dụ:
[webservers]
web-01 ansible_host=192.168.56.101 http_port=80
web-02 ansible_host=192.168.56.102 http_port=8080
Khi đó mỗi server có giá trị riêng:
web-01
http_port = 80
web-02
http_port = 8080
Nhưng khi project lớn, chúng ta sẽ không muốn tất cả variables nằm trong Inventory.
Đây là lý do Ansible có:
group_vars/
host_vars/
Chúng ta sẽ học sâu hơn ở Lab 5 — Variables & Facts.
21. Kiểm tra Inventory chi tiết
Một command rất quan trọng:
ansible-inventory \
-i inventory.ini \
--list
Có thể dùng:
ansible-inventory \
-i inventory.ini \
--graph
Hoặc kiểm tra một host:
ansible-inventory \
-i inventory.ini \
--host web-01
Đây là các command nên nhớ khi troubleshooting.
22. ansible.cfg
Khi project phát triển, việc luôn phải viết:
-i inventory.ini
không thuận tiện.
Chúng ta có thể tạo:
ansible.cfg
Cấu trúc:
ansible-lab-02/
├── ansible.cfg
├── inventory.ini
└── site.yml
Ví dụ:
[defaults]
inventory = ./inventory.ini
Sau đó:
ansible all -m ansible.builtin.ping
không cần:
-i inventory.ini
Ansible sẽ tự tìm Inventory được cấu hình.
23. Tại sao nên có ansible.cfg?
Khi project lớn:
ansible-project/
├── ansible.cfg
├── inventories/
├── playbooks/
├── roles/
└── group_vars/
team có thể thống nhất các default configuration.
Ví dụ:
Inventory
SSH settings
Timeout
Forks
Output behavior
Tuy nhiên:
Không nên tùy tiện copy một
ansible.cfgtrên Internet vào production.
Configuration của Ansible ảnh hưởng đến behavior của automation.
Hãy hiểu option trước khi thay đổi.
24. SSH Config
Ngoài Inventory, SSH cũng có file:
~/.ssh/config
Ví dụ:
Host web-01
HostName 192.168.56.101
User ubuntu
IdentityFile ~/.ssh/id_ed25519
Sau đó có thể:
ssh web-01
thay vì:
ssh -i ~/.ssh/id_ed25519 ubuntu@192.168.56.101
SSH Config và Ansible Inventory có thể cùng tồn tại.
Tuy nhiên trong series này, chúng ta sẽ ưu tiên làm rõ configuration nào thuộc:
SSH
và configuration nào thuộc:
Ansible
để tránh nhầm lẫn.
25. SSH Agent
Nếu sử dụng SSH key có passphrase, bạn có thể dùng SSH Agent để tránh nhập passphrase liên tục.
Kiểm tra:
ssh-add -l
Thêm key:
ssh-add ~/.ssh/id_ed25519
Sau đó Ansible có thể sử dụng key thông qua SSH authentication.
Mô hình:
SSH Agent
│
Private Key
│
▼
Control Node ───── SSH ─────► Server
Đây là một kỹ thuật hữu ích khi làm việc với nhiều server.
26. SSH Security trong Production
Trong môi trường production, không nên có mô hình:
Internet
│
│ SSH
▼
Production Server
│
└── root + password
Thay vào đó:
Developer / CI
│
│ SSH Key
▼
deploy user
│
│ sudo
▼
Production Server
Một số nguyên tắc quan trọng:
- Không dùng root trực tiếp nếu không cần.
- Ưu tiên SSH key.
- Không commit private key.
- Không hard-code password.
- Hạn chế quyền của user.
- Kiểm soát
sudo. - Có thể giới hạn nguồn truy cập SSH bằng firewall/security group.
- Audit quyền truy cập.
Các chủ đề security sâu hơn sẽ được đưa vào Lab 11 — Ansible Secrets & Vault.
27. Troubleshooting SSH
Đây là kỹ năng rất quan trọng.
Khi:
ansible all -m ansible.builtin.ping
fail, đừng ngay lập tức sửa Playbook.
Hãy kiểm tra theo thứ tự:
1. Server có hoạt động?
↓
2. Network có hoạt động?
↓
3. Port SSH có mở?
↓
4. SSH trực tiếp có được?
↓
5. User đúng?
↓
6. SSH key đúng?
↓
7. Ansible Inventory đúng?
↓
8. Ansible configuration đúng?
28. Debug SSH bằng -vvv
Nếu:
ssh ubuntu@192.168.56.101
không hoạt động, dùng:
ssh -vvv ubuntu@192.168.56.101
Bạn sẽ thấy quá trình:
Connecting...
Offering public key...
Authenticating...
Authentication successful
hoặc:
Permission denied
Đây là kỹ năng troubleshooting rất hữu ích cho DevOps Engineer.
29. Debug Ansible bằng -vvv
Nếu SSH trực tiếp hoạt động nhưng Ansible fail:
ansible webservers \
-m ansible.builtin.ping \
-vvv
Thông tin debug sẽ chi tiết hơn.
Có thể thấy:
SSH connection
User
Private key
Python
Module execution
Remote command
Không nên sử dụng -vvv cho mọi lần chạy.
Thông thường:
normal
↓
-v
↓
-vvv
chỉ tăng verbosity khi cần troubleshooting.
30. Một lỗi rất quan trọng: Python trên Managed Node
Ansible thường cần Python trên Linux Managed Node để thực thi nhiều module.
Kiểm tra:
ansible webservers \
-m ansible.builtin.command \
-a "python3 --version"
Nếu server chưa có Python, một số module có thể không hoạt động như mong đợi.
Đây là lý do khi provision server mới, chúng ta thường có bước bootstrap.
Ví dụ:
New Server
│
▼
SSH
│
▼
Install Python
│
▼
Ansible Automation
31. Thực hành tổng hợp
Bây giờ hãy xây dựng Inventory:
[webservers]
web-01 ansible_host=192.168.56.101
web-02 ansible_host=192.168.56.102
[appservers]
app-01 ansible_host=192.168.56.103
[dbservers]
db-01 ansible_host=192.168.56.104
[production:children]
webservers
appservers
dbservers
Cấu trúc:
production
│
├── webservers
│ ├── web-01
│ └── web-02
│
├── appservers
│ └── app-01
│
└── dbservers
└── db-01
32. Exercise 1 — Kiểm tra toàn bộ server
Chạy:
ansible all -m ansible.builtin.ping
Mục tiêu:
web-01 | SUCCESS
web-02 | SUCCESS
app-01 | SUCCESS
db-01 | SUCCESS
33. Exercise 2 — Kiểm tra Web Servers
ansible webservers \
-m ansible.builtin.command \
-a "hostname"
Kết quả phải chỉ chứa:
web-01
web-02
34. Exercise 3 — Kiểm tra Production
ansible production \
-m ansible.builtin.command \
-a "hostname"
Tất cả server phải được target.
35. Exercise 4 — Loại trừ Database
Chạy:
ansible 'production:!dbservers' \
-m ansible.builtin.command \
-a "hostname"
Kết quả:
web-01
web-02
app-01
Không được có:
db-01
36. Exercise 5 — Tạo ansible.cfg
Tạo:
[defaults]
inventory = ./inventory.ini
Sau đó thử:
ansible all -m ansible.builtin.ping
Nếu không cần -i inventory.ini nữa thì configuration đã hoạt động.
37. Exercise 6 — Tự troubleshoot
Cố tình sửa:
web-01 ansible_host=192.168.56.999
Sau đó:
ansible web-01 -m ansible.builtin.ping
Quan sát lỗi.
Sau đó sửa lại IP.
Tiếp tục thử đổi:
ansible_user
thành một user không tồn tại.
Quan sát lỗi:
Permission denied
Mục tiêu của bài này không phải là làm cho command chạy thành công ngay.
Mục tiêu là học quy trình:
Error
↓
Understand error
↓
Identify layer
↓
Check SSH
↓
Check Inventory
↓
Fix root cause
38. Production Best Practices
38.1. Không hard-code secret
Không nên:
ansible_password=MyPassword123
trong Inventory.
Sẽ học cách xử lý an toàn hơn ở Lab 11.
38.2. Không dùng IP trực tiếp nếu logical hostname giúp dễ quản lý hơn
Thay vì:
[webservers]
192.168.56.101
192.168.56.102
nên:
[webservers]
web-01 ansible_host=192.168.56.101
web-02 ansible_host=192.168.56.102
38.3. Tách configuration theo trách nhiệm
Khi project lớn:
Inventory
│
├── hosts
├── groups
│
└── variables
│
├── group_vars
└── host_vars
Không nên biến inventory.ini thành một file khổng lồ chứa tất cả configuration.
38.4. SSH key phải được bảo vệ
Private key:
chmod 600 ~/.ssh/id_ed25519
Không commit vào Git:
❌ id_ed25519
❌ *.pem
❌ *.key
Có thể thêm vào .gitignore:
*.pem
*.key
id_rsa
id_ed25519
39. 🧠 Senior Thinking
Người mới thường nghĩ:
Inventory là danh sách IP của server.
Nhưng trong production:
Inventory là một mô hình logic của infrastructure.
Ví dụ:
Infrastructure
│
┌────────────┼────────────┐
▼ ▼ ▼
Web App DB
│ │ │
web-01 app-01 db-01
web-02 app-02 db-02
Khi thiết kế Inventory, hãy suy nghĩ:
"Tôi sẽ muốn target những server nào cùng nhau?"
Ví dụ:
webservers
appservers
dbservers
monitoring
production
staging
development
Inventory tốt sẽ giúp Playbook đơn giản.
Inventory kém sẽ khiến Playbook trở nên phức tạp.
40. Một nguyên tắc thiết kế quan trọng
Đừng thiết kế Inventory chỉ dựa trên:
"Server này đang có IP nào?"
Hãy thiết kế dựa trên:
"Server này có vai trò gì trong hệ thống?"
Ví dụ:
192.168.1.10
không nói lên nhiều điều.
Nhưng:
web-01
cho chúng ta biết đây là một Web Server.
Đây là một bước nhỏ nhưng rất quan trọng trong tư duy Infrastructure as Code.
41. Challenge — Thiết kế Inventory
Hãy tưởng tượng công ty có:
Development:
- 2 Web Servers
- 2 App Servers
- 1 Database
Production:
- 3 Web Servers
- 3 App Servers
- 2 Database
Hãy tự thiết kế Inventory sao cho có thể chạy:
ansible development ...
ansible production ...
ansible webservers ...
ansible dbservers ...
và:
ansible 'production:!dbservers' ...
Không sử dụng Playbook.
Chỉ sử dụng Inventory.
Mục tiêu của Challenge là hiểu cách mô hình hóa infrastructure bằng Inventory.
42. Kiến thức cần ghi nhớ
Sau Lab 2, bạn nên nhớ:
Inventory
│
├── Host
├── Group
├── Children Group
├── Host Variables
└── Group Variables
SSH:
SSH
│
├── Host
├── User
├── Port
├── Public Key
├── Private Key
└── SSH Agent
Ansible:
Control Node
│
│ SSH
▼
Managed Node
Và flow troubleshooting:
Ansible failed
│
▼
Check SSH
│
▼
Check Inventory
│
▼
Check User
│
▼
Check SSH Key
│
▼
Check Ansible Config
43. Tổng kết Lab
Ở Lab 1, chúng ta mới chỉ biết:
Ansible
│
▼
One Server
Sau Lab 2:
Ansible
│
┌──────────┼──────────┐
▼ ▼ ▼
Web Tier App Tier DB Tier
│ │ │
web-01 app-01 db-01
web-02 app-02 db-02
Bạn đã học được:
- Inventory là gì.
- Host và Group.
allgroup.- Nested Groups.
- Ansible Patterns.
- SSH Authentication.
- SSH Key.
- Public Key / Private Key.
- SSH Agent.
ansible_user.ansible_host.ansible_port.ansible_ssh_private_key_file.ansible-inventory.ansible.cfg.- Troubleshooting SSH.
- Kiểm tra connectivity bằng
ping.
Quan trọng hơn, bạn đã bắt đầu chuyển từ:
"Server này có IP gì?"
sang:
"Server này đóng vai trò gì trong Infrastructure?"
Đây chính là tư duy cần thiết để bước tiếp sang Lab 3 — Ansible Ad-Hoc Commands, nơi chúng ta sẽ học cách sử dụng Ansible để thực hiện các tác vụ quản trị server nhanh chóng mà chưa cần viết Playbook.
All rights reserved