

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 在 Amazon ECS 上啟用 Application Signals AWS CDK
<a name="CloudWatch-Application-Signals-EKS-CDK"></a>

若要使用 在 Amazon ECS 上啟用 Application Signals AWS CDK，請執行下列動作。

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 的服務連結角色許可](using-service-linked-roles.md#service-linked-role-signals)。

1. 使用 AWS CDK 中的 [AWS：：ApplicationSignals Construct Library](https://www.npmjs.com/package/@aws-cdk/aws-applicationsignals-alpha) 檢測您的應用程式。本文件內的程式碼片段在 *TypeScript* 中提供。如需其他特定語言的替代方案，請參閱 [AWS CDK 支援的程式設計語言](https://docs.aws.amazon.com/cdk/v2/guide/languages.html)。
   + **使用附屬程式模式在 Amazon ECS 上啟用 Application Signals**

     1. 設定 `instrumentation` 以使用 AWS Distro for OpenTelemetry (ADOT) SDK Agent 檢測應用程式。以下是檢測 Java 應用程式的範例。如需所有受支援的語言版本，請參閱 [InstrumentationVersion](https://docs.aws.amazon.com/cdk/api/v2/docs/@aws-cdk_aws-applicationsignals-alpha.InstrumentationVersion.html)。

     1. 指定 `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 代理程式，作為常駐程式服務。

     1. 設定 `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 代理程式作為複本服務執行。

     1. 設定 `instrumentation` 以使用 ADOT Python 代理程式檢測應用程式。

     1. 將 `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,
            });
          }
        }
        ```

1. 設定採用 ESM 模組格式的 Node.js 應用程式。對於採用 ESM 模組格式的 Node.js 應用程式，我們提供的支援有限。如需詳細資訊，請參閱 [Node.js 搭配使用 ESM 的已知限制](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-supportmatrix.html#ESM-limitations)。

   對於 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. 將其他組態新增至您的應用程式容器。

     1. 設定 `NODE_OPTIONS`.

     1. (選用) 如果選擇附屬程式模式，新增 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,
            });
        }
        ```

1. 部署更新的堆疊 – 在應用程式的主目錄中執行 `cdk synth` 命令。若要在 AWS 帳戶中部署服務，請在應用程式的主目錄中執行 `cdk deploy`命令。

   如果您使用附屬程式策略，將看到系統會建立一項服務：
   + *APPLICATION\$1SERVICE* 是您應用程式的服務。其中包含以下三個容器：
     + `init`– 初始化 Application Signals 所需的容器。
     + `ecs-cwagent`– 執行 CloudWatch 代理程式的容器
     + `my-app`– 我們文件中的應用程式容器範例。在您的實際工作負載中，此特定容器可能不存在，或者可能被替換為您自己的服務容器。

   如果您使用常駐程式策略，將看到系統會建立兩項服務：
   + *CloudWatchAgentDaemon* 是 CloudWatch 代理程式常駐程式服務。
   + *APPLICATION\$1SERVICE* 是您應用程式的服務。其中包含以下兩個容器：
     + `init`– 初始化 Application Signals 所需的容器。
     + `my-app`– 我們文件中的應用程式容器範例。在您的實際工作負載中，此特定容器可能不存在，或者可能被替換為您自己的服務容器。

   如果您使用複本策略，將看到系統會建立兩項服務：
   + *CloudWatchAgentService* 是 CloudWatch 代理程式複本服務。
   + *APPLICATION\$1SERVICE* 是您應用程式的服務。其中包含以下兩個容器：
     + `init`– 初始化 Application Signals 所需的容器。
     + `my-app`– 我們文件中的應用程式容器範例。在您的實際工作負載中，此特定容器可能不存在，或者可能被替換為您自己的服務容器。