Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn là kỹ sư vận hành cho một công ty fintech ở TP.HCM. Ngày thứ Hai, hệ thống chạy trơn tru trên 30 server. Đến thứ Sáu, một trong 30 server đó bắt đầu trả lỗi 500 ngẫu nhiên. Bạn SSH vào từng máy, so sánh thủ công phiên bản Nginx, nội dung file cấu hình, biến môi trường... và phát hiện ra: server số 17 đang chạy phiên bản OpenSSL cũ hơn ba tháng, vì lần patch trước có người quên nó. Cả nhóm mất một buổi chiều chỉ để tìm ra sự khác biệt vô hình đó.
Vấn đề này có một cái tên: configuration drift (trôi dạt cấu hình). Khi bạn quản lý server bằng tay, mỗi máy dần trở thành một "bông tuyết" (snowflake server) — độc nhất, không lặp lại, và không ai dám đụng vào vì sợ vỡ. Configuration Management (Quản lý cấu hình) chính là kỷ luật kỹ thuật sinh ra để giết chết vấn đề này.
Ở Bài 36, bạn đã học cách dùng Terraform để tạo ra hạ tầng — server, network, load balancer. Nhưng Terraform dừng lại ở ranh giới "máy đã có". Câu hỏi tiếp theo là: bên trong mỗi máy đó cần cài gói gì, file config chứa nội dung gì, service nào phải chạy? Đó chính xác là địa hạt của Bài 37. Configuration Management biến việc "chăm sóc từng con server như thú cưng" thành "quản lý cả đàn gia súc theo một chuẩn duy nhất, có thể tái tạo".
Khái niệm cốt lõi
Configuration Management giải quyết bài toán gì
Bản chất của Configuration Management là quản lý state (trạng thái) của server một cách khai báo và lặp lại được. Cụ thể, nó trả lời ba câu hỏi:
- Package nào phải được cài? Ví dụ: Nginx 1.24, PostgreSQL client 15, Python 3.11.
- File config phải có nội dung gì? Ví dụ:
/etc/nginx/nginx.confphải chứa đúng block cấu hình worker và upstream. - Service nào phải đang chạy? Ví dụ:
nginxvànode-exporterphải ở trạng tháirunningvàenabled(tự khởi động khi reboot).
Idempotency — tính bất biến khi lặp lại
Đây là khái niệm quan trọng nhất trong bài này. Idempotency nghĩa là: bạn chạy cùng một cấu hình một lần hay một trăm lần, kết quả cuối cùng vẫn giống hệt nhau, và không gây tác dụng phụ.
So sánh hai cách viết:
Cách mệnh lệnh (imperative) — KHÔNG idempotent
echo "user vietcos;" >> /etc/nginx/nginx.conf
Chạy 3 lần => dòng này bị thêm 3 lần => file hỏng
Cách khai báo (declarative) — idempotent, dùng Ansible
- name: Đảm bảo dòng user tồn tại trong nginx.conf
lineinfile:
path: /etc/nginx/nginx.conf
line: "user vietcos;"
regexp: "^user "
Chạy 100 lần => dòng này chỉ có đúng 1 bản
Với công cụ Configuration Management, bạn mô tả "dòng này phải tồn tại". Nếu đã có, tool không làm gì. Nếu chưa có, tool thêm vào. Nếu sai, tool sửa lại. Đây chính là điều khiến bạn có thể chạy lại cấu hình mỗi giờ để chống drift mà không sợ làm hỏng gì.
Push vs Pull — hai triết lý vận hành
Đây là trục phân loại quan trọng khi so sánh các tool, chính là cột "Mode" trong ghi chú gốc của bài.
| Tool | Mode | Ngôn ngữ | Cần agent? | Đặc điểm |
|---|---|---|---|---|
| Ansible | Push (agentless) | YAML | Không (chỉ cần SSH) | Đơn giản, dễ bắt đầu, control node đẩy config qua SSH |
| Puppet | Pull (agent-based) | Puppet DSL | Có (puppet agent) | Mạnh cho fleet lớn, agent tự kéo config định kỳ |
| Chef | Pull (agent-based) | Ruby DSL | Có (chef-client) | Linh hoạt cao, đường học dốc hơn |
| SaltStack | Cả hai | YAML | Tùy chọn | Nhanh, dùng message bus, hợp fleet cực lớn |
Pull model (Puppet, Chef): Mỗi máy đích cài một agent. Agent này định kỳ (thường 30 phút một lần) tự kết nối về server trung tâm, kéo về "catalog" (bản mô tả trạng thái mong muốn của riêng nó) và tự áp dụng. Ưu điểm: chống drift tự động, máy mới tự cấu hình hóa khi agent chạy lần đầu, quản lý tốt fleet hàng nghìn máy. Nhược điểm: phải vận hành thêm hạ tầng master + agent, đường học dốc hơn.
Vị trí trong bức tranh lớn
Configuration Management khác với các công cụ lân cận mà bạn đã hoặc sẽ học:
- Terraform (Bài 36): provisioning — tạo ra máy/hạ tầng. "Có server chưa?"
- Configuration Management (Bài này): cấu hình bên trong máy đã có. "Máy có đúng package, config, service chưa?"
- Container image / Docker (Bài 42): đóng gói ứng dụng thành image bất biến. Trong thế giới container, một phần vai trò của Config Management được thay bằng việc build image.
Tình huống thực tế
Tình huống 1 — Sàn TMĐT ở Hà Nội chuẩn hóa 40 web server bằng Ansible
Một công ty thương mại điện tử tại Hà Nội có 40 web server chạy Nginx + PHP-FPM. Trước đây đội DevOps cài đặt bằng tay và script bash rời rạc. Kết quả: có máy chạy PHP 8.1, có máy PHP 8.2; timeout của Nginx mỗi máy một khác. Mỗi lần đưa tính năng mới lên là một lần hồi hộp vì không chắc môi trường đồng nhất.
Họ chuyển sang Ansible. Toàn bộ mô tả server được đóng gói thành một role tên webserver, gồm: cài PHP 8.2 và Nginx 1.24, đẩy file nginx.conf từ template (với các biến như số worker tính theo số CPU), đảm bảo service php-fpm và nginx đang chạy. Họ định nghĩa inventory 40 máy, và chạy một lệnh duy nhất: ansible-playbook -i production site.yml.
Diễn giải: Lần chạy đầu tiên mất 12 phút cho cả 40 máy chạy song song. Ansible báo cáo có 6 máy bị "changed" (nghĩa là 6 máy trước đó lệch chuẩn, giờ đã được kéo về đúng). Từ đó, mỗi sáng thứ Hai họ chạy lại playbook như một bài kiểm tra sức khỏe — nếu có máy nào bị ai đó sửa tay giữa tuần, lần chạy tiếp theo sẽ tự động khôi phục về chuẩn.
Bài học: Với fleet vừa (vài chục tới vài trăm máy) và đội chưa quen công cụ phức tạp, Ansible là điểm khởi đầu lý tưởng vì agentless — chỉ cần SSH là chạy được, không phải dựng thêm hạ tầng master.
Tình huống 2 — Ngân hàng số ở Singapore chọn Puppet cho fleet 2.000 máy
Một ngân hàng số ở Singapore vận hành hơn 2.000 máy chủ, trong đó nhiều máy nằm trong vùng mạng cô lập vì yêu cầu tuân thủ (compliance). Họ cần đảm bảo mọi máy, mọi lúc đều tuân thủ chuẩn bảo mật: SSH phải tắt đăng nhập bằng mật khẩu, phải cài agent giám sát, phải có đúng cấu hình firewall.
Với quy mô này, mô hình push của Ansible bắt đầu đuối: đẩy SSH tới 2.000 máy tuần tự thì chậm, và không có gì đảm bảo một máy vừa reboot lúc 2 giờ sáng đã đúng chuẩn. Họ chọn Puppet. Mỗi máy chạy puppet agent kéo catalog về mỗi 30 phút. Nếu một quản trị viên lỡ tay bật lại SSH password login lúc 3 giờ chiều, thì chậm nhất 30 phút sau, Puppet agent tự phát hiện lệch và tắt lại — không cần con người can thiệp.
Diễn giải: Điểm quyết định ở đây là compliance liên tục và tự phục hồi. Đội security không muốn phụ thuộc vào việc "ai đó nhớ chạy playbook". Họ muốn hệ thống tự đảm bảo trạng thái đúng 24/7. Pull model của Puppet cho đúng điều đó, đổi lại là chi phí vận hành Puppet master và quản lý chứng chỉ agent.
Bài học: Không có tool "tốt nhất" tuyệt đối. Ở quy mô lớn và ràng buộc compliance chặt, mô hình pull/agent-based như Puppet thắng thế nhờ khả năng tự phục hồi liên tục.
Tình huống 3 — Startup SaaS ở Đà Nẵng và bài học "config như code"
Một startup SaaS ở Đà Nẵng với đội 4 người dùng Ansible nhưng lưu playbook rải rác trên máy cá nhân, không đưa vào Git. Một hôm, kỹ sư chính nghỉ phép, server staging cần cấu hình lại. Không ai tìm được phiên bản playbook mới nhất, cuối cùng dựng lại thủ công và... sai lệch so với production.
Sau sự cố, họ áp dụng nguyên tắc cấu hình là code: mọi playbook/role đưa vào Git, review qua Pull Request, và chạy qua CI/CD. Mỗi khi merge vào nhánh chính, pipeline tự chạy playbook lên staging trước, chạy --check (chế độ dry-run của Ansible) để xem trước thay đổi, rồi mới áp dụng production.
Diễn giải: Giá trị thật của Configuration Management không chỉ nằm ở công cụ, mà ở việc cấu hình trở thành tài sản có phiên bản, review được, tái tạo được. Ai cũng đọc được server "phải như thế nào" chỉ bằng cách đọc Git.
Bài học: Tool chỉ là một nửa. Nửa còn lại là quy trình: đưa mọi thứ vào version control và pipeline.
Hướng dẫn từng bước
Dưới đây là lộ trình dựng một cấu hình Ansible cơ bản để quản lý một fleet web server — cách nhanh nhất để bắt đầu vì không cần agent.
Bước 1 — Cài Ansible trên control node. Chỉ cần một máy có Ansible và SSH tới các máy đích:
sudo apt update && sudo apt install -y ansible
ansible --version
Bước 2 — Khai báo inventory (danh sách máy). Tạo file inventory.ini nhóm các máy theo vai trò:
[webservers]
web01 ansible_host=10.0.1.11
web02 ansible_host=10.0.1.12[webservers:vars]
ansible_user=deploy
Bước 3 — Viết playbook với desired state. Tạo site.yml mô tả trạng thái mong muốn, chú ý mỗi task đều idempotent:
- hosts: webservers
become: true
tasks:
- name: Cài Nginx
apt:
name: nginx
state: present
update_cache: true - name: Đẩy file cấu hình từ template
template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
- name: Đảm bảo Nginx đang chạy và tự khởi động
service:
name: nginx
state: started
enabled: true
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
Bước 4 — Dry-run trước khi áp dụng. Luôn xem trước thay đổi bằng chế độ check:
ansible-playbook -i inventory.ini site.yml --check --diff
Bước 5 — Áp dụng và đọc báo cáo. Chạy thật và đọc dòng tổng kết ok / changed / failed:
ansible-playbook -i inventory.ini site.yml
ok là task đã đúng trạng thái (không đổi), changed là task vừa được sửa để đúng chuẩn. Chạy lại lần hai, lý tưởng là changed=0 — bằng chứng cho tính idempotent.
Bước 6 — Đưa vào Git và CI/CD. Commit toàn bộ playbook, template, inventory vào repo. Cấu hình pipeline chạy --check khi mở PR, và áp dụng thật khi merge.
Lỗi thường gặp & mẹo
- Viết task không idempotent. Dùng module
command/shellđể chạy lệnh thô là cạm bẫy phổ biến nhất, vì Ansible không biết trạng thái trước đó nên lần nào cũng chạy lại. Mẹo: ưu tiên các module chuyên dụng (apt,template,lineinfile,service). Nếu buộc phải dùngshell, hãy thêmcreates:hoặcchanged_when:để nó biết khi nào cần chạy.
- Không kiểm tra dry-run trước khi áp production. Áp thẳng một thay đổi lớn lên toàn fleet có thể gây sự cố diện rộng. Luôn chạy
--check --diffvà cân nhắc--limitđể thử trên một máy trước.
- Nhầm vai trò giữa Terraform và Config Management. Đừng cố dùng Ansible để tạo VPC hay đừng cố nhét toàn bộ logic cài đặt package vào Terraform
provisioner. Ranh giới rõ ràng: Terraform tạo máy, Config Management cấu hình bên trong.
- Bỏ quên handler và trạng thái service. Sửa file config mà quên restart service thì thay đổi không có hiệu lực. Dùng
notify+handlersđể chỉ restart khi config thực sự đổi — tránh restart thừa gây gián đoạn.
- Hardcode giá trị thay vì dùng biến/template. Cùng một role nên chạy được cho cả staging và production nhờ biến khác nhau. Dùng template Jinja2 (
.j2) và group_vars thay vì copy-paste nhiều file.
- Mẹo chống drift: Với mô hình push như Ansible, đặt một cron/CI chạy playbook định kỳ (ví dụ mỗi đêm) để tự kéo các máy lệch chuẩn về đúng — mô phỏng hành vi tự phục hồi của mô hình pull.
- Mẹo bảo mật: Đừng để mật khẩu, API key trần trong playbook. Dùng Ansible Vault để mã hóa secret (chi tiết về quản lý secret sẽ được nói ở Bài 33).
Bài tập thực hành
- Phân biệt push/pull. Cho ba tình huống: (a) fleet 15 máy, đội 2 người mới học; (b) fleet 3.000 máy yêu cầu compliance liên tục; (c) cần máy mới tự cấu hình ngay khi khởi động. Với mỗi tình huống, chọn mô hình push hay pull và giải thích trong 2–3 câu.
- Chứng minh idempotency. Dựng một playbook Ansible cài Nginx và đẩy một file
index.html. Chạy hai lần liên tiếp. Ghi lại sốchangedở mỗi lần và giải thích vì sao lần hai phải làchanged=0.
- Sửa task không idempotent. Cho task sau, hãy viết lại cho idempotent:
- shell: echo "127.0.0.1 app.local" >> /etc/hosts
(Gợi ý: dùng module lineinfile.)- Thiết kế role tái sử dụng. Phác thảo cấu trúc một Ansible role
monitoring-agentcàinode-exporter, đẩy file config từ template, và đảm bảo service chạy — sao cho dùng được cho cả môi trường staging và production chỉ bằng cách đổi biến.
Tóm tắt
- Configuration Management giải quyết configuration drift và snowflake server bằng cách quản lý desired state của server: package nào cài, config chứa gì, service nào chạy.
- Idempotency là nguyên lý cốt lõi: chạy bao nhiêu lần cũng cho cùng kết quả, cho phép chạy lặp lại để chống drift một cách an toàn.
- Hai triết lý chính: push/agentless (Ansible — đơn giản, hợp fleet vừa) và pull/agent-based (Puppet, Chef — tự phục hồi, hợp fleet lớn và compliance chặt).
- Config Management bổ sung cho Terraform: Terraform tạo vỏ (hạ tầng), Config Management lo ruột (bên trong máy).
- Giá trị thật đến khi cấu hình trở thành code có phiên bản: đưa vào Git, review qua PR, chạy qua CI/CD với dry-run trước khi áp production.