Bài giảng: hiểu luồng trước khi chạy lệnh
Bài toán: Phân biệt provisioning và configuration management, dùng provisioner đúng chỗ và chuyển sang Ansible khi máy đã tồn tại.
terraform fmt
terraform init
terraform validate
terraform plan
terraform apply
terraform plan -destroy
terraform destroyHai công cụ, hai trách nhiệm
Terraform biết tạo VPC, subnet, security group, EC2 và load balancer. Ansible mạnh ở việc vào máy, cài package, render config, restart service và chạy playbook lặp lại. Đừng biến Terraform thành một shell script khổng lồ.
Provisioner: dùng được nhưng có mùi cảnh báo
resource "aws_instance" "app" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
provisioner "remote-exec" {
inline = [
"sudo dnf install -y docker",
"sudo systemctl enable --now docker",
]
connection {
type = "ssh"
user = "ec2-user"
host = self.public_ip
private_key = file(var.ssh_private_key)
}
}
}
Provisioner chạy từ máy Terraform runner, phụ thuộc SSH/network và khó retry chính xác. Nếu command chạy xong nhưng Terraform mất kết nối, lần sau có thể không biết phần nào đã hoàn thành. Tốt hơn là bake AMI, dùng cloud-init/user data cho bootstrap ngắn, hoặc gọi Ansible từ pipeline sau khi Terraform output IP/instance ID.
Luồng Terraform -> Ansible
terraform apply -auto-approve
terraform output -raw app_ip > app_ip.txt
ansible-playbook -i "$(cat app_ip.txt)," playbook.yml
Playbook ví dụ:
- hosts: all
become: true
tasks:
- name: Cài Docker
ansible.builtin.package:
name: docker
state: present
- name: Bật Docker
ansible.builtin.service:
name: docker
state: started
enabled: true
Tốt hơn nữa, tạo inventory động từ AWS hoặc dùng SSM để không mở SSH public. Secret nên nằm trong Ansible Vault/secret manager, không trong command line dễ bị lộ ở log.
Creation-time và destroy-time
Provisioner mặc định chạy lúc resource được tạo. when = destroy chạy lúc destroy, nhưng destroy-time provisioner có giới hạn tham chiếu và dễ thất bại nếu network đã bị xóa trước. Không dùng nó để backup database quan trọng; backup phải có quy trình độc lập.
Quy tắc thực dụng
- Terraform tạo hạ tầng.
- Image/user data chuẩn bị baseline.
- Ansible cấu hình state của OS/app.
- CI orchestration gọi theo thứ tự và lưu log.
- Nếu provisioner thất bại, đọc kỹ
terraform applytrước khi chạy lại mù quáng.
Idempotency là bài kiểm tra bắt buộc
Chạy playbook hai lần không nên cài lại hoặc phá cấu hình mỗi lần. Trước khi nối vào CI, hãy kiểm tra:
ansible-playbook -i inventory playbook.yml --check --diff
ansible-playbook -i inventory playbook.yml
ansible-playbook -i inventory playbook.yml
Lần chạy thứ hai nên có ít thay đổi. Nếu app restart liên tục hoặc file config bị rewrite dù nội dung không đổi, deployment sẽ gây downtime và khó debug. Terraform cũng cần tính idempotent: plan lần hai sau khi apply nên gần như no-op.
Trong môi trường private, ưu tiên SSM/Ansible connection plugin hoặc runner đặt trong VPC. Mở SSH 0.0.0.0/0 chỉ để lab là một rủi ro cần đóng ngay sau khi thử.
Expected handoff và failure drills
Expected state của lab là Terraform plan/apply tạo hạ tầng và output machine-readable (instance ID/private address/SSM target), Ansible nhận đúng inventory/connection, playbook converge lần đầu và gần như no-op lần hai. Terraform state không sở hữu file/service OS; Ansible không tự tạo VPC/security group ngoài contract.
Thực hiện các drill:
- Để SSH/private route/SSM unavailable, phân loại Terraform resource health với Ansible connection failure; không mở SSH
0.0.0.0/0như remediation mặc định. - Làm task Ansible không idempotent hoặc secret xuất hiện trong command/log, chạy
--check --diff, sửa thành module/Vault/secret manager và redact output. - Cho provisioner fail giữa chừng, đọc state/taint/apply result và quyết định retry/recovery; không giả định command đã chạy hoàn toàn hoặc thêm provisioner lớn để che lỗi.
- Đổi Terraform output/inventory key, chạy pipeline contract check và xác nhận Ansible target không bị nhầm môi trường; cleanup EC2/EBS/IP theo
plan-destroy/owner.
Quality gate
Pass khi ownership Terraform/Ansible rõ, handoff output/inventory deterministic, connection qua SSM/private path được ưu tiên, playbook idempotency được chứng minh bằng hai run/check mode, secret không lộ, provisioner có exception/rollback rõ và destroy không phụ thuộc backup provisioner. Zero-downtime cần health/rolling strategy riêng, không mặc định từ Ansible task.