一 背景
随着云计算的普及,基础架构已成为IT部门的重点工作内容之一。如何快速且安全地构建复杂的生产环境,实现业务的稳定运行,是每个技术团队面临的共同课题。传统的手工建设流程低效、容易出错、难以审计和跟踪,无法适应业务的快速迭代需求。因此,采用自动化的技术交付手段势在必行。本文将探讨如何利用Hashicorp Terraform与AWS CodePipeline实现AWS云环境的自动化构建最佳实践。
企业客户在云上部署的一系列数据应用的过程中,数据开发团队往往负责脚本内容,而其背后一系列云上资源的管理通常由一支云运维职能团队通过IaC(Infrastructre as Code)实现。然而,当数据开发团队开发及部署相应脚本内容时,不可避免会涉及到云上资源的变动,如Glue、Lambda的资源增改等。这就造成了两个团队在职能边界上的紧耦合:数据开发团队的迭代内容都需要提报需求至云运维团队进行相应IaC的运维,双方都增加了工作量。
二 概述
Terraform是一种用于建设、变更和版本控制云基础架构的工具。通过编写使用Hashicorp HCL定义的配置文件,可以配合版本控制系统来管理对基础架构的变更。
AWS CodePipeline是一项持续交付服务,可自动化软件发布工作流程。通过创建编码阶段、构建阶段和部署阶段等,可以实现从代码提交到软件版本发布这一完整流程的自动化。
CodePipeline 是一种持续交付服务,可自动生成和测试您的软件并将其部署到生产环境中。
持续交付是实现发布流程自动化的软件开发方法。每个软件更改都将自动生成、测试并部署到生产环境中。在最终推送到生产环境之前,可由人员、自动化测试或业务规则决定最后的推送何时发生。虽然每次成功的软件更改都可以通过持续交付立即发布到生产环境中,但并非所有更改都需要立即发布。
持续集成是一种软件开发实践,团队成员使用版本控制系统并经常将其工作集成到同一位置,例如主分支。每项更改都经过生成和验证,以尽可能快地检测到集成错误。持续交付会自动执行整个软件发布过程,一直到最后的生产部署,而持续集成重点关注自动生成和测试代码。
三 相关概念
本文涉及的主要概念包括:
- Terraform:基础架构即代码(IaC)工具,用于云资源的自动化构建、变更和版本控制。
- AWS CodePipeline:CI/CD自动化流水线服务,实现从代码提交到软件版本发布完整的交付流程。
- HCL:Hashicorp Configuration Language,Terraform使用的配置语言。
- 资源:在Terraform中,AWS的每个产品如EC2、S3、Lambda等都是一个资源,可以通过HCL编写其配置。
- 模块:用于封装和重用配置的Terraform组件,实现DRY(Don't Repeat Yourself)原则。
四 实现流程
4.1 架构图
4.2 实现流程
本文实践的实现流程主要包含:
- 编写Terraform模块和HCL文件用于定义对AWS资源的配置。
- 为Terraform模块配置完成版本控制,存储在AWS CodeCommit代码仓库中。
- 在AWS CodePipeline中创建具有三个阶段的流水线:源阶段、构建阶段和部署阶段。
- 源阶段从CodeCommit代码仓库获取Terraform模块。
- 构建阶段使用AWS CodeBuild执行Terraform init、plan和apply/destroy操作完成资源部署。
- 本次在codebuild中创建两个构建,一个为apply创建资源,一个为destroy销毁资源,其利用aws s3作为后端backend,存储目标资源状态。
- 部署阶段将Terraform输出的AWS资源信息存储为可审计的构件。
- 通过修改Terraform模块并提交新版本触发流水线部署更新后的配置。
- 测试验证部署的资源是否符合预期。
五 实战
5.1 代码
5.1.1 结构
5.1.2 内容
- terraform内容
# main.tf
locals {
xuel_tag = {
application = "xuel_app_test"
environment = "dev"
purpose = "person test"
contact = "xuel@anchnet.com"
creator = "xuelei"
role = "cloud manager"
owner = "xuel"
project = "aws_partner_project"
team = "smartops"
organization = "mse"
company = "anchnet"
}
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
tags = local.xuel_tag
}
variable "public_subnet_cidrs" {
type = list(string)
description = "Public Subnet CIDR values"
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
variable "private_subnet_cidrs" {
type = list(string)
description = "Private Subnet CIDR values"
default = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
}
variable "azs" {
type = list(string)
description = "Availability Zones"
default = ["cn-north-1a", "cn-north-1b", "cn-north-1d"]
}
resource "aws_subnet" "public_subnets" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = element(var.public_subnet_cidrs, count.index)
availability_zone = element(var.azs, count.index)
tags = local.xuel_tag
}
resource "aws_subnet" "private_subnets" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = element(var.private_subnet_cidrs, count.index)
availability_zone = element(var.azs, count.index)
tags = local.xuel_tag
}
resource "aws_internet_gateway" "gw" {
vpc_id = aws_vpc.main.id
tags = local.xuel_tag
}
resource "aws_route_table" "second_rt" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.gw.id
}
tags = local.xuel_tag
}
resource "aws_route_table_association" "public_subnet_asso" {
count = length(var.public_subnet_cidrs)
subnet_id = element(aws_subnet.public_subnets[*].id, count.index)
route_table_id = aws_route_table.second_rt.id
}
# header.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.62"
}
}
# 后端backend使用s3,地域在cn-northwest-1
backend "s3" {
bucket = "xuel-tf-backend"
key = "tf-codebuild/terraform.tfstate"
region = "cn-northwest-1"
}
}
# vpc资源在cn-north-1
provider "aws" {
region = "cn-north-1"
}
- buildspec-apply.yaml
version: 0.2
env:
variables:
TF_VERSION: "1.3.7"
phases:
pre_build:
commands:
- echo "start pre build [install terraform]"
- cd /usr/bin
- "curl -s -qL -o terraform.zip https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip"
- unzip -o terraform.zip
- cd -
# - aws configure set aws_access_key_id $KEY_ID #设置AWS访问密钥ID
# - aws configure set aws_secret_access_key $KEY_SECRET #设置AWS私有访问密钥
- terraform init
- terraform fmt
build:
commands:
- terraform init
- terraform validate #校验Terraform语法
- terraform plan -out=plan #生成执行计划
- terraform apply --auto-approve
cache:
paths:
- '.terraform'
- buildspec-destroy.yaml
version: 0.2
env:
variables:
TF_VERSION: "1.3.7"
phases:
pre_build:
commands:
- echo "start pre build [install terraform]"
- cd /usr/bin
- "curl -s -qL -o terraform.zip https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip"
- unzip -o terraform.zip
- cd -
# - aws configure set aws_access_key_id $KEY_ID #设置AWS访问密钥ID
# - aws configure set aws_secret_access_key $KEY_SECRET #设置AWS私有访问密钥
- terraform init
- terraform fmt
build:
commands:
- terraform init
- terraform validate #校验Terraform语法
- terraform plan -out=plan #生成执行计划
- terraform destroy --auto-approve
cache:
paths:
- '.terraform'
5.2 代码Push至CodeCommit
存储库创建比较简单,创建完成后,需要创建代码操作用户,尽可能最小化权限控制用户。
5.3 CodeBuild创建
- 项目配置
CodeBuild项目的基础信息,包括标签、并非构建限制等。
- 源配置
制定代码源,改实践中为CodeCommit,制分支为main
- 环境配置
注意再次的角色需要授权terraform的后端存储s3的权限,及需要创建目标资源vpc权限,根据实际项目来配置。
- buildspec
创建不用的CodeBuild指定不同的buildspec文件,在此指定buildspec-apply.yaml后续指定buildspec-destroy.yaml文件即可。
- 构件&日志
在此未配置构件,也可以将terraform plan的结构生成未构件上传到AWS 对象存储内,日志建议开启,方便查阅构件日志。
同理创建第二个CodeBuild,结果如下:
5.4 CodePipeline创建
当CodeBuild创建完成后,可以进行构建测试,为了能自动化的触发构建,配置CodePipeline。
- 管道设置
配置管道基本信息
- 添加源
指定源码存储位置及仓库名称和代码分支
- 添加构建阶段
配置CodePipeline的构建阶段为CodeBuild,指定创建vpc的build
- 添加部署阶段
由于目标不是部署资源,而是使用terraform编排资源,在此跳过部署阶段。
- 创建管道
同理创建destroy CodePipeline
六 测试
6.1 触发构建
可以执行推送代码触发,也可以在pipeline上手动执行发布更改触发Pipeline。
6.2 查看构建日志
6.3 查看资源
在此创建的VPC资源
6.4 查看s3状态文件
查看S3状态文件。
也可以下载下来,查看内容。
6.4 清理资源
执行销毁pipeline,清理资源。
查看VPC资源已清理干净,S3中后端状态文件也没有资源信息。
七 注意事项
- 角色权限需要具备aws s3作为后端backend权限,需要具备terraform编排目标资源权限,在本实践中为vpc的权限。
- 在Codebuild中使用角色,不需要在terraform中配置用户AK信息。
- 在CodePipeline中,目前使用的每次去下载terraform可执行文件,有时会存在网络问题,如果异常手动在此触发即可。
- 本实践中为编排AWS资源,当然也可以编排其他云资源,例如腾讯/阿里/华为/GCP/Azure等,可以自行扩展,适用于混合多云场景。
八 反思
本文通过Terraform整合AWS CodePipeline实现AWS云上资源自动化编排实践,在改实践中,codebuild/codepipeline创建还都是手工创建,后期也可以利用terraform来进行这些配置的编排,实现更高层级的自动化,采用Terraform与CodePipeline打造自动化技术交付流程,可以帮助用户
- 加速环境构建进度,同时减少人为出错的概率。
- 实现基础架构即代码,通过版本控制管理环境变更。
- 简化运维复杂度,由脚本自动执行环境的部署与变更。
- 提高审计能力,每个流水线执行都会生成详细的构件,方便跟踪每次部署的变化
- 实现DRY原则,通过Terraform模块重用配置避免重复劳动。
- 满足业务快速迭代的需求,加速从开发到生产的软件交付速度。
但是,采用此方案也需要注意:
- 自动化并不能解决所有的问题,还需要人工检验和验证。
- 对资源配置的变更需要严谨慎重,可能导致意外的破坏。
- 需要投入大量时间进行模板开发、测试和维护。
- 可能增加环境的复杂性,也带来一定的学习和培训成本。
综上,Terraform与CodePipeline实现AWS云资源自动化部署是一个值得实践的方案。但在实施过程中,也需要关注自动化不足之处,并做好管理和控制,方能最大限度发挥其优势。理解技术优缺点,掌握适当的应用范围,是实现技术落地的关键所在。
参考链接
- developer.hashicorp.com/terraform/l…