用于建设ECS服务的 CDK 建构库
该图书馆为建造服务提供了高水平、可扩展的模式 使用亚马逊ECS部署.
import {
AppMeshExtension,
CloudwatchAgentExtension,
Container,
Environment,
FireLensExtension,
HttpLoadBalancerExtension,
Service,
ServiceDescription,
XRayExtension,
} from '@aws-cdk-containers/ecs-service-extensions';
如果你正在使用 @aws-cdk-containers/ecs-service-extensions v1 和需要迁移到
v2, 见 移徙指南。 。 。 .
Service 构造
这个 Service 本模块提供的构造可加可选 ServiceExtension 增加补充ECS特征的类别,包括:
- AWS X-射线 用于跟踪您的应用程序
- 亚马逊云表代理 用于获取每个任务数据
- AWS 应用程序 用于将您的应用程序添加到服务网格
- 应用程序负载平衡器,因为将你的服务公开
- AWS 火灯,用于过滤和路由应用日志
- 喷射器扩展,因为允许您通过许可和注射环境变量连接到其他 AWS 服务
- 队列扩展,因为允许您从 SQS 队列中输入信息, 而 SNS 的一个或多个主题可以被它订阅
- 社区扩展,为高级使用案例提供支持
这个 ServiceExtension 类是一个抽象类,您也可以在
命令,用于构建您自己的自定义服务扩展名,或
附带您的定制资源或辅助车.
示例
// Create an environment to deploy a service in.
const environment = new Environment(this, 'production');
// Build out the service description
const nameDescription = new ServiceDescription();
nameDescription.add(new Container({
cpu: 1024,
memoryMiB: 2048,
trafficPort: 80,
image: ecs.ContainerImage.fromRegistry('nathanpeck/name'),
environment: {
PORT: '80',
},
}));
declare const mesh: appmesh.Mesh;
nameDescription.add(new AppMeshExtension({ mesh }));
nameDescription.add(new FireLensExtension());
nameDescription.add(new XRayExtension());
nameDescription.add(new CloudwatchAgentExtension());
nameDescription.add(new HttpLoadBalancerExtension());
// Implement the service description as a real service inside
// an environment.
const nameService = new Service(this, 'name', {
environment: environment,
serviceDescription: nameDescription,
});
创建 Environment
一个 Environment 是部署服务的地方。 你可以有多种环境
在一个AWS账户上 例如,您可以创建 test 环境
作为 production 环境,所以您有地方验证您的申请
在你把它部署到一个活的环境之前,按原计划工作.
每一种环境都与其他环境相隔离. 换句话说, 当你创造了一个环境, 默认的构造供应自己的 VPC, ECS集群和任何其他环境所需资源:
const environment = new Environment(this, 'production');
然而,您也可以选择从原有的VPC中构建环境 或ECS集群:
declare const vpc: ec2.Vpc;
const cluster = new ecs.Cluster(this, 'Cluster', { vpc });
const environment = new Environment(this, 'production', {
vpc,
cluster,
});
定义您的 ServiceDescription
这个 ServiceDescription 定义您想要服务运行的应用程序
您想要在服务中添加什么可选扩展 。 最基本的一种形式 ServiceDescription 看起来像这样:
const nameDescription = new ServiceDescription();
nameDescription.add(new Container({
cpu: 1024,
memoryMiB: 2048,
trafficPort: 80,
image: ecs.ContainerImage.fromRegistry('nathanpeck/name'),
environment: {
PORT: '80',
},
}));
每个 ServiceDescription 需要至少添加一个 Container 扩展
定义服务的主要应用(基本)容器.
使用日志 awslogs 日志驱动程序
如果没有为某项服务配置可观察性扩展, ECS 服务扩展将配置一个 awslogs 将容器日志发送给 Cloud Watch Logs 的应用程序容器的日志驱动程序.
您可以提供日志组到 Container 扩展名或一个由 CDK 为您创建 .
以下为申请的示例: awslogs 为应用程序容器配置的日志驱动程序 :
import * as logs from 'aws-cdk-lib/aws-logs';
const environment = new Environment(this, 'production');
const nameDescription = new ServiceDescription();
nameDescription.add(new Container({
cpu: 1024,
memoryMiB: 2048,
trafficPort: 80,
image: ContainerImage.fromRegistry('nathanpeck/name'),
environment: {
PORT: '80',
},
logGroup: new logs.LogGroup(this, 'MyLogGroup'),
}));
如果不提供日志组,则未创建可观察扩展 ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER 启用特性标记,然后默认配置日志,并为您创建日志组.
这个 ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER 功能旗默认在使用 CDK v1.140.0 或 v 2.8.0 创建的任何 CDK 应用程序中启用.
要启用对先前版本的默认记录,请确保 ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER 应用程序堆栈上下文中的标记设置为真,就像这样:
import * as cxapi from '@aws-cdk/cx-api';
this.node.setContext(cxapi.ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER, true);
或者,您也可以在 cdk.json 文档。 欲了解更多信息,请参见 文档。 。 。 .
添加后 Container 扩展名,您可以选择为服务启用附加功能 ServiceDescription.add() 方法 :
declare const mesh: appmesh.Mesh;
const nameDescription = new ServiceDescription();
nameDescription.add(new AppMeshExtension({ mesh }));
nameDescription.add(new FireLensExtension());
nameDescription.add(new XRayExtension());
nameDescription.add(new CloudwatchAgentExtension());
nameDescription.add(new HttpLoadBalancerExtension());
nameDescription.add(new AssignPublicIpExtension());
发射 ServiceDescription 作为 Service
一旦服务描述被定义,您就可以作为一个服务启动:
const environment = new Environment(this, 'production');
const nameDescription = new ServiceDescription();
const nameService = new Service(this, 'name', {
environment: environment,
serviceDescription: nameDescription,
});
此时将创造出所有服务资源. 这包括ECS任务 定义、服务以及App Mesh虚拟软件等其他任何附带资源 节点或应用程序负载平衡器.
创建自己的任务
有时任务Role应该被定义在服务之外,这样您就可以创建严格的资源政策(即S3桶政策),限制在给定任务Role:
const taskRole = new iam.Role(this, 'CustomTaskRole', {
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
});
// Use taskRole in any CDK resource policies
// new s3.BucketPolicy(this, 'BucketPolicy, {});
const environment = new Environment(this, 'production');
const nameDescription = new ServiceDescription();
const nameService = new Service(this, 'name', {
environment: environment,
serviceDescription: nameDescription,
taskRole,
});
配置自定义健康检查
当您加入 HTTPloadBalancerExtension 时,您可以通过访问服务上的目标Group 字段来定制健康检查.
const service = new Service(stack, 'my-service', {
environment,
serviceDescription,
autoScaleTaskCount: {
maxTaskCount: 5,
},
});
service.targetGroup.configureHealthCheck({
path: '/',
port: '80',
});
任务自动缩放
您可以配置服务的任务计数以匹配需求 。 建议的实现方法是为您服务配置目标跟踪政策,以在目标值周围保持衡量标准.
您需要设置此服务的自动缩放目标 minTaskCount (默认为1)和 maxTaskCount 输入 Service 构造。 然后,您可以在服务中的所有任务中指定“CPU 利用”或“记忆利用”的目标值。 注意 desiredCount 值将设定为 undefined 如果配置了自动缩放目标.
如果您想要根据应用程序负载平衡器或SQS等资源配置自动缩放策略,可以在扩展名中设置相应的资源特有字段. 例如,您可以根据应用程序负载平衡器请求计数启用目标跟踪缩放:
const environment = new Environment(this, 'production');
const serviceDescription = new ServiceDescription();
serviceDescription.add(new Container({
cpu: 256,
memoryMiB: 512,
trafficPort: 80,
image: ecs.ContainerImage.fromRegistry('my-alb'),
}));
// Add the extension with target `requestsPerTarget` value set
serviceDescription.add(new HttpLoadBalancerExtension({ requestsPerTarget: 10 }));
// Configure the auto scaling target
new Service(this, 'my-service', {
environment,
serviceDescription,
desiredCount: 5,
// Task auto-scaling constuct for the service
autoScaleTaskCount: {
maxTaskCount: 10,
targetCpuUtilization: 70,
targetMemoryUtilization: 50,
},
});
您也可以定义自己的服务扩展名 其他自动缩放政策 通过使用 scalableTaskCount 属性 Service 课.
创建您的自定义 ServiceExtension
除了使用带有此模块的默认服务扩展外, 您
可以选择执行您的自定义服务扩展名。 这个 ServiceExtension
阶级是一个抽象的阶级,你可以执行自己。 以下例子
执行可添加到服务的自定义服务扩展名,以便
根据 SQS 队列大小的缩放间隔自动缩放 :
export class MyCustomAutoscaling extends ServiceExtension {
constructor() {
super('my-custom-autoscaling');
// Scaling intervals for the step scaling policy
this.scalingSteps = [{ upper: 0, change: -1 }, { lower: 100, change: +1 }, { lower: 500, change: +5 }];
this.sqsQueue = new sqs.Queue(this.scope, 'my-queue');
}
// This hook utilizes the resulting service construct
// once it is created
public useService(service: ecs.Ec2Service | ecs.FargateService) {
this.parentService.scalableTaskCount.scaleOnMetric('QueueMessagesVisibleScaling', {
metric: this.sqsQueue.metricApproximateNumberOfMessagesVisible(),
scalingSteps: this.scalingSteps,
});
}
}
这个 ServiceExtension 现在可以重新使用,并添加到任何数量不同的
服务描述。 这使得您可以开发可重复使用的配置位,
将其归入许多不同的服务,并集中管理。 更新
ServiceExtension 在一个地方将更新所有使用它的服务,而不是
要求分散向许多不同的服务提供最新情况.
每个 ServiceExtension 可以执行以下的钩子来修改属性
或利用由此而形成的构造:
addHooks()- 这个钩子是在所有扩展加到一个之后叫的 ServiceDescription,但在其他扩展钩被运行之前. 它使每个延长都有机会对整个服务进行一些检查 并查看添加了哪些扩展。 一些扩展可能想要注册 钩在其他扩展上修改它们. 例如,Firelens扩展 想要修改应用程序容器的设置为路由日志 通过Firelens.modifyTaskDefinitionProps()- 这是悬钩子通过 提议 阴道 任务定义Props for a Task Definition 即将创建的任务定义 . 这使得扩展可以修改任务定义道具 在任务定义创建之前。 例如, App Mesh 扩展修改 任务代理设置.useTaskDefinition()- 任务定义创建后,这个钩子是 通过已创建的实际任务定义构造。 这允许 扩展以添加任务中的容器,修改任务定义的IAM作用, 包括:.resolveContainerDependencies()- 一旦所有扩展都增加了集装箱, 每个扩展都有机会修改其容器的dependsOn设置. 扩展需要检查和查看启用哪些其他扩展并作出决定 它们的容器是否需要在另一个容器上等待才能首先开始.modifyServiceProps()- 在建立Ec2服务或远门服务之前,这个 悬钩被通过服务道具的草稿版本来更改. 每个延长部分都添加 自身对服务属性的修改。 例如, App Mesh 扩展 需要修改服务设置,以便能够发现 CloudMap 服务.useService()- 服务创建后,这个钩子有机会 利用这项服务。 这被诸如负载平衡器或 App Mesh 这样的扩展使用 扩展,用于创建其他 AWS 资源并将其链接到 ECS 扩展.connectToService()- 当用户想要连接一个服务时,这个钩子被调用 到另一个服务。 它允许扩展执行关于如何允许的逻辑 从一个服务连接到另一个服务。 例如, App Mesh 扩展执行 这种方法可以方便地将一个服务网点服务连接到另一个服务网点服务 允许服务方的"特使"代理侧车通过线路向对方通行.
连接服务
钩子之一 ServiceExtension 可以执行是连接的钩子
逻辑 在将一项服务与另一项服务连接起来时,例如,就利用了这一点.
用后端API连接一个面临网络服务的用户. 用法是这样的:
const frontendDescription = new ServiceDescription();
const frontend = new Service(this, 'frontend', {
environment,
serviceDescription: frontendDescription,
});
const backendDescription = new ServiceDescription();
const backend = new Service(this, 'backend', {
environment,
serviceDescription: backendDescription,
});
frontend.connectTo(backend);
一个服务将用来与另一个服务交谈的地址取决于
由连接的扩展创建的入侵类型.
例如,如果使用了 App Mesh 扩展,则服务可以访问
dNS地址为 <service name>.<environment name>。例如:
const environment = new Environment(this, 'production');
// Define the frontend tier
const frontendDescription = new ServiceDescription();
frontendDescription.add(new Container({
cpu: 1024,
memoryMiB: 2048,
trafficPort: 80,
image: ecs.ContainerImage.fromRegistry('my-frontend-service'),
environment: {
BACKEND_URL: 'http://backend.production',
},
}));
const frontend = new Service(this, 'frontend', {
environment,
serviceDescription: frontendDescription,
});
// Define the backend tier
const backendDescription = new ServiceDescription();
backendDescription.add(new Container({
cpu: 1024,
memoryMiB: 2048,
trafficPort: 80,
image: ecs.ContainerImage.fromRegistry('my-backend-service'),
environment: {
FRONTEND_URL: 'http://frontend.production',
},
}));
const backend = new Service(this, 'backend', {
environment,
serviceDescription: backendDescription,
});
// Connect the two tiers to each other
frontend.connectTo(backend);
上述代码使用众所周知的服务发现名称 将它作为环境变量传递到容器 容器在与 另一个服务.
导入先前存在的集群
要创建已有集群的环境, 您必须先导入集群, 然后使用 Environment.fromEnvironmentAttributes()。当一个集群被导入到一个环境中时,该集群被视作是不可改变的。 因此,任何扩展都不得修改集群来改变设置.
declare const cluster: ecs.Cluster;
const environment = Environment.fromEnvironmentAttributes(this, 'Environment', {
capacityType: ecs.EnvironmentCapacityType.EC2, // or `FARGATE`
cluster,
});
喷射器扩展
此服务扩展接受列表 Injectable 资源。 它允许获取这些资源,并在作为服务组成部分的任务中增加必要的环境变量.
例如,一个 InjectableTopic sNS主题允许任务角色,并将主题ARN作为环境变量添加到任务定义中.
出版到 SNS 主题
您可以使用此扩展设置 SNS 主题的出版权限 .
const nameDescription = new ServiceDescription();
nameDescription.add(new InjecterExtension({
injectables: [new InjectableTopic({
// SNS Topic the service will publish to
topic: new sns.Topic(this, 'my-topic'),
})],
}));
队列扩展
此服务扩展创建默认 SQS 队列 eventsQueue (如不提供)服务,也可选择接受 ISubscribable 对象 eventsQueue 可以订阅。 服务扩展创建订阅并设置权限,使服务能够从SQS队列中收录消息.
为 SQS 队列设置 SNS 主题订阅
您可以使用此扩展设置 SNS 主题订阅 eventsQueue。要做到这一点,请创建一个类型新对象 TopicSubscription 您想要的每个 SNS 话题 eventsQueue 以订阅和提供,作为对服务扩展的投入.
const nameDescription = new ServiceDescription();
const myServiceDescription = nameDescription.add(new QueueExtension({
// Provide list of topic subscriptions that you want the `eventsQueue` to subscribe to
subscriptions: [new TopicSubscription({
topic: new sns.Topic(this, 'my-topic'),
}],
}));
// To access the `eventsQueue` for the service, use the `eventsQueue` getter for the extension
const myQueueExtension = myServiceDescription.extensions.queue as QueueExtension;
const myEventsQueue = myQueueExtension.eventsQueue;
为设置特定主题的队列订阅, 您可以提供自定义队列 TopicSubscription 对象与 SNS 主题。 扩展将设置一个主题订阅, 而非默认的队列 eventsQueue 服务.
declare const myEventsQueue: sqs.Queue;
declare const myTopicQueue: sqs.Queue;
const nameDescription = new ServiceDescription();
nameDescription.add(new QueueExtension({
eventsQueue: myEventsQueue,
subscriptions: [new TopicSubscription({
topic: new sns.Topic(this, 'my-topic'),
// `myTopicQueue` will subscribe to the `my-topic` instead of `eventsQueue`
topicSubscriptionQueue: {
queue: myTopicQueue,
},
}],
}));
根据 SQS 队列配置自动缩放
通过跟踪每个任务的积压,您可以提升或降低服务规模,以保持可接受的排队间隔. 它以目标值(每个任务的可接受积压量)来配置目标跟踪缩放政策,其计算方法是除去目标值 acceptableLatency 以 messageProcessingTime例如,如果一个消息到达SQS队列后处理的最大可接受延迟是10分钟,而任务的平均处理时间是每条消息250毫秒,那么 acceptableBacklogPerTask = 10 * 60 / 0.25 = 2400。因此,在服务开始扩大前,每个队列最多可收到24:00的信息。 为此,目标跟踪政策将附在目标值的您服务的缩放目标上 2400更多信息请参见: https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-using-sqs-queue.html 页面存档备份,存于互联网档案馆 。 。 。 .
您可以根据 SQS 队列为您的服务配置自动缩放如下:
declare const myEventsQueue: sqs.Queue;
declare const myTopicQueue: sqs.Queue;
const nameDescription = new ServiceDescription();
nameDescription.add(new QueueExtension({
eventsQueue: myEventsQueue,
// Need to specify `scaleOnLatency` to configure auto scaling based on SQS Queue
scaleOnLatency: {
acceptableLatency: Duration.minutes(10),
messageProcessingTime: Duration.millis(250),
},
subscriptions: [new TopicSubscription({
topic: new sns.Topic(this, 'my-topic'),
// `myTopicQueue` will subscribe to the `my-topic` instead of `eventsQueue`
topicSubscriptionQueue: {
queue: myTopicQueue,
// Optionally provide `scaleOnLatency` for configuring separate autoscaling for `myTopicQueue`
scaleOnLatency: {
acceptableLatency: Duration.minutes(10),
messageProcessingTime: Duration.millis(250),
},
},
}],
}));
出版/订阅服务模式
这个 出版/订阅服务模式 用于实现服务之间的同步通信。 它涉及“publisher”服务, 向 SNS 专题发布活动, 传递给订阅的 SQS 队列.
以下例子补充: InjecterExtension 改为 Publisher 在 SNS 专题中发布事件并添加 QueueExtension 改为 Worker 能够进行投票的服务 eventsQueue 以使用该主题所包含的信息.
const environment = new Environment(this, 'production');
const pubServiceDescription = new ServiceDescription();
pubServiceDescription.add(new Container({
cpu: 256,
memoryMiB: 512,
trafficPort: 80,
image: ecs.ContainerImage.fromRegistry('sns-publish'),
}));
const myTopic = new sns.Topic(this, 'myTopic');
// Add the `InjecterExtension` to the service description to allow publishing events to `myTopic`
pubServiceDescription.add(new InjecterExtension({
injectables: [new InjectableTopic({
topic: myTopic,
}],
}));
// Create the `Publisher` Service
new Service(this, 'Publisher', {
environment: environment,
serviceDescription: pubServiceDescription,
});
const subServiceDescription = new ServiceDescription();
subServiceDescription.add(new Container({
cpu: 256,
memoryMiB: 512,
trafficPort: 80,
image: ecs.ContainerImage.fromRegistry('sqs-reader'),
}));
// Add the `QueueExtension` to the service description to subscribe to `myTopic`
subServiceDescription.add(new QueueExtension({
subscriptions: [new TopicSubscription({
topic: myTopic,
}],
}));
// Create the `Worker` Service
new Service(this, 'Worker', {
environment: environment,
serviceDescription: subServiceDescription,
});
社区扩展
我们鼓励发展社区服务扩展,以支持 高级特性。 我们审查了以下一些有用的扩展:
- 收听器规则扩展 对应用程序负载平衡器规则进行更精确的控制
请提交调用请求,以便我们审查您的服务扩展和 在这里列出.