5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ECS FargateのNext.jsプロジェクトでもDatadogを使いたい!

Posted at

吾輩は人間である

前書き

最近新規で立ち上げたアプリケーションに外形監視を導入したいと考えており、N社のサービスにするか、AWS内で完結するか悩んでいましたがDatadogを採用することに決めました。

ネットで調べてもNext.js + ECS Fargateの構成でのDatadog導入方法に関する情報があまり見つからなかったため、実際に行った作業手順をメモとして残します。
20250307102727.png

やりたいこと

アプリケーションのログAPM両方をDatadogに送信したい。
既存のインフラはCDKで管理しているため、CDKを活用して適切なアーキテクチャを構築したい。

CDKの変更(差分のみ)

DatadogのAPIキーを保存するSecrets Managerを追加する。

ECSから接続する際にキーの設定が必要になりますが、Datadogの「Organization Settings」→「API Keys」からキーを取得・更新できます。
また、初回ECS接続時の待機画面でも、設定ファイル内にAPIキーが表示されます。

Secrets Managerデプロイできたら、取得したキーを追加しましょう。

ecs-construct.ts
// Datadog API キー用シークレットの作成
const datadogSecret = new secretsmanager.Secret(this, 'DatadogSecret', {
  secretName: `${props.envName}-${props.projectName}-datadog-api-key`,
  description: 'Datadog API Key - Update manually after deployment',
  secretStringValue: cdk.SecretValue.unsafePlainText('placeholder-update-manually'),
});
datadogSecret.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);

// タスク実行ロールにDatadogシークレットへのアクセス権限を付与
datadogSecret.grantRead(executionRole);

メインコンテナの設定

ECSのログをそのままDatadogに送信する方がCloudWatch経由より安価なため、FireLensを使用します。

ecs-construct.ts
const clientContainer = serviceTaskDefinition
  .addContainer('client', {
    image: appImage,
    logging: ecs.LogDrivers.firelens({
      options: {
        Name: 'datadog',
        Host: 'http-intake.logs.ap1.datadoghq.com',
        TLS: 'on',
        dd_service: 'service_name',
        dd_source: 'nodejs',
        dd_tags: `env:${props.envName}`,
        provider: 'ecs',
      },
      secretOptions: {
        apikey: ecs.Secret.fromSecretsManager(datadogSecret),
      },
    }),
    environment: {
      // ... 他の環境変数
      DD_ENV: props.envName,
      DD_SERVICE: 'service_name',
      DD_VERSION: props.imageTag,
      DD_LOGS_INJECTION: 'true',
      DD_AGENT_HOST: '127.0.0.1',
      OTEL_TRACES_EXPORTER: 'otlp',
      OTEL_EXPORTER_OTLP_PROTOCOL: 'http/protobuf',
      OTEL_EXPORTER_OTLP_ENDPOINT: 'http://127.0.0.1:4318',
      OTEL_SERVICE_NAME: 'service_name',
      OTEL_RESOURCE_ATTRIBUTES: `deployment.environment=${props.envName},service.version=${props.imageTag}`,
    },
    // ...
  });

Datadog Agentサイドカーコンテナ

DD_SITEは使用しているDatadogのリージョンに合わせて設定する必要があります。
設定が間違っているとログが送信されないため注意が必要です。

東京リージョンの場合、ap1.datadoghq.comになります。

ecs-construct.ts
const datadogLogGroup = new logs.LogGroup(this, 'DatadogLogGroup', {
  logGroupName: `/aws/ecs/datadog/${props.envName}`,
  retention: logs.RetentionDays.THREE_MONTHS,
  removalPolicy: props.envName === 'prd' ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
});

const datadogContainer = serviceTaskDefinition.addContainer('datadog-agent', {
  image: ecs.ContainerImage.fromRegistry('public.ecr.aws/datadog/agent:latest'),
  memoryLimitMiB: 512,
  containerName: 'datadog',
  essential: false,
  logging: ecs.LogDriver.awsLogs({
    streamPrefix: 'datadog',
    logGroup: datadogLogGroup,
  }),
  secrets: {
    DD_API_KEY: ecs.Secret.fromSecretsManager(datadogSecret),
  },
  environment: {
    ECS_FARGATE: 'true',
    DD_APM_ENABLED: 'true',
    DD_APM_NON_LOCAL_TRAFFIC: 'true',
    DD_SITE: 'ap1.datadoghq.com',
    DD_LOGS_ENABLED: 'true',
    DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL: 'true',
  },
});

datadogContainer.addEnvironment(
  'DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT',
  '0.0.0.0:4318',
);

// APM用ポートマッピング追加
datadogContainer.addPortMappings({
  containerPort: 4318,
  protocol: ecs.Protocol.TCP,
});

FireLens Log Router設定

ecs-construct.ts
serviceTaskDefinition.addFirelensLogRouter('FirelensLogRouter', {
  image: ecs.ContainerImage.fromRegistry('amazon/aws-for-fluent-bit:stable'),
  essential: false,
  memoryReservationMiB: 50,
  firelensConfig: {
    type: ecs.FirelensLogRouterType.FLUENTBIT,
    options: {
      enableECSLogMetadata: true,
      configFileType: ecs.FirelensConfigFileType.FILE,
      configFileValue: '/fluent-bit/configs/parse-json.conf',
    },
  },
  logging: ecs.LogDriver.awsLogs({
    streamPrefix: 'firelens',
    logGroup: logGroup,
  }),
});

Next.js側の変更

dd-traceを追加します。このプロジェクトではBunを使用しているため、以下のように追加します。

ちなみに、Bunは多才でおすすめです。

% bun add dd-trace

App Routerを使用しているため、src/配下にtracer.tsを作成し、以下のコードを記述します。

tracer.ts
import tracer from 'dd-trace';

tracer.init({
  logInjection: true,
  version: process.env.DD_VERSION,
  env: process.env.DD_ENV,
  service: process.env.DD_SERVICE,
});

export default tracer;

作成したファイルを同じ階層にinstrumentation.tsを作って、中からimportします。instrumentation.tsはNext.jsのアプリケーション起動時に一度だけ実行されるファイルで、計測や監視の初期化によく使用されます。

instrumentation.ts
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./tracer');
  }
}

これらの設定で、Next.jsのアプリからのログ、APMをDatadogへ転送することができました。

9CE692B2-5EB6-4548-A06B-E4E02AE93BBC.jpeg

参考資料

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?