SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-22

Ansible hay Terraform: Bạn cần công cụ nào?

Terraform tạo VPS, Ansible cấu hình bên trong. Xem ranh giới thực tế, lệnh bàn giao, vì sao provisioner dễ lỗi và khi nào chỉ cần Ansible.

Ansible và Terraform trong một câu

Ansible và Terraform không phải là lựa chọn giữa hai công cụ làm cùng một việc. Terraform khai báo hạ tầng nào cần tồn tại: server, disk, network, bản ghi DNS. Ansible khai báo trạng thái cần có bên trong một máy đã tồn tại: package, user, file cấu hình, service đang chạy. Terraform tạo VPS. Ansible biến VPS đó thành web server.

Cả hai đều mang tính khai báo và đều được gọi là infrastructure as code (IaC). Điểm khác biệt thực sự là cách chúng ghi nhớ trạng thái. Terraform ghi một state file ánh xạ từng resource trong code với object thực tế mà nó đã tạo thông qua API. Vì vậy, Terraform biết việc xóa 5 dòng có nghĩa là phải hủy 1 server. Ansible không ghi nhớ gì giữa các lần chạy. Nó kết nối qua SSH, kiểm tra máy và chỉ thay đổi những gì chưa khớp với playbook.

Chỉ một khác biệt này đã giải thích các phần còn lại của hướng dẫn, bao gồm lý do việc gộp hai nhiệm vụ này vào một công cụ thường dẫn đến lỗi.

Terraform thực sự làm gì

Terraform giao tiếp với một API thông qua provider plugin. Trang registry của provider định nghĩa các loại resource bạn có thể khai báo. Vì vậy, server trên host này và server trên host khác là các resource khác nhau, với các argument khác nhau.

resource "cloud_server" "web" {
  name  = "web1"
  image = "ubuntu-24.04"
  type  = "small"
}

output "web_ip" {
  value = cloud_server.web.ipv4_address
}

Thay cloud_server bằng loại resource được provider của bạn document. Block output là phần quan trọng trong guide này, vì nó quyết định địa chỉ rời khỏi Terraform như thế nào.

terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplan

terraform init tải provider xuống và ghi lock file. terraform plan in ra khác biệt giữa code của bạn và state file, kết thúc bằng một dòng như Plan: 1 to add, 0 to change, 0 to destroy. Hãy đọc dòng này mỗi lần. Một số argument không thể thay đổi tại chỗ. Plan sẽ cho biết điều đó bằng # forces replacement bên cạnh attribute, sau đó là 1 to add, 0 to change, 1 to destroy. Khi apply plan đó, Terraform sẽ xóa server và tạo một server mới, rỗng. Đây là cách người dùng làm mất dữ liệu mà họ tưởng là an toàn.

Lưu plan vào file rồi apply file đó, thay vì chạy terraform apply trực tiếp, bảo đảm thứ bạn đã review chính là thứ được thực thi. Giữa hai command, người khác có thể đã thay đổi infrastructure.

terraform.tfstate là bộ nhớ của Terraform. Nếu mất nó, Terraform không còn biết những server đó thuộc về bạn. Lần apply tiếp theo sẽ cố tạo các bản sao. Hãy lưu nó trong remote backend ngay khi có nhiều hơn một người chạy command, vì 2 người apply cùng lúc sẽ tạo ra lỗi này:

Error: Error acquiring the state lock

OpenTofu là một fork của Terraform, sử dụng cùng command và cùng file format. Tính đến tháng 7 năm 2026, mọi thứ trong guide này đều hoạt động nếu bạn nhập tofu thay cho terraform.

Ansible thực sự làm gì

Ansible không cần agent và cũng không cần API. Nó mở kết nối SSH, sao chép một Python module nhỏ lên máy đích, chạy module đó rồi xóa nó. Bất cứ hệ thống nào bạn truy cập được bằng SSH và mật khẩu sudo đều có thể được Ansible cấu hình.

- name: Base web server
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Ensure nginx is running at boot
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

Module ping kiểm tra SSH, Python và sudo trước khi bạn bắt đầu debug playbook. Kết quả bình thường là web1 | SUCCESS => {"ping": "pong"}. Lệnh chạy với --check --diff là thứ gần với plan nhất trong Ansible: nó báo những gì sẽ thay đổi nhưng không thực hiện thay đổi. Tuy nhiên, các task phụ thuộc vào task trước đó có thể báo sai trong check mode, vì thay đổi của task trước chưa thực sự được thực hiện.

Mỗi lần chạy kết thúc bằng một recap như ok=6 changed=2 unreachable=0 failed=0. Hãy chạy cùng playbook 2 lần. Lần chạy thứ 2 phải báo changed=0. Một task báo changed ở mọi lần chạy là task không idempotent. Thông thường đó là task command hoặc shell, trong khi task này đáng ra phải dùng module chuyên dụng. Nếu đây là phần mới với bạn, hãy bắt đầu bằng playbook Ansible đầu tiên trên một VPS duy nhất rồi mở rộng từ đó.

Khi hai công cụ này chồng lấn chức năng và khi chúng xung đột

Terraform có thể chạy lệnh trên server mới bằng provisioner remote-exec. Tài liệu chính thức của HashiCorp gọi provisioner là phương án cuối cùng. Có lý do chính đáng cho việc này.

Provisioner chỉ chạy khi resource được tạo. Nếu sửa script, server hiện có vẫn không thay đổi, vì theo cách nhìn của Terraform, resource đã khớp với code. Các bước của provisioner không bao giờ xuất hiện trong terraform plan, nên quá trình review không thể hiện chúng. Nếu script fail, Terraform đánh dấu resource là tainted. Lần apply tiếp theo sẽ xóa và tạo lại một server vốn có thể vẫn hoạt động bình thường.

Lỗi cũng xảy ra không đúng thời điểm. Provider báo server đã được tạo ngay khi API trả về trạng thái đó, trong khi hệ điều hành vẫn đang boot và sshd chưa listening.

Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refused

Ansible có một xu hướng ngược lại. Các cloud module có thể tạo server, và cách này phù hợp với một vài máy. Nhưng bạn sẽ mất dependency graph và state file. Ansible vẫn sẽ tạo resource, nhưng nếu xóa task khỏi playbook thì resource đó vẫn tiếp tục chạy và phát sinh chi phí, vì không có gì ghi nhận rằng resource đó thuộc quyền quản lý của bạn.

Quy tắc rút ra là: để Terraform quản lý các object được tạo và xóa thông qua API, còn Ansible quản lý mọi thứ bên trong một hệ điều hành đã boot.

Bàn giao, đã thực hiện

Bàn giao là một ranh giới, không phải một tích hợp. Terraform hoàn tất, xuất một địa chỉ rồi dừng. Ansible bắt đầu từ địa chỉ đó.

terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml

terraform output -raw in ra một giá trị không có dấu ngoặc kép và không có lớp bọc JSON. Đây là định dạng phù hợp khi dùng trong phép thay thế shell. Với nhiều server, hãy dùng terraform output -json rồi tạo inventory từ kết quả đó, vì -raw chỉ xử lý một chuỗi, số hoặc giá trị boolean.

Bước ping nằm giữa hai công cụ này nên được giữ lại. Nó phân biệt lỗi “Terraform cung cấp sai địa chỉ” với lỗi “playbook của tôi có lỗi”. Hai vấn đề này trông giống hệt nhau khi playbook là thứ đầu tiên truy cập vào server mới.

Đọc Terraform state làm Ansible inventory

Nếu không muốn tự viết file inventory, collection cloud.terraform có thể đọc trực tiếp state.

ansible-galaxy collection install cloud.terraform

Tạo file terraform.yml bên cạnh playbook:

plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infra
ansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml

Có 2 điểm cần biết trước khi sử dụng. Plugin chạy terraform show với project_path, vì vậy thư mục đó phải được khởi tạo trước. Nếu chưa, plugin sẽ lỗi. Plugin cũng không tự tạo host từ các resource server của bạn. Nó đọc các resource ansible_host và ansible_group, được khai báo trong mã Terraform bằng Ansible provider. Sẽ không có gì xuất hiện trong ansible-inventory --graph cho đến khi bạn thêm chúng.

File inventory được tạo ở dạng plain text dễ debug hơn và hoạt động với mọi provider. Plugin trở nên hữu ích khi inventory đã có hơn một vài máy chủ và việc chỉnh sửa thủ công bắt đầu gây ra lỗi gõ sai. Đây cũng là lúc quản lý nhiều máy chủ Linux từ một máy control trở thành một quy trình thực tế thay vì chỉ là thói quen.

Bạn có thực sự cần Terraform không?

Hầu hết người đọc bài này chưa cần Terraform, ít nhất là chưa cần ngay. Terraform đáng dùng khi việc tạo và hủy infrastructure trở thành một công việc lặp lại. Nếu bạn tạo một VPS qua control panel và dự định giữ nó trong hai năm, Terraform chỉ mô tả một việc xảy ra một lần, đồng thời thêm một state file mà bạn không được để mất.

Hãy dùng Terraform khi bạn thường xuyên rebuild environment, khi staging phải khớp chính xác với production, khi nhiều người cùng thay đổi infrastructure và bạn muốn có một plan có thể review trước khi bất cứ thứ gì bị xóa, hoặc khi phạm vi quản lý vượt ra ngoài server, bao gồm DNS record, load balancer và firewall rule nằm trong provider API.

Chỉ dùng Ansible khi server có vòng đời dài và số lượng ít, đồng thời câu hỏi hằng ngày là “server này đã được cấu hình đúng chưa” thay vì “server này có tồn tại không”. Một playbook duy nhất để harden server mới bao quát cùng phạm vi với 10 phút đầu tiên trên một VPS mới, đồng thời có lợi thế là chạy theo cùng một cách trên server tiếp theo.

Thứ tự học cũng xuất phát từ đó. Ansible đem lại hiệu quả ngay từ server đầu tiên bạn sở hữu. Terraform đem lại hiệu quả từ environment thứ ba bạn rebuild.

Lỗi ở bước bàn giao

Máy chủ chưa sẵn sàng. Terraform chạy thành công, nhưng Ansible fail ngay lập tức.

fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}

API trả về địa chỉ trước khi sshd bắt đầu listen. Hãy chờ cổng thay vì thêm thời gian sleep cố định. Ansible có ansible.builtin.wait_for_connection cho đúng trường hợp này; chạy nó dưới dạng task đầu tiên của play. Khi cùng một playbook target một group thay vì một máy mới duy nhất, hãy quyết định trước phải xử lý thế nào khi một host vẫn không thể truy cập, vì Ansible sẽ loại host đó khỏi phần còn lại của lần chạy và dòng recap là nơi duy nhất cho bạn biết điều này.

Host key đã thay đổi. Bạn đã destroy rồi tạo lại máy chủ, và máy mới trả lời trên cùng địa chỉ bằng một key mới.

Host key verification failed.

Xóa entry cũ bằng ssh-keygen -R 203.0.113.10. Việc này xảy ra thường xuyên khi Terraform thực hiện việc rebuild, nên đây là một lý do chính đáng để hạn chế rebuild trên các máy đang lưu dữ liệu.

Sudo fail. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} có nghĩa là become: true cần password trên host đó. Bạn có thể cấu hình sudo không cần password cho deploy user, hoặc truyền --ask-become-pass.

Terraform muốn destroy thứ bạn không hề chỉnh sửa. Plan hiển thị những thay đổi bạn chưa từng viết trong code. Điều đó có nghĩa là infrastructure thực tế đã drift khỏi code, thường vì ai đó thay đổi setting trong web panel của provider. Chạy terraform plan -refresh-only để xem riêng phần khác biệt đó, rồi quyết định code hay resource đang chạy mới là bên sai. Không bao giờ apply một plan có thao tác phá hủy mà bạn không thể giải thích từng dòng.

Ansible báo changed ở mọi lần chạy. Một task shell không có guard creates hoặc when sẽ luôn chạy. Đây không chỉ là vấn đề hiển thị, vì bạn sẽ không còn dùng được changed=0 làm tín hiệu cho biết server đã ở đúng trạng thái mong muốn.

FAQ

Terraform có thể thay thế Ansible không?

Không thể thay thế việc cấu hình bên trong server. Terraform có thể gọi script bằng provisioner remote-exec, nhưng các script này chỉ chạy khi tạo resource, không bao giờ xuất hiện trong terraform plan, và sẽ đánh dấu resource là cần tạo lại nếu bị lỗi. Khi đó, lần apply tiếp theo sẽ lên lịch xóa rồi tạo lại resource. Terraform không có cơ chế tương đương module kiểm tra nginx đã được cài hay chưa và không làm gì nếu đã cài. Hãy dùng Terraform để tạo máy, sau đó chuyển việc cấu hình cho Ansible.

Ansible có thể thay thế Terraform không?

Có, nếu bạn chỉ quản lý một số ít server chạy lâu dài. Ansible có các cloud module để tạo server. Nếu bạn đặt mua 2 VPS rồi giữ chúng, như vậy là đủ. Nhưng bạn sẽ mất state file và dependency graph. Nếu xóa một task khỏi playbook, resource vẫn tiếp tục chạy và phát sinh chi phí, vì Ansible không ghi nhận rằng nó đã tạo resource đó. Terraform sẽ lập kế hoạch xóa resource.

Tôi nên học công cụ nào trước?

Học Ansible trước nếu hiện bạn đã quản lý server. Công cụ này phát huy hiệu quả ngay từ máy đầu tiên, chỉ cần SSH, và kỹ năng đó vẫn áp dụng được cho server bạn tự đặt mua. Terraform phát huy hiệu quả về sau, khi bạn thường xuyên dựng lại environment hoặc quản lý các resource của provider ngoài server, chẳng hạn DNS record và firewall rule.

Làm cách nào truyền IP của server mới từ Terraform sang Ansible?

Khai báo output trong code Terraform, sau đó đọc giá trị này sau khi apply. terraform output -raw web_ip in ra giá trị thuần để dùng trong shell substitution, còn terraform output -json trả về toàn bộ output cùng lúc khi có nhiều host. Ghi giá trị đó vào inventory file, hoặc cài collection cloud.terraform rồi trỏ ansible-inventory -i terraform.yml --graph đến thư mục project.

Vì sao playbook của tôi fail ngay sau khi Terraform hoàn tất?

Provider báo server đã được tạo ngay khi API của provider trả về trạng thái đó, trong khi operating system vẫn đang boot. Vì vậy, SSH bị từ chối trong vài giây đầu. Lỗi này là UNREACHABLE! với Connection refused. Đặt ansible.builtin.wait_for_connection làm task đầu tiên trong play thay vì đoán một khoảng thời gian sleep, vì thời gian boot thay đổi theo image và plan.