0

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.cfg trê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.
  • all group.
  • 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí