使用 Amazon CDK 在 Amazon ECS 上启用 Application Signals - Amazon CloudWatch
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

使用 Amazon CDK 在 Amazon ECS 上启用 Application Signals

要使用 Amazon CDK 在 Amazon ECS 上启用 Application Signals,请执行以下操作。

  1. 为应用程序启用 Application Signals – 如果您尚未在此账户中启用 Application Signals,则必须向 Application Signals 授予发现您的服务所需的权限。

    import { aws_applicationsignals as applicationsignals } from 'aws-cdk-lib'; const cfnDiscovery = new applicationsignals.CfnDiscovery(this, 'ApplicationSignalsServiceRole', { } );

    Discovery CloudFormation 资源授予 Application Signals 下列权限:

    • xray:GetServiceGraph

    • logs:StartQuery

    • logs:GetQueryResults

    • cloudwatch:GetMetricData

    • cloudwatch:ListMetrics

    • tag:GetResources

    有关该角色的更多信息,请参阅CloudWatch Application Signals 的服务相关角色权限

  2. 使用 Amazon CDK 中的 AWS::ApplicationSignals 构造库来检测您的应用程序。本文档中的代码片段为 TypeScript 格式。有关其他特定语言的备选方案,请参阅 Amazon CDK 支持的编程语言

    • 使用附加模式在 Amazon ECS 上启用 Application Signals

      1. 配置 instrumentation,以使用 Amazon Distro for OpenTelemetry(ADOT)SDK Agent 检测应用程序。以下是检测 Java 应用程序的示例。请参阅 InstrumentationVersion,了解所有可支持的语言版本。

      2. 指定 cloudWatchAgentSidecar 以将 CloudWatch 代理配置为附加容器。

        import { Construct } from 'constructs'; import * as appsignals from '@aws-cdk/aws-applicationsignals-alpha'; import * as cdk from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as ecs from 'aws-cdk-lib/aws-ecs'; class MyStack extends cdk.Stack { public constructor(scope?: Construct, id?: string, props: cdk.StackProps = {}) { super(); const vpc = new ec2.Vpc(this, 'TestVpc', {}); const cluster = new ecs.Cluster(this, 'TestCluster', { vpc }); const fargateTaskDefinition = new ecs.FargateTaskDefinition(this, 'SampleAppTaskDefinition', { cpu: 2048, memoryLimitMiB: 4096, }); fargateTaskDefinition.addContainer('app', { image: ecs.ContainerImage.fromRegistry('test/sample-app'), }); new appsignals.ApplicationSignalsIntegration(this, 'ApplicationSignalsIntegration', { taskDefinition: fargateTaskDefinition, instrumentation: { sdkVersion: appsignals.JavaInstrumentationVersion.V2_10_0, }, serviceName: 'sample-app', cloudWatchAgentSidecar: { containerName: 'ecs-cwagent', enableLogging: true, cpu: 256, memoryLimitMiB: 512, } }); new ecs.FargateService(this, 'MySampleApp', { cluster: cluster, taskDefinition: fargateTaskDefinition, desiredCount: 1, }); } }
    • 使用进程守护程序模式在 Amazon ECS 上启用 Application Signals

      注意

      进程守护程序部署策略在 Amazon ECS Fargate 上不受支持,仅在 Amazon EC2 的 Amazon ECS 上受支持。

      1. HOST 网络模式将 CloudWatch 代理作为守护进程服务运行。

      2. 配置 instrumentation,以使用 ADOT Python 代理检测应用程序。

        import { Construct } from 'constructs'; import * as appsignals from '@aws-cdk/aws-applicationsignals-alpha'; import * as cdk from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as ecs from 'aws-cdk-lib/aws-ecs'; class MyStack extends cdk.Stack { public constructor(scope?: Construct, id?: string, props: cdk.StackProps = {}) { super(scope, id, props); const vpc = new ec2.Vpc(this, 'TestVpc', {}); const cluster = new ecs.Cluster(this, 'TestCluster', { vpc }); // Define Task Definition for CloudWatch agent (Daemon) const cwAgentTaskDefinition = new ecs.Ec2TaskDefinition(this, 'CloudWatchAgentTaskDefinition', { networkMode: ecs.NetworkMode.HOST, }); new appsignals.CloudWatchAgentIntegration(this, 'CloudWatchAgentIntegration', { taskDefinition: cwAgentTaskDefinition, containerName: 'ecs-cwagent', enableLogging: false, cpu: 128, memoryLimitMiB: 64, portMappings: [ { containerPort: 4316, hostPort: 4316, }, { containerPort: 2000, hostPort: 2000, }, ], }); // Create the CloudWatch Agent daemon service new ecs.Ec2Service(this, 'CloudWatchAgentDaemon', { cluster, taskDefinition: cwAgentTaskDefinition, daemon: true, // Runs one container per EC2 instance }); // Define Task Definition for user application const sampleAppTaskDefinition = new ecs.Ec2TaskDefinition(this, 'SampleAppTaskDefinition', { networkMode: ecs.NetworkMode.HOST, }); sampleAppTaskDefinition.addContainer('app', { image: ecs.ContainerImage.fromRegistry('test/sample-app'), cpu: 0, memoryLimitMiB: 512, }); // No CloudWatch Agent sidecar is needed as application container communicates to CloudWatch Agent daemon through host network new appsignals.ApplicationSignalsIntegration(this, 'ApplicationSignalsIntegration', { taskDefinition: sampleAppTaskDefinition, instrumentation: { sdkVersion: appsignals.PythonInstrumentationVersion.V0_8_0 }, serviceName: 'sample-app' }); new ecs.Ec2Service(this, 'MySampleApp', { cluster, taskDefinition: sampleAppTaskDefinition, desiredCount: 1, }); } }
    • 使用副本模式在 Amazon ECS 上启用 Application Signals

      注意

      使用副本模式运行 CloudWatch 代理服务需要特定的安全组配置,才能与其他服务进行通信。要使用 Application Signals 功能,请使用最低入站规则配置安全组:端口 2000(HTTP)和端口 4316(HTTP)。此配置可确保 CloudWatch 代理与相关服务之间的连接正确。

      1. 使用服务连接将 CloudWatch 代理作为副本服务运行。

      2. 配置 instrumentation,以使用 ADOT Python 代理检测应用程序。

      3. 配置 overrideEnvironments 来使用服务连接端点与 CloudWatch 代理服务器通信,以覆盖环境变量。

        import { Construct } from 'constructs'; import * as appsignals from '@aws-cdk/aws-applicationsignals-alpha'; import * as cdk from 'aws-cdk-lib'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as ecs from 'aws-cdk-lib/aws-ecs'; import { PrivateDnsNamespace } from 'aws-cdk-lib/aws-servicediscovery'; class MyStack extends cdk.Stack { public constructor(scope?: Construct, id?: string, props: cdk.StackProps = {}) { super(scope, id, props); const vpc = new ec2.Vpc(this, 'TestVpc', {}); const cluster = new ecs.Cluster(this, 'TestCluster', { vpc }); const dnsNamespace = new PrivateDnsNamespace(this, 'Namespace', { vpc, name: 'local', }); const securityGroup = new ec2.SecurityGroup(this, 'ECSSG', { vpc }); securityGroup.addIngressRule(securityGroup, ec2.Port.tcpRange(0, 65535)); // Define Task Definition for CloudWatch agent (Replica) const cwAgentTaskDefinition = new ecs.FargateTaskDefinition(this, 'CloudWatchAgentTaskDefinition', {}); new appsignals.CloudWatchAgentIntegration(this, 'CloudWatchAgentIntegration', { taskDefinition: cwAgentTaskDefinition, containerName: 'ecs-cwagent', enableLogging: false, cpu: 128, memoryLimitMiB: 64, portMappings: [ { name: 'cwagent-4316', containerPort: 4316, hostPort: 4316, }, { name: 'cwagent-2000', containerPort: 2000, hostPort: 2000, }, ], }); // Create the CloudWatch Agent replica service with service connect new ecs.FargateService(this, 'CloudWatchAgentService', { cluster: cluster, taskDefinition: cwAgentTaskDefinition, securityGroups: [securityGroup], serviceConnectConfiguration: { namespace: dnsNamespace.namespaceArn, services: [ { portMappingName: 'cwagent-4316', dnsName: 'cwagent-4316-http', port: 4316, }, { portMappingName: 'cwagent-2000', dnsName: 'cwagent-2000-http', port: 2000, }, ], }, desiredCount: 1, }); // Define Task Definition for user application const sampleAppTaskDefinition = new ecs.FargateTaskDefinition(this, 'SampleAppTaskDefinition', {}); sampleAppTaskDefinition.addContainer('app', { image: ecs.ContainerImage.fromRegistry('test/sample-app'), cpu: 0, memoryLimitMiB: 512, }); // Overwrite environment variables to connect to the CloudWatch Agent service just created new appsignals.ApplicationSignalsIntegration(this, 'ApplicationSignalsIntegration', { taskDefinition: sampleAppTaskDefinition, instrumentation: { sdkVersion: appsignals.PythonInstrumentationVersion.V0_8_0, }, serviceName: 'sample-app', overrideEnvironments: [ { name: appsignals.CommonExporting.OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT, value: 'http://cwagent-4316-http:4316/v1/metrics', }, { name: appsignals.TraceExporting.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, value: 'http://cwagent-4316-http:4316/v1/traces', }, { name: appsignals.TraceExporting.OTEL_TRACES_SAMPLER_ARG, value: 'endpoint=http://cwagent-2000-http:2000', }, ], }); // Create ECS Service with service connect configuration new ecs.FargateService(this, 'MySampleApp', { cluster: cluster, taskDefinition: sampleAppTaskDefinition, serviceConnectConfiguration: { namespace: dnsNamespace.namespaceArn, }, desiredCount: 1, }); } }
  3. 使用 ESM 模块格式设置 Node.js 应用程序。我们对采用 ESM 模块格式的 Node.js 应用程序提供有限的支持。有关更多信息,请参阅使用 ESM 的 Node.js 的已知限制

    对于 ESM 模块格式,通过使用 init 容器注入 Node.js 检测 SDK 启用 Application Signals 不适用。跳过此程序的第 2 步,改为执行以下操作。

    • 将相关依赖项安装到您的 Node.js 应用程序中以进行自动检测。

      npm install @aws/aws-distro-opentelemetry-node-autoinstrumentation npm install @opentelemetry/instrumentation@0.54.
    • 更新任务定义。

      1. 将其他配置添加到您的应用程序容器。

      2. Configure NODE_OPTIONS

      3. (可选)如果选择附加模式,则添加 CloudWatch 代理。

        import { Construct } from 'constructs'; import * as appsignals from '@aws-cdk/aws-applicationsignals-alpha'; import * as ecs from 'aws-cdk-lib/aws-ecs'; class MyStack extends cdk.Stack { public constructor(scope?: Construct, id?: string, props: cdk.StackProps = {}) { super(scope, id, props); const fargateTaskDefinition = new ecs.FargateTaskDefinition(stack, 'TestTaskDefinition', { cpu: 256, memoryLimitMiB: 512, }); const appContainer = fargateTaskDefinition.addContainer('app', { image: ecs.ContainerImage.fromRegistry('docker/cdk-test'), }); const volumeName = 'opentelemetry-auto-instrumentation' fargateTaskDefinition.addVolume({name: volumeName}); // Inject additional configurations const injector = new appsignals.NodeInjector(volumeName, appsignals.NodeInstrumentationVersion.V0_5_0); injector.renderDefaultContainer(fargateTaskDefinition); // Configure NODE_OPTIONS appContainer.addEnvironment('NODE_OPTIONS', '--import @aws/aws-distro-opentelemetry-node-autoinstrumentation/register --experimental-loader=@opentelemetry/instrumentation/hook.mjs') // Optional: add CloudWatch agent const cwAgent = new appsignals.CloudWatchAgentIntegration(stack, 'AddCloudWatchAgent', { containerName: 'ecs-cwagent', taskDefinition: fargateTaskDefinition, memoryReservationMiB: 50, }); appContainer.addContainerDependencies({ container: cwAgent.agentContainer, condition: ecs.ContainerDependencyCondition.START, }); }
  4. 部署更新后的堆栈 – 在应用程序的主目录中运行 cdk synth 命令。要在您的 Amazon 账户中部署服务,请在应用程序的主目录中运行 cdk deploy 命令。

    如果您使用的是挎斗策略,则会看到创建了一个服务:

    • APPLICATION_SERVICE 是您应用程序的服务。该服务包含以下三个容器:

      • init – Application Signals 初始化所必需的容器。

      • ecs-cwagent – 运行 CloudWatch 代理的容器

      • my-app – 这是我们文档中的示例应用程序容器。在实际工作负载中,此特定的容器可能不存在,或者可能被您自己的服务容器所取代。

    如果您使用的是进程守护程序策略,则会看到创建了两个服务:

    • CloudWatchAgentDaemon 是 CloudWatch 代理进程守护程序服务。

    • APPLICATION_SERVICE 是您应用程序的服务。该服务包含以下两个容器:

      • init – Application Signals 初始化所必需的容器。

      • my-app – 这是我们文档中的示例应用程序容器。在实际工作负载中,此特定的容器可能不存在,或者可能被您自己的服务容器所取代。

    如果您使用的是进程守护程序策略,则会看到创建了两个服务:

    • CloudWatchAgentService 是 CloudWatch 代理进程守护程序服务。

    • APPLICATION_SERVICE 是您应用程序的服务。该服务包含以下两个容器:

      • init – Application Signals 初始化所必需的容器。

      • my-app – 这是我们文档中的示例应用程序容器。在实际工作负载中,此特定的容器可能不存在,或者可能被您自己的服务容器所取代。