- Tại sao dùng riêng Terraform hoặc riêng Ansible đều dẫn đến nợ kỹ thuật nghiêm trọng ở quy mô Production?
- 1. Kiến trúc Phân Tầng Vòng Đời Hạ Tầng: Terraform (Day-0/Day-1) Kết Hợp Ansible (Day-1/Day-2)
- Bảng phân định trách nhiệm giữa Terraform và Ansible
- 2. Thiết kế Terraform Chuẩn Production: Remote State Locking và Cấu trúc Module Bất Biến
- Định nghĩa tài nguyên Compute kèm chuẩn hóa Nhãn (Tags) cho Ansible Dynamic Inventory
- 3. Liên kết Tự động Terraform sang Ansible bằng Collection cloud.terraform
- 4. Thiết kế Ansible Role Lũy đẳng (Idempotent) với Kiểm tra Cú pháp validate
- 5. Tự động hóa Quy trình Kiểm duyệt và Thực thi IaC qua CI/CD Pipeline
- 6. Chiến lược Phát hiện Trôi dạt Cấu hình (Drift Detection) Định kỳ
- Tài liệu tham khảo & Nguồn chuẩn (Primary References)
Tại sao dùng riêng Terraform hoặc riêng Ansible đều dẫn đến nợ kỹ thuật nghiêm trọng ở quy mô Production?
Trong vận hành hạ tầng hiện đại, một trong những sai lầm kiến trúc phổ biến nhất là cố gắng ép một công cụ duy nhất đảm nhiệm toàn bộ vòng đời hệ thống: hoặc dùng local-exec / remote-exec provisioners bên trong Terraform để chạy kịch bản cài đặt phần mềm, hoặc dùng Ansible để gọi API tạo VPC, Subnet, Load Balancer và máy chủ ảo trên đám mây.
Cả hai cách tiếp cận cực đoan này đều phá vỡ tính bất biến (Immutability) và tính lũy đẳng (Idempotency) của Infrastructure as Code (IaC):
- Khi lạm dụng
remote-execvàuser_datakhổng lồ trong Terraform: Terraform là công cụ khai báo trạng thái hạ tầng (Declarative Infrastructure Provisioning) dựa trên đồ thị phụ thuộc (DAG) và tệp trạng tháiterraform.tfstate. Terraform không theo dõi trạng thái cấu hình bên trong hệ điều hành sau khi máy chủ đã khởi tạo xong. Nếu bạn sửa một dòng cấu hình/etc/sysctl.conftronguser_data, Terraform buộc phải hủy (Destroy) và tạo lại (Recreate) toàn bộ máy chủ ảo hoặc không thể thực thi cập nhật tại chỗ (In-place Day-2 configuration). - Khi dùng Ansible để cấp phát tài nguyên Cloud (VPC, RDS, EKS, Bare Metal): Ansible hoạt động theo mô hình thực thi tuần tự không lưu trạng thái trung tâm (Stateless Procedural/Declarative Hybrid). Khi xóa một máy chủ khỏi danh sách biến của Ansible, Ansible sẽ không tự động biết phải thu hồi (Deprovision) máy chủ đó trên AWS hay Proxmox nếu bạn không viết riêng một Playbook với
state: absent.
Giải pháp chuẩn Production được áp dụng tại các hệ thống quy mô lớn là phân tách ranh giới trách nhiệm rõ ràng theo vòng đời Day-0, Day-1 và Day-2, trong đó Terraform quản lý tầng hạ tầng nền tảng (Cloud/Virtualization API, mạng L3/L4, ổ đĩa, bản ghi DNS) và chuyển giao siêu dữ liệu tự động sang Ansible thông qua Dynamic Inventory để cấu hình nhân Linux, bảo mật Zero Trust và khởi chạy các cụm Kubernetes High Availability.
1. Kiến trúc Phân Tầng Vòng Đời Hạ Tầng: Terraform (Day-0/Day-1) Kết Hợp Ansible (Day-1/Day-2)
flowchart TB
subgraph GitOpsCI["Tầng Quản Trị Mã Nguồn & Kiểm Tra Bảo Mật (CI/CD)"]
Git["Git Repository (Monorepo IaC)"]
Lint["Static Analysis Gate<br/>tflint + trivy config + ansible-lint"]
end
subgraph TerraformLayer["Tầng Cấp Phát Hạ Tầng — Terraform (Declarative DAG)"]
TFState["Remote State Backend<br/>S3 Compatible + State Locking"]
TFExec["terraform plan & apply"]
Infra["Tài nguyên Hạ tầng: VPC, Subnet, Security Group,<br/>Compute Nodes, Load Balancer, Tags"]
end
subgraph Handoff["Cơ Chế Chuyển Giao Không Chạm (Zero-Touch Handoff)"]
DynInv["Ansible Dynamic Inventory Plugin<br/>(aws_ec2 / cloud.terraform.terraform_provider)"]
end
subgraph AnsibleLayer["Tầng Cấu Hình Hệ Điều Hành — Ansible (Idempotent Roles)"]
Playbook["site.yml Playbook"]
RoleOS["Role 1: Linux Kernel & sysctl Hardening"]
RoleSec["Role 2: SSH CA, UFW/nftables & Auditd"]
RoleRuntime["Role 3: containerd / Docker / Node Exporter"]
end
Git --> Lint
Lint --> TFExec
TFExec <--> TFState
TFExec --> Infra
Infra --> DynInv
TFState --> DynInv
DynInv --> Playbook
Playbook --> RoleOS
Playbook --> RoleSec
Playbook --> RoleRuntime
Bảng phân định trách nhiệm giữa Terraform và Ansible
| Tiêu chí kỹ thuật | Terraform (Infrastructure Provisioning) | Ansible (Configuration Management) |
|---|---|---|
| Mô hình quản lý trạng thái | Lưu trạng thái toàn cục trong terraform.tfstate (có khóa đồng thời State Locking). |
Không lưu tệp trạng thái; truy vấn trực tiếp trạng thái thực tế của máy chủ qua module setup (Facts) mỗi lần chạy. |
| Đơn vị trừu tượng chính | Tài nguyên API hạ tầng: VPC, Route Table, VM, Volume, Firewall, IAM, DNS, Kubernetes Cluster. | Cấu hình bên trong hệ điều hành: Gói phần mềm (apt/dnf), tệp cấu hình Jinja2, dịch vụ systemd, tham số sysctl, người dùng POSIX. |
| Hành vi khi thay đổi cấu hình | Tính toán chênh lệch đồ thị (Diff) giữa mã .tf và tfstate; cập nhật qua API hoặc thay thế (-/+) tài nguyên. |
Thực thi tuần tự các Task lũy đẳng (Idempotent); chỉ kích hoạt handlers (ví dụ reload nginx) khi tệp cấu hình thực sự thay đổi (changed: true). |
| Giai đoạn vận hành tối ưu | Day-0 & Day-1: Khởi tạo kiến trúc mạng, máy chủ, lưu trữ và mở rộng quy mô ngang (Auto Scaling). | Day-1 & Day-2: Chuẩn hóa hệ điều hành sau khi máy chủ khởi động, vá lỗi bảo mật định kỳ, xoay vòng chứng chỉ. |
2. Thiết kế Terraform Chuẩn Production: Remote State Locking và Cấu trúc Module Bất Biến
Sai lầm nguy hiểm nhất khi làm việc nhóm với Terraform là lưu tệp terraform.tfstate trên máy cá nhân hoặc đẩy trực tiếp lên Git (gây lộ mật khẩu/token dưới dạng văn bản thuần và xung đột ghi đè khi hai kỹ sư cùng chạy terraform apply).
# File: environments/production/backend.tf
terraform {
required_version = ">= 1.8.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.60"
}
}
backend "s3" {
bucket = "infoteck-prod-tfstate-ap-southeast-1"
key = "infrastructure/compute-cluster/terraform.tfstate"
region = "ap-southeast-1"
encrypt = true
kms_key_id = "alias/infoteck-tfstate-kms"
dynamodb_table = "infoteck-prod-tfstate-locks"
}
}
Định nghĩa tài nguyên Compute kèm chuẩn hóa Nhãn (Tags) cho Ansible Dynamic Inventory
Thay vì dùng local-exec để ghi địa chỉ IP tĩnh ra tệp văn bản, phương pháp chuẩn kiến trúc là gắn các thẻ siêu dữ liệu (tags) nhất quán lên từng máy chủ trong Terraform, đồng thời xuất thông tin định danh qua outputs.tf để plugin chính thức cloud.terraform.terraform_provider của Ansible đọc trực tiếp từ Remote State:
# File: environments/production/main.tf
variable "environment" {
type = string
default = "production"
}
variable "node_count" {
type = number
default = 3
}
resource "aws_instance" "app_worker" {
count = var.node_count
ami = "ami-0df7a207adb9748c7" # Ubuntu 24.04 LTS x86_64
instance_type = "c6i.xlarge"
subnet_id = var.private_subnet_ids[count.index % length(var.private_subnet_ids)]
vpc_security_group_ids = [aws_security_group.worker_sg.id]
iam_instance_profile = aws_iam_instance_profile.ssm_node_profile.name
root_block_device {
volume_type = "gp3"
volume_size = 80
iops = 3000
throughput = 250
encrypted = true
delete_on_termination = true
}
metadata_options {
http_endpoint = "enabled"
http_tokens = "required" # Bắt buộc IMDSv2 chống SSRF
http_put_response_hop_limit = 2
}
tags = {
Name = "infoteck-${var.environment}-worker-0${count.index + 1}"
Environment = var.environment
Role = "container_worker"
ManagedBy = "terraform"
}
}
output "worker_private_ips" {
description = "Danh sách địa chỉ IP nội bộ của các máy chủ Worker"
value = aws_instance.app_worker[*].private_ip
}
3. Liên kết Tự động Terraform sang Ansible bằng Collection cloud.terraform
Từ phiên bản Ansible Collection cloud.terraform chính thức (do HashiCorp và Red Hat đồng phát triển), Ansible có thể đọc trực tiếp tệp trạng thái từ S3 Backend của Terraform thông qua tài nguyên ansible_host và ansible_group mà không cần viết script chuyển đổi thủ công:
# File: environments/production/ansible_inventory.tf
resource "ansible_group" "container_workers" {
name = "container_workers"
children = []
variables = {
ansible_user = "ubuntu"
ansible_ssh_common_args = "-o StrictHostKeyChecking=accept-new"
node_environment = var.environment
node_exporter_listen_address = "0.0.0.0:9100"
}
}
resource "ansible_host" "worker_nodes" {
count = var.node_count
name = aws_instance.app_worker[count.index].private_ip
groups = [ansible_group.container_workers.name]
variables = {
hostname_fqdn = "worker-0${count.index + 1}.prod.infoteck.internal"
node_index = count.index + 1
}
}
Tệp cấu hình Dynamic Inventory phía Ansible chỉ cần khai báo plugin cloud.terraform.terraform_provider:
# File: ansible/inventories/production/terraform_aws.yml
plugin: cloud.terraform.terraform_provider
project_path: ../../../environments/production
state_file: "" # Tự động đọc từ Remote Backend đã cấu hình trong backend.tf
4. Thiết kế Ansible Role Lũy đẳng (Idempotent) với Kiểm tra Cú pháp validate
Một Playbook Ansible đạt chuẩn Production bắt buộc phải đảm bảo tính Lũy đẳng (Idempotency): chạy lần thứ nhất trả về changed > 0, nhưng chạy lại lần thứ hai ngay lập tức trên cùng hệ thống phải trả về changed=0 và failed=0. Đặc biệt, khi cập nhật các tệp cấu hình nhạy cảm như /etc/ssh/sshd_config hoặc /etc/nginx/nginx.conf, phải luôn sử dụng tham số validate để kiểm tra cú pháp trước khi ghi đè tệp thật và kích hoạt handlers:
# File: ansible/roles/linux_baseline/tasks/main.yml
---
- name: "Thiết lập định danh máy chủ chuẩn FQDN"
ansible.builtin.hostname:
name: "{{ hostname_fqdn }}"
tags: ["baseline", "hostname"]
- name: "Tối ưu hóa tham số mạng và bộ nhớ nhân Linux cho máy chủ tải cao"
ansible.posix.sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
sysctl_file: /etc/sysctl.d/90-infoteck-production.conf
state: present
reload: true
loop:
- { key: "net.core.somaxconn", value: "65535" }
- { key: "net.ipv4.tcp_max_syn_backlog", value: "65535" }
- { key: "net.ipv4.ip_local_port_range", value: "10240 65535" }
- { key: "vm.swappiness", value: "10" }
- { key: "fs.file-max", value: "2097152" }
tags: ["baseline", "sysctl"]
- name: "Chuẩn hóa cấu hình bảo mật OpenSSH Daemon kèm kiểm tra cú pháp"
ansible.builtin.template:
src: sshd_config.j2
dest: /etc/ssh/sshd_config
owner: root
group: root
mode: "0600"
validate: "/usr/sbin/sshd -t -f %s"
notify: "Reload OpenSSH Server"
tags: ["baseline", "security", "ssh"]
# File: ansible/roles/linux_baseline/handlers/main.yml
---
- name: "Reload OpenSSH Server"
ansible.builtin.systemd:
name: ssh
state: reloaded
daemon_reload: true
Cách thiết kế này kết hợp chặt chẽ với các nguyên lý tinh chỉnh nhân hệ điều hành đã trình bày trong bài Tuning Linux Kernel cho máy chủ phục vụ hàng triệu kết nối đồng thời và quản lý bí mật tập trung bằng HashiCorp Vault.
5. Tự động hóa Quy trình Kiểm duyệt và Thực thi IaC qua CI/CD Pipeline
sequenceDiagram
autonumber
participant Eng as Kỹ sư DevOps (PR)
participant CI as GitHub Actions CI/CD
participant Sec as Trivy & ansible-lint
participant Lock as DynamoDB State Lock
participant Cloud as Hạ tầng Đám mây / Bare Metal
Eng->>CI: Tạo Pull Request thay đổi mã .tf và Ansible Role
CI->>Sec: Chạy terraform fmt/validate, trivy config & ansible-lint
Sec-->>CI: PASS (0 cấu hình sai lệch bảo mật, 0 lỗi cú pháp)
CI->>Lock: Yêu cầu khóa đọc trạng thái (Acquire Lock)
CI->>Cloud: Chạy terraform plan -out=tfplan.binary
CI-->>Eng: Đăng kết quả Plan lên bình luận Pull Request để phê duyệt
Eng->>CI: Merge PR vào nhánh main (Gate phê duyệt)
CI->>Lock: Khóa ghi trạng thái độc quyền
CI->>Cloud: Thực thi terraform apply tfplan.binary
CI->>Cloud: Thực thi ansible-playbook -i terraform_aws.yml site.yml --diff
CI->>Lock: Giải phóng khóa trạng thái (Release Lock)
Dưới đây là quy trình kiểm tra tĩnh và thực thi an toàn kết hợp CI/CD GitHub Actions giúp ngăn chặn hoàn toàn thao tác chạy lệnh thủ công từ máy trạm cá nhân:
#!/usr/bin/env bash
# File: scripts/ci-verify-and-deploy-iac.sh
# Mô tả: Quy trình kiểm tra tĩnh bảo mật IaC và triển khai hai pha Terraform -> Ansible
set -euo pipefail
TF_DIR="environments/production"
ANSIBLE_DIR="ansible"
echo "=== PHA 1: Kiểm tra định dạng, cú pháp và bảo mật Terraform ==="
terraform -chdir="${TF_DIR}" fmt -check -recursive
terraform -chdir="${TF_DIR}" init -input=false
terraform -chdir="${TF_DIR}" validate
trivy config --severity HIGH,CRITICAL --exit-code 1 "${TF_DIR}"
echo "=== PHA 2: Lập kế hoạch và áp dụng thay đổi hạ tầng ==="
terraform -chdir="${TF_DIR}" plan -input=false -out=tfplan.binary
terraform -chdir="${TF_DIR}" apply -input=false tfplan.binary
echo "=== PHA 3: Kiểm tra chuẩn Ansible và cấu hình hệ điều hành ==="
ansible-lint "${ANSIBLE_DIR}/site.yml"
ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook
-i "${ANSIBLE_DIR}/inventories/production/terraform_aws.yml"
"${ANSIBLE_DIR}/site.yml"
--diff
6. Chiến lược Phát hiện Trôi dạt Cấu hình (Drift Detection) Định kỳ
Ngay cả khi đã áp dụng IaC, cấu hình thực tế trên Production vẫn có nguy cơ bị thay đổi ngoài luồng (Configuration Drift) khi kỹ sư đăng nhập khẩn cấp qua SSH để xử lý sự cố lúc nửa đêm. Để kiểm soát triệt để hiện tượng này:
- Ở tầng Terraform: Thiết lập lịch chạy tự động mỗi 6 giờ lệnh
terraform plan -detailed-exitcode. Mã thoát0nghĩa là hạ tầng khớp tuyệt đối với mã nguồn; mã thoát2nghĩa là phát hiện sai lệch (Drift) và kích hoạt cảnh báo về kênh vận hành. - Ở tầng Ansible: Thiết lập lịch chạy
ansible-playbook site.yml --check --diffở chế độ Dry-Run chỉ đọc. Bất kỳ tác vụ nào báo trạng tháichangedtrong chế độ--checkđều chỉ ra rằng một tệp cấu hình hoặc dịch vụ trên máy chủ đã bị sửa đổi thủ công ngoài Git.
Tài liệu tham khảo & Nguồn chuẩn (Primary References)
- HashiCorp Terraform Official Documentation: Backend Configuration: S3 State Storage & DynamoDB Locking — https://developer.hashicorp.com/terraform/language/backend/s3
- Ansible Official Documentation: Ansible Collection
cloud.terraform(terraform_providerInventory Plugin) — https://docs.ansible.com/ansible/latest/collections/cloud/terraform/index.html - Red Hat Ansible Best Practices: Validating Configuration Files and Idempotent Playbook Design — https://docs.ansible.com/ansible/latest/tips_tricks/ansible_tips_tricks.html
- Aqua Security Trivy Documentation: Infrastructure as Code (Terraform) Misconfiguration Scanning — https://aquasecurity.github.io/trivy/latest/docs/scanner/misconfiguration/