一、 从“盖房子”与“装修房子”说起
想象一下,你要建造并运营一个现代化的数据中心。这个过程可以拆解成两个主要阶段:
- 盖房子(构建基础设施):你需要买地、打地基、砌墙、安装门窗、通水电。这些是基础框架,一旦建好,改动起来成本很高。
- 装修与入住(配置与管理):房子盖好了,你需要安装灯具、布置家具、开通网络、设置安防系统。这些是内部的、灵活的配置,可以根据需要随时调整。
在IT世界里,Terraform就像是那位出色的“建筑师”和“结构工程师”,专注于“盖房子”阶段。它用代码定义并创建云服务器、网络、存储、数据库实例等基础设施资源。而Ansible则像是万能的“室内设计师”和“物业管家”,负责“装修与入住”阶段,在创建好的服务器内部安装软件、配置服务、部署应用、进行日常管理。
它们俩一个主外,一个主内,协同工作,就能实现从零到一,再到稳定运行的完整自动化流程,这就是“基础设施即代码”的完美配合。
二、 核心工具简介:Terraform 与 Ansible 各司其职
Terraform 由 HashiCorp 公司开发,它使用一种名为 HCL 的声明式语言。你只需告诉它“我想要什么状态”(比如,我想要两台 Ubuntu 22.04 的云服务器,在一个安全组内),它就会自动计算如何从当前状态达到你描述的目标状态,并调用云厂商的 API 去创建。它的强项在于生命周期管理:创建、修改、销毁基础设施。它有一个状态文件,记录着它管理的所有资源,这是它的“设计图纸”。
Ansible 使用 YAML 语言编写“剧本”,通过 SSH 或 WinRM 协议连接到目标服务器,执行一系列任务。它的核心思想是无代理和幂等性。无代理意味着你不需要在目标服务器上预先安装客户端;幂等性意味着同一个剧本运行多次,效果是一样的(比如,安装软件包,如果已经安装了就不会重复安装)。它擅长处理服务器内部的、动态的配置。
简单来说:Terraform 负责“有没有”,Ansible 负责“好不好用”。
三、 如何协同:两种经典工作模式
两者协同的关键在于“信息传递”。Terraform 创建了服务器,它需要把服务器的 IP 地址等信息告诉 Ansible,Ansible 才能知道去配置谁。
模式一:Terraform 置备 + Ansible 配置(最常用) 这是最直观的流程。Terraform 先运行,创建出所有基础设施,并输出一些关键信息(如服务器 IP 地址列表)。然后,我们手动或通过 CI/CD 工具触发 Ansible,将这些信息作为“库存”输入,对服务器进行配置。
模式二:Terraform 调用本地 Ansible(紧密集成)
Terraform 本身可以通过 local-exec 或 null_resource 触发器,在资源创建完成后,在本地执行一条命令来调用 Ansible-playbook。这种模式更自动化,但耦合也更紧密,适合简单的场景。
下面,我们通过一个完整的示例来演示第一种模式。
四、 完整示例:部署一个高可用的 Web 应用集群
技术栈声明: 本示例全程使用 AWS 作为云平台。
场景: 我们需要在 AWS 上创建一个高可用的 Web 集群,包含一个负载均衡器、两个位于不同可用区的 Web 服务器,并在服务器上部署 Nginx 和我们的应用代码。
步骤 1:Terraform 构建基础设施 (main.tf)
# 声明使用 AWS 提供商
provider "aws" {
region = "us-east-1"
}
# 1. 创建 VPC 和子网(我们的“地皮”和“楼层规划”)
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "MainVPC"
}
}
resource "aws_subnet" "public_a" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
}
resource "aws_subnet" "public_b" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
availability_zone = "us-east-1b"
}
# 2. 创建安全组(防火墙规则,允许 HTTP 和 SSH)
resource "aws_security_group" "web_sg" {
name = "web_sg"
description = "Allow HTTP and SSH"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # 生产环境应限制为特定IP
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# 3. 创建启动模板(定义服务器“蓝图”:镜像、类型、密钥对)
resource "aws_launch_template" "web_lt" {
name_prefix = "web-template"
image_id = "ami-0c55b159cbfafe1f0" # Ubuntu 22.04 LTS
instance_type = "t2.micro"
key_name = "my-key-pair" # 你需要提前在 AWS 创建密钥对
network_interfaces {
associate_public_ip_address = true
security_groups = [aws_security_group.web_sg.id]
}
tag_specifications {
resource_type = "instance"
tags = {
Name = "WebServer"
Role = "Web"
}
}
}
# 4. 创建自动伸缩组(自动创建并管理两个 Web 服务器)
resource "aws_autoscaling_group" "web_asg" {
name_prefix = "web-asg-"
desired_capacity = 2
max_size = 4
min_size = 2
vpc_zone_identifier = [aws_subnet.public_a.id, aws_subnet.public_b.id]
launch_template {
id = aws_launch_template.web_lt.id
version = "$Latest"
}
tag {
key = "Name"
value = "WebServer"
propagate_at_launch = true
}
}
# 5. 创建应用负载均衡器
resource "aws_lb" "web_alb" {
name = "web-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.web_sg.id]
subnets = [aws_subnet.public_a.id, aws_subnet.public_b.id]
}
resource "aws_lb_target_group" "web_tg" {
name = "web-tg"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
}
resource "aws_lb_listener" "front_end" {
load_balancer_arn = aws_lb.web_alb.arn
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.web_tg.arn
}
}
# 6. 将自动伸缩组关联到目标组
resource "aws_autoscaling_attachment" "asg_attachment" {
autoscaling_group_name = aws_autoscaling_group.web_asg.name
alb_target_group_arn = aws_lb_target_group.web_tg.arn
}
# 7. 关键输出:输出负载均衡器的访问地址,以及我们需要的服务器信息。
# 这里我们通过 AWS 实例数据源来获取已创建的实例的私有 IP。
output "alb_dns_name" {
value = aws_lb.web_alb.dns_name
description = "负载均衡器的 DNS 名称,用于访问网站"
}
# 获取自动伸缩组内所有实例的私有 IP,用于 Ansible 库存
data "aws_instances" "web_instances" {
instance_tags = {
"aws:autoscaling:groupName" = aws_autoscaling_group.web_asg.name
}
depends_on = [aws_autoscaling_group.web_asg] # 确保实例已创建
}
output "web_instance_private_ips" {
value = data.aws_instances.web_instances.private_ips
description = "Web 服务器的私有 IP 地址列表,供 Ansible 使用"
sensitive = false
}
运行 terraform apply 后,基础设施就创建好了。你会得到 alb_dns_name (如 web-alb-123456.us-east-1.elb.amazonaws.com) 和 web_instance_private_ips (如 ["10.0.1.10", "10.0.2.20"])。
步骤 2:Ansible 配置服务器 (inventory.ini 与 site.yml)
我们将 Terraform 输出的 IP 列表写入 Ansible 的库存文件。这个过程可以手动,也可以通过脚本自动完成(例如,用 terraform output -json 解析)。
库存文件 inventory.ini:
[web_servers]
10.0.1.10 ansible_user=ubuntu ansible_ssh_private_key_file=/path/to/my-key-pair.pem
10.0.2.20 ansible_user=ubuntu ansible_ssh_private_key_file=/path/to/my-key-pair.pem
[web_servers:vars]
# 可以在这里定义组变量
app_version = "1.0.0"
Ansible 剧本 site.yml:
---
- name: 配置高可用 Web 集群
hosts: web_servers # 对应库存文件中的组
become: yes # 使用 sudo 权限
tasks:
- name: 更新 apt 软件包缓存
apt:
update_cache: yes
cache_valid_time: 3600
- name: 安装 Nginx
apt:
name: nginx
state: present
- name: 创建应用代码目录
file:
path: /var/www/myapp
state: directory
owner: www-data
group: www-data
- name: 部署应用静态文件
copy:
src: ./myapp_files/index.html # 假设你的应用文件在本地这个目录
dest: /var/www/myapp/
owner: www-data
group: www-data
notify:
- restart nginx # 触发处理器,如果文件有变化则重启nginx
- name: 配置 Nginx 虚拟主机
template:
src: ./templates/nginx.conf.j2 # Jinja2 模板文件
dest: /etc/nginx/sites-available/myapp
owner: root
group: root
notify:
- restart nginx
- name: 启用 Nginx 站点配置
file:
src: /etc/nginx/sites-available/myapp
dest: /etc/nginx/sites-enabled/myapp
state: link
notify:
- restart nginx
handlers:
- name: restart nginx
service:
name: nginx
state: restarted
Jinja2 模板 templates/nginx.conf.j2:
server {
listen 80 default_server;
root /var/www/myapp;
index index.html;
server_name _;
location / {
try_files $uri $uri/ =404;
}
}
现在,运行 ansible-playbook -i inventory.ini site.yml,Ansible 就会连接到那两台服务器,完成所有软件安装和配置。最后,访问 Terraform 输出的 alb_dns_name,你就能看到通过负载均衡器访问到的、由两台服务器承载的网站了。
五、 应用场景、优缺点与注意事项
应用场景:
- 混合云/多云部署:Terraform 统一管理不同云资源,Ansible 提供一致的配置。
- 完整的 CI/CD 流水线:代码提交后,CI 工具先调用 Terraform 创建/更新环境,再调用 Ansible 部署新版本应用。
- 灾难恢复:用代码定义的环境可以快速在另一个区域重建。
- 开发与生产环境一致性:使用相同的 Terraform 和 Ansible 代码,确保环境无差异。
技术优缺点:
- 优点:
- 职责分离:架构清晰,Terraform 管资源,Ansible 管配置,符合单一职责原则。
- 工具优势最大化:利用了 Terraform 强大的资源管理和状态跟踪,以及 Ansible 灵活的配置管理和丰富的模块。
- 灵活性高:可以独立更新基础设施或应用配置。
- 缺点/挑战:
- 学习两种工具:团队需要掌握两种不同的语言和范式。
- 状态与信息传递:需要妥善管理 Terraform 的状态文件(
.tfstate)并建立可靠的方式将输出传递给 Ansible。 - 潜在的不一致:如果绕过工具手动修改了资源或配置,会导致状态不一致。
注意事项:
- 状态文件是命根子:Terraform 的
.tfstate文件必须安全存储(如使用 S3 后端 + DynamoDB 锁),并严格进行版本控制备份。 - 密钥管理:在代码中避免硬编码密码、密钥。使用 Terraform 的变量文件(
.tfvars)或云厂商的密钥管理服务(如 AWS Secrets Manager),Ansible 则可以使用 Vault。 - 库存动态管理:对于自动伸缩组,服务器 IP 可能会变。建议使用 Ansible 的动态库存插件(如
aws_ec2插件),直接从 AWS 获取实时服务器列表,而不是静态文件。 - 执行顺序与依赖:确保 Ansible 剧本在 Terraform 完全创建资源并处于稳定状态后再运行。可以在 CI/CD 管道中设置明确的阶段和依赖。
六、 总结
将 Ansible 与 Terraform 结合,构建了一套从基础设施到应用配置的端到端自动化体系。Terraform 像一位精准的蓝图执行者,负责搭建稳定、可复现的底层环境;Ansible 像一位细致的配置艺术家,负责让环境变得生动、可用。这种组合不仅提升了效率,更重要的是它带来了可预测性、可审计性和极强的可恢复能力,是现代化运维和 DevOps 实践中不可或缺的利器。掌握它们的协同,意味着你能够用代码真正驾驭从云资源到业务服务的全生命周期。
评论