构建面向生产的 CI/CD 流水线:使用 Terraform、GitHub Actions 和 Ansible 自动化基础设施
发布: (2025年12月10日 GMT+8 05:15)
5 min read
原文: Dev.to
Source: Dev.to
项目目标与概述
本项目的主要目标是为一个多服务的 TODO 应用创建一个全自动化的部署流水线,实现完整的基础设施自动化。解决方案需要满足以下关键需求:
核心需求
- 从代码提交到生产部署的全流程自动化
- 使用声明式配置进行基础设施供应
- 自动化配置管理,确保服务器设置一致
- 零停机部署并实现 SSL/TLS 终止
- 漂移检测,保持基础设施一致性
- 分布式追踪,用于调试微服务交互
- 以安全为先,使用加密的密钥并最小化攻击面
选用技术栈
- 基础设施即代码:Terraform,用于 AWS 资源供应
- 配置管理:Ansible,用于服务器配置和应用部署
- CI/CD 编排:GitHub Actions,用于工作流自动化
- 容器化:Docker 与 Docker Compose,实现服务隔离
- 反向代理:Traefik,用于路由、负载均衡和自动 SSL
- 可观测性:Zipkin,用于分布式请求追踪
- 消息队列:Redis,用于异步日志处理
最终目标是实现一个系统,能够通过一次 git push 同时部署基础设施变更和应用更新,并内置漂移检测、邮件通知以及生产环境的手动审批门等安全机制。
系统架构与设计
该应用采用微服务模式,共有七个不同的服务,每个服务使用最适合其职责的语言实现。
架构图

图 1:完整的微服务架构,展示所有服务、技术栈以及数据流向。
服务职责
| # | 服务 | 语言 / 平台 | 关键职责 |
|---|---|---|---|
| 1 | Frontend Service | Vue.js | SPA UI,调用后端 API,Zipkin 客户端,提供静态资源 |
| 2 | Auth API | Go | 认证与授权,JWT 处理,通过 Users API 验证凭证 |
| 3 | Todos API | Node.js | TODO 项目的增删改查,向 Redis 发布事件,进行 JWT 验证 |
| 4 | Users API | Spring Boot (Java) | 用户资料管理,为 Auth 服务提供只读查询 |
| 5 | Log Message Processor | Python | 消费 Redis 消息,记录事件以供监控 |
| 6 | Redis | — | 内存消息队列(pub/sub) |
| 7 | Zipkin | — | 分布式追踪收集器与 UI |
| 8 | Traefik | — | 反向代理,自动服务发现,Let’s Encrypt SSL,路由与仪表盘 |
网络架构

图 2:Docker 网络示意图,展示隔离的 app-network,Traefik 为唯一的外部网关。
所有服务通过名为 app-network 的专用 Docker 桥接网络进行通信,提供以下优势:
- 与宿主机系统隔离
- 通过容器名称(DNS)实现服务间通信
- 除 Traefik 外不暴露端口
- 外部客户端与 Traefik 之间的流量已加密
使用 Terraform 的基础设施即代码
Terraform 之所以被选中,是因为其声明式配置、状态管理以及成熟的 AWS 提供程序。
供应的 AWS 资源
1. EC2 实例
# Data source ensures we always use the latest Ubuntu AMI
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical's AWS account
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# EC2 Instance resource definition
resource "aws_instance" "todo_app" {
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
key_name = aws_key_pair.deployer.key_name
vpc_security_group_ids = [aws_security_group.todo_app.id]
tags = {
Name = "todo-app-server-v2"
Environment = "production"
Project = "hngi13-stage6"
}
}
2. 安全组
resource "aws_security_group" "todo_app" {
name = "todo-app-sg"
description = "Security group for TODO application"
# HTTP access for initial Let's Encrypt challenges
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# HTTPS access
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# SSH access (restricted)
ingress {
description = "SSH"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["YOUR.TRUSTED.IP/32"]
}
# Allow all outbound traffic
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}