Cursor+GitOps 自动化运维实战:智能编写 Ansible 与 Terraform



Cursor+GitOps 自动化运维实战:智能编写 Ansible 与 Terraform


目录

  • 摘要
  • 一、开发环境搭建与工具链初始化配置
    • [1.1 硬件与操作系统要求](#1.1 硬件与操作系统要求)
    • [1.2 Cursor IDE 安装与基础配置](#1.2 Cursor IDE 安装与基础配置)
    • [1.3 Git 环境配置与 SSH 密钥生成](#1.3 Git 环境配置与 SSH 密钥生成)
    • [1.4 Ansible 安装与连接验证](#1.4 Ansible 安装与连接验证)
    • [1.5 Terraform 安装与 Provider 初始化](#1.5 Terraform 安装与 Provider 初始化)
    • [1.6 辅助工具链安装(Python、Docker、kubectl)](#1.6 辅助工具链安装(Python、Docker、kubectl))
    • [1.7 项目目录结构规划](#1.7 项目目录结构规划)
  • [二、Cursor 核心功能解析与 AI 上下文设定](#二、Cursor 核心功能解析与 AI 上下文设定)
    • [2.1 Cursor 编辑器核心界面与快捷键](#2.1 Cursor 编辑器核心界面与快捷键)
    • [2.2 AI 对话面板与 Composer 模式](#2.2 AI 对话面板与 Composer 模式)
    • [2.3 上下文管理:@符号引用与文件索引](#2.3 上下文管理:@符号引用与文件索引)
    • [2.4 .cursorrules 文件编写规范](#2.4 .cursorrules 文件编写规范)
    • [2.5 自定义 Prompt 模板与运维场景适配](#2.5 自定义 Prompt 模板与运维场景适配)
    • [2.6 模型选择策略与 Token 管理](#2.6 模型选择策略与 Token 管理)
  • [三、利用 Cursor 快速生成标准化 Ansible 剧本](#三、利用 Cursor 快速生成标准化 Ansible 剧本)
    • [3.1 Ansible 基础概念与目录规范](#3.1 Ansible 基础概念与目录规范)
    • [3.2 使用自然语言生成 Playbook 骨架](#3.2 使用自然语言生成 Playbook 骨架)
    • [3.3 Roles 角色化拆分与 AI 辅助重构](#3.3 Roles 角色化拆分与 AI 辅助重构)
    • [3.4 变量管理与 Vault 加密配置](#3.4 变量管理与 Vault 加密配置)
    • [3.5 Jinja2 模板生成与条件逻辑编写](#3.5 Jinja2 模板生成与条件逻辑编写)
    • [3.6 Handler 与幂等性保障](#3.6 Handler 与幂等性保障)
    • [3.7 完整实战:Web 服务自动化部署 Playbook](#3.7 完整实战:Web 服务自动化部署 Playbook)
  • [四、基于自然语言描述构建 Terraform 基础设施代码](#四、基于自然语言描述构建 Terraform 基础设施代码)
    • [4.1 Terraform 核心概念:Provider、Resource、State](#4.1 Terraform 核心概念:Provider、Resource、State)
    • [4.2 自然语言到 HCL 代码的转换技巧](#4.2 自然语言到 HCL 代码的转换技巧)
    • [4.3 VPC 网络基础设施代码生成](#4.3 VPC 网络基础设施代码生成)
    • [4.4 计算资源(EC2/ECS)自动化创建](#4.4 计算资源(EC2/ECS)自动化创建)
    • [4.5 模块化设计与变量参数化](#4.5 模块化设计与变量参数化)
    • [4.6 State 管理与远程后端配置](#4.6 State 管理与远程后端配置)
    • [4.7 完整实战:多云环境基础设施编排](#4.7 完整实战:多云环境基础设施编排)
  • [五、集成 Git 工作流实现代码版本管理与协作](#五、集成 Git 工作流实现代码版本管理与协作)
    • [5.1 GitOps 核心理念与运维场景映射](#5.1 GitOps 核心理念与运维场景映射)
    • [5.2 仓库结构设计与分支策略](#5.2 仓库结构设计与分支策略)
    • [5.3 Commit 规范与自动化提交信息生成](#5.3 Commit 规范与自动化提交信息生成)
    • [5.4 Pull Request 审查流程与 AI 辅助 Code Review](#5.4 Pull Request 审查流程与 AI 辅助 Code Review)
    • [5.5 Git Hooks 与 Pre-commit 自动化检查](#5.5 Git Hooks 与 Pre-commit 自动化检查)
    • [5.6 多环境分支管理与标签策略](#5.6 多环境分支管理与标签策略)
  • [六、搭建 CI/CD 流水线自动执行部署脚本](#六、搭建 CI/CD 流水线自动执行部署脚本)
    • [6.1 CI/CD 流水线架构设计](#6.1 CI/CD 流水线架构设计)
    • [6.2 GitHub Actions 工作流配置](#6.2 GitHub Actions 工作流配置)
    • [6.3 Ansible 自动化执行 Pipeline](#6.3 Ansible 自动化执行 Pipeline)
    • [6.4 Terraform Plan/Apply 自动化流程](#6.4 Terraform Plan/Apply 自动化流程)
    • [6.5 密钥管理与 Secrets 注入](#6.5 密钥管理与 Secrets 注入)
    • [6.6 通知与回滚机制](#6.6 通知与回滚机制)
  • 七、全流程实操:从代码生成到云端资源交付
    • [7.1 项目需求分析与架构设计](#7.1 项目需求分析与架构设计)
    • [7.2 使用 Cursor 生成全部基础设施代码](#7.2 使用 Cursor 生成全部基础设施代码)
    • [7.3 本地验证与 Lint 检查](#7.3 本地验证与 Lint 检查)
    • [7.4 推送代码触发 CI/CD](#7.4 推送代码触发 CI/CD)
    • [7.5 云端资源验证与监控接入](#7.5 云端资源验证与监控接入)
    • [7.6 端到端测试与交付确认](#7.6 端到端测试与交付确认)
  • [八、常见语法报错分析与 AI 辅助排错技巧](#八、常见语法报错分析与 AI 辅助排错技巧)
    • [8.1 Ansible 常见报错与解决方案](#8.1 Ansible 常见报错与解决方案)
    • [8.2 Terraform 常见报错与解决方案](#8.2 Terraform 常见报错与解决方案)
    • [8.3 Git 冲突处理与合并策略](#8.3 Git 冲突处理与合并策略)
    • [8.4 CI/CD 流水线故障排查](#8.4 CI/CD 流水线故障排查)
    • [8.5 利用 Cursor AI 进行智能排错](#8.5 利用 Cursor AI 进行智能排错)
    • [8.6 日志分析与错误模式识别](#8.6 日志分析与错误模式识别)
  • 九、提升脚本安全性的最佳实践与审查要点
    • [9.1 密钥与敏感信息管理](#9.1 密钥与敏感信息管理)
    • [9.2 最小权限原则与 IAM 策略](#9.2 最小权限原则与 IAM 策略)
    • [9.3 代码静态安全扫描](#9.3 代码静态安全扫描)
    • [9.4 Ansible 安全加固实践](#9.4 Ansible 安全加固实践)
    • [9.5 Terraform 安全基线配置](#9.5 Terraform 安全基线配置)
    • [9.6 审计日志与合规检查](#9.6 审计日志与合规检查)
  • 十、进阶技巧:自定义规则优化运维代码质量
    • [10.1 自定义 .cursorrules 运维规则集](#10.1 自定义 .cursorrules 运维规则集)
    • [10.2 Ansible Lint 规则定制](#10.2 Ansible Lint 规则定制)
    • [10.3 Terraform 自定义策略(Sentinel/OPA)](#10.3 Terraform 自定义策略(Sentinel/OPA))
    • [10.4 代码生成质量评估与迭代优化](#10.4 代码生成质量评估与迭代优化)
    • [10.5 团队知识库与 Prompt 工程](#10.5 团队知识库与 Prompt 工程)
    • [10.6 自动化文档生成与知识沉淀](#10.6 自动化文档生成与知识沉淀)
  • 常见陷阱与问题排除
  • 总结
  • 详细参考资料
  • 附录

摘要

随着云原生时代的全面到来,基础设施即代码(Infrastructure as Code, IaC)已成为现代运维的核心范式。Ansible 作为配置管理的利器,Terraform 作为基础设施编排的标准,二者结合构成了企业级自动化运维的基石。然而,编写高质量的 IaC 代码仍然面临学习曲线陡峭、语法繁琐、调试困难等挑战。

Cursor 作为一款深度集成 AI 能力的代码编辑器,为运维工程师带来了革命性的开发体验。通过自然语言描述需求,AI 可以自动生成符合最佳实践的 Ansible Playbook 和 Terraform 配置文件,将原本数小时的编码工作压缩到几分钟内完成。

本文将手把手带你从零开始,完整掌握 Cursor + GitOps + Ansible + Terraform 的全栈自动化运维工作流。从环境搭建到代码生成,从版本管理到 CI/CD 流水线,从安全审查到进阶优化,每一个环节都配有详细的代码示例、操作截图说明和常见问题解答。无论你是刚接触运维的新手,还是希望提升效率的资深工程师,都能从本文中获得切实可行的实践指导。

适用读者:运维工程师、DevOps 工程师、SRE、后端开发人员、对自动化运维感兴趣的技术爱好者

前置要求:基本的 Linux 命令行操作能力、了解 YAML/HCL 基本语法(文中会详细讲解)

预计学习时间:通读约 3-4 小时,实操练习约 2-3 天


一、开发环境搭建与工具链初始化配置

1.1 硬件与操作系统要求

在开始之前,确保你的开发环境满足以下最低要求:

硬件要求:

组件 最低配置 推荐配置
CPU 4 核 8 核及以上
内存 8 GB RAM 16 GB RAM
磁盘 50 GB SSD 100 GB NVMe SSD
网络 稳定的互联网连接 带宽 ≥ 50 Mbps

操作系统支持:

  • Linux:Ubuntu 22.04 LTS / CentOS Stream 9 / Fedora 39+(推荐)
  • macOS:macOS 13 Ventura 及以上
  • Windows:Windows 11(需安装 WSL2)

新手提示:如果你使用 Windows 系统,强烈建议安装 WSL2(Windows Subsystem for Linux 2),在 Linux 子系统中进行所有操作。Ansible 原生不支持 Windows 作为控制节点。

1.2 Cursor IDE 安装与基础配置

1.2.1 下载与安装 Cursor

Linux (Ubuntu/Debian):

bash 复制代码
# 下载 Cursor AppImage(以 x86_64 为例)
wget https://download.cursor.sh/linux/appimage/latest -O cursor.AppImage

# 添加执行权限
chmod +x cursor.AppImage

# 运行 Cursor(首次运行)
./cursor.AppImage

# 可选:移动到系统目录方便后续使用
sudo mkdir -p /opt/cursor
sudo mv cursor.AppImage /opt/cursor/
sudo ln -s /opt/cursor/cursor.AppImage /usr/local/bin/cursor

macOS:

bash 复制代码
# 使用 Homebrew 安装(如果有 cask)
brew install --cask cursor

# 或者从官网下载 .dmg 文件,拖入 Applications 文件夹
# 访问 https://cursor.sh 下载最新版本

Windows (WSL2 环境):

powershell 复制代码
# 在 PowerShell 中安装 WSL2(如果尚未安装)
wsl --install -d Ubuntu-22.04

# 重启电脑后,在 WSL2 中按照 Linux 步骤安装
# Windows 原生版本可直接从 cursor.sh 下载安装包
1.2.2 首次启动配置

安装完成后,首次启动 Cursor 会引导你完成以下配置:

  1. 选择主题:推荐 Dark+ 或 One Dark Pro(运维场景下长时间使用深色主题更护眼)
  2. 导入 VS Code 配置:如果你之前使用 VS Code,可以选择导入设置和扩展
  3. 登录账号:注册并登录 Cursor 账号以激活 AI 功能
  4. 选择 AI 模型:初始推荐选择 Claude 3.5 Sonnet 或 GPT-4o
1.2.3 必装扩展

在 Cursor 中安装以下扩展(通过 Ctrl+Shift+X 打开扩展面板):

复制代码
# YAML 支持(Ansible 依赖)
- redhat.vscode-yaml
- redhat.ansible

# Terraform 支持
- hashicorp.terraform

# Git 增强
- eamodio.gitlens
- mhutchie.git-graph

# 代码质量
- ms-python.python(Ansible 依赖 Python)
- streetsidesoftware.code-spell-checker

# 终端增强
- pkief.material-icon-theme

使用命令行批量安装:

bash 复制代码
# 安装 Ansible 相关扩展
cursor --install-extension redhat.vscode-yaml
cursor --install-extension redhat.ansible

# 安装 Terraform 扩展
cursor --install-extension hashicorp.terraform

# 安装 Git 增强扩展
cursor --install-extension eamodio.gitlens
cursor --install-extension mhutchie.git-graph

1.3 Git 环境配置与 SSH 密钥生成

1.3.1 Git 安装与全局配置
bash 复制代码
# Ubuntu/Debian
sudo apt update
sudo apt install -y git

# CentOS/RHEL
sudo dnf install -y git

# macOS
brew install git

# 验证安装
git --version
# 输出示例:git version 2.43.0

配置全局用户信息:

bash 复制代码
# 设置用户名(替换为你的真实姓名或 GitHub 用户名)
git config --global user.name "Zhang San"

# 设置邮箱(必须与 GitHub/GitLab 账号邮箱一致)
git config --global user.email "zhangsan@example.com"

# 设置默认分支名为 main
git config --global init.defaultBranch main

# 设置默认编辑器为 Cursor
git config --global core.editor "cursor --wait"

# 启用颜色输出
git config --global color.ui auto

# 设置 pull 策略为 rebase(保持提交历史线性)
git config --global pull.rebase true

# 查看配置
git config --global --list
1.3.2 SSH 密钥生成与添加
bash 复制代码
# 生成 Ed25519 类型的 SSH 密钥(推荐)
ssh-keygen -t ed25519 -C "zhangsan@example.com" -f ~/.ssh/id_ed25519_gitops

# 如果系统较旧不支持 ed25519,使用 RSA
# ssh-keygen -t rsa -b 4096 -C "zhangsan@example.com" -f ~/.ssh/id_rsa_gitops

# 启动 ssh-agent 并添加密钥
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519_gitops

# 查看公钥内容(复制到 GitHub/GitLab)
cat ~/.ssh/id_ed25519_gitops.pub

配置 SSH config 文件 ~/.ssh/config

复制代码
# ~/.ssh/config - SSH 连接配置

# GitHub 配置
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_ed25519_gitops
    PreferredAuthentications publickey

# GitLab 配置(如果使用)
Host gitlab.com
    HostName gitlab.com
    User git
    IdentityFile ~/.ssh/id_ed25519_gitops
    PreferredAuthentications publickey

# 通用设置
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
bash 复制代码
# 设置 SSH 配置文件权限
chmod 600 ~/.ssh/config
chmod 600 ~/.ssh/id_ed25519_gitops
chmod 644 ~/.ssh/id_ed25519_gitops.pub

# 测试 SSH 连接
ssh -T git@github.com
# 成功输出:Hi username! You've successfully authenticated...

1.4 Ansible 安装与连接验证

1.4.1 安装 Ansible
bash 复制代码
# 方法一:使用 pip 安装(推荐,版本最新)
# 首先确保 Python 3.9+ 已安装
python3 --version

# 创建虚拟环境(推荐做法,避免污染系统 Python)
python3 -m venv ~/ansible-env
source ~/ansible-env/bin/activate

# 安装 Ansible
pip install ansible==9.5.1  # 指定稳定版本
# 或者安装最新版
# pip install ansible

# 方法二:使用系统包管理器
# Ubuntu/Debian
sudo apt update
sudo apt install -y ansible

# CentOS/RHEL
sudo dnf install -y ansible-core

# 验证安装
ansible --version
# 输出示例:
# ansible [core 2.16.x]
#   config file = /etc/ansible/ansible.cfg
#   python version = 3.11.x
1.4.2 基础配置

创建 Ansible 配置文件:

bash 复制代码
# 创建项目目录
mkdir -p ~/gitops-project
cd ~/gitops-project

# 创建 ansible.cfg
cat > ansible.cfg << 'EOF'
[defaults]
# 清单文件路径
inventory = ./inventory/hosts.yml

# 角色路径
roles_path = ./roles

# 禁用 host key 检查(开发环境,生产环境请启用)
host_key_checking = False

# 设置默认远程用户
remote_user = deploy

# 并行连接数
forks = 20

# 超时设置(秒)
timeout = 30

# 日志路径
log_path = ./logs/ansible.log

# 禁用 cowsay(如果安装了的话,避免输出干扰)
nocows = 1

# 重试文件保存路径
retry_files_enabled = False

# 使用 YAML 回调插件(更美观的输出)
stdout_callback = yaml

# 收集事实时使用的子集
gather_subset = !all,!min,network,hardware

[privilege_escalation]
# 提权设置
become = True
become_method = sudo
become_user = root
become_ask_pass = False

[ssh_connection]
# SSH 长连接复用(大幅提升性能)
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r
EOF

# 创建日志目录
mkdir -p logs
1.4.3 创建测试清单并验证连接
bash 复制代码
# 创建 inventory 目录
mkdir -p inventory

# 创建测试清单文件
cat > inventory/hosts.yml << 'EOF'
---
# Ansible 清单文件 - 定义受管节点
all:
  children:
    # Web 服务器组
    webservers:
      hosts:
        web-01:
          ansible_host: 192.168.1.101
          ansible_port: 22
        web-02:
          ansible_host: 192.168.1.102
          ansible_port: 22
      vars:
        http_port: 80
        https_port: 443
    
    # 数据库服务器组
    dbservers:
      hosts:
        db-01:
          ansible_host: 192.168.1.201
      vars:
        db_port: 5432
    
    # 开发环境
    development:
      hosts:
        localhost:
          ansible_connection: local
          ansible_python_interpreter: "{{ ansible_playbook_python }}"
EOF

# 测试本地连接
ansible localhost -m ping
# 成功输出:
# localhost | SUCCESS => {
#     "changed": false,
#     "ping": "pong"
# }

# 测试清单解析
ansible-inventory --list

1.5 Terraform 安装与 Provider 初始化

1.5.1 安装 Terraform
bash 复制代码
# Ubuntu/Debian - 使用官方仓库
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform

# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/terraform

# CentOS/RHEL
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraform

# 验证安装
terraform version
# 输出示例:Terraform v1.8.x on linux_amd64
1.5.2 配置自动补全与格式化
bash 复制代码
# Bash 自动补全
terraform -install-autocomplete

# 或者手动添加到 ~/.bashrc
echo 'complete -C /usr/bin/terraform terraform' >> ~/.bashrc
source ~/.bashrc

# 配置默认格式化
cat > ~/.terraformrc << 'EOF'
# Terraform CLI 配置
provider_installation {
  # 使用文件系统镜像(离线环境)
  # filesystem_mirror {
  #   path    = "/opt/terraform/providers"
  #   include = ["*/*"]
  # }
  
  # 默认从 registry 下载
  direct {
    exclude = []
  }
}

# 插件缓存(避免重复下载)
plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"
EOF

# 创建插件缓存目录
mkdir -p ~/.terraform.d/plugin-cache
1.5.3 初始化测试项目
bash 复制代码
# 在项目中创建 Terraform 目录
mkdir -p ~/gitops-project/terraform
cd ~/gitops-project/terraform

# 创建基础 main.tf
cat > main.tf << 'EOF'
# Terraform 基础配置
terraform {
  # 指定 Terraform 最低版本
  required_version = ">= 1.6.0"
  
  # 指定需要的 Provider
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# 配置 AWS Provider
provider "aws" {
  region  = var.aws_region
  profile = var.aws_profile
  
  # 默认标签(所有资源都会带上)
  default_tags {
    tags = {
      Environment = var.environment
      Project     = "gitops-demo"
      ManagedBy   = "terraform"
    }
  }
}

# 变量定义
variable "aws_region" {
  description = "AWS 区域"
  type        = string
  default     = "ap-southeast-1"
}

variable "aws_profile" {
  description = "AWS CLI Profile 名称"
  type        = string
  default     = "default"
}

variable "environment" {
  description = "部署环境标识"
  type        = string
  default     = "development"
}

# 输出
output "aws_region" {
  description = "当前部署的 AWS 区域"
  value       = var.aws_region
}
EOF

# 初始化 Terraform(下载 Provider)
terraform init

# 验证配置语法
terraform validate

# 预览执行计划(不会实际创建资源)
terraform plan

1.6 辅助工具链安装(Python、Docker、kubectl)

bash 复制代码
# ===== Python 环境 =====
# Ubuntu/Debian
sudo apt install -y python3 python3-pip python3-venv

# 安装常用 Python 工具
pip install yamllint ansible-lint jinja2-cli

# ===== Docker(用于本地测试)=====
# Ubuntu/Debian
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# 将当前用户加入 docker 组(免 sudo)
sudo usermod -aG docker $USER
newgrp docker

# 验证
docker --version
docker compose version

# ===== kubectl(如果需要管理 K8s)=====
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
kubectl version --client

# ===== 其他实用工具 =====
# jq - JSON 处理
sudo apt install -y jq

# tree - 目录结构查看
sudo apt install -y tree

# hcl2json - HCL 格式转换
# go install github.com/tmccombs/hcl2json@latest

1.7 项目目录结构规划

一个良好的项目结构是 GitOps 实践的基础。以下是推荐的标准目录结构:

复制代码
gitops-project/
├── .cursorrules                    # Cursor AI 规则配置
├── .gitignore                      # Git 忽略规则
├── .pre-commit-config.yaml         # Pre-commit 钩子配置
├── README.md                       # 项目说明文档
├── Makefile                        # 常用命令快捷入口
│
├── ansible/                        # Ansible 相关配置
│   ├── ansible.cfg                 # Ansible 主配置
│   ├── inventory/                  # 清单文件
│   │   ├── hosts.yml              # 主清单
│   │   ├── group_vars/            # 组变量
│   │   │   ├── all.yml
│   │   │   ├── webservers.yml
│   │   │   └── dbservers.yml
│   │   └── host_vars/             # 主机变量
│   │       ├── web-01.yml
│   │       └── db-01.yml
│   ├── playbooks/                  # Playbook 文件
│   │   ├── site.yml               # 主入口 Playbook
│   │   ├── deploy-web.yml         # Web 部署
│   │   └── setup-monitoring.yml   # 监控配置
│   ├── roles/                      # 角色目录
│   │   ├── nginx/
│   │   │   ├── tasks/main.yml
│   │   │   ├── handlers/main.yml
│   │   │   ├── templates/
│   │   │   ├── files/
│   │   │   ├── vars/main.yml
│   │   │   └── defaults/main.yml
│   │   └── postgresql/
│   └── collections/                # 自定义集合
│       └── requirements.yml
│
├── terraform/                      # Terraform 相关配置
│   ├── environments/               # 多环境配置
│   │   ├── development/
│   │   │   ├── main.tf
│   │   │   ├── variables.tf
│   │   │   ├── outputs.tf
│   │   │   └── terraform.tfvars
│   │   ├── staging/
│   │   └── production/
│   ├── modules/                    # 可复用模块
│   │   ├── vpc/
│   │   │   ├── main.tf
│   │   │   ├── variables.tf
│   │   │   └── outputs.tf
│   │   ├── ec2/
│   │   ├── rds/
│   │   └── security-group/
│   └── backend/                    # 后端配置
│       └── s3-backend.tf
│
├── .github/                        # GitHub Actions
│   └── workflows/
│       ├── ansible-deploy.yml
│       ├── terraform-plan.yml
│       └── terraform-apply.yml
│
├── scripts/                        # 辅助脚本
│   ├── setup.sh                   # 环境初始化
│   ├── validate.sh                # 代码验证
│   └── cleanup.sh                 # 资源清理
│
├── docs/                           # 文档
│   ├── architecture.md
│   ├── runbook.md
│   └── troubleshooting.md
│
└── logs/                           # 日志目录(不提交到 Git)
    ├── ansible.log
    └── terraform.log

创建此目录结构的命令:

bash 复制代码
#!/bin/bash
# scripts/init-project.sh - 项目目录初始化脚本

set -euo pipefail

PROJECT_DIR="gitops-project"

echo "🚀 初始化 GitOps 项目目录结构..."

# 创建顶层目录
mkdir -p ${PROJECT_DIR}/{ansible,terraform,scripts,docs,logs}

# Ansible 目录结构
mkdir -p ${PROJECT_DIR}/ansible/{inventory/{group_vars,host_vars},playbooks,roles,collections}

# Terraform 目录结构
mkdir -p ${PROJECT_DIR}/terraform/{environments/{development,staging,production},modules/{vpc,ec2,rds,security-group},backend}

# GitHub Actions
mkdir -p ${PROJECT_DIR}/.github/workflows

# 创建 .gitignore
cat > ${PROJECT_DIR}/.gitignore << 'GITIGNORE'
# Terraform
*.tfstate
*.tfstate.*
*.tfstate.backup
.terraform/
.terraform.lock.hcl
crash.log
override.tf
override.tf.json
*_override.tf
*_override.tf.json
*.tfvars
!example.tfvars

# Ansible
*.retry
logs/
.vault_pass

# Python
__pycache__/
*.py[cod]
*$py.class
*.egg-info/
dist/
build/
venv/
.venv/

# IDE
.idea/
.vscode/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

# 敏感信息
*.pem
*.key
.env
secrets/
GITIGNORE

echo "✅ 项目结构初始化完成!"
tree -L 3 ${PROJECT_DIR}/

二、Cursor 核心功能解析与 AI 上下文设定

2.1 Cursor 编辑器核心界面与快捷键

Cursor 基于 VS Code 构建,保留了 VS Code 的所有操作习惯,同时增加了 AI 专属功能。

核心界面布局:

复制代码
┌─────────────────────────────────────────────────────────┐
│  菜单栏 / 命令面板 (Ctrl+Shift+P)                        │
├──────────┬──────────────────────────────┬───────────────┤
│          │                              │               │
│  文件    │     编辑器主区域              │   AI 面板     │
│  资源    │     (代码编辑)               │   (Chat/      │
│  管理器  │                              │    Composer)  │
│          │                              │               │
│          │                              │               │
├──────────┴──────────────────────────────┴───────────────┤
│  终端 / 输出 / 问题 / 调试控制台                          │
├─────────────────────────────────────────────────────────┤
│  状态栏 (分支名、编码、行号等)                            │
└─────────────────────────────────────────────────────────┘

核心快捷键速查表:

功能 快捷键 (Linux/Win) 快捷键 (macOS)
打开 AI 聊天 Ctrl + L Cmd + L
打开 Composer Ctrl + I Cmd + I
行内 AI 编辑 Ctrl + K Cmd + K
AI 生成代码 Ctrl + Shift + L Cmd + Shift + L
命令面板 Ctrl + Shift + P Cmd + Shift + P
终端 ``Ctrl + ``` ``Cmd + ```
文件搜索 Ctrl + P Cmd + P
全局搜索 Ctrl + Shift + F Cmd + Shift + F
接受 AI 建议 Tab Tab
拒绝 AI 建议 Esc Esc
多行光标 Alt + Click Option + Click

2.2 AI 对话面板与 Composer 模式

2.2.1 Chat 模式(Ctrl+L)

Chat 模式适合提问、咨询和获取代码片段。AI 不会直接修改文件,而是在聊天面板中给出建议。

运维场景示例:

复制代码
用户输入:
"请帮我写一个 Ansible task,检查 Nginx 是否正在运行,
如果没有运行则启动它,并注册服务开机自启"

AI 输出:
```yaml
# tasks/check_nginx.yml - 检查并确保 Nginx 运行
- name: 检查 Nginx 服务状态
  ansible.builtin.service_facts:
  register: service_status

- name: 确保 Nginx 服务正在运行且开机自启
  ansible.builtin.systemd:
    name: nginx
    state: started
    enabled: yes
  when: "'nginx.service' not in service_status.ansible_facts.services 
         or service_status.ansible_facts.services['nginx.service'].state != 'running'"
2.2.2 Composer 模式(Ctrl+I)

Composer 模式是 Cursor 最强大的功能之一。AI 可以直接创建、修改多个文件,适合大规模代码生成。

使用技巧:

  1. 打开 Composer(Ctrl+I
  2. 描述你的需求(越详细越好)
  3. AI 会展示将要进行的文件变更
  4. 逐一审查后点击 "Accept All" 或逐个接受

示例 Prompt:

复制代码
创建一个完整的 Ansible Role 用于部署 Nginx 反向代理:
- 安装 nginx 包
- 配置反向代理到后端应用(端口 8080)
- 配置 SSL(使用 Let's Encrypt)
- 设置 systemd 服务管理
- 包含健康检查 handler
- 使用变量参数化所有可配置项
2.2.3 行内编辑(Ctrl+K)

选中一段代码后按 Ctrl+K,可以直接对选中内容进行 AI 修改。

场景: 选中一段 Ansible task,输入"添加错误处理和重试机制",AI 会自动添加 retriesdelayregisteruntil 等关键字。

2.3 上下文管理:@符号引用与文件索引

Cursor 支持通过 @ 符号向 AI 提供精确上下文:

符号 功能 示例
@filename 引用特定文件 @main.tf 这个文件有什么错误?
@folder 引用整个文件夹 @roles/nginx 优化这个角色
@code 引用选中的代码 @code 重构这段代码
@docs 引用外部文档 @docs terraform aws_instance
@git 引用 Git 信息 @git 最近的变更有什么风险?
@web 联网搜索 @web ansible 2.16 新特性
@codebase 引用整个代码库 @codebase 找出所有硬编码的 IP 地址

最佳实践:

复制代码
# 好的 Prompt(提供充分上下文)
"@ansible/playbooks/deploy-web.yml @ansible/roles/nginx/tasks/main.yml 
请检查这个部署流程是否有遗漏的步骤,特别是 SSL 证书更新后的重载"

# 不好的 Prompt(上下文不足)
"帮我修一下这个 bug"

2.4 .cursorrules 文件编写规范

.cursorrules 文件放在项目根目录,用于告诉 AI 项目的规范、偏好和约束。这是提升 AI 生成代码质量的关键。

markdown 复制代码
# .cursorrules - GitOps 自动化运维项目 AI 规则

## 项目概述
这是一个基于 GitOps 的自动化运维项目,使用 Ansible 进行配置管理,
Terraform 进行基础设施编排。所有基础设施变更必须通过 Git 提交触发。

## 代码规范

### Ansible 规范
- 所有 Playbook 和 Task 文件必须使用 YAML 格式
- 文件以 `---` 开头
- 使用完整模块名(如 `ansible.builtin.copy` 而非 `copy`)
- 每个 task 必须有 `name` 字段,使用中文描述
- 变量使用 snake_case 命名
- 敏感信息必须使用 Ansible Vault 加密
- 使用 `when` 条件而非 `ignore_errors`
- Handler 用于服务重启等有副作用的操作
- 优先使用 `ansible.builtin` 集合中的模块
- 每个 Role 必须包含 defaults/main.yml 定义默认变量

### Terraform 规范
- 使用 HCL2 语法
- 每个资源必须有 `tags` 标签
- 变量必须有 `description`、`type`、`default`(如适用)
- 输出值必须有 `description`
- 使用 `terraform fmt` 格式化
- 敏感输出标记 `sensitive = true`
- 使用模块化设计,避免重复代码
- State 文件绝不提交到 Git
- 使用 `terraform.tfvars.example` 提供变量示例

### Git 规范
- 提交信息遵循 Conventional Commits 规范
- 格式:`type(scope): description`
- type: feat/fix/docs/refactor/chore/ci
- 分支命名:feature/xxx, fix/xxx, hotfix/xxx

## 安全要求
- 绝不在代码中硬编码密码、密钥、Token
- 使用环境变量或 Vault 管理敏感信息
- AWS 安全组必须限制入站规则
- 所有 S3 桶必须启用加密和版本控制
- Terraform 中禁止使用 `0.0.0.0/0` 作为入站规则(除特殊说明)

## 输出偏好
- 代码注释使用中文
- 解释说明使用中文
- 提供完整的可运行代码,不使用省略号
- 复杂逻辑添加行内注释
- 给出多种方案时说明各自优缺点

2.5 自定义 Prompt 模板与运维场景适配

在 Cursor 中,你可以创建常用的 Prompt 模板,提高日常工作效率。

模板一:Ansible Role 生成器

复制代码
请为我创建一个 Ansible Role,要求如下:

【角色名称】:{{role_name}}
【目标系统】:{{target_os}} (Ubuntu 22.04 / CentOS 9)
【功能描述】:{{description}}
【需要安装的软件包】:{{packages}}
【需要配置的端口】:{{ports}}
【配置文件路径】:{{config_path}}
【服务管理】:使用 systemd
【变量化要求】:所有可能变化的值都提取为变量
【幂等性】:确保多次执行结果一致
【Handler】:配置变更后自动重启服务

请生成完整的 Role 目录结构,包含 tasks、handlers、templates、
defaults、vars、meta 所有子目录的文件内容。

模板二:Terraform 模块生成器

复制代码
请为我创建一个 Terraform 模块,要求如下:

【模块名称】:{{module_name}}
【云提供商】:{{provider}} (AWS / Alicloud / Azure)
【资源类型】:{{resource_type}}
【核心功能】:{{description}}
【输入变量】:
  - 环境标识 (environment)
  - 项目名称 (project_name)
  - 区域/可用区
  - {{custom_vars}}
【输出值】:
  - 资源 ID
  - 资源名称
  - {{custom_outputs}}
【标签要求】:
  - Environment
  - Project
  - ManagedBy = "terraform"
  - CreatedAt
【安全要求】:{{security_requirements}}

请生成 main.tf、variables.tf、outputs.tf、README.md 四个文件。

模板三:故障排查助手

复制代码
我遇到了以下运维问题,请帮我分析和解决:

【错误信息】:
{{error_message}}

【执行环境】:
- 操作系统:{{os}}
- 工具版本:{{tool_version}}
- 执行命令:{{command}}

【已尝试的解决方案】:
{{attempted_solutions}}

【相关配置文件】:
{{config_content}}

请分析:
1. 错误的根本原因
2. 推荐的解决方案(按优先级排序)
3. 如何避免此类问题再次发生
4. 相关的最佳实践建议

2.6 模型选择策略与 Token 管理

不同场景的模型选择建议:

任务类型 推荐模型 原因
简单代码补全 Claude 3.5 Haiku / GPT-4o-mini 速度快,成本低
Playbook 编写 Claude 3.5 Sonnet / GPT-4o 理解能力强,代码质量高
复杂架构设计 Claude 3.5 Opus / GPT-4 推理能力最强
代码审查 Claude 3.5 Sonnet 细致,能发现潜在问题
错误排查 Claude 3.5 Sonnet / GPT-4o 需要广泛的知识库

Token 管理技巧:

  1. 精简上下文 :只引用必要的文件,避免 @codebase 引入过多无关代码
  2. 分步执行:将大任务拆分为小步骤,每步独立对话
  3. 利用 .cursorrules:将规范写入规则文件,减少每次重复描述
  4. 及时开启新对话:上下文过长时,AI 性能会下降

三、利用 Cursor 快速生成标准化 Ansible 剧本

3.1 Ansible 基础概念与目录规范

在开始生成代码之前,确保你理解 Ansible 的核心概念:

  • Playbook:定义"做什么"的 YAML 文件,包含一个或多个 Play
  • Task:Playbook 中的最小执行单元,调用一个模块
  • Role:可复用的 Playbook 组织单元
  • Inventory:定义受管主机的清单
  • Module:Ansible 执行具体操作的代码单元
  • Handler:被通知时触发的特殊 Task(如重启服务)
  • Variable:参数化配置
  • Template:Jinja2 模板文件

标准 Role 目录结构:

复制代码
roles/
└── nginx/
    ├── defaults/
    │   └── main.yml          # 默认变量(优先级最低)
    ├── vars/
    │   └── main.yml          # 角色变量(优先级较高)
    ├── tasks/
    │   ├── main.yml          # 任务入口
    │   ├── install.yml       # 安装相关任务
    │   ├── configure.yml     # 配置相关任务
    │   └── service.yml       # 服务管理任务
    ├── handlers/
    │   └── main.yml          # Handler 定义
    ├── templates/
    │   ├── nginx.conf.j2     # Jinja2 模板
    │   └── vhost.conf.j2     # 虚拟主机模板
    ├── files/
    │   └── index.html        # 静态文件
    ├── meta/
    │   └── main.yml          # 角色元数据(依赖关系)
    ├── tests/
    │   ├── inventory
    │   └── test.yml          # 测试 Playbook
    └── README.md             # 角色说明文档

3.2 使用自然语言生成 Playbook 骨架

在 Cursor Composer 中输入以下 Prompt:

复制代码
创建一个 Ansible Playbook 用于在 Ubuntu 22.04 服务器上部署 Nginx Web 服务:

要求:
1. 更新 apt 缓存
2. 安装 nginx 和必要工具(curl, vim)
3. 配置 nginx 主配置文件(使用模板)
4. 创建一个简单的虚拟主机配置
5. 确保 nginx 服务启动并设置开机自启
6. 配置 UFW 防火墙允许 80 和 443 端口
7. 所有操作需要幂等
8. 使用 become 提权
9. 添加适当的 tags 便于选择性执行

AI 生成的代码:

yaml 复制代码
---
# playbooks/deploy-nginx.yml
# Nginx Web 服务自动化部署 Playbook
# 目标系统:Ubuntu 22.04 LTS
# 用法:ansible-playbook playbooks/deploy-nginx.yml -i inventory/hosts.yml

- name: 部署 Nginx Web 服务
  hosts: webservers
  become: true                          # 提权执行
  gather_facts: true                    # 收集目标主机信息
  
  vars:
    nginx_version: "latest"             # Nginx 版本
    http_port: 80                       # HTTP 端口
    https_port: 443                     # HTTPS 端口
    server_name: "example.com"          # 服务器名称
    web_root: "/var/www/html"           # Web 根目录
    worker_processes: "auto"            # Worker 进程数(auto=CPU核心数)
    worker_connections: 1024            # 每个 Worker 最大连接数

  tasks:
    # ========== 系统准备阶段 ==========
    - name: 更新 apt 软件包缓存
      ansible.builtin.apt:
        update_cache: yes               # 等同于 apt-get update
        cache_valid_time: 3600          # 缓存有效期 1 小时,避免频繁更新
      tags:
        - system
        - prepare

    - name: 安装基础依赖工具
      ansible.builtin.apt:
        name:
          - curl                        # HTTP 请求工具
          - vim                         # 文本编辑器
          - gnupg2                      # GPG 工具
          - software-properties-common  # 软件源管理
        state: present                  # 确保已安装
      tags:
        - system
        - prepare

    # ========== Nginx 安装阶段 ==========
    - name: 安装 Nginx
      ansible.builtin.apt:
        name: "nginx={{ nginx_version }}" if nginx_version != "latest" else "nginx"
        state: "{{ 'present' if nginx_version == 'latest' else 'present' }}"
        update_cache: no                # 前面已更新,无需重复
      tags:
        - nginx
        - install

    - name: 确保 Nginx 服务已启动并设置开机自启
      ansible.builtin.systemd:
        name: nginx
        state: started                  # 确保运行中
        enabled: yes                    # 开机自启
        daemon_reload: yes              # 重载 systemd 配置
      tags:
        - nginx
        - service

    # ========== Nginx 配置阶段 ==========
    - name: 创建 Web 根目录
      ansible.builtin.file:
        path: "{{ web_root }}"
        state: directory                # 确保目录存在
        owner: www-data                 # 属主
        group: www-data                 # 属组
        mode: "0755"                    # 权限
      tags:
        - nginx
        - configure

    - name: 部署 Nginx 主配置文件
      ansible.builtin.template:
        src: templates/nginx.conf.j2    # Jinja2 模板源
        dest: /etc/nginx/nginx.conf     # 目标路径
        owner: root
        group: root
        mode: "0644"
        backup: yes                     # 修改前备份原文件
      notify: Reload Nginx              # 配置变更时触发 Handler
      tags:
        - nginx
        - configure

    - name: 部署默认虚拟主机配置
      ansible.builtin.template:
        src: templates/vhost.conf.j2
        dest: "/etc/nginx/sites-available/{{ server_name }}.conf"
        owner: root
        group: root
        mode: "0644"
      notify: Reload Nginx
      tags:
        - nginx
        - configure

    - name: 启用虚拟主机(创建符号链接)
      ansible.builtin.file:
        src: "/etc/nginx/sites-available/{{ server_name }}.conf"
        dest: "/etc/nginx/sites-enabled/{{ server_name }}.conf"
        state: link                     # 创建符号链接
      notify: Reload Nginx
      tags:
        - nginx
        - configure

    - name: 移除默认站点配置(避免冲突)
      ansible.builtin.file:
        path: /etc/nginx/sites-enabled/default
        state: absent                   # 确保不存在
      notify: Reload Nginx
      tags:
        - nginx
        - configure

    # ========== 防火墙配置阶段 ==========
    - name: 配置 UFW 允许 HTTP 流量
      community.general.ufw:
        rule: allow
        port: "{{ http_port }}"
        proto: tcp
        comment: "Allow HTTP traffic"
      tags:
        - firewall
        - security

    - name: 配置 UFW 允许 HTTPS 流量
      community.general.ufw:
        rule: allow
        port: "{{ https_port }}"
        proto: tcp
        comment: "Allow HTTPS traffic"
      tags:
        - firewall
        - security

    - name: 确保 UFW 防火墙已启用
      community.general.ufw:
        state: enabled
        policy: deny                    # 默认拒绝所有入站
      tags:
        - firewall
        - security

    # ========== 验证阶段 ==========
    - name: 等待 Nginx 端口就绪
      ansible.builtin.wait_for:
        port: "{{ http_port }}"
        host: "127.0.0.1"
        delay: 2                        # 等待 2 秒后开始检查
        timeout: 30                     # 最多等待 30 秒
        state: started                  # 等待端口处于监听状态
      tags:
        - nginx
        - verify

    - name: 验证 Nginx 响应
      ansible.builtin.uri:
        url: "http://127.0.0.1:{{ http_port }}"
        status_code: 200                # 期望返回 200
        return_content: no
      register: nginx_check
      retries: 3                        # 失败重试 3 次
      delay: 5                          # 每次重试间隔 5 秒
      until: nginx_check.status == 200
      tags:
        - nginx
        - verify

  handlers:
    - name: Reload Nginx
      ansible.builtin.systemd:
        name: nginx
        state: reloaded                 # 优雅重载,不中断连接

    - name: Restart Nginx
      ansible.builtin.systemd:
        name: nginx
        state: restarted                # 完全重启

3.3 Roles 角色化拆分与 AI 辅助重构

当 Playbook 变得复杂时,需要拆分为 Role。在 Cursor 中,你可以让 AI 帮你重构:

Prompt:

复制代码
@playbooks/deploy-nginx.yml 
请将这个 Playbook 重构为标准的 Ansible Role 结构,
拆分为 install、configure、service 三个任务文件,
并创建对应的 templates 和 defaults。

AI 重构后的文件结构:

yaml 复制代码
# roles/nginx/tasks/main.yml
---
# Nginx 角色 - 任务入口文件
# 按顺序引入各子任务文件

- name: 引入安装任务
  ansible.builtin.import_tasks: install.yml
  tags:
    - nginx
    - install

- name: 引入配置任务
  ansible.builtin.import_tasks: configure.yml
  tags:
    - nginx
    - configure

- name: 引入服务管理任务
  ansible.builtin.import_tasks: service.yml
  tags:
    - nginx
    - service
yaml 复制代码
# roles/nginx/tasks/install.yml
---
# Nginx 安装相关任务

- name: 更新 apt 缓存
  ansible.builtin.apt:
    update_cache: yes
    cache_valid_time: 3600
  when: ansible_os_family == "Debian"

- name: 安装 Nginx 及依赖
  ansible.builtin.apt:
    name:
      - nginx
      - curl
    state: present
  when: ansible_os_family == "Debian"

- name: 安装 Nginx (RedHat 系列)
  ansible.builtin.yum:
    name:
      - nginx
      - curl
    state: present
  when: ansible_os_family == "RedHat"
yaml 复制代码
# roles/nginx/tasks/configure.yml
---
# Nginx 配置相关任务

- name: 创建必要目录
  ansible.builtin.file:
    path: "{{ item }}"
    state: directory
    owner: root
    group: root
    mode: "0755"
  loop:
    - "{{ nginx_web_root }}"
    - /etc/nginx/conf.d
    - /etc/nginx/snippets

- name: 部署主配置文件
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: "0644"
    backup: yes
    validate: "nginx -t -c %s"    # 部署前验证配置语法
  notify: Reload Nginx

- name: 部署站点配置
  ansible.builtin.template:
    src: vhost.conf.j2
    dest: "/etc/nginx/sites-available/{{ nginx_server_name }}.conf"
    owner: root
    group: root
    mode: "0644"
    backup: yes
  notify: Reload Nginx

- name: 启用站点
  ansible.builtin.file:
    src: "/etc/nginx/sites-available/{{ nginx_server_name }}.conf"
    dest: "/etc/nginx/sites-enabled/{{ nginx_server_name }}.conf"
    state: link
  notify: Reload Nginx

- name: 移除默认站点
  ansible.builtin.file:
    path: /etc/nginx/sites-enabled/default
    state: absent
  notify: Reload Nginx
  when: nginx_remove_default_site | bool
yaml 复制代码
# roles/nginx/defaults/main.yml
---
# Nginx 角色 - 默认变量
# 这些变量优先级最低,可在 inventory 或 playbook 中覆盖

# 基础配置
nginx_server_name: "example.com"
nginx_web_root: "/var/www/html"
nginx_http_port: 80
nginx_https_port: 443

# 性能配置
nginx_worker_processes: "auto"
nginx_worker_connections: 1024
nginx_keepalive_timeout: 65
nginx_client_max_body_size: "10m"

# 安全配置
nginx_server_tokens: "off"           # 隐藏版本号
nginx_remove_default_site: true      # 移除默认站点

# SSL 配置(默认关闭)
nginx_ssl_enabled: false
nginx_ssl_certificate: ""
nginx_ssl_certificate_key: ""

# 日志配置
nginx_access_log: "/var/log/nginx/access.log"
nginx_error_log: "/var/log/nginx/error.log"
nginx_log_format: "combined"

# 防火墙配置
nginx_configure_firewall: true
nginx_allowed_ips: []                # 空数组表示允许所有
yaml 复制代码
# roles/nginx/handlers/main.yml
---
# Nginx Handler 定义
# Handler 只在被 notify 触发时执行,且每个 Play 结束前只执行一次

- name: Reload Nginx
  ansible.builtin.systemd:
    name: nginx
    state: reloaded
  listen: "nginx reload"              # 使用 listen 允许多个名称触发

- name: Restart Nginx
  ansible.builtin.systemd:
    name: nginx
    state: restarted
  listen: "nginx restart"

- name: Test Nginx Config
  ansible.builtin.command: nginx -t
  changed_when: false                 # 此操作不会改变系统状态
  listen: "nginx test"

3.4 变量管理与 Vault 加密配置

yaml 复制代码
# inventory/group_vars/webservers.yml
---
# Web 服务器组变量

# 应用配置
app_name: "my-webapp"
app_version: "2.1.0"
app_port: 8080

# Nginx 配置覆盖
nginx_server_name: "web.example.com"
nginx_worker_connections: 2048

# 部署配置
deploy_user: "deploy"
deploy_dir: "/opt/{{ app_name }}"

# 监控配置
enable_monitoring: true
monitoring_port: 9100
yaml 复制代码
# inventory/group_vars/all/vault.yml (加密前)
---
# 敏感信息 - 必须使用 ansible-vault 加密
vault_db_password: "SuperSecret123!"
vault_api_key: "sk-xxxxxxxxxxxxxxxxxxxx"
vault_ssl_private_key: |
  -----BEGIN RSA PRIVATE KEY-----
  MIIEpAIBAAKCAQEA...
  -----END RSA PRIVATE KEY-----
bash 复制代码
# 使用 Ansible Vault 加密敏感文件
ansible-vault encrypt inventory/group_vars/all/vault.yml

# 创建 vault 密码文件(用于 CI/CD 自动解密)
echo "my-vault-password" > ~/.vault_pass
chmod 600 ~/.vault_pass

# 在 ansible.cfg 中配置自动使用密码文件
# [defaults]
# vault_password_file = ~/.vault_pass

# 编辑加密文件
ansible-vault edit inventory/group_vars/all/vault.yml

# 查看加密文件内容
ansible-vault view inventory/group_vars/all/vault.yml

3.5 Jinja2 模板生成与条件逻辑编写

jinja2 复制代码
{# roles/nginx/templates/nginx.conf.j2 #}
{# Nginx 主配置模板 - 由 Ansible 管理,请勿手动修改 #}
{# 生成时间:{{ ansible_date_time.iso8601 }} #}
{# 目标主机:{{ inventory_hostname }} #}

user www-data;
worker_processes {{ nginx_worker_processes }};
pid /run/nginx.pid;

{# 错误日志配置 #}
error_log {{ nginx_error_log }} warn;

events {
    worker_connections {{ nginx_worker_connections }};
    multi_accept on;
    use epoll;
}

http {
    {# ===== 基础设置 ===== #}
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    {# ===== 日志格式 ===== #}
    log_format main '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent" '
                    'rt=$request_time';

    access_log {{ nginx_access_log }} main;

    {# ===== 性能优化 ===== #}
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout {{ nginx_keepalive_timeout }};
    types_hash_max_size 2048;
    client_max_body_size {{ nginx_client_max_body_size }};

    {# ===== 安全设置 ===== #}
    server_tokens {{ nginx_server_tokens }};

    {# 隐藏不必要的响应头 #}
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    {# ===== Gzip 压缩 ===== #}
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css application/json 
               application/javascript text/xml application/xml 
               application/xml+rss text/javascript image/svg+xml;

    {# ===== 速率限制 ===== #}
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    limit_req_zone $binary_remote_addr zone=login_limit:10m rate=1r/s;

    {# ===== SSL 全局设置(如果启用)===== #}
    {% if nginx_ssl_enabled | bool %}
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    {% endif %}

    {# ===== 包含虚拟主机配置 ===== #}
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
jinja2 复制代码
{# roles/nginx/templates/vhost.conf.j2 #}
{# 虚拟主机配置模板 #}

{# HTTP 服务器块 #}
server {
    listen {{ nginx_http_port }};
    listen [::]:{{ nginx_http_port }};
    server_name {{ nginx_server_name }};

    {% if nginx_ssl_enabled | bool %}
    {# 如果启用 SSL,HTTP 重定向到 HTTPS #}
    return 301 https://$server_name$request_uri;
    {% else %}
    root {{ nginx_web_root }};
    index index.html index.htm;

    location / {
        try_files $uri $uri/ =404;
    }

    {# API 代理(如果配置了后端)#}
    {% if app_port is defined %}
    location /api/ {
        proxy_pass http://127.0.0.1:{{ app_port }}/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        {# 超时设置 #}
        proxy_connect_timeout 30s;
        proxy_send_timeout 30s;
        proxy_read_timeout 30s;
    }
    {% endif %}

    {# 健康检查端点 #}
    location /health {
        access_log off;
        return 200 "OK\n";
        add_header Content-Type text/plain;
    }

    {# 禁止访问隐藏文件 #}
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }
    {% endif %}
}

{% if nginx_ssl_enabled | bool %}
{# HTTPS 服务器块 #}
server {
    listen {{ nginx_https_port }} ssl http2;
    listen [::]:{{ nginx_https_port }} ssl http2;
    server_name {{ nginx_server_name }};

    ssl_certificate {{ nginx_ssl_certificate }};
    ssl_certificate_key {{ nginx_ssl_certificate_key }};

    root {{ nginx_web_root }};
    index index.html index.htm;

    {# HSTS 头 #}
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        try_files $uri $uri/ =404;
    }
}
{% endif %}

3.6 Handler 与幂等性保障

幂等性是 Ansible 的核心原则。以下是确保幂等性的关键技巧:

yaml 复制代码
# 幂等性最佳实践示例

# ✅ 正确:使用 state 参数确保状态
- name: 确保目录存在(幂等)
  ansible.builtin.file:
    path: /opt/app/data
    state: directory
    mode: "0755"

# ❌ 错误:使用 command 创建目录(不幂等)
- name: 创建目录(不幂等,每次都会执行)
  ansible.builtin.command: mkdir -p /opt/app/data

# ✅ 正确:使用 creates/removes 参数
- name: 初始化数据库(仅在数据目录不存在时执行)
  ansible.builtin.command: postgresql-setup initdb
  args:
    creates: /var/lib/pgsql/data/PG_VERSION

# ✅ 正确:使用 lineinfile 确保配置存在
- name: 确保 SSH 禁止 root 登录
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "^#?PermitRootLogin"
    line: "PermitRootLogin no"
    state: present
  notify: Restart SSHD

# ✅ 正确:使用 stat 检查后再操作
- name: 检查配置文件是否存在
  ansible.builtin.stat:
    path: /etc/app/config.yml
  register: config_file

- name: 备份现有配置
  ansible.builtin.copy:
    src: /etc/app/config.yml
    dest: "/etc/app/config.yml.bak.{{ ansible_date_time.date }}"
    remote_src: yes
  when: config_file.stat.exists

3.7 完整实战:Web 服务自动化部署 Playbook

yaml 复制代码
---
# playbooks/site.yml - 主入口 Playbook
# 用法:ansible-playbook playbooks/site.yml -i inventory/hosts.yml --limit webservers

- name: 基础系统配置
  ansible.builtin.import_playbook: common.yml
  tags:
    - common
    - base

- name: Web 服务部署
  ansible.builtin.import_playbook: deploy-web.yml
  tags:
    - web
    - deploy

- name: 监控配置
  ansible.builtin.import_playbook: setup-monitoring.yml
  tags:
    - monitoring
yaml 复制代码
---
# playbooks/deploy-web.yml - Web 服务完整部署

- name: 部署 Web 应用服务
  hosts: webservers
  become: true
  gather_facts: true
  serial: "30%"                       # 滚动部署,每次 30% 的主机
  max_fail_percentage: 20             # 失败超过 20% 则中止

  pre_tasks:
    - name: 验证目标主机连通性
      ansible.builtin.ping:
      
    - name: 检查磁盘空间(至少 2GB 可用)
      ansible.builtin.shell: |
        df -BG / | awk 'NR==2 {print $4}' | tr -d 'G'
      register: disk_space
      changed_when: false
      
    - name: 磁盘空间不足则中止
      ansible.builtin.fail:
        msg: "磁盘空间不足!可用空间 {{ disk_space.stdout }}GB,需要至少 2GB"
      when: disk_space.stdout | int < 2

  roles:
    - role: nginx
      tags: [nginx]
    - role: app-deploy
      tags: [app]

  post_tasks:
    - name: 执行健康检查
      ansible.builtin.uri:
        url: "http://localhost/health"
        status_code: 200
      register: health_check
      retries: 5
      delay: 10
      until: health_check.status == 200
      
    - name: 部署成功通知
      ansible.builtin.debug:
        msg: "✅ {{ inventory_hostname }} 部署成功!版本:{{ app_version }}"

四、基于自然语言描述构建 Terraform 基础设施代码

4.1 Terraform 核心概念:Provider、Resource、State

核心概念图解:

复制代码
┌─────────────────────────────────────────────────────────┐
│                    Terraform 工作流                       │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  编写 .tf 文件 → terraform init → terraform plan        │
│       │                                    │            │
│       │                                    ▼            │
│       │                           terraform apply       │
│       │                                    │            │
│       ▼                                    ▼            │
│  HCL 声明式语法              实际云资源创建/修改/删除      │
│  (期望状态)                              │              │
│                                          ▼              │
│                              State 文件记录实际状态       │
│                              (.tfstate)                 │
└─────────────────────────────────────────────────────────┘

关键术语:

  • Provider:与云平台 API 交互的插件(AWS、Azure、Alicloud 等)
  • Resource:要管理的基础设施对象(EC2 实例、VPC、S3 桶等)
  • Data Source:读取已有基础设施信息(不创建资源)
  • State:记录 Terraform 管理的所有资源的当前状态
  • Module:可复用的 Terraform 配置包
  • Variable:输入参数
  • Output:输出值
  • Backend:State 文件的存储位置

4.2 自然语言到 HCL 代码的转换技巧

有效的 Prompt 编写技巧:

复制代码
# ❌ 模糊的描述
"帮我创建一个服务器"

# ✅ 精确的描述
"在 AWS ap-southeast-1 区域创建一台 EC2 实例:
- 实例类型:t3.medium
- AMI:Ubuntu 22.04 LTS(使用 data source 查找最新 AMI)
- VPC:使用已有 VPC(ID 通过变量传入)
- 子网:公共子网
- 安全组:允许 22(SSH)、80(HTTP)、443(HTTPS) 入站
- 根卷:50GB gp3
- 标签:Name=web-server-01, Environment=production
- 启用详细监控
- 关联 Elastic IP"

转换对照表:

自然语言 HCL 对应
"创建一台服务器" resource "aws_instance" "web" {}
"在 VPC 内" vpc_id = var.vpc_id
"允许 80 端口" ingress { from_port = 80 ... }
"使用最新的 Ubuntu" data "aws_ami" "ubuntu" {}
"标签" tags = { ... }
"输出 IP 地址" output "public_ip" { value = ... }

4.3 VPC 网络基础设施代码生成

Cursor Prompt:

复制代码
创建一个 AWS VPC 模块,包含:
- VPC(CIDR: 10.0.0.0/16)
- 2 个公有子网(跨 AZ)
- 2 个私有子网(跨 AZ)
- Internet Gateway
- NAT Gateway(每个 AZ 一个)
- 路由表(公有、私有各一个)
- 所有资源带标准标签

生成的代码:

hcl 复制代码
# terraform/modules/vpc/main.tf
# AWS VPC 网络基础设施模块
# 提供完整的网络隔离和 NAT 出口能力

terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# ===== 数据源:获取可用区信息 =====
data "aws_availability_zones" "available" {
  state = "available"
}

# ===== VPC 主体 =====
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_support   = true    # 启用 DNS 解析
  enable_dns_hostnames = true    # 启用 DNS 主机名

  tags = {
    Name = "${var.project_name}-vpc"
  }
}

# ===== Internet Gateway(VPC 互联网出口)=====
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "${var.project_name}-igw"
  }
}

# ===== 公有子网(可访问互联网)=====
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       = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true  # 自动分配公网 IP

  tags = {
    Name = "${var.project_name}-public-${count.index + 1}"
    Type = "public"
    # K8s 需要的标签(如果使用 EKS)
    "kubernetes.io/role/elb" = "1"
  }
}

# ===== 私有子网(仅内网访问)=====
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 = data.aws_availability_zones.available.names[count.index]

  tags = {
    Name = "${var.project_name}-private-${count.index + 1}"
    Type = "private"
    "kubernetes.io/role/internal-elb" = "1"
  }
}

# ===== Elastic IP(NAT Gateway 使用)=====
resource "aws_eip" "nat" {
  count  = var.nat_gateway_count
  domain = "vpc"

  tags = {
    Name = "${var.project_name}-nat-eip-${count.index + 1}"
  }

  depends_on = [aws_internet_gateway.main]
}

# ===== NAT Gateway(私有子网出网)=====
resource "aws_nat_gateway" "main" {
  count = var.nat_gateway_count

  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id  # NAT 必须在公有子网

  tags = {
    Name = "${var.project_name}-nat-${count.index + 1}"
  }

  depends_on = [aws_internet_gateway.main]
}

# ===== 公有路由表 =====
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  # 默认路由指向 Internet Gateway
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = "${var.project_name}-public-rt"
  }
}

# ===== 私有路由表 =====
resource "aws_route_table" "private" {
  count  = var.nat_gateway_count
  vpc_id = aws_vpc.main.id

  # 默认路由指向 NAT Gateway
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main[count.index].id
  }

  tags = {
    Name = "${var.project_name}-private-rt-${count.index + 1}"
  }
}

# ===== 路由表关联 =====
# 公有子网关联公有路由表
resource "aws_route_table_association" "public" {
  count = length(aws_subnet.public)

  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

# 私有子网关联私有路由表
resource "aws_route_table_association" "private" {
  count = length(aws_subnet.private)

  subnet_id      = aws_subnet.private[count.index].id
  route_table_id = aws_route_table.private[count.index % var.nat_gateway_count].id
}
hcl 复制代码
# terraform/modules/vpc/variables.tf
# VPC 模块输入变量定义

variable "project_name" {
  description = "项目名称,用于资源命名前缀"
  type        = string
}

variable "vpc_cidr" {
  description = "VPC CIDR 地址段"
  type        = string
  default     = "10.0.0.0/16"
  
  validation {
    condition     = can(cidrhost(var.vpc_cidr, 0))
    error_message = "VPC CIDR 格式无效,请使用标准 CIDR 表示法,如 10.0.0.0/16"
  }
}

variable "public_subnet_cidrs" {
  description = "公有子网 CIDR 列表"
  type        = list(string)
  default     = ["10.0.1.0/24", "10.0.2.0/24"]
}

variable "private_subnet_cidrs" {
  description = "私有子网 CIDR 列表"
  type        = list(string)
  default     = ["10.0.10.0/24", "10.0.11.0/24"]
}

variable "nat_gateway_count" {
  description = "NAT Gateway 数量(建议与 AZ 数量一致)"
  type        = number
  default     = 2
  
  validation {
    condition     = var.nat_gateway_count >= 1 && var.nat_gateway_count <= 3
    error_message = "NAT Gateway 数量必须在 1-3 之间"
  }
}
hcl 复制代码
# terraform/modules/vpc/outputs.tf
# VPC 模块输出值

output "vpc_id" {
  description = "VPC ID"
  value       = aws_vpc.main.id
}

output "vpc_cidr" {
  description = "VPC CIDR 地址段"
  value       = aws_vpc.main.cidr_block
}

output "public_subnet_ids" {
  description = "公有子网 ID 列表"
  value       = aws_subnet.public[*].id
}

output "private_subnet_ids" {
  description = "私有子网 ID 列表"
  value       = aws_subnet.private[*].id
}

output "internet_gateway_id" {
  description = "Internet Gateway ID"
  value       = aws_internet_gateway.main.id
}

output "nat_gateway_ids" {
  description = "NAT Gateway ID 列表"
  value       = aws_nat_gateway.main[*].id
}

4.4 计算资源(EC2/ECS)自动化创建

hcl 复制代码
# terraform/modules/ec2/main.tf
# EC2 实例模块 - 支持单台或多台实例

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# 查找最新的 Ubuntu 22.04 AMI
data "aws_ami" "ubuntu" {
  most_recent = true
  owners      = ["099720109477"]  # Canonical 官方账号 ID

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  filter {
    name   = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name   = "root-device-type"
    values = ["ebs"]
  }
}

# 安全组
resource "aws_security_group" "instance" {
  name_prefix = "${var.project_name}-${var.instance_name}-sg"
  vpc_id      = var.vpc_id
  description = "Security group for ${var.instance_name}"

  # SSH 入站(限制来源 IP)
  dynamic "ingress" {
    for_each = var.allowed_ssh_cidrs
    content {
      from_port   = 22
      to_port     = 22
      protocol    = "tcp"
      cidr_blocks = [ingress.value]
      description = "SSH access from ${ingress.value}"
    }
  }

  # HTTP 入站
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "HTTP access"
  }

  # HTTPS 入站
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "HTTPS access"
  }

  # 所有出站
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow all outbound"
  }

  tags = {
    Name = "${var.project_name}-${var.instance_name}-sg"
  }

  lifecycle {
    create_before_destroy = true  # 先创建新 SG 再删除旧的
  }
}

# EC2 实例
resource "aws_instance" "main" {
  ami                    = data.aws_ami.ubuntu.id
  instance_type          = var.instance_type
  subnet_id              = var.subnet_id
  vpc_security_group_ids = [aws_security_group.instance.id]
  key_name               = var.ssh_key_name

  # 根卷配置
  root_block_device {
    volume_type           = "gp3"
    volume_size           = var.root_volume_size
    encrypted             = true
    delete_on_termination = true
    iops                  = 3000
    throughput            = 125
  }

  # 监控
  monitoring = var.enable_detailed_monitoring

  # 元数据服务安全配置(IMDSv2)
  metadata_options {
    http_endpoint               = "enabled"
    http_tokens                 = "required"  # 强制 IMDSv2
    http_put_response_hop_limit = 1
  }

  # 用户数据(初始化脚本)
  user_data = templatefile("${path.module}/templates/user_data.sh.tpl", {
    hostname     = var.instance_name
    app_port     = var.app_port
    environment  = var.environment
  })

  tags = {
    Name        = "${var.project_name}-${var.instance_name}"
    Environment = var.environment
    Role        = var.instance_role
  }

  # 生命周期规则
  lifecycle {
    ignore_changes = [
      user_data,  # 忽略 user_data 变更(避免重建)
    ]
  }
}

# Elastic IP(可选)
resource "aws_eip" "instance" {
  count    = var.assign_eip ? 1 : 0
  instance = aws_instance.main.id
  domain   = "vpc"

  tags = {
    Name = "${var.project_name}-${var.instance_name}-eip"
  }
}

4.5 模块化设计与变量参数化

hcl 复制代码
# terraform/environments/development/main.tf
# 开发环境 - 主配置文件
# 组合各模块构建完整基础设施

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }

  # 远程 State 存储(S3 + DynamoDB 锁)
  backend "s3" {
    bucket         = "gitops-terraform-state"
    key            = "development/terraform.tfstate"
    region         = "ap-southeast-1"
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
}

provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Environment = "development"
      Project     = var.project_name
      ManagedBy   = "terraform"
      Team        = "platform"
    }
  }
}

# ===== VPC 模块 =====
module "vpc" {
  source = "../../modules/vpc"

  project_name       = var.project_name
  vpc_cidr           = "10.0.0.0/16"
  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.10.0/24", "10.0.11.0/24"]
  nat_gateway_count  = 1  # 开发环境只需 1 个 NAT(节省成本)
}

# ===== Web 服务器模块 =====
module "web_server" {
  source = "../../modules/ec2"

  project_name  = var.project_name
  instance_name = "web-01"
  instance_type = "t3.medium"
  environment   = "development"
  instance_role = "webserver"

  vpc_id    = module.vpc.vpc_id
  subnet_id = module.vpc.public_subnet_ids[0]

  ssh_key_name         = var.ssh_key_name
  allowed_ssh_cidrs    = var.allowed_ssh_cidrs
  root_volume_size     = 50
  assign_eip           = true
  app_port             = 8080
}

# ===== 数据库模块(示例)=====
# module "database" {
#   source = "../../modules/rds"
#   ...
# }

4.6 State 管理与远程后端配置

State 文件是 Terraform 的核心数据,记录了所有受管资源的实际状态。在生产环境中,必须使用远程后端存储 State,避免本地丢失或团队冲突。

hcl 复制代码
# terraform/backend/s3-backend-setup.tf
# 此文件用于一次性创建 State 存储基础设施
# 执行:terraform apply -target=aws_s3_bucket.terraform_state

terraform {
  required_version = ">= 1.6.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "ap-southeast-1"
}

# ===== S3 存储桶:存放 Terraform State 文件 =====
resource "aws_s3_bucket" "terraform_state" {
  bucket = "gitops-terraform-state-2024"

  # 防止意外删除
  lifecycle {
    prevent_destroy = true
  }

  tags = {
    Name      = "terraform-state-bucket"
    Purpose   = "Terraform State Storage"
    ManagedBy = "manual"
  }
}

# 启用版本控制(可恢复误删的 State)
resource "aws_s3_bucket_versioning" "state_versioning" {
  bucket = aws_s3_bucket.terraform_state.id

  versioning_configuration {
    status = "Enabled"
  }
}

# 服务端加密(保护 State 中的敏感数据)
resource "aws_s3_bucket_server_side_encryption_configuration" "state_encryption" {
  bucket = aws_s3_bucket.terraform_state.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "aws:kms"  # 使用 KMS 加密
    }
    bucket_key_enabled = true
  }
}

# 阻止所有公共访问
resource "aws_s3_bucket_public_access_block" "state_access" {
  bucket = aws_s3_bucket.terraform_state.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# 生命周期规则:旧版本 State 90 天后转入冷存储
resource "aws_s3_bucket_lifecycle_configuration" "state_lifecycle" {
  bucket = aws_s3_bucket.terraform_state.id

  rule {
    id     = "state-versioning-lifecycle"
    status = "Enabled"

    # 非当前版本 90 天后转为 IA 存储
    noncurrent_version_transition {
      noncurrent_days = 90
      storage_class   = "STANDARD_IA"
    }

    # 非当前版本 365 天后删除
    noncurrent_version_expiration {
      noncurrent_days = 365
    }
  }
}

# ===== DynamoDB 表:State 锁(防止并发操作冲突)=====
resource "aws_dynamodb_table" "terraform_lock" {
  name         = "terraform-state-lock"
  billing_mode = "PAY_PER_REQUEST"  # 按需付费(锁操作频率低)
  hash_key     = "LockID"           # 锁的唯一标识

  attribute {
    name = "LockID"
    type = "S"  # String 类型
  }

  tags = {
    Name      = "terraform-state-lock"
    Purpose   = "Terraform State Locking"
    ManagedBy = "manual"
  }
}

# ===== 输出 =====
output "state_bucket_name" {
  description = "State 存储桶名称"
  value       = aws_s3_bucket.terraform_state.id
}

output "lock_table_name" {
  description = "State 锁表名称"
  value       = aws_dynamodb_table.terraform_lock.id
}

在各环境中引用远程后端:

hcl 复制代码
# terraform/environments/production/main.tf(头部)
terraform {
  required_version = ">= 1.6.0"

  backend "s3" {
    # State 文件在 S3 中的路径
    bucket         = "gitops-terraform-state-2024"
    key            = "production/terraform.tfstate"
    region         = "ap-southeast-1"

    # DynamoDB 锁表(防止多人同时 apply)
    dynamodb_table = "terraform-state-lock"

    # 加密
    encrypt        = true

    # 可选:使用 KMS 自定义密钥
    # kms_key_id   = "arn:aws:kms:ap-southeast-1:123456789:key/xxx"
  }
}

State 管理常用命令:

bash 复制代码
# 查看当前 State 中的资源列表
terraform state list

# 查看某个资源的详细属性
terraform state show aws_instance.web

# 从 State 中移除资源(不销毁实际资源)
terraform state rm aws_instance.old_server

# 导入已有资源到 State
terraform import aws_instance.existing i-1234567890abcdef0

# 备份 State(重要操作前必做)
terraform state pull > backup_$(date +%Y%m%d_%H%M%S).tfstate

# 强制解锁(仅在确认无人操作时使用)
terraform force-unlock LOCK_ID

# State 迁移(更换后端时使用)
terraform init -migrate-state

4.7 完整实战:多云环境基础设施编排

以下是一个完整的生产级 Terraform 项目,编排 AWS 上的完整 Web 应用基础设施:

hcl 复制代码
# terraform/environments/production/main.tf
# 生产环境 - 完整基础设施编排
# 包含:VPC + EC2 + RDS + ALB + Route53 + CloudWatch

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.6"
    }
  }

  backend "s3" {
    bucket         = "gitops-terraform-state-2024"
    key            = "production/terraform.tfstate"
    region         = "ap-southeast-1"
    dynamodb_table = "terraform-state-lock"
    encrypt        = true
  }
}

# ===== Provider 配置 =====
provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Environment = "production"
      Project     = var.project_name
      ManagedBy   = "terraform"
      Team        = "platform-engineering"
      CostCenter  = "engineering"
    }
  }
}

# 生成随机后缀(避免资源名冲突)
resource "random_string" "suffix" {
  length  = 6
  special = false
  upper   = false
}

# ===== 数据源 =====
# 获取当前账号信息
data "aws_caller_identity" "current" {}

# 获取可用区
data "aws_availability_zones" "available" {
  state = "available"
}

# ===== 网络层 =====
module "network" {
  source = "../../modules/vpc"

  project_name       = var.project_name
  vpc_cidr           = "10.100.0.0/16"
  public_subnet_cidrs  = ["10.100.1.0/24", "10.100.2.0/24"]
  private_subnet_cidrs = ["10.100.10.0/24", "10.100.11.0/24"]
  nat_gateway_count  = 2  # 生产环境每个 AZ 一个 NAT
}

# ===== 安全组 =====
module "security_groups" {
  source = "../../modules/security-group"

  project_name = var.project_name
  vpc_id       = module.network.vpc_id

  # ALB 安全组
  alb_security_group_name = "${var.project_name}-alb-sg"
  alb_ingress_rules = [
    {
      port        = 80
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
      description = "HTTP"
    },
    {
      port        = 443
      protocol    = "tcp"
      cidr_blocks = ["0.0.0.0/0"]
      description = "HTTPS"
    }
  ]

  # 应用服务器安全组
  app_security_group_name = "${var.project_name}-app-sg"
  app_ingress_rules = [
    {
      port                     = 8080
      protocol                 = "tcp"
      source_security_group_id = module.security_groups.alb_sg_id
      description              = "App traffic from ALB"
    },
    {
      port        = 22
      protocol    = "tcp"
      cidr_blocks = var.bastion_allowed_cidrs
      description = "SSH from bastion"
    }
  ]

  # 数据库安全组
  db_security_group_name = "${var.project_name}-db-sg"
  db_ingress_rules = [
    {
      port                     = 5432
      protocol                 = "tcp"
      source_security_group_id = module.security_groups.app_sg_id
      description              = "PostgreSQL from app servers"
    }
  ]
}

# ===== 应用服务器 =====
module "app_servers" {
  source   = "../../modules/ec2"
  for_each = toset(["web-01", "web-02"])  # 创建 2 台

  project_name  = var.project_name
  instance_name = each.value
  instance_type = "t3.large"
  environment   = "production"
  instance_role = "webserver"

  vpc_id    = module.network.vpc_id
  subnet_id = module.network.private_subnet_ids[index(keys(toset(["web-01", "web-02"])), each.value)]

  ssh_key_name      = var.ssh_key_name
  allowed_ssh_cidrs = var.bastion_allowed_cidrs
  root_volume_size  = 100
  assign_eip        = false  # 私有子网不需要 EIP
  app_port          = 8080
}

# ===== 应用负载均衡器 =====
module "alb" {
  source = "../../modules/alb"

  project_name    = var.project_name
  vpc_id          = module.network.vpc_id
  subnet_ids      = module.network.public_subnet_ids
  security_groups = [module.security_groups.alb_sg_id]

  # 目标组配置
  target_group_port     = 8080
  target_group_protocol = "HTTP"
  health_check_path     = "/health"
  health_check_interval = 30

  # 监听器配置
  http_listener  = true
  https_listener = true
  ssl_certificate_arn = var.ssl_certificate_arn
}

# ===== 数据库 =====
module "database" {
  source = "../../modules/rds"

  project_name     = var.project_name
  environment      = "production"
  instance_class   = "db.t3.medium"
  engine           = "postgres"
  engine_version   = "15.4"
  allocated_storage = 100

  vpc_id            = module.network.vpc_id
  subnet_ids        = module.network.private_subnet_ids
  security_group_id = module.security_groups.db_sg_id

  db_name  = "appdb"
  username = "appadmin"

  # 生产环境配置
  multi_az            = true   # 多 AZ 高可用
  deletion_protection = true   # 防止误删
  backup_retention    = 7      # 备份保留 7 天
  storage_encrypted   = true   # 存储加密
}

# ===== DNS 配置 =====
resource "aws_route53_record" "app" {
  zone_id = var.route53_zone_id
  name    = "app.${var.domain_name}"
  type    = "A"

  alias {
    name                   = module.alb.dns_name
    zone_id                = module.alb.zone_id
    evaluate_target_health = true
  }
}

# ===== CloudWatch 告警 =====
resource "aws_cloudwatch_metric_alarm" "high_cpu" {
  alarm_name          = "${var.project_name}-high-cpu"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 3
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = 300
  statistic           = "Average"
  threshold           = 80
  alarm_description   = "CPU 使用率超过 80% 持续 15 分钟"
  alarm_actions       = [var.sns_topic_arn]

  dimensions = {
    InstanceId = module.app_servers["web-01"].instance_id
  }

  tags = {
    Name = "${var.project_name}-cpu-alarm"
  }
}
hcl 复制代码
# terraform/environments/production/variables.tf
# 生产环境变量定义

variable "aws_region" {
  description = "AWS 部署区域"
  type        = string
  default     = "ap-southeast-1"
}

variable "project_name" {
  description = "项目名称"
  type        = string
  default     = "gitops-webapp"
}

variable "domain_name" {
  description = "应用域名"
  type        = string
  default     = "example.com"
}

variable "ssh_key_name" {
  description = "EC2 SSH 密钥对名称"
  type        = string
  sensitive   = true
}

variable "bastion_allowed_cidrs" {
  description = "允许 SSH 访问的 CIDR 列表"
  type        = list(string)
  default     = []  # 默认为空,必须显式配置

  validation {
    condition     = length(var.bastion_allowed_cidrs) > 0
    error_message = "必须至少配置一个允许 SSH 访问的 CIDR 地址"
  }
}

variable "ssl_certificate_arn" {
  description = "ACM SSL 证书 ARN"
  type        = string
  sensitive   = true
}

variable "route53_zone_id" {
  description = "Route53 托管区域 ID"
  type        = string
}

variable "sns_topic_arn" {
  description = "告警通知 SNS Topic ARN"
  type        = string
}
hcl 复制代码
# terraform/environments/production/outputs.tf
# 生产环境输出值

output "vpc_id" {
  description = "VPC ID"
  value       = module.network.vpc_id
}

output "alb_dns_name" {
  description = "应用负载均衡器 DNS 名称"
  value       = module.alb.dns_name
}

output "app_url" {
  description = "应用访问 URL"
  value       = "https://app.${var.domain_name}"
}

output "database_endpoint" {
  description = "数据库连接地址"
  value       = module.database.endpoint
  sensitive   = true  # 标记为敏感,不会在终端显示
}

output "app_server_ids" {
  description = "应用服务器实例 ID"
  value       = { for k, v in module.app_servers : k => v.instance_id }
}

五、集成 Git 工作流实现代码版本管理与协作

5.1 GitOps 核心理念与运维场景映射

GitOps 是一种以 Git 仓库为唯一事实来源(Single Source of Truth)管理基础设施和应用的运维方法论。

GitOps 四大原则:

原则 含义 运维映射
声明式 用代码描述期望状态 Terraform/Ansible 配置
版本化 所有变更通过 Git 提交 每次部署都有记录
自动同步 自动将实际状态向期望状态收敛 CI/CD 自动执行
可审计 所有变更可追溯 Git log + PR 审查

传统运维 vs GitOps 运维:

复制代码
传统运维:
  工程师 → SSH 到服务器 → 手动修改 → 祈祷不出错
  问题:无记录、不可复现、无法回滚

GitOps 运维:
  工程师 → 修改代码 → Git 提交 → PR 审查 → 合并 → CI/CD 自动执行
  优势:有记录、可复现、秒级回滚、多人协作

5.2 仓库结构设计与分支策略

推荐的仓库结构(Mono-repo 模式):

复制代码
gitops-infrastructure/
├── .github/
│   ├── workflows/           # CI/CD 流水线
│   ├── CODEOWNERS           # 代码所有者
│   └── pull_request_template.md
├── ansible/                 # 配置管理
├── terraform/               # 基础设施编排
├── docs/                    # 文档
├── scripts/                 # 工具脚本
├── .cursorrules             # AI 规则
├── .pre-commit-config.yaml  # 提交前检查
├── Makefile                 # 命令入口
└── README.md

分支策略(Git Flow 简化版):

复制代码
main (生产环境)
  │
  ├── develop (开发/测试环境)
  │     │
  │     ├── feature/add-monitoring    ← 新功能
  │     ├── feature/upgrade-nginx     ← 新功能
  │     └── fix/security-patch        ← 修复
  │
  ├── release/v2.1.0                  ← 发布准备
  │
  └── hotfix/critical-db-fix          ← 紧急修复(从 main 拉出)

分支保护规则(GitHub Settings → Branches):

yaml 复制代码
# main 分支保护
- 禁止直接推送
- 必须通过 Pull Request
- 至少 1 人审查通过
- 所有 CI 检查必须通过
- 必须签名提交(可选)

# develop 分支保护
- 禁止直接推送(管理员除外)
- 必须通过 Pull Request
- CI 检查通过

5.3 Commit 规范与自动化提交信息生成

Conventional Commits 规范:

复制代码
<type>(<scope>): <subject>

[body]

[footer]

Type 类型说明:

Type 含义 示例
feat 新功能 feat(nginx): 添加 SSL 自动续期配置
fix 修复 fix(terraform): 修复安全组规则冲突
docs 文档 docs(ansible): 更新部署手册
refactor 重构 refactor(vpc): 拆分子网模块
chore 杂务 chore(deps): 升级 Ansible 到 2.16
ci CI/CD ci(github): 添加 Terraform plan 步骤
perf 性能 perf(nginx): 优化 worker 连接数
security 安全 security(iam): 收紧 S3 访问策略

使用 Cursor AI 生成 Commit Message:

在 Cursor 中,暂存文件后,打开 AI 聊天(Ctrl+L),输入:

复制代码
请根据以下 git diff 生成符合 Conventional Commits 规范的提交信息:
@git

配置 commitlint 强制规范:

bash 复制代码
# 安装 commitlint
npm install -g @commitlint/cli @commitlint/config-conventional

# 创建配置文件
cat > commitlint.config.js << 'EOF'
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    // type 必须是以下之一
    'type-enum': [2, 'always', [
      'feat', 'fix', 'docs', 'style', 'refactor',
      'perf', 'test', 'chore', 'ci', 'security', 'revert'
    ]],
    // scope 最大长度
    'scope-max-length': [2, 'always', 20],
    // subject 最大长度
    'subject-max-length': [2, 'always', 72],
    // subject 不以句号结尾
    'subject-full-stop': [2, 'never', '.'],
    // body 最大行长度
    'body-max-line-length': [1, 'always', 100],
  }
};
EOF

5.4 Pull Request 审查流程与 AI 辅助 Code Review

PR 模板(.github/pull_request_template.md):

markdown 复制代码
## 变更描述
<!-- 简要描述本次变更的内容和目的 -->

## 变更类型
- [ ] 新功能 (feat)
- [ ] 修复 (fix)
- [ ] 重构 (refactor)
- [ ] 配置变更 (chore)
- [ ] 文档更新 (docs)
- [ ] 安全修复 (security)

## 影响范围
<!-- 本次变更影响哪些环境/服务 -->
- [ ] Development
- [ ] Staging
- [ ] Production

## 测试验证
- [ ] 本地 `terraform plan` 通过
- [ ] 本地 `ansible-playbook --syntax-check` 通过
- [ ] 已在开发环境验证
- [ ] 回滚方案已确认

## 变更详情
<!-- 列出主要变更点 -->
1. 
2. 
3. 

## 风险评估
<!-- 是否有破坏性变更?是否需要停机? -->
风险等级:低 / 中 / 高

## 回滚方案
<!-- 如果部署失败,如何回滚 -->

## 截图/日志
<!-- 附上关键输出 -->

使用 Cursor AI 辅助 Code Review:

复制代码
Prompt:
请审查以下 Terraform 代码变更,关注:
1. 安全问题(开放端口、过度权限)
2. 成本影响(不必要的资源、过大的实例)
3. 最佳实践违反(缺少标签、缺少加密)
4. 潜在的部署风险(破坏性变更)

@git 最近的 diff

5.5 Git Hooks 与 Pre-commit 自动化检查

yaml 复制代码
# .pre-commit-config.yaml
# Pre-commit 钩子配置
# 安装:pip install pre-commit && pre-commit install

repos:
  # ===== 通用检查 =====
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace          # 去除行尾空格
      - id: end-of-file-fixer            # 确保文件以换行结尾
      - id: check-yaml                   # YAML 语法检查
        args: ['--allow-multiple-documents']
      - id: check-json                   # JSON 语法检查
      - id: check-merge-conflict         # 检查未解决的合并冲突
      - id: detect-private-key           # 检测私钥文件
      - id: check-added-large-files      # 检查大文件
        args: ['--maxkb=1000']
      - id: no-commit-to-branch          # 禁止直接提交到 main
        args: ['--branch', 'main']

  # ===== YAML Lint =====
  - repo: https://github.com/adrienverge/yamllint
    rev: v1.33.0
    hooks:
      - id: yamllint
        args: ['-d', '{extends: relaxed, rules: {line-length: {max: 150}}}']
        files: \.(yml|yaml)$

  # ===== Ansible Lint =====
  - repo: https://github.com/ansible/ansible-lint
    rev: v6.22.0
    hooks:
      - id: ansible-lint
        files: ansible/
        args: ['-c', 'ansible/.ansible-lint']

  # ===== Terraform 检查 =====
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.86.0
    hooks:
      - id: terraform_fmt                # 格式化检查
      - id: terraform_validate           # 语法验证
      - id: terraform_docs               # 自动更新文档
        args: ['--args=--lockfile=false']
      - id: terraform_tflint             # Lint 检查
        args:
          - '--args=--only=terraform_deprecated_interpolation'
          - '--args=--only=terraform_unused_declarations'
          - '--args=--only=terraform_naming_convention'
      - id: terraform_checkov            # 安全扫描
        args: ['--args=--quiet']

  # ===== 密钥检测 =====
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets
        args: ['--baseline', '.secrets.baseline']
bash 复制代码
# 安装 pre-commit
pip install pre-commit

# 初始化(安装所有钩子)
cd gitops-project
pre-commit install

# 手动运行所有检查
pre-commit run --all-files

# 仅运行特定钩子
pre-commit run terraform_fmt --all-files

# 更新钩子到最新版本
pre-commit autoupdate

5.6 多环境分支管理与标签策略

环境对应关系:

复制代码
分支/标签          →  环境          →  触发方式
─────────────────────────────────────────────────
develop            →  Development   →  推送自动部署
release/*          →  Staging       →  推送自动部署
main               →  Production    →  手动审批后部署
v*.*.*             →  版本快照      →  打标签时归档

标签规范:

bash 复制代码
# 版本标签格式:v主版本.次版本.补丁号
git tag -a v2.1.0 -m "Release v2.1.0: 添加监控告警功能"

# 环境快照标签
git tag -a prod-2024-01-15 -m "Production snapshot 2024-01-15"

# 推送标签
git push origin v2.1.0
git push origin --tags

# 查看所有标签
git tag -l "v*"

# 查看标签详情
git show v2.1.0

六、搭建 CI/CD 流水线自动执行部署脚本

6.1 CI/CD 流水线架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                      CI/CD 流水线架构                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  开发者推送代码                                              │
│       │                                                     │
│       ▼                                                     │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐              │
│  │  Lint &  │───▶│  Plan &  │───▶│  Apply & │              │
│  │ Validate │    │  Review  │    │  Deploy  │              │
│  └──────────┘    └──────────┘    └──────────┘              │
│       │               │               │                     │
│       ▼               ▼               ▼                     │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐              │
│  │ 语法检查 │    │ 变更预览 │    │ 实际部署 │              │
│  │ 安全扫描 │    │ PR 评论  │    │ 健康检查 │              │
│  │ 格式验证 │    │ 人工审批 │    │ 通知推送 │              │
│  └──────────┘    └──────────┘    └──────────┘              │
│                                                             │
│  触发条件:                                                  │
│  - Push to develop → Lint + Plan (dev)                     │
│  - PR to main → Lint + Plan + 审批                         │
│  - Merge to main → Apply (production)                      │
│  - Tag v* → 归档 + 通知                                    │
└─────────────────────────────────────────────────────────────┘

6.2 GitHub Actions 工作流配置

yaml 复制代码
# .github/workflows/terraform-plan.yml
# Terraform Plan 工作流 - 在 PR 中预览变更
# 触发条件:PR 修改了 terraform/ 目录下的文件

name: "Terraform Plan"

on:
  pull_request:
    branches: [main, develop]
    paths:
      - 'terraform/**'
      - '.github/workflows/terraform-plan.yml'

# 并发控制:同一 PR 只运行一个 Plan
concurrency:
  group: terraform-plan-${{ github.ref }}
  cancel-in-progress: true

# 权限设置(最小权限原则)
permissions:
  contents: read
  pull-requests: write

env:
  TF_VERSION: "1.7.0"
  AWS_REGION: "ap-southeast-1"
  WORKING_DIR: "terraform/environments"

jobs:
  # ===== 代码质量检查 =====
  terraform-lint:
    name: "🔍 Terraform Lint & Validate"
    runs-on: ubuntu-latest
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 安装 Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: Terraform Format Check
        id: fmt
        run: terraform fmt -check -recursive ${{ env.WORKING_DIR }}
        continue-on-error: true

      - name: Terraform Init (validate 需要)
        run: |
          for dir in ${{ env.WORKING_DIR }}/*/; do
            if [ -f "$dir/main.tf" ]; then
              echo "Initializing $dir"
              cd "$dir"
              terraform init -backend=false
              cd -
            fi
          done

      - name: Terraform Validate
        id: validate
        run: |
          for dir in ${{ env.WORKING_DIR }}/*/; do
            if [ -f "$dir/main.tf" ]; then
              echo "Validating $dir"
              cd "$dir"
              terraform validate -no-color
              cd -
            fi
          done

      - name: TFLint 检查
        uses: terraform-linters/setup-tflint@v4
        with:
          tflint_version: latest

      - name: Run TFLint
        run: |
          for dir in ${{ env.WORKING_DIR }}/*/; do
            if [ -f "$dir/main.tf" ]; then
              cd "$dir"
              tflint --init
              tflint --format compact
              cd -
            fi
          done

      - name: 发布 Lint 结果到 PR
        if: steps.fmt.outcome == 'failure'
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '⚠️ Terraform 格式检查未通过,请运行 `terraform fmt -recursive` 修复'
            })

  # ===== Terraform Plan =====
  terraform-plan:
    name: "📋 Terraform Plan (${{ matrix.environment }})"
    runs-on: ubuntu-latest
    needs: terraform-lint
    strategy:
      matrix:
        environment: [development, staging, production]
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 安装 Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: 配置 AWS 凭证
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Terraform Init
        working-directory: ${{ env.WORKING_DIR }}/${{ matrix.environment }}
        run: terraform init

      - name: Terraform Plan
        id: plan
        working-directory: ${{ env.WORKING_DIR }}/${{ matrix.environment }}
        run: |
          terraform plan -no-color -out=tfplan \
            -var-file=terraform.tfvars 2>&1 | tee plan_output.txt
        continue-on-error: true

      - name: 发布 Plan 结果到 PR
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const planOutput = fs.readFileSync(
              '${{ env.WORKING_DIR }}/${{ matrix.environment }}/plan_output.txt',
              'utf8'
            );
            
            const output = `## 📋 Terraform Plan - ${{ matrix.environment }}
            
            \`\`\`
            ${planOutput.substring(0, 60000)}
            \`\`\`
            
            *由 GitHub Actions 自动生成*`;
            
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            });

      - name: Plan 失败则标记
        if: steps.plan.outcome == 'failure'
        run: exit 1

6.3 Ansible 自动化执行 Pipeline

yaml 复制代码
# .github/workflows/ansible-deploy.yml
# Ansible 部署工作流
# 触发条件:合并到 main 分支且修改了 ansible/ 目录

name: "Ansible Deploy"

on:
  push:
    branches: [main]
    paths:
      - 'ansible/**'
  workflow_dispatch:  # 允许手动触发
    inputs:
      environment:
        description: '部署目标环境'
        required: true
        default: 'development'
        type: choice
        options:
          - development
          - staging
          - production
      playbook:
        description: '要执行的 Playbook'
        required: true
        default: 'site.yml'
        type: string
      limit:
        description: '限制目标主机(可选)'
        required: false
        type: string

env:
  ANSIBLE_VERSION: "9.5.1"
  PYTHON_VERSION: "3.11"

jobs:
  # ===== 语法检查 =====
  ansible-lint:
    name: "🔍 Ansible Lint"
    runs-on: ubuntu-latest
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 安装 Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}

      - name: 安装 Ansible 和 Lint 工具
        run: |
          pip install ansible==${{ env.ANSIBLE_VERSION }} ansible-lint yamllint

      - name: YAML 语法检查
        run: |
          yamllint -d relaxed ansible/

      - name: Ansible 语法检查
        run: |
          cd ansible
          ansible-playbook playbooks/site.yml --syntax-check

      - name: Ansible Lint
        run: |
          cd ansible
          ansible-lint playbooks/ roles/

  # ===== 部署执行 =====
  ansible-deploy:
    name: "🚀 Deploy to ${{ github.event.inputs.environment || 'production' }}"
    runs-on: ubuntu-latest
    needs: ansible-lint
    environment: ${{ github.event.inputs.environment || 'production' }}
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 安装 Python 和 Ansible
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}

      - name: 安装 Ansible
        run: |
          pip install ansible==${{ env.ANSIBLE_VERSION }}

      - name: 安装 Ansible Collections
        run: |
          cd ansible
          ansible-galaxy collection install -r collections/requirements.yml

      - name: 配置 SSH 密钥
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.DEPLOY_SSH_KEY }}" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key
          ssh-keyscan -H ${{ secrets.TARGET_HOSTS }} >> ~/.ssh/known_hosts 2>/dev/null || true

      - name: 配置 Vault 密码
        run: |
          echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > .vault_pass
          chmod 600 .vault_pass

      - name: 执行 Ansible Playbook
        working-directory: ansible
        run: |
          ENVIRONMENT="${{ github.event.inputs.environment || 'production' }}"
          PLAYBOOK="${{ github.event.inputs.playbook || 'site.yml' }}"
          LIMIT="${{ github.event.inputs.limit || '' }}"
          
          ANSIBLE_CMD="ansible-playbook playbooks/${PLAYBOOK} \
            -i inventory/hosts.yml \
            --vault-password-file ../.vault_pass \
            -e target_environment=${ENVIRONMENT} \
            -v"
          
          # 如果指定了 limit
          if [ -n "$LIMIT" ]; then
            ANSIBLE_CMD="${ANSIBLE_CMD} --limit ${LIMIT}"
          fi
          
          echo "执行命令: ${ANSIBLE_CMD}"
          eval ${ANSIBLE_CMD}

      - name: 清理敏感文件
        if: always()
        run: |
          rm -f ~/.ssh/deploy_key
          rm -f .vault_pass

6.4 Terraform Plan/Apply 自动化流程

yaml 复制代码
# .github/workflows/terraform-apply.yml
# Terraform Apply 工作流 - 实际创建/修改云资源
# 触发条件:PR 合并到 main 或手动触发(需审批)

name: "Terraform Apply"

on:
  push:
    branches: [main]
    paths:
      - 'terraform/**'
  workflow_dispatch:
    inputs:
      environment:
        description: '目标环境'
        required: true
        type: choice
        options:
          - development
          - staging
          - production
      auto_approve:
        description: '跳过人工审批(仅限 development)'
        required: false
        type: boolean
        default: false

permissions:
  contents: read
  id-token: write  # 用于 OIDC 认证

env:
  TF_VERSION: "1.7.0"
  AWS_REGION: "ap-southeast-1"

jobs:
  # ===== Plan 阶段 =====
  terraform-plan:
    name: "📋 Plan (${{ matrix.environment }})"
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment: [development, staging, production]
    outputs:
      plan_result: ${{ steps.plan.outputs.exitcode }}
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 安装 Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: 配置 AWS(使用 OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ env.AWS_REGION }}

      - name: Terraform Init
        working-directory: terraform/environments/${{ matrix.environment }}
        run: terraform init

      - name: Terraform Plan
        id: plan
        working-directory: terraform/environments/${{ matrix.environment }}
        run: |
          terraform plan -no-color -out=tfplan
          echo "exitcode=$?" >> $GITHUB_OUTPUT

      - name: 上传 Plan 文件
        uses: actions/upload-artifact@v4
        with:
          name: tfplan-${{ matrix.environment }}
          path: terraform/environments/${{ matrix.environment }}/tfplan
          retention-days: 5

  # ===== Apply 阶段(需要审批)=====
  terraform-apply:
    name: "🚀 Apply (${{ matrix.environment }})"
    runs-on: ubuntu-latest
    needs: terraform-plan
    # 生产环境需要人工审批
    environment: ${{ matrix.environment }}
    strategy:
      matrix:
        environment: [development, staging, production]
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 安装 Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: 配置 AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ env.AWS_REGION }}

      - name: 下载 Plan 文件
        uses: actions/download-artifact@v4
        with:
          name: tfplan-${{ matrix.environment }}
          path: terraform/environments/${{ matrix.environment }}

      - name: Terraform Init
        working-directory: terraform/environments/${{ matrix.environment }}
        run: terraform init

      - name: Terraform Apply
        working-directory: terraform/environments/${{ matrix.environment }}
        run: |
          terraform apply -auto-approve -no-color tfplan

      - name: 输出部署结果
        working-directory: terraform/environments/${{ matrix.environment }}
        run: |
          echo "## 部署输出" >> $GITHUB_STEP_SUMMARY
          echo '```' >> $GITHUB_STEP_SUMMARY
          terraform output -no-color >> $GITHUB_STEP_SUMMARY
          echo '```' >> $GITHUB_STEP_SUMMARY

6.5 密钥管理与 Secrets 注入

GitHub Secrets 配置清单:

Secret 名称 用途 来源
AWS_ACCESS_KEY_ID AWS 访问密钥 IAM 用户
AWS_SECRET_ACCESS_KEY AWS 密钥 IAM 用户
AWS_ROLE_ARN OIDC 角色 ARN IAM Role
DEPLOY_SSH_KEY 部署用 SSH 私钥 手动生成
ANSIBLE_VAULT_PASSWORD Vault 解密密码 手动设置
SLACK_WEBHOOK_URL 通知 Webhook Slack App
TARGET_HOSTS 目标主机 IP 运维配置

推荐使用 AWS OIDC 代替长期密钥:

yaml 复制代码
# 在 workflow 中使用 OIDC(更安全)
- name: 配置 AWS(OIDC 方式)
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
    aws-region: ap-southeast-1
    role-session-name: GitHubActions-${{ github.run_id }}

6.6 通知与回滚机制

yaml 复制代码
# 添加到 workflow 末尾的通知步骤

  # ===== 部署通知 =====
  notify:
    name: "📢 部署通知"
    runs-on: ubuntu-latest
    needs: [terraform-apply]
    if: always()  # 无论成功失败都通知
    steps:
      - name: 发送 Slack 通知
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ needs.terraform-apply.result }}
          fields: repo,commit,author,action,workflow
          text: |
            🔄 Terraform 部署 ${{ needs.terraform-apply.result == 'success' && '成功 ✅' || '失败 ❌' }}
            环境: production
            提交: ${{ github.event.head_commit.message }}
            操作人: ${{ github.actor }}
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

  # ===== 自动回滚 =====
  rollback:
    name: "⏪ 自动回滚"
    runs-on: ubuntu-latest
    needs: [terraform-apply]
    if: failure()  # 仅在 Apply 失败时触发
    steps:
      - name: 检出代码
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.before }}  # 回滚到上一个提交

      - name: 安装 Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: 配置 AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ${{ env.AWS_REGION }}

      - name: 执行回滚
        working-directory: terraform/environments/production
        run: |
          echo "⚠️ 检测到部署失败,正在回滚到上一个版本..."
          terraform init
          terraform apply -auto-approve -no-color

      - name: 回滚通知
        run: |
          echo "⏪ 已自动回滚到提交 ${{ github.event.before }}"

七、全流程实操:从代码生成到云端资源交付

7.1 项目需求分析与架构设计

实战场景: 为一家电商公司部署 Web 应用基础设施

需求:

  • 2 台 Web 服务器(Nginx + Node.js)
  • 1 台 PostgreSQL 数据库
  • VPC 网络隔离
  • 负载均衡器
  • SSL/TLS 加密
  • 自动化监控告警
  • 通过 Git 提交自动部署

架构图:

复制代码
                    ┌─────────────┐
                    │   Route53   │
                    │   (DNS)     │
                    └──────┬──────┘
                           │
                    ┌──────▼──────┐
                    │     ALB     │
                    │ (负载均衡)   │
                    └──┬──────┬──┘
                       │      │
              ┌────────▼┐  ┌▼────────┐
              │ Web-01  │  │ Web-02  │  ← 私有子网
              │ (Nginx) │  │ (Nginx) │
              └────┬────┘  └────┬────┘
                   │            │
                   └─────┬──────┘
                         │
                  ┌──────▼──────┐
                  │ PostgreSQL  │  ← 私有子网
                  │   (RDS)     │
                  └─────────────┘

7.2 使用 Cursor 生成全部基础设施代码

Step 1:打开 Cursor Composer(Ctrl+I),输入:

复制代码
根据以下架构需求,生成完整的 Terraform 配置:

1. VPC:10.200.0.0/16,2个公有子网,2个私有子网
2. ALB:面向公网,HTTP/HTTPS 监听
3. 2台 EC2(t3.medium)在私有子网,运行 Nginx 反向代理
4. RDS PostgreSQL 15(db.t3.medium)在私有子网
5. 安全组:ALB 允许 80/443,EC2 只允许 ALB 访问 8080,RDS 只允许 EC2 访问 5432
6. CloudWatch 告警:CPU > 80%
7. 所有资源带 Environment/Project/ManagedBy 标签

目标环境:staging
AWS 区域:ap-southeast-1

Step 2:AI 生成代码后,审查并调整

Step 3:生成对应的 Ansible Playbook:

复制代码
为上述 2 台 Web 服务器生成 Ansible Playbook:
1. 安装并配置 Nginx 作为反向代理(代理到 localhost:3000)
2. 安装 Node.js 20 LTS
3. 部署示例 Node.js 应用
4. 配置 PM2 进程管理
5. 设置 logrotate
6. 配置 fail2ban 防暴力破解

7.3 本地验证与 Lint 检查

bash 复制代码
#!/bin/bash
# scripts/validate.sh - 本地验证脚本
# 用法:./scripts/validate.sh

set -euo pipefail

echo "=========================================="
echo "  GitOps 项目本地验证"
echo "=========================================="

ERRORS=0

# ===== 1. Terraform 验证 =====
echo ""
echo "📦 [1/5] Terraform 验证..."
cd terraform/environments/staging

# 格式化检查
echo "  → 检查格式..."
if ! terraform fmt -check -recursive .; then
    echo "  ❌ 格式不正确,运行 'terraform fmt' 修复"
    ERRORS=$((ERRORS + 1))
else
    echo "  ✅ 格式正确"
fi

# 初始化(不连接后端)
echo "  → 初始化..."
terraform init -backend=false -input=false > /dev/null 2>&1

# 语法验证
echo "  → 语法验证..."
if terraform validate -no-color; then
    echo "  ✅ 语法正确"
else
    echo "  ❌ 语法错误"
    ERRORS=$((ERRORS + 1))
fi

cd ../../..

# ===== 2. Ansible 验证 =====
echo ""
echo "📦 [2/5] Ansible 验证..."
cd ansible

# YAML 语法检查
echo "  → YAML 语法检查..."
if yamllint -d relaxed playbooks/ roles/ inventory/ 2>/dev/null; then
    echo "  ✅ YAML 语法正确"
else
    echo "  ⚠️ 存在 YAML 警告"
fi

# Playbook 语法检查
echo "  → Playbook 语法检查..."
if ansible-playbook playbooks/site.yml --syntax-check > /dev/null 2>&1; then
    echo "  ✅ Playbook 语法正确"
else
    echo "  ❌ Playbook 语法错误"
    ERRORS=$((ERRORS + 1))
fi

# Ansible Lint
echo "  → Ansible Lint..."
if ansible-lint playbooks/ roles/ --quiet 2>/dev/null; then
    echo "  ✅ Lint 通过"
else
    echo "  ⚠️ 存在 Lint 警告"
fi

cd ..

# ===== 3. 密钥检测 =====
echo ""
echo "📦 [3/5] 密钥泄露检测..."
if command -v detect-secrets > /dev/null 2>&1; then
    if detect-secrets scan --baseline .secrets.baseline > /dev/null 2>&1; then
        echo "  ✅ 未检测到密钥泄露"
    else
        echo "  ❌ 检测到可能的密钥泄露!"
        ERRORS=$((ERRORS + 1))
    fi
else
    echo "  ⚠️ detect-secrets 未安装,跳过"
fi

# ===== 4. Git 状态检查 =====
echo ""
echo "📦 [4/5] Git 状态检查..."
if [ -n "$(git status --porcelain)" ]; then
    echo "  ⚠️ 存在未提交的变更"
else
    echo "  ✅ 工作区干净"
fi

# ===== 5. 文件权限检查 =====
echo ""
echo "📦 [5/5] 文件权限检查..."
INSECURE_FILES=$(find . -name "*.pem" -o -name "*.key" -o -name "id_rsa*" | head -5)
if [ -n "$INSECURE_FILES" ]; then
    echo "  ⚠️ 发现敏感文件,请确保已加入 .gitignore"
    echo "$INSECURE_FILES"
else
    echo "  ✅ 未发现敏感文件"
fi

# ===== 汇总 =====
echo ""
echo "=========================================="
if [ $ERRORS -eq 0 ]; then
    echo "  ✅ 所有验证通过!可以提交代码。"
else
    echo "  ❌ 发现 ${ERRORS} 个错误,请修复后再提交。"
    exit 1
fi
echo "=========================================="

7.4 推送代码触发 CI/CD

bash 复制代码
# 完整的提交流程

# 1. 确保所有变更已保存
git status

# 2. 运行本地验证
./scripts/validate.sh

# 3. 暂存所有变更
git add -A

# 4. 提交(遵循 Conventional Commits)
git commit -m "feat(infrastructure): 添加 staging 环境完整基础设施

- 新增 VPC 网络配置(2 AZ)
- 新增 ALB 负载均衡器
- 新增 2 台 Web 服务器(Nginx 反向代理)
- 新增 RDS PostgreSQL 数据库
- 新增 CloudWatch CPU 告警
- 配置 Ansible 自动化部署 Playbook

Closes #42"

# 5. 推送到远程
git push origin develop

# 6. 在 GitHub 上创建 PR(develop → main)
# 或使用 GitHub CLI:
gh pr create \
  --title "feat(infrastructure): 添加 staging 环境完整基础设施" \
  --body "## 变更内容
- 新增完整的 staging 环境 Terraform 配置
- 新增 Ansible 部署 Playbook

## 测试验证
- [x] terraform plan 通过
- [x] ansible --syntax-check 通过
- [x] 本地 Docker 环境验证通过

## 风险评估
风险等级:低(新环境,不影响生产)" \
  --base main \
  --head develop

7.5 云端资源验证与监控接入

bash 复制代码
# 部署完成后的验证清单

# 1. 验证 Terraform 输出
cd terraform/environments/staging
terraform output

# 2. 验证 EC2 实例状态
aws ec2 describe-instances \
  --filters "Name=tag:Environment,Values=staging" \
  --query "Reservations[].Instances[].[InstanceId,State.Name,PrivateIpAddress]" \
  --output table

# 3. 验证 ALB 健康状态
aws elbv2 describe-target-health \
  --target-group-arn $(terraform output -raw target_group_arn) \
  --query "TargetHealthDescriptions[].{Target:Target.Id,Health:TargetHealth.State}" \
  --output table

# 4. 验证 RDS 状态
aws rds describe-db-instances \
  --db-instance-identifier $(terraform output -raw db_instance_id) \
  --query "DBInstances[0].{Status:DBInstanceStatus,Endpoint:Endpoint.Address}" \
  --output table

# 5. 验证 DNS 解析
dig +short app-staging.example.com

# 6. 验证 HTTP 响应
curl -sI https://app-staging.example.com | head -20

# 7. 验证 SSL 证书
echo | openssl s_client -connect app-staging.example.com:443 2>/dev/null | \
  openssl x509 -noout -dates -subject

7.6 端到端测试与交付确认

yaml 复制代码
# ansible/playbooks/e2e-test.yml
# 端到端验证 Playbook - 部署完成后执行
# 用法:ansible-playbook playbooks/e2e-test.yml -e app_url=https://app-staging.example.com

---
- name: 端到端部署验证
  hosts: localhost
  gather_facts: false
  connection: local
  
  vars:
    app_url: "https://app-staging.example.com"
    expected_status: 200
    timeout: 30
    ssl_min_days: 30          # SSL 证书最少剩余天数
    max_response_time: 5      # 最大响应时间(秒)
    test_results: []          # 收集测试结果

  tasks:
    # ===== 1. 基础连通性测试 =====
    - name: 验证应用首页可访问
      ansible.builtin.uri:
        url: "{{ app_url }}"
        status_code: "{{ expected_status }}"
        timeout: "{{ timeout }}"
        validate_certs: true
        follow_redirects: all
      register: homepage_check
      retries: 3
      delay: 5
      until: homepage_check.status == expected_status | int
      failed_when: false

    - name: 记录首页测试结果
      ansible.builtin.set_fact:
        test_results: "{{ test_results + [{
          'name': '首页访问',
          'status': 'PASS' if homepage_check.status == expected_status | int else 'FAIL',
          'detail': 'HTTP ' + (homepage_check.status | default('N/A') | string)
        }] }}"

    # ===== 2. 健康检查端点 =====
    - name: 验证健康检查端点
      ansible.builtin.uri:
        url: "{{ app_url }}/health"
        status_code: 200
        timeout: "{{ timeout }}"
        return_content: true
      register: health_check
      retries: 3
      delay: 5
      until: health_check.status == 200
      failed_when: false

    - name: 记录健康检查结果
      ansible.builtin.set_fact:
        test_results: "{{ test_results + [{
          'name': '健康检查',
          'status': 'PASS' if health_check.status | default(0) == 200 else 'FAIL',
          'detail': health_check.content | default('无响应')
        }] }}"

    # ===== 3. API 端点验证 =====
    - name: 验证 API 状态端点
      ansible.builtin.uri:
        url: "{{ app_url }}/api/v1/status"
        status_code: 200
        method: GET
        timeout: "{{ timeout }}"
        headers:
          Accept: "application/json"
          User-Agent: "E2E-Test/1.0"
      register: api_check
      failed_when: false

    - name: 记录 API 测试结果
      ansible.builtin.set_fact:
        test_results: "{{ test_results + [{
          'name': 'API 状态端点',
          'status': 'PASS' if api_check.status | default(0) == 200 else 'FAIL',
          'detail': api_check.json | default({}) | to_nice_json
        }] }}"

    # ===== 4. SSL 证书验证 =====
    - name: 获取 SSL 证书信息
      ansible.builtin.shell: |
        echo | openssl s_client -connect {{ app_url | regex_replace('https?://', '') }}:443 \
          -servername {{ app_url | regex_replace('https?://', '') }} 2>/dev/null | \
          openssl x509 -noout -enddate -subject 2>/dev/null
      register: ssl_info
      changed_when: false
      failed_when: false

    - name: 检查 SSL 证书有效期
      ansible.builtin.shell: |
        END_DATE=$(echo | openssl s_client -connect {{ app_url | regex_replace('https?://', '') }}:443 \
          -servername {{ app_url | regex_replace('https?://', '') }} 2>/dev/null | \
          openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
        END_EPOCH=$(date -d "$END_DATE" +%s)
        NOW_EPOCH=$(date +%s)
        DAYS_LEFT=$(( (END_EPOCH - NOW_EPOCH) / 86400 ))
        echo $DAYS_LEFT
      register: ssl_days
      changed_when: false
      failed_when: false

    - name: 记录 SSL 测试结果
      ansible.builtin.set_fact:
        test_results: "{{ test_results + [{
          'name': 'SSL 证书',
          'status': 'PASS' if (ssl_days.stdout | default('0') | int) >= ssl_min_days else 'WARN',
          'detail': '剩余 ' + (ssl_days.stdout | default('未知')) + ' 天'
        }] }}"

    # ===== 5. 响应时间测试 =====
    - name: 测量响应时间
      ansible.builtin.shell: |
        curl -o /dev/null -s -w "%{time_total}" {{ app_url }}
      register: response_time
      changed_when: false

    - name: 记录响应时间结果
      ansible.builtin.set_fact:
        test_results: "{{ test_results + [{
          'name': '响应时间',
          'status': 'PASS' if (response_time.stdout | float) <= max_response_time else 'WARN',
          'detail': response_time.stdout + 's (阈值: ' + max_response_time | string + 's)'
        }] }}"

    # ===== 6. 数据库连接验证(通过 API)=====
    - name: 验证数据库连接(通过应用 API)
      ansible.builtin.uri:
        url: "{{ app_url }}/api/v1/db/health"
        status_code: 200
        timeout: 10
      register: db_health
      failed_when: false

    - name: 记录数据库测试结果
      ansible.builtin.set_fact:
        test_results: "{{ test_results + [{
          'name': '数据库连接',
          'status': 'PASS' if db_health.status | default(0) == 200 else 'FAIL',
          'detail': db_health.json | default({'error': '无法连接'}) | to_nice_json
        }] }}"

    # ===== 7. 生成测试报告 =====
    - name: 输出测试报告
      ansible.builtin.debug:
        msg: |
          ╔══════════════════════════════════════════════════╗
          ║         端到端测试报告                            ║
          ╠══════════════════════════════════════════════════╣
          {% for result in test_results %}
          ║ {{ '%-2s' | format(loop.index) }}. {{ '%-12s' | format(result.name) }} 
             {{ '%-6s' | format(result.status) }}  {{ result.detail }}
          {% endfor %}
          ╠══════════════════════════════════════════════════╣
          ║ 总计: {{ test_results | length }} 项测试
          ║ 通过: {{ test_results | selectattr('status', 'eq', 'PASS') | list | length }} 项
          ║ 失败: {{ test_results | selectattr('status', 'eq', 'FAIL') | list | length }} 项
          ║ 警告: {{ test_results | selectattr('status', 'eq', 'WARN') | list | length }} 项
          ╚══════════════════════════════════════════════════╝

    # ===== 8. 失败则中止 =====
    - name: 检查是否有失败项
      ansible.builtin.fail:
        msg: "❌ 端到端测试存在失败项,请检查!"
      when: test_results | selectattr('status', 'eq', 'FAIL') | list | length > 0

交付确认清单(Checklist):

markdown 复制代码
## 📋 部署交付确认清单

### 基础设施验证
- [ ] Terraform apply 成功,无错误
- [ ] 所有 EC2 实例状态为 running
- [ ] ALB 目标组健康检查全部通过
- [ ] RDS 实例状态为 available
- [ ] VPC 路由表配置正确
- [ ] 安全组规则符合预期

### 应用验证
- [ ] 首页返回 HTTP 200
- [ ] /health 端点正常
- [ ] API 接口响应正确
- [ ] SSL 证书有效且未过期
- [ ] 响应时间在可接受范围内

### 运维验证
- [ ] CloudWatch 告警已配置
- [ ] 日志收集正常
- [ ] 备份策略已生效
- [ ] 监控面板可访问

### 文档更新
- [ ] README 已更新
- [ ] 变更记录已填写
- [ ] 回滚方案已确认
- [ ] 相关方已通知

### 安全验证
- [ ] 无不必要的开放端口
- [ ] 密钥未暴露在代码中
- [ ] IAM 权限符合最小原则
- [ ] 安全组无 0.0.0.0/0 入站规则(除 80/443)

八、常见语法报错分析与 AI 辅助排错技巧

8.1 Ansible 常见报错与解决方案

8.1.1 YAML 语法错误

错误示例 1:缩进不一致

复制代码
ERROR! Syntax Error while loading YAML.
  mapping values are not allowed in this context

The error appears to be in '/playbooks/deploy.yml': line 12, column 8

错误代码:

yaml 复制代码
# ❌ 错误:冒号后缺少空格,或缩进混乱
- name: 安装软件包
  ansible.builtin.apt:
    name: nginx
      state: present    # 缩进多了一级

正确代码:

yaml 复制代码
# ✅ 正确:统一使用 2 空格缩进
- name: 安装软件包
  ansible.builtin.apt:
    name: nginx
    state: present

错误示例 2:特殊字符未转义

yaml 复制代码
# ❌ 错误:包含特殊字符的字符串未加引号
- name: 设置环境变量
  ansible.builtin.lineinfile:
    path: /etc/environment
    line: PATH=/usr/local/bin:$PATH:/opt/app/bin  # $ 可能被解析

# ✅ 正确:使用引号包裹
- name: 设置环境变量
  ansible.builtin.lineinfile:
    path: /etc/environment
    line: "PATH=/usr/local/bin:$PATH:/opt/app/bin"

错误示例 3:Tab 与空格混用

复制代码
ERROR! Syntax Error while loading YAML.
  found character that cannot start any token
bash 复制代码
# 解决方案:将所有 Tab 替换为空格
sed -i 's/\t/  /g' playbooks/*.yml

# 或在 Cursor 中设置:
# Settings → Editor → Insert Spaces: true
# Settings → Editor → Tab Size: 2
8.1.2 模块参数错误

错误:使用已废弃的模块参数

复制代码
[WARNING]: The value "yes" for "state" is deprecated. Use "true" instead.
ERROR! Invalid options for ansible.builtin.apt: update-cache
yaml 复制代码
# ❌ 错误:使用连字符(旧语法)
- name: 更新缓存
  ansible.builtin.apt:
    update-cache: yes      # 连字符语法已废弃

# ✅ 正确:使用下划线(新语法)
- name: 更新缓存
  ansible.builtin.apt:
    update_cache: true     # 下划线 + 布尔值

错误:模块名不完整

复制代码
ERROR! couldn't resolve module/action 'copy'. This often indicates a misspelling...
yaml 复制代码
# ❌ 可能出错(取决于配置)
- name: 复制文件
  copy:
    src: file.txt
    dest: /tmp/

# ✅ 推荐:使用完整模块名
- name: 复制文件
  ansible.builtin.copy:
    src: file.txt
    dest: /tmp/
    owner: root
    group: root
    mode: "0644"
8.1.3 变量与模板错误

错误:变量未定义

复制代码
fatal: [web-01]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'app_port' is undefined"}
yaml 复制代码
# 解决方案 1:设置默认值
- name: 配置应用端口
  ansible.builtin.template:
    src: app.conf.j2
    dest: /etc/app/config.conf
  vars:
    app_port: "{{ app_port | default(8080) }}"  # 默认 8080

# 解决方案 2:使用 when 条件跳过
- name: 配置应用端口
  ansible.builtin.template:
    src: app.conf.j2
    dest: /etc/app/config.conf
  when: app_port is defined

# 解决方案 3:在 defaults 中定义
# roles/app/defaults/main.yml
# app_port: 8080

错误:Jinja2 模板语法错误

复制代码
AnsibleError: template error while templating string: unexpected '{'
jinja2 复制代码
{# ❌ 错误:JSON 花括号与 Jinja2 冲突 #}
config = { "key": "{{ value }}" }

{# ✅ 正确:使用 raw 块或转义 #}
config = {% raw %}{% endraw %}{ "key": "{{ value }}" }

{# 或者使用 set 变量 #}
{% set config = {"key": value} %}
config = {{ config | to_json }}
8.1.4 连接与权限错误

错误:SSH 连接超时

复制代码
fatal: [web-01]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.1.101 port 22: Connection timed out"}
bash 复制代码
# 排查步骤:
# 1. 检查网络连通性
ping 192.168.1.101

# 2. 检查 SSH 端口
nc -zv 192.168.1.101 22

# 3. 检查防火墙
sudo iptables -L -n | grep 22

# 4. 检查 SSH 服务
ssh -vvv user@192.168.1.101  # 详细调试模式

# 5. 增加超时时间(ansible.cfg)
# [defaults]
# timeout = 60

错误:权限不足

复制代码
fatal: [web-01]: FAILED! => {"msg": "Permission denied (publickey)."}
yaml 复制代码
# 解决方案:确保使用正确的用户和密钥
# inventory/hosts.yml
webservers:
  hosts:
    web-01:
      ansible_host: 192.168.1.101
      ansible_user: deploy              # 指定用户
      ansible_ssh_private_key_file: ~/.ssh/deploy_key  # 指定密钥
      ansible_become: true              # 需要提权
      ansible_become_method: sudo

8.2 Terraform 常见报错与解决方案

8.2.1 Provider 初始化错误

错误:Provider 下载失败

复制代码
Error: Failed to install provider
Could not retrieve the list of available versions for provider hashicorp/aws
bash 复制代码
# 解决方案 1:检查网络/代理
export HTTPS_PROXY=http://proxy.company.com:8080
terraform init

# 解决方案 2:使用镜像源
# ~/.terraformrc
provider_installation {
  filesystem_mirror {
    path    = "/opt/terraform/providers"
    include = ["registry.terraform.io/*/*"]
  }
  direct {
    exclude = ["registry.terraform.io/*/*"]
  }
}

# 解决方案 3:清除缓存重新初始化
rm -rf .terraform .terraform.lock.hcl
terraform init
8.2.2 State 锁冲突

错误:State 被锁定

复制代码
Error acquiring the state lock
Lock Info:
  ID:        a1b2c3d4-e5f6-7890-abcd-ef1234567890
  Path:      production/terraform.tfstate
  Operation: OperationTypePlan
  Who:       user@hostname
  Version:   1.7.0
  Created:   2024-01-15 10:30:00 +0000 UTC
bash 复制代码
# 解决方案 1:等待锁释放(其他人正在操作)
# 等待几分钟后重试

# 解决方案 2:确认无人操作后强制解锁
terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890

# ⚠️ 警告:强制解锁前必须确认没有其他人正在执行 terraform 操作
# 否则可能导致 State 损坏

# 解决方案 3:检查 DynamoDB 表中的锁记录
aws dynamodb scan \
  --table-name terraform-state-lock \
  --filter-expression "LockID = :id" \
  --expression-attribute-values '{":id": {"S": "production/terraform.tfstate"}}'
8.2.3 资源属性冲突

错误:资源已存在

复制代码
Error: creating EC2 Instance: InvalidParameterValue: Value (web-01) for parameter groupId is invalid
hcl 复制代码
# 解决方案 1:使用 data source 引用已有资源
data "aws_security_group" "existing" {
  filter {
    name   = "tag:Name"
    values = ["existing-sg"]
  }
}

# 解决方案 2:导入已有资源
terraform import aws_security_group.web sg-0123456789abcdef0

# 解决方案 3:使用 lifecycle 忽略特定属性
resource "aws_instance" "web" {
  # ...
  lifecycle {
    ignore_changes = [
      tags,           # 忽略标签变更
      ami,            # 忽略 AMI 变更(避免重建)
    ]
  }
}
8.2.4 变量类型错误

错误:类型不匹配

复制代码
Error: Incorrect attribute value type
Inappropriate value for attribute "cidr_blocks": element 0: string required.
hcl 复制代码
# ❌ 错误:传入列表而非字符串
ingress {
  cidr_blocks = var.allowed_ips  # 如果 allowed_ips 是 list(list)
}

# ✅ 正确:确保类型匹配
variable "allowed_ips" {
  type    = list(string)  # 明确声明类型
  default = ["10.0.0.0/8", "172.16.0.0/12"]
}

ingress {
  cidr_blocks = var.allowed_ips  # 直接使用 list(string)
}
8.2.5 循环依赖错误

错误:Cycle 检测

复制代码
Error: Cycle: aws_instance.web, aws_security_group.web, aws_vpc.main
hcl 复制代码
# 解决方案:打破循环依赖
# 方法 1:使用 depends_on 明确依赖顺序
resource "aws_security_group" "web" {
  vpc_id = aws_vpc.main.id
  # 不引用 instance
}

resource "aws_instance" "web" {
  vpc_security_group_ids = [aws_security_group.web.id]
  # 依赖 SG,SG 不依赖 instance
}

# 方法 2:拆分资源到不同文件/模块
# 将安全组和实例分到不同模块中

8.3 Git 冲突处理与合并策略

8.3.1 Terraform 文件冲突
复制代码
<<<<<<< HEAD
resource "aws_instance" "web" {
  instance_type = "t3.medium"
  ami           = "ami-0123456789"
=======
resource "aws_instance" "web" {
  instance_type = "t3.large"
  ami           = "ami-9876543210"
>>>>>>> feature/upgrade-instance

解决策略:

bash 复制代码
# 1. 查看冲突文件
git status

# 2. 在 Cursor 中打开冲突文件(有可视化冲突解决器)
cursor terraform/main.tf

# 3. 选择保留的版本或手动合并
# 4. 标记冲突已解决
git add terraform/main.tf

# 5. 完成合并
git commit -m "fix(terraform): 合并实例配置冲突,采用 t3.large"

# 6. 重要:合并后必须重新验证
terraform plan  # 确认变更符合预期
8.3.2 预防冲突的最佳实践
bash 复制代码
# 1. 频繁拉取最新代码
git pull --rebase origin main

# 2. 小而频繁的提交(避免大范围修改)
git add -p  # 分块暂存,精确控制每次提交内容

# 3. 使用 .gitattributes 统一行尾
cat > .gitattributes << 'EOF'
*.tf text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.sh text eol=lf
*.md text eol=lf
EOF

# 4. 模块化设计减少文件冲突
# 不同人负责不同模块,减少修改同一文件的概率

8.4 CI/CD 流水线故障排查

常见 CI/CD 失败原因及解决:

错误类型 典型信息 解决方案
权限不足 AccessDenied 检查 IAM 策略和 Secrets
超时 Job timed out 增加 timeout 或优化步骤
依赖缺失 command not found 在 steps 中安装依赖
State 锁 state lock 等待或 force-unlock
网络问题 connection refused 检查安全组和网络 ACL
yaml 复制代码
# 调试 CI/CD 流水线技巧
# 在 workflow 中添加调试步骤

- name: 调试信息输出
  run: |
    echo "=== 环境信息 ==="
    echo "工作目录: $(pwd)"
    echo "文件列表:"
    ls -la
    echo ""
    echo "=== 版本信息 ==="
    terraform version
    python3 --version
    echo ""
    echo "=== 网络测试 ==="
    curl -sI https://registry.terraform.io | head -5

- name: 带详细日志的 Terraform Plan
  run: |
    export TF_LOG=DEBUG
    export TF_LOG_PATH=/tmp/terraform-debug.log
    terraform plan -no-color
  continue-on-error: true

- name: 上传调试日志
  if: failure()
  uses: actions/upload-artifact@v4
  with:
    name: debug-logs
    path: /tmp/terraform-debug.log

8.5 利用 Cursor AI 进行智能排错

排错 Prompt 模板:

复制代码
我遇到了以下错误,请帮我分析原因并给出解决方案:

【错误信息】:
Error: creating DB Instance: Cannot modify engine version from 14.9 to 15.4 
in a single apply. Please perform a major version upgrade separately.

【相关代码】:
@terraform/modules/rds/main.tf

【期望行为】:
将 PostgreSQL 从 14.9 升级到 15.4

【约束条件】:
- 不能停机超过 5 分钟
- 必须保留所有数据
- 生产环境

AI 辅助排错工作流:

复制代码
步骤 1:复制完整错误信息
步骤 2:在 Cursor 中打开相关文件
步骤 3:Ctrl+L 打开 AI 聊天
步骤 4:粘贴错误 + @引用相关文件
步骤 5:让 AI 分析根因
步骤 6:让 AI 给出修复方案
步骤 7:在 Composer 中应用修复
步骤 8:本地验证

实用排错 Prompt 示例:

复制代码
# Prompt 1:解释错误
"请解释这个 Terraform 错误的根本原因,以及为什么会出现:
[粘贴错误信息]"

# Prompt 2:对比分析
"@main.tf @main.tf.backup 请对比这两个文件的差异,
找出可能导致部署失败的变更"

# Prompt 3:生成修复
"请修复这个 Ansible Playbook 中的幂等性问题,
确保重复执行不会产生副作用:@deploy.yml"

# Prompt 4:性能诊断
"这个 Ansible Playbook 执行太慢(超过 30 分钟),
请分析可能的性能瓶颈并优化:@site.yml"

8.6 日志分析与错误模式识别

bash 复制代码
#!/bin/bash
# scripts/analyze-logs.sh - 日志分析辅助脚本

LOG_DIR="./logs"
TODAY=$(date +%Y-%m-%d)

echo "===== Ansible 日志分析 (${TODAY}) ====="

# 统计执行结果
echo ""
echo "📊 执行统计:"
grep -c "ok=" ${LOG_DIR}/ansible.log 2>/dev/null | xargs -I{} echo "  成功任务: {}"
grep -c "changed=" ${LOG_DIR}/ansible.log 2>/dev/null | xargs -I{} echo "  变更任务: {}"
grep -c "failed=" ${LOG_DIR}/ansible.log 2>/dev/null | xargs -I{} echo "  失败任务: {}"
grep -c "unreachable=" ${LOG_DIR}/ansible.log 2>/dev/null | xargs -I{} echo "  不可达主机: {}"

# 提取错误信息
echo ""
echo "❌ 错误详情:"
grep -A 5 "FAILED\|ERROR\|UNREACHABLE" ${LOG_DIR}/ansible.log 2>/dev/null | tail -30

# Terraform 日志
echo ""
echo "===== Terraform 日志分析 ====="
if [ -f "${LOG_DIR}/terraform.log" ]; then
    echo "错误条目:"
    grep -i "error\|fatal" ${LOG_DIR}/terraform.log | tail -10
    
    echo ""
    echo "警告条目:"
    grep -i "warning" ${LOG_DIR}/terraform.log | tail -5
fi

九、提升脚本安全性的最佳实践与审查要点

9.1 密钥与敏感信息管理

9.1.1 敏感信息分类
类别 示例 管理方式
云凭证 AWS Access Key、Service Account IAM Role / OIDC
密码 数据库密码、API Key Ansible Vault / Secrets Manager
证书 SSL 私钥、SSH 私钥 加密存储 / KMS
Token GitHub Token、Slack Webhook CI/CD Secrets
连接串 数据库 URL、Redis URL 环境变量 / Vault
9.1.2 Ansible Vault 完整实践
bash 复制代码
# 创建加密的变量文件
ansible-vault create inventory/group_vars/production/vault.yml

# 文件内容示例(加密前):
# ---
# vault_db_admin_password: "Pr0d_S3cur3_P@ssw0rd!"
# vault_api_secret_key: "sk-prod-xxxxxxxxxxxxxxxx"
# vault_jwt_signing_key: "hs256-secret-key-here"

# 在 Playbook 中引用加密变量
# 注意:vault 变量名必须以 vault_ 前缀开头(最佳实践)
yaml 复制代码
# inventory/group_vars/production/main.yml(非加密)
---
# 引用 vault 变量(间接引用,避免暴露)
db_admin_password: "{{ vault_db_admin_password }}"
api_secret_key: "{{ vault_api_secret_key }}"

# 非敏感配置直接写
db_host: "db.internal.example.com"
db_port: 5432
db_name: "production_app"
yaml 复制代码
# 在 Playbook 中使用加密变量
- name: 配置数据库连接
  ansible.builtin.template:
    src: db_config.j2
    dest: /etc/app/database.yml
    owner: app
    group: app
    mode: "0600"          # 严格权限:仅属主可读写
  vars:
    db_password: "{{ vault_db_admin_password }}"
  no_log: true            # 不在日志中输出此任务详情
9.1.3 Terraform 敏感变量处理
hcl 复制代码
# 标记敏感变量
variable "db_password" {
  description = "数据库管理员密码"
  type        = string
  sensitive   = true  # 不会在 plan/apply 输出中显示
}

variable "api_key" {
  description = "外部 API 密钥"
  type        = string
  sensitive   = true
}

# 敏感输出
output "db_connection_string" {
  description = "数据库连接字符串"
  value       = "postgresql://${var.db_user}:${var.db_password}@${aws_db_instance.main.endpoint}/${var.db_name}"
  sensitive   = true  # terraform output 不会直接显示
}

# 查看敏感输出(需要显式指定)
# terraform output -raw db_connection_string
9.1.4 CI/CD 中的密钥注入
yaml 复制代码
# GitHub Actions - 安全的密钥使用方式
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # ✅ 方式 1:使用 OIDC(最安全,无长期密钥)
      - name: Configure AWS
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-southeast-1

      # ✅ 方式 2:使用 GitHub Secrets
      - name: Deploy
        env:
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
          API_KEY: ${{ secrets.API_KEY }}
        run: |
          # 密钥通过环境变量传递,不出现在日志中
          ansible-playbook deploy.yml -e "db_password=${DB_PASSWORD}"

      # ❌ 绝对禁止:硬编码密钥
      # run: ansible-playbook deploy.yml -e "db_password=MySecret123"

9.2 最小权限原则与 IAM 策略

hcl 复制代码
# terraform/modules/iam/main.tf
# 最小权限 IAM 策略示例

# Terraform 执行角色(仅允许管理特定资源)
resource "aws_iam_role" "terraform_role" {
  name = "terraform-execution-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
        }
        Action = "sts:AssumeRoleWithWebIdentity"
        Condition = {
          StringEquals = {
            "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
            "token.actions.githubusercontent.com:sub" = "repo:myorg/gitops-infrastructure:ref:refs/heads/main"
          }
        }
      }
    ]
  })
}

# 精确到具体操作的策略
resource "aws_iam_role_policy" "terraform_policy" {
  name = "terraform-minimal-policy"
  role = aws_iam_role.terraform_role.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "EC2Management"
        Effect = "Allow"
        Action = [
          "ec2:DescribeInstances",
          "ec2:RunInstances",
          "ec2:TerminateInstances",
          "ec2:CreateTags",
          "ec2:DescribeImages",
          "ec2:DescribeSecurityGroups",
          "ec2:AuthorizeSecurityGroupIngress",
          "ec2:RevokeSecurityGroupIngress"
        ]
        Resource = "*"
        Condition = {
          StringEquals = {
            "aws:RequestedRegion" = "ap-southeast-1"
          }
        }
      },
      {
        Sid    = "S3StateAccess"
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject",
          "s3:ListBucket"
        ]
        Resource = [
          "arn:aws:s3:::gitops-terraform-state-2024",
          "arn:aws:s3:::gitops-terraform-state-2024/*"
        ]
      },
      {
        Sid    = "DynamoDBLock"
        Effect = "Allow"
        Action = [
          "dynamodb:GetItem",
          "dynamodb:PutItem",
          "dynamodb:DeleteItem"
        ]
        Resource = "arn:aws:dynamodb:*:*:table/terraform-state-lock"
      }
    ]
  })
}

9.3 代码静态安全扫描

yaml 复制代码
# 集成 Checkov(Terraform 安全扫描)到 CI/CD
# .github/workflows/security-scan.yml

name: "Security Scan"

on:
  pull_request:
    branches: [main]

jobs:
  checkov:
    name: "🔒 Checkov Security Scan"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: terraform/
          framework: terraform
          output_format: sarif
          soft_fail: false
          # 跳过的检查(需有正当理由)
          skip_check: CKV_AWS_18  # S3 访问日志(开发环境不需要)

  trivy:
    name: "🔒 Trivy IaC Scan"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run Trivy
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'config'
          scan-ref: 'terraform/'
          format: 'table'
          exit-code: '1'
          severity: 'CRITICAL,HIGH'

  ansible-security:
    name: "🔒 Ansible Security Check"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install tools
        run: pip install ansible-lint

      - name: Run security-focused lint
        run: |
          cd ansible
          ansible-lint --profile security playbooks/ roles/

9.4 Ansible 安全加固实践

yaml 复制代码
# roles/security-hardening/tasks/main.yml
---
# 系统安全加固任务

# ===== SSH 加固 =====
- name: 配置 SSH 安全选项
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
    state: present
    backup: yes
  loop:
    - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
    - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
    - { regexp: '^#?PermitEmptyPasswords', line: 'PermitEmptyPasswords no' }
    - { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
    - { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
    - { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 2' }
    - { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
    - { regexp: '^#?Protocol', line: 'Protocol 2' }
  notify: Restart SSHD

# ===== 防火墙配置 =====
- name: 配置 UFW 默认策略
  community.general.ufw:
    direction: "{{ item.direction }}"
    policy: "{{ item.policy }}"
  loop:
    - { direction: incoming, policy: deny }
    - { direction: outgoing, policy: allow }

# ===== 禁用不必要的服务 =====
- name: 禁用不必要的服务
  ansible.builtin.systemd:
    name: "{{ item }}"
    state: stopped
    enabled: false
  loop:
    - telnet.socket
    - rsh.socket
    - rlogin.socket
  failed_when: false  # 某些系统可能没有这些服务

# ===== 文件权限加固 =====
- name: 设置关键文件权限
  ansible.builtin.file:
    path: "{{ item.path }}"
    owner: root
    group: root
    mode: "{{ item.mode }}"
  loop:
    - { path: /etc/passwd, mode: "0644" }
    - { path: /etc/shadow, mode: "0000" }
    - { path: /etc/group, mode: "0644" }
    - { path: /etc/gshadow, mode: "0000" }
    - { path: /etc/ssh/sshd_config, mode: "0600" }

# ===== 安装并配置 Fail2Ban =====
- name: 安装 Fail2Ban
  ansible.builtin.apt:
    name: fail2ban
    state: present

- name: 配置 Fail2Ban SSH 防护
  ansible.builtin.copy:
    content: |
      [sshd]
      enabled = true
      port = ssh
      filter = sshd
      logpath = /var/log/auth.log
      maxretry = 3
      bantime = 3600
      findtime = 600
    dest: /etc/fail2ban/jail.local
    owner: root
    mode: "0644"
  notify: Restart Fail2Ban

9.5 Terraform 安全基线配置

hcl 复制代码
# terraform/modules/security-baseline/main.tf
# 安全基线模块 - 所有环境必须包含

# ===== S3 桶安全基线 =====
resource "aws_s3_bucket" "secure_bucket" {
  bucket = var.bucket_name

  # 强制 SSL 访问
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid       = "EnforceSSL"
        Effect    = "Deny"
        Principal = "*"
        Action    = "s3:*"
        Resource  = [
          "arn:aws:s3:::${var.bucket_name}",
          "arn:aws:s3:::${var.bucket_name}/*"
        ]
        Condition = {
          Bool = {
            "aws:SecureTransport" = "false"
          }
        }
      }
    ]
  })
}

# 阻止公共访问
resource "aws_s3_bucket_public_access_block" "block" {
  bucket = aws_s3_bucket.secure_bucket.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# 启用加密
resource "aws_s3_bucket_server_side_encryption_configuration" "encrypt" {
  bucket = aws_s3_bucket.secure_bucket.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

# ===== 安全组基线(禁止全开)=====
resource "aws_security_group" "restricted" {
  name_prefix = "${var.project_name}-restricted-"
  vpc_id      = var.vpc_id

  # 默认不添加任何入站规则
  # 通过变量精确控制允许的入站

  dynamic "ingress" {
    for_each = var.allowed_ingress_rules
    content {
      from_port   = ingress.value.from_port
      to_port     = ingress.value.to_port
      protocol    = ingress.value.protocol
      cidr_blocks = ingress.value.cidr_blocks
      description = lookup(ingress.value, "description", "Managed by Terraform")
    }
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    description = "Allow all outbound"
  }

  tags = {
    Name = "${var.project_name}-restricted-sg"
  }
}

# ===== CloudTrail(审计日志)=====
resource "aws_cloudtrail" "audit" {
  name                          = "${var.project_name}-audit-trail"
  s3_bucket_name                = aws_s3_bucket.secure_bucket.id
  include_global_service_events = true
  is_multi_region_trail         = true
  enable_log_file_validation    = true  # 日志完整性验证

  event_selector {
    read_write_type           = "All"
    include_management_events = true
  }
}

9.6 审计日志与合规检查

yaml 复制代码
# ansible/playbooks/audit-check.yml
---
# 安全审计检查 Playbook
# 定期执行,确保系统符合安全基线

- name: 安全合规审计
  hosts: all
  become: true
  gather_facts: true

  tasks:
    # 检查是否有无密码用户
    - name: 检查空密码用户
      ansible.builtin.shell: |
        awk -F: '($2 == "" ) { print $1 }' /etc/shadow
      register: empty_password_users
      changed_when: false
      failed_when: false

    - name: 报告空密码用户
      ansible.builtin.fail:
        msg: "⚠️ 发现空密码用户: {{ empty_password_users.stdout_lines }}"
      when: empty_password_users.stdout_lines | length > 0

    # 检查 SSH 配置
    - name: 检查 SSH 是否禁止 root 登录
      ansible.builtin.shell: |
        grep -E "^PermitRootLogin no" /etc/ssh/sshd_config
      register: root_login_check
      changed_when: false
      failed_when: false

    - name: 报告 root 登录状态
      ansible.builtin.debug:
        msg: "{{ '✅ SSH root 登录已禁止' if root_login_check.rc == 0 else '❌ SSH root 登录未禁止!' }}"

    # 检查开放端口
    - name: 获取当前监听端口
      ansible.builtin.shell: |
        ss -tlnp | awk '{print $4}' | grep -oE '[0-9]+$' | sort -un
      register: listening_ports
      changed_when: false

    - name: 检查是否有非预期端口
      ansible.builtin.debug:
        msg: |
          当前监听端口: {{ listening_ports.stdout_lines | join(', ') }}
          预期端口: 22, 80, 443, 8080
      when: listening_ports.stdout_lines | difference(['22', '80', '443', '8080']) | length > 0

    # 检查系统更新
    - name: 检查待更新的安全补丁
      ansible.builtin.shell: |
        apt list --upgradable 2>/dev/null | grep -i security | wc -l
      register: pending_updates
      changed_when: false
      when: ansible_os_family == "Debian"

    - name: 报告安全更新状态
      ansible.builtin.debug:
        msg: "待安装安全补丁: {{ pending_updates.stdout | default('0') }} 个"

十、进阶技巧:自定义规则优化运维代码质量

10.1 自定义 .cursorrules 运维规则集

随着项目发展,.cursorrules 需要不断迭代。以下是进阶版规则:

markdown 复制代码
# .cursorrules - 进阶运维 AI 规则 v2.0

## 项目上下文
- 项目类型:企业级 GitOps 自动化运维平台
- 技术栈:Ansible 2.16 + Terraform 1.7 + GitHub Actions
- 目标环境:AWS (ap-southeast-1)
- 团队规模:5 名运维工程师
- 合规要求:SOC2、ISO 27001

## Ansible 高级规范

### 任务编写
- 每个 task 的 name 必须使用中文,格式为"动词 + 对象 + 条件"
- 示例:"安装 Nginx 软件包"、"配置 SSL 证书(仅生产环境)"
- 使用 block/rescue/always 处理错误
- 所有 shell/command 模块必须有 changed_when 或 creates/removes
- 循环优先使用 loop 而非 with_items(新语法)
- 复杂条件使用 set_fact 预计算

### 变量管理
- 角色默认变量放在 defaults/main.yml
- 环境差异变量放在 inventory/group_vars/
- 敏感变量必须以 vault_ 前缀命名
- 变量命名:角色名_用途,如 nginx_worker_processes

### 性能优化
- 使用 gather_subset 限制收集的事实
- 大量文件操作使用 synchronize 替代 copy
- 使用 pipeline 和 ControlPersist 加速 SSH
- 合理使用 serial 进行滚动部署

## Terraform 高级规范

### 代码组织
- 每个模块必须有 README.md 说明用法
- 使用 terraform-docs 自动生成文档
- 模块输入变量不超过 15 个(过多则拆分)
- 使用 locals 块计算复杂表达式

### 安全强制
- 所有安全组入站规则必须有 description
- RDS 必须启用 deletion_protection
- S3 必须启用版本控制和加密
- IAM 策略禁止使用 Action: "*"
- 禁止在代码中硬编码账号 ID(使用 data source)

### 状态管理
- 每个环境独立的 state 文件
- 使用 workspace 或目录隔离环境
- 重要操作前备份 state
- 使用 moved 块处理资源重命名

## Git 提交规范(强制)
- feat: 新增基础设施或配置
- fix: 修复部署问题
- refactor: 代码重构(不改变功能)
- security: 安全相关变更(需要 2 人审查)
- ci: CI/CD 流水线变更
- docs: 文档更新
- chore: 依赖升级、工具配置

## 代码审查检查清单
AI 在审查代码时必须检查:
1. 是否有硬编码的密钥或密码
2. 安全组是否过于宽松
3. 是否缺少必要的标签
4. 变量是否有默认值和描述
5. 是否处理了错误情况
6. 幂等性是否得到保证
7. 是否有不必要的资源(成本)
8. 命名是否规范一致

## 输出格式要求
- Terraform 代码必须通过 terraform fmt 格式化
- Ansible YAML 使用 2 空格缩进
- 所有代码块标注语言类型
- 复杂逻辑添加中文注释
- 提供使用说明和注意事项

10.2 Ansible Lint 规则定制

yaml 复制代码
# ansible/.ansible-lint
# 自定义 Ansible Lint 规则配置

profile: production  # 使用生产级别规则集

# 排除路径
exclude_paths:
  - .cache/
  - collections/
  - tests/fixtures/

# 跳过的规则(需注释理由)
skip_list:
  - yaml[line-length]      # 允许长行(某些 URL 很长)
  - name[casing]           # 允许中文任务名

# 警告级别规则
warn_list:
  - experimental           # 实验性规则仅警告
  - no-changed-when        # 某些 command 确实无法判断变更

# 启用额外规则
enable_list:
  - no-log-password        # 强制密码操作使用 no_log
  - no-same-owner          # 检查文件属主一致性

# 自定义规则
rules:
  # 强制使用完整模块名
  - id: fqcn-required
    severity: HIGH
    
  # 禁止使用 shell 模块(除非必要)
  - id: avoid-shell
    severity: MEDIUM

10.3 Terraform 自定义策略(Sentinel/OPA)

hcl 复制代码
# terraform/policies/security.sentinel
# Sentinel 策略示例(Terraform Cloud/Enterprise)

# 策略 1:所有 EC2 必须加密
rule "ec2_must_be_encrypted" {
  condition = all tfplan.resource_changes as _, rc {
    rc.type is not "aws_instance" or
    rc.change.after.root_block_device[0].encrypted is true
  }
  error_message = "所有 EC2 实例的根卷必须启用加密"
}

# 策略 2:安全组禁止全开入站
rule "no_open_ingress" {
  condition = all tfplan.resource_changes as _, rc {
    rc.type is not "aws_security_group" or
    all rc.change.after.ingress as _, rule {
      rule.cidr_blocks is not ["0.0.0.0/0"] or
      rule.from_port is in [80, 443]  # 仅 80/443 允许全开
    }
  }
  error_message = "安全组入站规则不允许 0.0.0.0/0(80/443 除外)"
}

# 策略 3:所有资源必须有标签
rule "mandatory_tags" {
  condition = all tfplan.resource_changes as _, rc {
    rc.type is not "aws_instance" or
    (rc.change.after.tags includes "Environment" and
     rc.change.after.tags includes "Project" and
     rc.change.after.tags includes "ManagedBy")
  }
  error_message = "所有资源必须包含 Environment、Project、ManagedBy 标签"
}

使用 OPA (Open Policy Agent) 的替代方案:

rego 复制代码
# terraform/policies/security.rego
# OPA 策略文件

package terraform.security

import future.keywords.in

# 违规:安全组全开
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_security_group"
    ingress := resource.change.after.ingress[_]
    "0.0.0.0/0" in ingress.cidr_blocks
    not ingress.from_port in [80, 443]
    msg := sprintf("安全组 %s 存在全开入站规则(端口 %d)", [resource.name, ingress.from_port])
}

# 违规:缺少加密
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_db_instance"
    not resource.change.after.storage_encrypted
    msg := sprintf("RDS 实例 %s 未启用存储加密", [resource.name])
}

# 违规:缺少删除保护
deny[msg] {
    resource := input.resource_changes[_]
    resource.type == "aws_db_instance"
    not resource.change.after.deletion_protection
    msg := sprintf("RDS 实例 %s 未启用删除保护", [resource.name])
}
bash 复制代码
# 在 CI/CD 中运行 OPA 检查
terraform plan -out=tfplan
terraform show -json tfplan > plan.json

opa eval \
  --data terraform/policies/security.rego \
  --input plan.json \
  --format pretty \
  "data.terraform.security.deny"

10.4 代码生成质量评估与迭代优化

AI 生成代码的质量评估清单:

markdown 复制代码
## AI 生成代码审查清单

### 功能性 (40%)
- [ ] 代码是否能正确实现需求
- [ ] 边界条件是否处理
- [ ] 错误处理是否完善
- [ ] 幂等性是否保证

### 安全性 (25%)
- [ ] 无硬编码密钥
- [ ] 权限最小化
- [ ] 输入验证
- [ ] 无已知漏洞

### 可维护性 (20%)
- [ ] 命名清晰规范
- [ ] 注释充分
- [ ] 模块化设计
- [ ] 无重复代码

### 性能 (15%)
- [ ] 无不必要的资源
- [ ] 执行效率合理
- [ ] 无冗余操作

迭代优化 Prompt 技巧:

复制代码
# 第一轮:生成基础代码
"创建一个 Nginx 反向代理配置"

# 第二轮:优化安全性
"@vhost.conf.j2 请加强安全配置:添加速率限制、安全响应头、
隐藏版本号、限制请求体大小"

# 第三轮:优化可维护性
"@roles/nginx 请将硬编码的值提取为变量,
添加条件判断支持多环境部署"

# 第四轮:添加监控
"@nginx.conf.j2 请添加 stub_status 监控端点,
配置访问日志的 JSON 格式便于日志分析"

10.5 团队知识库与 Prompt 工程

建立团队 Prompt 库:

markdown 复制代码
# docs/prompt-library.md
# 团队 Prompt 模板库

## 常用运维场景 Prompt

### 场景 1:新服务部署

请为 服务名 创建完整的部署配置:

  • 目标系统:Ubuntu 22.04 / CentOS 9

  • 部署方式:Docker / 裸机 / K8s

  • 端口:端口号

  • 依赖:依赖列表

  • 配置项:需要参数化的配置

  • 监控:健康检查端点

  • 回滚:回滚策略

    场景 2:故障恢复

当前故障:描述

影响范围:受影响的服务

已尝试:已执行的恢复步骤

请给出:

  1. 根因分析

  2. 紧急恢复步骤

  3. 长期修复方案

  4. 预防措施

    场景 3:性能优化

当前性能指标:CPU/内存/响应时间

目标指标:期望值

瓶颈分析:已知瓶颈

请优化以下配置:@相关文件

复制代码

10.6 自动化文档生成与知识沉淀

yaml 复制代码
# .github/workflows/docs-generation.yml
# 自动生成文档的工作流

name: "Auto Generate Docs"

on:
  push:
    branches: [main]
    paths:
      - 'terraform/modules/**'
      - 'ansible/roles/**'

jobs:
  generate-docs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: 生成 Terraform 模块文档
        uses: terraform-docs/gh-actions@v1
        with:
          working-dir: terraform/modules/
          output-file: README.md
          output-method: inject
          git-push: true

      - name: 生成 Ansible Role 文档
        run: |
          pip install ansible-docgen
          for role in ansible/roles/*/; do
            role_name=$(basename $role)
            ansible-docgen -r $role -o docs/roles/${role_name}.md 2>/dev/null || true
          done

      - name: 提交文档更新
        run: |
          git config user.name "docs-bot"
          git config user.email "docs-bot@example.com"
          git add docs/ terraform/modules/*/README.md
          git diff --cached --quiet || git commit -m "docs: 自动更新模块文档"
          git push

使用 Cursor AI 生成 Runbook:

复制代码
Prompt:
@terraform/environments/production/ @ansible/playbooks/site.yml
请根据以上代码生成完整的运维 Runbook,包含:
1. 系统架构概述
2. 日常运维操作手册
3. 故障处理流程(按严重程度分级)
4. 扩缩容操作步骤
5. 备份恢复流程
6. 监控告警说明
7. 联系方式和升级路径

常见陷阱与问题排除

陷阱一:State 文件泄露

问题描述:.tfstate 文件提交到 Git 仓库,导致敏感信息(密码、密钥)泄露。

解决方案:

bash 复制代码
# 1. 确保 .gitignore 包含
*.tfstate
*.tfstate.*
.terraform/

# 2. 如果已经提交,立即从历史中移除
git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch *.tfstate*' \
  --prune-empty --tag-name-filter cat -- --all

# 3. 轮换所有泄露的密钥
# 4. 启用远程后端(S3 + 加密)

陷阱二:Ansible Vault 密码丢失

问题描述: 忘记了 Vault 密码,无法解密敏感变量。

预防措施:

bash 复制代码
# 1. Vault 密码存储在安全的位置(如密码管理器)
# 2. CI/CD 中使用 GitHub Secrets 存储
# 3. 团队中至少 2 人知道密码
# 4. 定期备份加密前的内容(在安全环境中)

# 如果密码丢失:
# - 检查 CI/CD Secrets 中是否有
# - 检查团队成员的本地 ~/.vault_pass
# - 最坏情况:重新创建加密文件(需要重新设置所有密码)

陷阱三:Terraform 资源漂移

问题描述: 有人手动修改了云资源,导致实际状态与 State 不一致。

bash 复制代码
# 检测漂移
terraform plan -detailed-exitcode
# 退出码 2 表示存在差异

# 解决方案 1:将手动变更纳入管理
terraform import <resource_type>.<name> <resource_id>

# 解决方案 2:恢复为代码定义的状态
terraform apply  # 将实际状态改回代码定义

# 预防措施:
# - 使用 IAM 策略限制控制台手动修改
# - 设置 Config Rules 检测漂移
# - 定期运行 terraform plan 检查

陷阱四:CI/CD 中的并发部署冲突

问题描述: 多个 PR 同时触发部署,导致资源冲突。

yaml 复制代码
# 解决方案:使用并发控制
concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false  # 不取消,等待完成

# 或使用环境锁
jobs:
  deploy:
    environment:
      name: production
      url: https://app.example.com
    # GitHub Environment 会自动排队

陷阱五:AI 生成代码的隐含假设

问题描述: AI 生成的代码可能假设特定环境存在,但实际环境中不存在。

yaml 复制代码
# 示例:AI 假设已安装 python3-pip
- name: 安装 Python 包
  ansible.builtin.pip:
    name: requests
  # 如果目标机器没有 pip,会失败

# 解决:添加依赖检查
- name: 确保 pip 已安装
  ansible.builtin.apt:
    name: python3-pip
    state: present
  when: ansible_os_family == "Debian"

- name: 安装 Python 包
  ansible.builtin.pip:
    name: requests

陷阱六:过度依赖 AI 不做审查

问题描述: 直接使用 AI 生成的代码而不审查,可能引入安全漏洞或逻辑错误。

最佳实践:

复制代码
1. AI 生成 → 人工审查 → 本地测试 → 提交 PR → 同事审查 → 合并
2. 永远不要将 AI 输出直接部署到生产环境
3. 重点审查:安全配置、权限设置、资源规格(成本)
4. 使用 Lint 工具自动检查
5. 在开发环境充分测试后再推进

问题排除速查表

问题 可能原因 解决方案
terraform init 失败 网络问题/版本不兼容 检查代理、清除缓存重试
Ansible 连接超时 防火墙/SSH 配置 检查安全组、sshd_config
Playbook 执行慢 gather_facts/串行执行 限制 facts、增加 forks
CI/CD 超时 资源创建慢 增加 timeout、使用 -target
State 损坏 并发操作/网络中断 从 S3 版本恢复
模块版本冲突 依赖不兼容 锁定版本、更新 lock 文件
Vault 解密失败 密码错误/文件损坏 检查密码、重新加密
安全组不生效 规则优先级/关联错误 检查 NACL、规则顺序

总结

本文从零开始,完整介绍了如何利用 Cursor AI 编辑器结合 GitOps 理念,实现 Ansible 与 Terraform 的智能化、自动化运维。让我们回顾核心要点:

核心收获

1. 工具链搭建是基础

  • Cursor IDE + Git + Ansible + Terraform 构成完整的工具链
  • 合理的项目目录结构是长期维护的保障
  • 环境配置一次做好,后续效率倍增

2. AI 是加速器而非替代品

  • Cursor AI 能将编码效率提升 5-10 倍
  • 但人工审查不可省略,特别是安全相关配置
  • .cursorrules 是提升 AI 输出质量的关键杠杆

3. GitOps 是协作的基石

  • 所有变更通过 Git 管理,可追溯、可回滚
  • 分支策略 + PR 审查 + CI/CD 构成完整的交付流水线
  • 自动化检查(Lint、安全扫描)是质量门禁

4. 安全是不可妥协的底线

  • 密钥管理、最小权限、加密存储缺一不可
  • 安全扫描应集成到 CI/CD 中,而非事后补救
  • 定期审计确保持续合规

5. 持续优化是长期实践

  • 自定义规则集随项目演进不断迭代
  • 团队知识库沉淀最佳实践
  • 从错误中学习,完善排错流程

推荐学习路径

复制代码
第 1 周:环境搭建 + Cursor 基础 + 简单 Playbook
第 2 周:Ansible Role 开发 + Terraform 基础
第 3 周:Git 工作流 + CI/CD 流水线
第 4 周:完整项目实战 + 安全加固
第 5 周起:进阶优化 + 团队推广

最后的建议

"自动化运维的终极目标不是消除人的参与,而是将人的精力从重复劳动中解放出来,投入到更有价值的架构设计、性能优化和创新探索中。"

AI 工具正在深刻改变运维工程师的工作方式。掌握 Cursor + GitOps + IaC 的组合,你将在 DevOps 时代拥有显著的竞争优势。但请记住:工具在变,原则不变------可靠性、安全性、可维护性永远是运维工作的核心追求。


详细参考资料

官方文档

工具 文档地址 说明
Cursor https://docs.cursor.sh AI 编辑器官方文档
Ansible https://docs.ansible.com 配置管理权威指南
Terraform https://developer.hashicorp.com/terraform/docs IaC 标准参考
GitHub Actions https://docs.github.com/en/actions CI/CD 工作流
Git https://git-scm.com/doc 版本控制基础

推荐书籍

  1. 《Ansible 权威指南》 - 深入理解 Ansible 架构与最佳实践
  2. 《Terraform: Up & Running》 (第 3 版) - Terraform 实战经典
  3. 《GitOps and Kubernetes》 - GitOps 理念与云原生实践
  4. 《The Phoenix Project》 - DevOps 思维启蒙
  5. 《Infrastructure as Code》 (第 2 版) - IaC 设计原则

社区资源

在线课程


附录

附录 A:完整 .gitignore 模板

gitignore 复制代码
# ===== Terraform =====
# State 文件(绝不提交)
*.tfstate
*.tfstate.*
*.tfstate.backup

# Provider 目录
.terraform/
.terraform.lock.hcl

# 崩溃日志
crash.log
crash.*.log

# 变量文件(可能包含敏感信息)
*.tfvars
*.tfvars.json
!example.tfvars
!example.tfvars.json

# 覆盖文件
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# CLI 配置
.terraformrc
terraform.rc

# Plan 文件
*.tfplan
tfplan

# ===== Ansible =====
*.retry
*.vault_pass
.vault_pass
logs/
*.log

# ===== Python =====
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
.venv/
env/
*.egg-info/
dist/
build/
*.egg

# ===== IDE =====
.idea/
.vscode/
*.swp
*.swo
*~
.DS_Store

# ===== 敏感信息 =====
*.pem
*.key
*.crt
*.p12
*.pfx
.env
.env.*
secrets/
credentials/
*.credential

# ===== OS =====
Thumbs.db
.DS_Store
Desktop.ini

# ===== 临时文件 =====
tmp/
temp/
*.tmp
*.bak
*.orig

附录 B:Makefile 快捷命令

makefile 复制代码
# Makefile - 项目常用命令快捷入口
# 用法:make <target>

.PHONY: help init validate plan apply deploy lint clean

# 默认目标
help: ## 显示帮助信息
	@echo "GitOps 自动化运维项目 - 可用命令:"
	@echo ""
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
		awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

# ===== 初始化 =====
init: ## 初始化项目(安装依赖)
	@echo "🚀 初始化项目..."
	pip install -r requirements.txt
	pre-commit install
	@echo "✅ 初始化完成"

# ===== 验证 =====
validate: ## 验证所有配置
	@echo "🔍 运行验证..."
	@./scripts/validate.sh

lint: ## 运行 Lint 检查
	@echo "📝 运行 Lint..."
	cd ansible && ansible-lint playbooks/ roles/
	cd terraform && terraform fmt -check -recursive .
	pre-commit run --all-files

# ===== Terraform =====
tf-init: ## Terraform Init(开发环境)
	cd terraform/environments/development && terraform init

tf-plan: ## Terraform Plan(开发环境)
	cd terraform/environments/development && terraform plan

tf-apply: ## Terraform Apply(开发环境,需确认)
	cd terraform/environments/development && terraform apply

tf-destroy: ## ⚠️ Terraform Destroy(危险操作)
	@echo "⚠️  即将销毁所有资源!"
	@read -p "确认?(yes/no): " confirm && [ "$$confirm" = "yes" ]
	cd terraform/environments/development && terraform destroy

# ===== Ansible =====
deploy-dev: ## 部署到开发环境
	cd ansible && ansible-playbook playbooks/site.yml \
		-i inventory/hosts.yml \
		-e target_environment=development

deploy-staging: ## 部署到预发环境
	cd ansible && ansible-playbook playbooks/site.yml \
		-i inventory/hosts.yml \
		-e target_environment=staging

deploy-prod: ## 部署到生产环境(需确认)
	@echo "⚠️  即将部署到生产环境!"
	@read -p "确认?(yes/no): " confirm && [ "$$confirm" = "yes" ]
	cd ansible && ansible-playbook playbooks/site.yml \
		-i inventory/hosts.yml \
		-e target_environment=production

# ===== Git =====
git-clean: ## 清理 Git 未跟踪文件(预览)
	git clean -fdn

git-reset: ## 重置到上一次提交(保留文件)
	git reset --soft HEAD~1

# ===== 清理 =====
clean: ## 清理临时文件
	rm -rf .terraform/
	rm -f *.tfplan
	rm -rf logs/*
	find . -name "*.retry" -delete
	find . -name "__pycache__" -type d -exec rm -rf {} +
	@echo "✅ 清理完成"

附录 C:常用 Ansible 模块速查

模块 用途 常用参数
ansible.builtin.apt Debian 包管理 name, state, update_cache
ansible.builtin.yum RedHat 包管理 name, state
ansible.builtin.copy 复制文件 src, dest, mode, owner
ansible.builtin.template 模板渲染 src, dest, vars
ansible.builtin.file 文件/目录管理 path, state, mode
ansible.builtin.service 服务管理 name, state, enabled
ansible.builtin.systemd Systemd 管理 name, state, daemon_reload
ansible.builtin.command 执行命令 cmd, creates, removes
ansible.builtin.shell 执行 Shell cmd, executable
ansible.builtin.uri HTTP 请求 url, method, status_code
ansible.builtin.wait_for 等待条件 port, timeout, state
ansible.builtin.lineinfile 修改文件行 path, regexp, line
ansible.builtin.blockinfile 修改文件块 path, block, marker
ansible.builtin.user 用户管理 name, groups, shell
ansible.builtin.cron 定时任务 name, minute, job
ansible.builtin.git Git 操作 repo, dest, version
ansible.builtin.docker_container Docker 容器 name, image, state

附录 D:Terraform 命令速查

bash 复制代码
# ===== 基础操作 =====
terraform init          # 初始化(下载 Provider)
terraform validate      # 语法验证
terraform plan          # 预览变更
terraform apply         # 执行变更
terraform destroy       # 销毁资源
terraform output        # 查看输出
terraform show          # 显示当前状态

# ===== State 管理 =====
terraform state list                    # 列出资源
terraform state show <resource>         # 查看资源详情
terraform state rm <resource>           # 移除资源(不销毁)
terraform state mv <old> <new>          # 重命名/移动资源
terraform import <resource> <id>        # 导入已有资源
terraform state pull > backup.tfstate   # 备份 State
terraform state push backup.tfstate     # 恢复 State

# ===== 工作区 =====
terraform workspace list                # 列出工作区
terraform workspace new <name>          # 创建工作区
terraform workspace select <name>       # 切换工作区
terraform workspace show                # 当前工作区

# ===== 调试 =====
terraform plan -target=<resource>       # 只规划特定资源
terraform apply -auto-approve           # 跳过确认
terraform fmt -recursive                # 格式化所有文件
terraform graph | dot -Tpng > graph.png # 生成依赖图
TF_LOG=DEBUG terraform plan             # 调试日志

# ===== 版本管理 =====
terraform version                       # 查看版本
terraform providers                   # 列出 Provider
terraform providers lock -platform=linux_amd64  # 锁定版本

附录 E:GitHub Actions 表达式参考

yaml 复制代码
# 条件表达式
if: github.ref == 'refs/heads/main'           # 仅 main 分支
if: github.event_name == 'pull_request'        # 仅 PR 事件
if: contains(github.event.head_commit.message, '[skip ci]')  # 跳过
if: success()                                  # 前序成功
if: failure()                                  # 前序失败
if: always()                                   # 始终执行
if: cancelled()                                # 被取消

# 上下文变量
${{ github.actor }}            # 触发者
${{ github.ref }}              # 分支/标签引用
${{ github.sha }}              # 提交 SHA
${{ github.event_name }}       # 事件类型
${{ github.run_id }}           # 运行 ID
${{ github.workflow }}         # 工作流名称
${{ matrix.environment }}      # 矩阵变量
${{ secrets.MY_SECRET }}       # 密钥
${{ vars.MY_VAR }}             # 仓库变量
${{ needs.job1.outputs.result }}  # 跨 Job 输出

# 常用函数
join(array, separator)         # 拼接数组
contains(search, item)         # 包含判断
startsWith(str, prefix)        # 前缀匹配
format(string, args)           # 格式化
toJson(obj)                    # 转 JSON
fromJSON(str)                  # 解析 JSON

附录 F:项目 requirements.txt

复制代码
# requirements.txt - Python 依赖
# 安装:pip install -r requirements.txt

# Ansible 核心
ansible==9.5.1
ansible-core==2.16.6

# 代码质量
ansible-lint==6.22.0
yamllint==1.33.0

# Pre-commit
pre-commit==3.6.0

# 密钥检测
detect-secrets==1.4.0

# Jinja2(模板引擎)
jinja2==3.1.3

# YAML 处理
pyyaml==6.0.1

# AWS SDK(Ansible AWS 模块需要)
boto3==1.34.14
botocore==1.34.14

# 工具
python-dotenv==1.0.0
rich==13.7.0        # 美观终端输出

附录 G:故障恢复快速参考卡

复制代码
┌─────────────────────────────────────────────────────────┐
│              🚨 紧急故障恢复速查卡                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  1️⃣  确认影响范围                                       │
│     → 哪些服务受影响?多少用户?                          │
│     → 通知相关方(Slack/邮件)                           │
│                                                         │
│  2️⃣  回滚到上一个已知正常版本                            │
│     Git:  git revert <commit> && git push               │
│     TF:   git checkout <good-commit> -- terraform/      │
│           terraform apply                               │
│     Ansible: git checkout <good> -- ansible/            │
│              ansible-playbook site.yml                   │
│                                                         │
│  3️⃣  验证恢复                                          │
│     → 健康检查通过?                                    │
│     → 监控恢复正常?                                    │
│     → 用户可正常访问?                                  │
│                                                         │
│  4️⃣  事后复盘(48小时内)                               │
│     → 根因分析(5 Whys)                                │
│     → 时间线记录                                        │
│     → 改进措施                                          │
│     → 更新 Runbook                                      │
│                                                         │
│  📞 升级路径:                                          │
│     L1 值班 → L2 团队 Lead → L3 CTO                    │
│     超时标准:15min / 30min / 1h                        │
│                                                         │
└─────────────────────────────────────────────────────────┘

本文完

版本:v1.0 | 最后更新:2026 年 8 月

适用工具版本:Cursor 0.45+ / Ansible 2.16+ / Terraform 1.7+ / Git 2.40+

相关推荐
happymagic2 小时前
java spring boot做的jar包程序,如何实现自动运行启动
java·运维·服务器·spring boot·jar
星空你好2 小时前
AI辅助运维:开源一个内网日志监控工具
运维
风曦Kisaki2 小时前
Kubernetes(K8s)笔记Day06: 持久化存储(emptyDir,hostPath,NFS),PV 和 PVC,StorageClass存储类
linux·运维·笔记·云原生·容器·kubernetes
YFJ_mily3 小时前
【IEEE征稿】第二届智能制造、机器人与自动化国际学术会议(IMRA 2026)| 马鞍山12月举办,EI/Scopus稳定检索,3-5工作日录用
机器人·自动化·智能制造·工业自动化·ieee会议·rdlink研发家·安徽马鞍山会议
nbzy8884 小时前
电器铝锌压铸配件资料检索报错排查:倒排索引与结构化过滤的组合实现
自动化·内容生成·电器铝锌压铸配件
Crazy________7 小时前
k8s部署若依微服务架构流程,v3.6.6
运维·云原生·容器·kubernetes·状态模式
云飞云共享云桌面7 小时前
广东精密机械设备工厂8-10人共享一台SolidWorks设计服务器
运维·服务器·3d·自动化·汽车·制造
jieyucx8 小时前
【高级利用】条件竞争与逻辑漏洞:与服务器赛跑的艺术
android·运维·服务器·web安全·文件上传
tianyuanwo8 小时前
服务器OS组件研发效能度量可视系统——顶层方案设计与阶段性规划
运维·服务器·度量系统