4. 基础设施即代码入门
在数据平台建设中,数据湖、ETL 集群、调度器往往不是一次性资源,而是需要反复创建、更新和销毁的基础设施。如果每次都靠控制台点击,环境之间就会出现配置漂移,回滚和审计也无从谈起。基础设施即代码(Infrastructure as Code,IaC)的核心思路,是把这些资源用声明式模板或编程语言描述出来,交给版本控制和自动化流水线管理。AWS 上最常用的两种工具是 CloudFormation 和 AWS CDK:前者是原生声明式模板,后者是用编程语言生成 CloudFormation 模板的抽象层。
CloudFormation 与 CDK 的分工
CloudFormation 以 YAML 或 JSON 模板为输入,模板中声明资源、参数、映射、条件和输出。它天然支持变更集(Change Set),可以在真正执行前预览资源增删改。CDK 则允许用 TypeScript、Python、Java 等语言编写“构造块”(Construct),最终通过 cdk synth 生成 CloudFormation 模板。两者不是替代关系,而是同一套底层引擎的不同表达方式:CDK 适合复杂逻辑、复用和类型检查,CloudFormation 适合直接控制模板细节和跨账号部署。
对于数据管道,典型资源包括:S3 数据湖分层存储桶、Glue 数据目录与 ETL 作业、EMR 或 Glue 集群、Step Functions 或 MWAA 调度器、IAM 角色与策略、KMS 加密密钥、CloudWatch 日志与告警。这些资源之间存在明确的依赖关系,例如 Glue 作业需要 IAM 角色访问 S3,调度器需要调用 Glue 作业,数据湖桶需要加密和生命周期策略。IaC 的价值就在于把这些依赖显式化,而不是靠人工记忆。
用 CDK 定义数据湖与 ETL 资源
下面以 TypeScript CDK 为例,定义一个包含 S3 数据湖桶、Glue 作业和 Step Functions 调度器的栈。示例参数为教学假设,实际部署时需替换为真实值。
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as glue from 'aws-cdk-lib/aws-glue';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks';
export class DataPipelineStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 数据湖分层桶:raw / curated / analytics
const rawBucket = new s3.Bucket(this, 'RawBucket', {
bucketName: `data-lake-raw-${this.account}-${this.region}`,
encryption: s3.BucketEncryption.KMS_MANAGED,
versioned: true,
lifecycleRules: [{ expiration: cdk.Duration.days(365) }],
});
const curatedBucket = new s3.Bucket(this, 'CuratedBucket', {
bucketName: `data-lake-curated-${this.account}-${this.region}`,
encryption: s3.BucketEncryption.KMS_MANAGED,
versioned: true,
});
// Glue 作业角色
const glueRole = new iam.Role(this, 'GlueJobRole', {
assumedBy: new iam.ServicePrincipal('glue.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSGlueServiceRole'),
],
});
rawBucket.grantRead(glueRole);
curatedBucket.grantReadWrite(glueRole);
// Glue ETL 作业
const etlJob = new glue.CfnJob(this, 'EtlJob', {
name: 'raw-to-curated-etl',
role: glueRole.roleArn,
command: {
name: 'glueetl',
scriptLocation: `s3://${rawBucket.bucketName}/scripts/etl.py`,
pythonVersion: '3',
},
glueVersion: '4.0',
workerType: 'G.1X',
numberOfWorkers: 10,
timeout: 60,
});
// Step Functions 调度器
const startJob = new tasks.GlueStartJobRun(this, 'StartEtl', {
glueJobName: etlJob.name!,
integrationPattern: sfn.IntegrationPattern.RUN_JOB,
});
new sfn.StateMachine(this, 'EtlScheduler', {
definition: startJob,
timeout: cdk.Duration.minutes(30),
});
}
}
这段代码的关键点在于:S3 桶通过 grantRead 和 grantReadWrite 自动生成最小权限策略,而不是手写通配符;Glue 作业的 workerType 和 numberOfWorkers 是成本与性能的调节旋钮;Step Functions 使用 RUN_JOB 集成模式,会等待 Glue 作业完成后再进入下一状态。执行 cdk deploy 后,CloudFormation 会按依赖顺序创建资源,并在控制台输出资源栈事件。
CloudFormation 模板的等价表达
如果团队更倾向直接维护模板,可以用 YAML 表达同样的资源。下面只展示 S3 桶和 Glue 作业的核心片段,省略 IAM 角色的完整策略:
Resources:
RawBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "data-lake-raw-${AWS::AccountId}-${AWS::Region}"
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
VersioningConfiguration:
Status: Enabled
EtlJob:
Type: AWS::Glue::Job
Properties:
Name: raw-to-curated-etl
Role: !GetAtt GlueJobRole.Arn
Command:
Name: glueetl
ScriptLocation: !Sub "s3://${RawBucket}/scripts/etl.py"
PythonVersion: "3"
GlueVersion: "4.0"
WorkerType: G.1X
NumberOfWorkers: 10
Timeout: 60
对比可见,CloudFormation 更接近资源原貌,CDK 则把权限授予、命名拼接、依赖推断等重复逻辑封装起来。选择哪种方式,取决于团队的语言习惯、复用需求和审查流程。两者可以混用:用 CDK 生成模板,再交给 CloudFormation 做变更集审批。
可重复部署的关键实践
- 环境参数化:把账号、区域、桶名后缀、作业并发数抽成参数或上下文变量,避免硬编码。CDK 中可用
cdk.json的 context 或环境变量注入。 - 变更集先行:生产环境部署前先执行
cdk diff或创建 Change Set,确认没有意外删除或替换资源。 - 状态与漂移检测:CloudFormation 会记录资源栈状态,定期用
detect-stack-drift检查控制台手动修改造成的漂移。 - 回滚策略:为资源栈配置失败回滚,关键资源启用保留策略(如 S3 桶的
DeletionPolicy: Retain),防止误删数据。 - CI/CD 集成:把
cdk synth和cdk deploy放入流水线,代码合并后自动部署到测试环境,生产环境加人工审批。
从模板到数据管道的验证
部署完成后,验证顺序建议为:先在 S3 控制台确认 raw 和 curated 桶已创建且加密开启;再在 Glue 控制台查看作业定义和 IAM 角色是否正确绑定;然后手动启动一次 Step Functions 执行,观察 Glue 作业是否被触发并成功写入 curated 层;最后检查 CloudWatch 日志中是否有权限拒绝或脚本错误。如果调度器执行失败,优先查看 Step Functions 的执行事件历史和 Glue 作业的 CloudWatch 日志组,通常能定位到是 IAM 权限不足、脚本路径错误还是 worker 资源不足。
把数据湖、ETL 集群和调度器纳入 IaC 之后,环境重建从“半天手工操作”变成“一条命令加一次审批”。更重要的是,资源关系被写进代码,评审、回滚和审计都有了明确依据。下一阶段可以把这套模板接入持续交付流水线,让每次数据管道变更都经过自动化测试和分阶段发布。