【免费下载链接】aws-cdk-examples
Example projects using the AWS CDK
本篇基于aws-cdk-examples仓库中的python/ecs/fargate-service-with-efs示例,讲解如何用 AWS CDK 部署一个"公网 ALB 前置 + ECS Fargate 服务 + EFS 文件系统挂载"的完整架构。读完本文,你能掌握 EFS 访问点、EfsVolumeConfiguration与 IAM 任务角色的配置要点、Fargate 任务定义中卷与挂载点的接线方式,以及如何通过 ALB 与 CPU/内存双维自动扩缩容让这个带共享存储的服务可对外提供服务。该示例源自 AWS 官方博客关于 ECS 支持 EFS 的方案,演示的核心场景是:让无服务器化计算(Fargate)能够使用独立于计算节点的共享存储。
一、示例架构与资源全景
这个 CDK 应用由单个 Python 文件 app.py 构成,一次部署会创建以下资源:
- 一个 VPC(
max_azs=2,限制可用区数量以规避资源配额限制) - 一个 ECS 集群
- 一个 EFS 文件系统及其 Access Point(访问点)
- 一个 Fargate 任务定义,其中声明了 EFS 卷与容器挂载点
- 一个由
ApplicationLoadBalancedFargateService模式构造的、带公网 Application Load Balancer 的 Fargate 服务 - 双向放通 2049 端口(NFS)的安全组规则
- 基于 CPU 与内存利用率的 Application Auto Scaling 策略(1~20 个任务)
示例中所有资源使用统一的efs-sample-前缀命名,方便在 AWS 控制台中按名称检索:
PREFIX = 'efs-sample-' APP_PATH = '/var/www/' VOLUME_NAME = 'cdk-ecs-sample-efs-volume'(以上常量定义见 app.py)
二、环境准备与部署步骤
该示例是标准的 Python CDK 项目布局:cdk.json 中通过"app": "python3 app.py"告诉 CDK Toolkit 如何执行应用,并配置了watch的包含/排除规则以及一组 CDK 上下文开关(如@aws-cdk/aws-efs:denyAnonymousAccess、@aws-cdk/aws-ecs:arnFormatIncludesClusterName等,用于保持与当前 aws-cdk-lib 行为一致)。依赖声明在 requirements.txt 中:
aws-cdk-lib>=2.262.0,<3.0.0 constructs>=10.5.0,<11.0.0因此该示例面向 CDK v2(aws-cdk-lib 2.x),要求环境中已全局安装 CDK CLI。
完整的部署流程如下(继承自 README):
创建并激活虚拟环境。init 过程会在项目下创建
.env目录存放虚拟环境,前提是 PATH 中有带venv包的python3(Windows 上为python)。自动创建失败时可手动创建:$ python3 -m venv .env激活方式,macOS/Linux:
$ source .env/bin/activateWindows:
% .env\Scripts\activate.bat安装依赖:
$ pip install -r requirements.txt首次部署该账户/环境时,先执行 bootstrap 初始化 CDK 默认环境(用于放置 S3 资产桶等 CDK 基础设施):
$ cdk bootstrap部署 CloudFormation 模板:
$ cdk deploy
三、基础设施源码剖析
3.1 VPC 与 ECS 集群
网络与集群的创建非常克制——VPC 通过max_azs=2限制可用区数量(EFS 挂载目标、NAT 网关等资源会随 AZ 数量线性增加),随后在该 VPC 上创建集群:
vpc = ec2.Vpc( self, PREFIX + 'Vpc', max_azs=2 ) ecs_cluster = ecs.Cluster( self, PREFIX + 'Cluster', vpc=vpc, )3.2 EFS 文件系统与 Access Point
文件系统的两个关键参数值得注意:
file_system = efs.FileSystem( self, PREFIX + 'EFS', vpc=vpc, lifecycle_policy=efs.LifecyclePolicy.AFTER_14_DAYS, performance_mode=efs.PerformanceMode.GENERAL_PURPOSE, ) access_point = efs.AccessPoint( self, PREFIX + 'AccessPoint', file_system=file_system, )lifecycle_policy=AFTER_14_DAYS:14 天未被访问的文件会被自动分层到低频存储,降低存储成本;performance_mode=GENERAL_PURPOSE:通用型性能模式(对应底层bursting吞吐量模式),适合多数共享存储场景;- 单独创建
AccessPoint而非直接挂根路径,是 ECS 挂载 EFS 的推荐做法——访问点为挂载提供统一的"入口 + 权限位",配合 IAM 鉴权可以实现文件系统级别的细粒度访问控制。
3.3 卷配置:EfsVolumeConfiguration
这是把 EFS 与 ECS 任务"接线"的核心,app.py 中写为:
efs_volume_configuration = ecs.EfsVolumeConfiguration( file_system_id=file_system.file_system_id, # The logical ID of the Access Point to use. # This is a string, not an ARN. authorization_config=ecs.AuthorizationConfig( access_point_id=access_point.access_point_id, iam='ENABLED', ), transit_encryption='ENABLED', )三个要点:
file_system_id传的是文件系统ID 字符串;access_point_id同样是字符串而非 ARN(源码注释也特别强调了这一点),这是 CloudFormationEFSVolumeConfiguration的原生要求;authorization_config中iam='ENABLED'表示启用 IAM 授权,此时访问控制由任务角色的 EFS 客户端权限决定;transit_encryption='ENABLED'强制 NFS 数据在传输过程中加密(要求客户端支持 TLS 挂载,Fargate 原生支持)。
3.4 任务角色与 IAM 策略
Fargate 任务要挂载 EFS,任务角色必须持有 EFS 客户端权限。示例先创建了一个带信任条件限制的 IAM 角色(app.py):
task_role = iam.Role( self, PREFIX + 'EcsTaskRole', assumed_by=iam.ServicePrincipal('ecs-tasks.amazonaws.com').with_conditions({ "StringEquals": { "aws:SourceAccount": Stack.of(self).account }, "ArnLike": { "aws:SourceArn": "arn:aws:ecs:" + Stack.of(self).region + ":" + Stack.of(self).account + ":*" }, }), )aws:SourceAccount/aws:SourceArn条件用于防止"凭证混淆"攻击——只允许本账户、本区域的 ECS 服务使用该角色。随后附加的内联策略分两段:
iam.PolicyStatement( effect=iam.Effect.ALLOW, resources=['*'], actions=[ "ecr:GetAuthorizationToken", "ec2:DescribeAvailabilityZones" ] ), iam.PolicyStatement( sid='AllowEfsAccess', effect=iam.Effect.ALLOW, resources=['*'], actions=[ 'elasticfilesystem:ClientRootAccess', 'elasticfilesystem:ClientWrite', 'elasticfilesystem:ClientMount', 'elasticfilesystem:DescribeMountTargets' ] )前两条是 Fargate 拉取镜像、解析可用区的基础权限;AllowEfsAccess四条Client*权限则是 EFS 挂载的最小权限组合(ClientRootAccess授予根目录访问,ClientMount+ClientWrite允许挂载与写入,DescribeMountTargets让任务发现挂载点位置)。
3.5 任务定义:卷、挂载点与容器
task_definition = ecs.FargateTaskDefinition( self, PREFIX + 'FargateTaskDef', task_role=task_role, ) task_definition.add_volume( name=VOLUME_NAME, efs_volume_configuration=efs_volume_configuration, ) mount_point = ecs.MountPoint( container_path=APP_PATH + VOLUME_NAME, source_volume=VOLUME_NAME, read_only=False, ) port_mapping = ecs.PortMapping( container_port=80, host_port=80, protocol=ecs.Protocol.TCP, ) container = ecs.ContainerDefinition( self, 'ecs-cdk-sample-container', task_definition=task_definition, image=ecs.ContainerImage.from_registry('amazon/amazon-ecs-sample'), logging=ecs.LogDrivers.aws_logs( stream_prefix='cdk-ecs-sample', log_retention=logs.RetentionDays.ONE_MONTH, ) ) container.add_mount_points(mount_point) container.add_port_mappings(port_mapping)要点解读:
add_volume声明任务级卷,卷名cdk-ecs-sample-efs-volume是后续挂载点引用的键;- 容器路径为
APP_PATH + VOLUME_NAME,即容器内 EFS 内容出现在/var/www/cdk-ecs-sample-efs-volume,read_only=False表示可写——这正是"共享存储"的价值:多个 Fargate 任务实例读写同一份数据; - 容器使用官方演示镜像
amazon/amazon-ecs-sample(返回简单 JSON 的健康检查负载),监听 80 端口,日志走 CloudWatch Logs 并保留一个月。
3.6 服务编排:ApplicationLoadBalancedFargateService 模式
fargate_service = ecs_patterns.ApplicationLoadBalancedFargateService( self, PREFIX + 'Service', cluster=ecs_cluster, desired_count=1, task_definition=task_definition, task_subnets=ec2.SubnetSelection( subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS, ), platform_version=ecs.FargatePlatformVersion.LATEST, public_load_balancer=True, enable_execute_command=True, enable_ecs_managed_tags=True, )这里复用了自定义任务定义(而不是通过task_image_options让模式新建),因为 EFS 卷必须预先声明在任务定义中。参数含义:
task_subnets限定任务只调度到私有子网(带 NAT 出口),保持 ECS 最佳实践——计算节点不直接暴露公网,入口流量由 ALB 承接;public_load_balancer=True创建公网 ALB 并自动完成监听器、目标组、健康检查与容器端口的对接(对应前文的 80 端口映射);enable_execute_command=True开启 ECS Exec,可远程进入容器调试挂载问题;enable_ecs_managed_tags=True开启 ECS 托管标签。
3.7 安全组与自动扩缩容
EFS 挂载依赖 NFS 协议,默认端口 2049/TCP,因此需要双向放通(app.py):
# 允许 ECS 服务访问 EFS fargate_service.service.connections.allow_from(file_system, ec2.Port.tcp(2049)) # 允许 EFS 访问 ECS 服务 fargate_service.service.connections.allow_to(file_system, ec2.Port.tcp(2049))最后配置应用自动扩缩容:
scalable_target = fargate_service.service.auto_scale_task_count( min_capacity=1, max_capacity=20, ) scalable_target.scale_on_cpu_utilization("CpuScaling", target_utilization_percent=50) scalable_target.scale_on_memory_utilization("MemoryScaling", target_utilization_percent=50)服务维持在 1~20 个任务之间,当任务集平均 CPU 或内存任一维度超过 50% 时扩容,低于目标时缩容。这与仓库内姊妹示例 fargate-service-with-autoscaling 的扩缩容思路一致,可对照学习。
四、部署后验证
部署成功后可按 README 的验证清单在控制台核对三类资源(<Unique ID>为 CDK 为每个资源生成的唯一后缀,与 Stack ID 后缀一致):
- ECS 集群:名称形如
AWS-CDK-Fargate-efssampleCluster<Unique ID>; - EFS 文件系统:名称形如
AWS-CDK-Fargate-/efs-sample-EFS(<Unique ID>); - Application Load Balancer:名称形如
AWS-CD-efssa--<Unique ID>。
拿到 ALB 的 DNS 名称后直接访问,amazon/amazon-ecs-sample镜像会返回一段 JSON 负载,证明"公网 ALB → Fargate 任务 → 80 端口容器"链路连通;进一步可在容器内执行ls /var/www/cdk-ecs-sample-efs-volume验证 EFS 挂载生效,并在多个任务实例间互相写入文件以确认共享语义。
五、适用前提与关键结论
从 requirements.txt 与 cdk.json 看,该示例要求 CDK v2 环境(aws-cdk-lib 2.x、constructs 10.x),部署前需完成cdk bootstrap。这个样本浓缩了 Fargate + EFS 集成的最小可行骨架,核心链路为:
FileSystem + AccessPoint → EfsVolumeConfiguration(IAM 鉴权 + 传输加密)→ 任务定义 add_volume → 容器 MountPoint → 2049 端口安全组 → ALB 前置的 Fargate 服务 → CPU/内存双指标扩缩容。
理解这条链路后,即可将其替换为自己的业务镜像与容器路径,落地"无服务器计算 + 共享存储"的组合;若还需控制 EFS 的访问权限边界,可参考 cdk.json 中已启用的@aws-cdk/aws-efs:denyAnonymousAccess等上下文开关所对应的安全默认行为。
【免费下载链接】aws-cdk-examples
Example projects using the AWS CDK
相关推荐
aws-cdk-examples:用 AWS CDK(Python)实现 ECS Fargate 服务的昼夜定时扩缩容
aws cdk examples:用 AWS CDK(Python)实现 ECS Fargate 服务的昼夜定时扩缩容 本篇基于 python/ecs sche
aws-cdk-examples Python 实战:用 ECS Service Connect 实现前后端 Fargate 容器零配置服务通信
aws cdk examples Python 实战:用 ECS Service Connect 实现前后端 Fargate 容器零配置服务通信 本文基于 aw
使用 AWS CDK Java 构建 ECS Fargate 负载均衡服务:fargate-load-balanced-service 实战指南
使用 AWS CDK Java 构建 ECS Fargate 负载均衡服务:fargate load balanced service 实战指南 本文基于 aw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考