| 概念 | 说明 |
|---|
| Provider | 云服务商插件(AWS/Azure/阿里云等) |
| Resource | 基础设施资源定义(VM/VPC/DB等) |
| State | 资源状态文件,记录已创建的资源 |
| Module | 可复用的 Terraform 配置包 |
| Plan | 预览变更(干运行) |
| Apply | 执行变更 |
terraform/
├── main.tf # 主配置
├── variables.tf # 变量定义
├── outputs.tf # 输出值
├── providers.tf # Provider 配置
├── backend.tf # State 后端配置
├── versions.tf # 版本约束
├── terraform.tfvars # 变量值
├── environments/
│ ├── dev.tfvars
│ ├── staging.tfvars
│ └── prod.tfvars
└── modules/
├── vpc/
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── ec2/
└── rds/
# providers.tf
terraform {
required_version = ">= 1.6"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# 远程 State 存储
backend "s3" {
bucket = "terraform-state-prod"
key = "infrastructure/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
ManagedBy = "terraform"
Project = "myapp"
}
}
}
# modules/vpc/main.tf
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.name}-vpc"
}
}
# 公有子网
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.name}-public-${count.index + 1}"
Tier = "public"
}
}
# 私有子网
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = {
Name = "${var.name}-private-${count.index + 1}"
Tier = "private"
}
}
# 互联网网关
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.name}-igw"
}
}
# NAT 网关(每个 AZ 一个)
resource "aws_nat_gateway" "main" {
count = length(var.public_subnet_cidrs)
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = {
Name = "${var.name}-nat-${count.index + 1}"
}
}
resource "aws_eip" "nat" {
count = length(var.public_subnet_cidrs)
domain = "vpc"
}
# 公有路由表
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.name}-public-rt"
}
}
# 私有路由表
resource "aws_route_table" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[count.index].id
}
tags = {
Name = "${var.name}-private-rt-${count.index + 1}"
}
}
# modules/vpc/variables.tf
variable "name" {
description = "VPC 名称前缀"
type = string
}
variable "vpc_cidr" {
description = "VPC CIDR 地址块"
type = string
default = "10.0.0.0/16"
}
variable "public_subnet_cidrs" {
description = "公有子网 CIDR 列表"
type = list(string)
}
variable "private_subnet_cidrs" {
description = "私有子网 CIDR 列表"
type = list(string)
}
variable "availability_zones" {
description = "可用区列表"
type = list(string)
}
# main.tf
module "vpc" {
source = "./modules/vpc"
name = "${var.project}-${var.environment}"
vpc_cidr = var.vpc_cidr
public_subnet_cidrs = var.public_subnet_cidrs
private_subnet_cidrs = var.private_subnet_cidrs
availability_zones = var.availability_zones
}
# EC2 实例
resource "aws_instance" "app" {
count = var.app_instance_count
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = module.vpc.private_subnet_ids[count.index % length(module.vpc.private_subnet_ids)]
vpc_security_group_ids = [aws_security_group.app.id]
user_data = templatefile("${path.module}/user_data.sh.tpl", {
environment = var.environment
db_host = aws_db_instance.main.address
})
tags = {
Name = "${var.project}-${var.environment}-app-${count.index + 1}"
}
}
# RDS 数据库
resource "aws_db_instance" "main" {
identifier = "${var.project}-${var.environment}-db"
engine = "postgresql"
engine_version = "16.1"
instance_class = var.db_instance_class
allocated_storage = 100
max_allocated_storage = 500
storage_encrypted = true
db_name = "myapp"
username = "dbadmin"
password = var.db_password
vpc_security_group_ids = [aws_security_group.db.id]
db_subnet_group_name = aws_db_subnet_group.main.name
backup_retention_period = 7
backup_window = "03:00-04:00"
maintenance_window = "Mon:04:00-Mon:05:00"
skip_final_snapshot = false
final_snapshot_identifier = "${var.project}-${var.environment}-final"
}
# 数据源 - 获取最新 Ubuntu AMI
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
}
# variables.tf
variable "project" {
description = "项目名称"
type = string
default = "myapp"
}
variable "environment" {
description = "环境名称"
type = string
}
variable "aws_region" {
description = "AWS 区域"
type = string
default = "us-east-1"
}
variable "db_password" {
description = "数据库密码"
type = string
sensitive = true
}
# outputs.tf
output "vpc_id" {
description = "VPC ID"
value = module.vpc.vpc_id
}
output "app_instance_ids" {
description = "应用实例 ID 列表"
value = aws_instance.app[*].id
}
output "db_endpoint" {
description = "数据库连接端点"
value = aws_db_instance.main.endpoint
}
# 初始化(下载 Provider 和模块)
terraform init
# 格式化代码
terraform fmt
# 验证语法
terraform validate
# 预览变更
terraform plan -var-file="environments/prod.tfvars" -out=tfplan
# 执行变更
terraform apply tfplan
# 查看状态
terraform state list
terraform state show aws_instance.app[0]
# 销毁所有资源
terraform destroy -var-file="environments/prod.tfvars"
| 概念 | 说明 |
|---|
| Inventory | 主机清单,定义管理的主机 |
| Playbook | YAML 格式的任务编排文件 |
| Role | 可复用的任务集合 |
| Module | 执行具体操作的模块 |
| Handler | 被通知后执行的任务(如重启服务) |
# inventory/production.ini
[webservers]
web1 ansible_host=10.0.1.10
web2 ansible_host=10.0.1.11
[appservers]
app1 ansible_host=10.0.10.10
app2 ansible_host=10.0.10.11
[dbservers]
db1 ansible_host=10.0.20.10
[production:children]
webservers
appservers
dbservers
[production:vars]
ansible_user=ec2-user
ansible_ssh_private_key_file=~/.ssh/prod_key
ansible_python_interpreter=/usr/bin/python3
# playbooks/deploy-app.yml
---
- name: 部署 Web 应用
hosts: appservers
become: true
vars:
app_version: '2.3.1'
app_port: 3000
app_dir: /opt/myapp
tasks:
- name: 安装依赖
ansible.builtin.apt:
name:
- curl
- python3-pip
state: present
update_cache: true
- name: 创建应用目录
ansible.builtin.file:
path: '{{ app_dir }}'
state: directory
mode: '0755'
- name: 下载应用包
ansible.builtin.get_url:
url: 'https://releases.example.com/myapp/{{ app_version }}/myapp-linux-amd64'
dest: '{{ app_dir }}/myapp'
mode: '0755'
notify: restart myapp
- name: 部署配置文件
ansible.builtin.template:
src: templates/app.conf.j2
dest: '{{ app_dir }}/app.conf'
mode: '0644'
notify: restart myapp
- name: 部署 systemd 服务
ansible.builtin.template:
src: templates/myapp.service.j2
dest: /etc/systemd/system/myapp.service
mode: '0644'
notify: restart myapp
- name: 确保 myapp 服务运行
ansible.builtin.systemd:
name: myapp
state: started
enabled: true
daemon_reload: true
- name: 等待应用就绪
ansible.builtin.wait_for:
port: '{{ app_port }}'
delay: 5
timeout: 60
handlers:
- name: restart myapp
ansible.builtin.systemd:
name: myapp
state: restarted
roles/
└── nginx/
├── tasks/
│ └── main.yml # 主任务
├── handlers/
│ └── main.yml # 处理器
├── templates/
│ └── nginx.conf.j2 # 模板文件
├── files/
│ └── nginx.repo # 静态文件
├── vars/
│ └── main.yml # 角色变量
├── defaults/
│ └── main.yml # 默认变量
└── meta/
└── main.yml # 角色依赖
# roles/nginx/templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus }};
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections {{ nginx_worker_connections | default(1024) }};
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
upstream app_backend {
{% for host in groups['appservers'] %}
server {{ hostvars[host]['ansible_host'] }}:{{ app_port }};
{% endfor %}
}
server {
listen 80;
server_name {{ nginx_server_name }};
location / {
proxy_pass http://app_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
Pulumi 是使用通用编程语言(Python/TypeScript/Go)编写基础设施即代码的工具:
| 特性 | Terraform | Pulumi |
|---|
| 语言 | HCL(专用语言) | Python/TS/Go/C# |
| 状态管理 | 自管理/SaaS | Pulumi Cloud |
| 测试 | 有限 | 原生单元测试 |
| 逻辑 | 声明式 | 命令式+声明式 |
| 生态 | 最丰富 | 增长中 |
# __main__.py
import pulumi
import pulumi_aws as aws
# 配置
config = pulumi.Config()
environment = config.get("environment") or "dev"
# 创建 VPC
vpc = aws.ec2.Vpc("main-vpc",
cidr_block="10.0.0.0/16",
enable_dns_hostnames=True,
tags={"Name": f"myapp-{environment}-vpc"},
)
# 创建子网
public_subnet = aws.ec2.Subnet("public-subnet",
vpc_id=vpc.id,
cidr_block="10.0.1.0/24",
availability_zone="us-east-1a",
map_public_ip_on_launch=True,
tags={"Name": f"myapp-{environment}-public"},
)
# 创建安全组
sg = aws.ec2.SecurityGroup("app-sg",
vpc_id=vpc.id,
description="Application security group",
ingress=[
aws.ec2.SecurityGroupIngressArgs(
protocol="tcp",
from_port=443,
to_port=443,
cidr_blocks=["0.0.0.0/0"],
),
],
egress=[
aws.ec2.SecurityGroupEgressArgs(
protocol="-1",
from_port=0,
to_port=0,
cidr_blocks=["0.0.0.0/0"],
),
],
)
# 输出
pulumi.export("vpc_id", vpc.id)
pulumi.export("subnet_id", public_subnet.id)
pulumi.export("security_group_id", sg.id)
| 原则 | 说明 |
|---|
| 声明式描述 | 系统状态用声明式方式描述 |
| Git 为唯一信源 | 所有变更通过 Git 提交 |
| 自动拉取 | Agent 自动拉取并应用期望状态 |
| 持续协调 | 持续比对实际状态与期望状态 |
# argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/k8s-manifests.git
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# gotk-sync.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: myapp
namespace: flux-system
spec:
interval: 1m
url: https://github.com/org/k8s-manifests.git
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: myapp
namespace: flux-system
spec:
interval: 5m
sourceRef:
kind: GitRepository
name: myapp
path: './overlays/production'
prune: true
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: myapp
namespace: production
import boto3
import concurrent.futures
ec2 = boto3.resource('ec2')
def get_instances_by_tag(tag_key: str, tag_value: str):
"""根据标签获取实例列表"""
return list(ec2.instances.filter(Filters=[
{'Name': f'tag:{tag_key}', 'Values': [tag_value]},
{'Name': 'instance-state-name', 'Values': ['running']}
]))
def execute_ssm_command(instance_ids: list, command: str):
"""通过 SSM 执行命令"""
ssm = boto3.client('ssm')
response = ssm.send_command(
InstanceIds=instance_ids,
DocumentName='AWS-RunShellScript',
Parameters={'commands': [command]},
TimeoutSeconds=300,
)
return response['Command']['CommandId']
def rolling_restart(tag_key: str, tag_value: str, batch_size: int = 1):
"""滚动重启实例"""
instances = get_instances_by_tag(tag_key, tag_value)
print(f"找到 {len(instances)} 个实例")
for i in range(0, len(instances), batch_size):
batch = instances[i:i + batch_size]
ids = [inst.id for inst in batch]
# 停止实例
print(f"停止实例: {ids}")
for inst in batch:
inst.stop()
for inst in batch:
inst.wait_until_stopped()
# 启动实例
print(f"启动实例: {ids}")
for inst in batch:
inst.start()
for inst in batch:
inst.wait_until_running()
# 健康检查
print(f"等待实例就绪: {ids}")
execute_ssm_command(ids, 'curl -sf http://localhost:3000/health || exit 1')
print(f"批次 {i // batch_size + 1} 完成")
import boto3
from datetime import datetime, timedelta
def cleanup_unused_resources(dry_run: bool = True):
"""清理未使用的云资源"""
ec2 = boto3.resource('ec2')
findings = []
# 未附加的 EBS 卷
for vol in ec2.volumes.filter(Filters=[
{'Name': 'status', 'Values': ['available']}
]):
findings.append({
'type': 'EBS Volume',
'id': vol.id,
'size': f"{vol.size}GB",
'age': str(datetime.now() - vol.create_time.replace(tzinfo=None)),
})
if not dry_run:
vol.delete()
# 未使用的弹性 IP
for eip in ec2.vpc_addresses.all():
if eip.instance_id is None:
findings.append({
'type': 'Elastic IP',
'id': eip.allocation_id,
'ip': eip.public_ip,
})
if not dry_run:
eip.release()
# 过期的快照(>30天)
cutoff = datetime.now() - timedelta(days=30)
for snap in ec2.snapshots.filter(OwnerIds=['self']):
if snap.start_time.replace(tzinfo=None) < cutoff:
# 检查是否被 AMI 引用
is_used = any(
snap.id in [b['Ebs']['SnapshotId'] for b in img.block_device_mappings]
for img in ec2.images.filter(OwnerIds=['self'])
)
if not is_used:
findings.append({
'type': 'Snapshot',
'id': snap.id,
'age': str(datetime.now() - snap.start_time.replace(tzinfo=None)),
})
if not dry_run:
snap.delete()
return findings
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- 'alerts/*.yml'
scrape_configs:
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
# alerts/app-alerts.yml
groups:
- name: app-alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: '高错误率: {{ $labels.instance }}'
description: '5xx 错误率超过 5%,当前值: {{ $value | humanizePercentage }}'
- alert: HighLatency
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 1
for: 5m
labels:
severity: warning
annotations:
summary: '高延迟: {{ $labels.instance }}'
description: 'P95 延迟超过 1s,当前值: {{ $value }}s'
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 5m
labels:
severity: critical
annotations:
summary: 'Pod 崩溃重启: {{ $labels.namespace }}/{{ $labels.pod }}'
// 关键监控面板
{
dashboard: {
title: 'Application Overview',
panels: [
{
title: '请求速率 (QPS)',
type: 'timeseries',
targets: [{ expr: 'sum(rate(http_requests_total[5m]))' }],
},
{
title: '错误率',
type: 'gauge',
targets: [
{
expr: 'sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))',
},
],
},
{
title: 'P95 延迟',
type: 'timeseries',
targets: [
{
expr: 'histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))',
},
],
},
{
title: 'CPU 使用率',
type: 'timeseries',
targets: [
{
expr: 'sum(rate(container_cpu_usage_seconds_total{namespace="production"}[5m])) by (pod)',
},
],
},
{
title: '内存使用',
type: 'timeseries',
targets: [
{ expr: 'sum(container_memory_working_set_bytes{namespace="production"}) by (pod)' },
],
},
],
},
}
# 创建 CPU 告警
aws cloudwatch put-metric-alarm \
--alarm-name "high-cpu-alert" \
--alarm-description "CPU 使用率超过 80%" \
--metric-name CPUUtilization \
--namespace AWS/EC2 \
--statistic Average \
--period 300 \
--threshold 80 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 2 \
--dimensions Name=AutoScalingGroupName,Value=myapp-asg \
--alarm-actions arn:aws:sns:us-east-1:xxx:ops-alerts
# 创建自定义指标告警
aws cloudwatch put-metric-alarm \
--alarm-name "high-error-rate" \
--metric-name ErrorRate \
--namespace MyApp \
--statistic Average \
--period 60 \
--threshold 5 \
--comparison-operator GreaterThanThreshold \
--evaluation-periods 3 \
--datapoints-to-alarm 2 \
--treat-missing-data breaching \
--alarm-actions arn:aws:sns:us-east-1:xxx:ops-alerts
指标采集 → 数据存储 → 可视化 → 告警 → 通知
│ │ │ │ │
Prometheus TSDB Grafana Alert Slack/钉钉
CloudWatch CloudWatch AWS SNS Email/SMS
Datadog Datadog Datadog Monitor PagerDuty
| 层级 | 工具 | 关注点 |
|---|
| 基础设施 | CloudWatch、Node Exporter | CPU/内存/磁盘/网络 |
| 应用层 | APM、自定义指标 | 延迟/错误率/吞吐量 |
| 业务层 | 自定义指标、日志分析 | 订单量/转化率 |
| 安全层 | GuardDuty、WAF 日志 | 异常访问/攻击检测 |