AWS CDK を使用して Amazon ECS で Application Signals を有効にする - Amazon CloudWatch

AWS CDK を使用して Amazon ECS で Application Signals を有効にする

AWS 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. AWS CDK の AWS::ApplicationSignals コンストラクトライブラリを使用してアプリケーションを計測します。このドキュメントのコードスニペットは TypeScript で提供されています。その他の言語固有の代替については、「AWS CDK のサポートされているプログラミング言語」を参照してください。

    • サイドカーモードを使用して Amazon ECS で Application Signals を有効にする

      1. AWS Distro for OpenTelemetry (ADOT) SDK エージェントを使用してアプリケーションを計測するように instrumentation を設定します。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 ECS on Amazon EC2 でのみサポートされています。

      1. HOST ネットワークモードを使用して CloudWatch エージェントをデーモンサービスとして実行します。

      2. ADOT Python エージェントを使用してアプリケーションを計測するように instrumentation を設定します。

        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. Service Connect を使用して CloudWatch エージェントをレプリカサービスとして実行します。

      2. ADOT Python エージェントを使用してアプリケーションを計測するように instrumentation を設定します。

      3. Service Connect エンドポイントを使用して CloudWatch エージェントサーバーと通信するように overrideEnvironments を設定して、環境変数を上書きします。

        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.
    • TaskDefinition を更新します。

      1. アプリケーションコンテナに設定を追加します。

      2. 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 コマンドを実行します。AWS アカウントでサービスをデプロイするには、cdk deploy コマンドをアプリケーションのメインディレクトリで実行します。

    サイドカー戦略を使用した場合、次の 1 つのサービスが作成されます。

    • APPLICATION_SERVICE はアプリケーションのサービスです。これには、次の 3 つのコンテナが含まれます。

      • init- Application Signals を初期化するために必要なコンテナ。

      • ecs-cwagent– CloudWatch エージェントを実行するコンテナ

      • my-app– これは、ドキュメントのアプリケーションコンテナの例です。実際のワークロードでは、この特定のコンテナが存在しないか、独自のサービスコンテナに置き換えられる可能性があります。

    デーモン戦略を使用した場合、次の 2 つのサービスが作成されます。

    • CloudWatchAgentDaemon は CloudWatch エージェントデーモンサービスです。

    • APPLICATION_SERVICE はアプリケーションのサービスです。これには、次の 2 つのコンテナが含まれます。

      • init- Application Signals を初期化するために必要なコンテナ。

      • my-app– これは、ドキュメントのアプリケーションコンテナの例です。実際のワークロードでは、この特定のコンテナが存在しないか、独自のサービスコンテナに置き換えられる可能性があります。

    レプリカ戦略を使用した場合、次の 2 つのサービスが作成されます。

    • CloudWatchAgentService は CloudWatch エージェントレプリカサービスです。

    • APPLICATION_SERVICE はアプリケーションのサービスです。これには、次の 2 つのコンテナが含まれます。

      • init- Application Signals を初期化するために必要なコンテナ。

      • my-app– これは、ドキュメントのアプリケーションコンテナの例です。実際のワークロードでは、この特定のコンテナが存在しないか、独自のサービスコンテナに置き換えられる可能性があります。