Terraform
← Tổng quan series

Kết hợp Ansible với Terraform

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.

Instructor walkthrough

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.

01Chuẩn bịkiểm tra account, region, provider và chi phí
02Đọc planđối chiếu resource address, diff và dependency
03Thực hànhapply mutation nhỏ trong sandbox, lưu evidence
04Khôi phụcre-run, failure drill, destroy hoặc rollback
Command reference · baseline
terraform fmt
terraform init
terraform validate
terraform plan
terraform apply
terraform plan -destroy
terraform destroy

Hai 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 apply trướ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:

  1. Để 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/0 như remediation mặc định.
  2. 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.
  3. 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.
  4. Đổ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.