ECS Fargate に New Relic を導入してみた

ECS Fargate に New Relic を導入した
2021.10.20

はじめに

おはようございます、もきゅりんです。

皆さん、日々可観測してますか?

自分は最近、稼働サービス収益に直結する、アプリケーションにおけるレイテンシや分散システムのトレーシングの課題について相談されるといったことが続いておりました。

目の前の課題に対して、暫定的な対応や処置を進めていくにせよ、中長期にどのようなモニタリングを目指していくのか、しっかりと検討の上、取り組めるようなオトナになりたいと思いました。

そんな気持ちもあって、弊社とも関係があります New Relic で、まずは ECS Fargate 設定を一通りしてみてよう、ということで コンテナアプリケーションのオブザーバビリティ実現手順 で紹介されている下図を参考に一通り設定してみました。

nr_fargate

本稿では、アラートシステムの検討、設定についての対応はしません。

なお、そもそも New Relic の概要などは、下記ブログも参考として下さい。

今回の構成

AWS のチュートリアル 現代的なウェブアプリケーションの構築 の Mythical Mysfits というサンプルウェブサイトを構築して New Relic を導入していきます。

mm_archi

出来合いで、かつそれなりのアプリケーションが欲しかったので選択したのですが、構築にそれなりにかかります。。(2時間くらい構築だけでかかる)

前提

  • 上記の構築が終わっていること
  • New Relic アカウントを持っていること

1 AWS Cloud Integration

選択した枠を設定します。

この設定は、Amazon CloudWatch Metric Streams で New Relic にメトリックデータを流し込んでみる(Step-by-Step) | DevelopersIO のブログでの手順が丁寧です。

参照下さい。

CloudFormatin テンプレートで作成すると速いです。

完了して、 DynamoDB のメトリクスを Infrastructure で確認しているイメージです。

2 ECS Integration

選択した枠を設定します。

以下リンクを参考にすれば、すんなりと設定できます。

本稿では、 AWSCloudFormation テンプレート を使用します。何を作成するかは上記ページを参照下さい。ライセンスキーをパラメータ入力して実行します。

cd ~/enviroment/aws-modern-application-workshop/module-2/aws-cli/
curl -O https://download.newrelic.com/infrastructure_agent/integrations/ecs/newrelic-infra-ecs-fargate-example-latest.json

次に、実際に監視したいタスク定義にダウンロードしたタスク定義の例を追加し、以下の部分を修正します。タスク実行ロール及び Secret ARN は、デプロイした Cloudformation で自動生成されています。

  • executionRoleArn: タスク起動に使用する IAM Role であるタスク実行ロールのARNを指定します
  • containerDefinitions: newrelic-infra コンテナ部分を追加します
  • containerDefinitions - newrelic-infra - secret - valueFrom: New Relic のライセンスキーが格納されている Secret ARN を指定します

ちなみに、 このSecret ARN は APM エージェントでも使います。

こんな感じで稼働コンテナのメトリクスが Container , APM 画面で表示されています。

3 Application Logs

選択した枠を設定します。

ログの収集には、 Three Ways To Forward Logs From Amazon ECS To New Relic | New Relic の通り、3つの方法がありますが、とりあえず Lambdaから収集する方法にします。

ログの用途が明確ではない場合、必要そうなログから収集して利用してみて、用途に応じて整形するなりしてログ送信する、さらには必要なログだけに限定して収集するなどを検討すると良いのでは、と考えています。

設定は、以下を参照下さい。

4 APM (Python agent)

選択した枠を設定します。

このアプリのバックエンドは Python で動いているので、以下リンクを参考にベースイメージを作成して進めます。

なお、 下記リンクの通り、 Python agent version 7.0.0.166 or higher では、Distributed Tracing はデフォルトで設定済みです。

やること

  1. New Relic の環境変数を追加してタスク定義の更新
  2. APM Agentを導入したベースイメージの作成
  3. ECR or DockerHub に登録 (ECRの場合、タスクロールにイメージDL権限を付与)
  4. Logs in Context 用に Python コードを更新
  5. 新タスク定義で実行
  6. 2を反映したアプリイメージの更新/プッシュから CodePipeline でデプロイ

Codepipeline のローリングデプロイは、 実行中のタスク定義を更新するので、タスク定義をAWS CLI で更新したら別途サービスを更新する必要があります。

その後にコード変更して別途デプロイします。

1. NewRelic の環境変数を追加してタスク定義の更新

// cd ~/environment/aws-modern-application-workshop/module-2/app
{
  "family": "mythicalmysfitsservice",
  "cpu": "256",
  "memory": "512",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "executionRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ecsTaskExecutionRole",
  "taskRoleArn": "arn:aws:iam::ACCOUNT_ID:role/ECS_TASK_ROLE_NAME",
  "containerDefinitions": [
    {
      "name": "MythicalMysfits-Service",
      "image": "ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/mythicalmysfits/service:latest",
      "portMappings": [
        {
          "containerPort": 8080,
          "protocol": "http"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "mythicalmysfits-logs",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "awslogs-mythicalmysfits-service"
        }
      },
      "secrets": [
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:ACCOUNT_ID:secret:NewRelicLicenseKeySecret-xxxxxxxxxx",
          "name": "NEW_RELIC_LICENSE_KEY"
        }
      ],
      "environment": [
        {
          "name": "NEW_RELIC_APP_NAME",
          "value": "MythicalMysfits"
        },
        {
          "name": "NEW_RELIC_LOG",
          "value": "stdout"
        }
      ],
      "essential": true
    },
    {
      "environment": [
        {
          "name": "NRIA_OVERRIDE_HOST_ROOT",
          "value": ""
        },
        {
          "name": "NRIA_IS_FORWARD_ONLY",
          "value": "true"
        },
        {
          "name": "FARGATE",
          "value": "true"
        },
        {
          "name": "ENABLE_NRI_ECS",
          "value": "true"
        },
        {
          "name": "NRIA_PASSTHROUGH_ENVIRONMENT",
          "value": "ECS_CONTAINER_METADATA_URI,ENABLE_NRI_ECS,FARGATE"
        },
        {
          "name": "NRIA_CUSTOM_ATTRIBUTES",
          "value": "{\"nrDeployMethod\":\"downloadPage\"}"
        }
      ],
      "secrets": [
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:ACCOUNT_ID:secret:NewRelicLicenseKeySecret-xxxxxxxx",
          "name": "NRIA_LICENSE_KEY"
        }
      ],
      "cpu": 256,
      "memoryReservation": 512,
      "image": "newrelic/infrastructure-bundle:2.6.4",
      "name": "newrelic-infra"
    }
  ]
}

NEW_RELIC_LICENSE_KEYNRIA_LICENSE_KEY には、ECS Integration で作成した SecretsManger の ARN を記入します。

完了したら更新します。

// タスク定義の更新
aws ecs register-task-definition --cli-input-json file://~/environment/aws-modern-application-workshop/module-2/aws-cli/task-definition.json

タスク実行ロールに SecretsManager を参照する権限を付与するのを忘れずにして、ECS サービスを更新した新しいタスク定義で実行しておきます。

2. APM Agentを導入したベースイメージの作成

FROM ubuntu:latest
RUN echo Updating existing packages, installing and upgrading python and pip.
RUN apt-get update -y
RUN apt-get install -y python3-pip python3-dev build-essential
RUN pip3 install --upgrade pip
RUN pip3 install --no-cache-dir newrelic
ENTRYPOINT ["newrelic-admin", "run-program"]

2. ECR に登録

docker build . -t  ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/mythicalmysfits/python_newrelic:latest
aws ecr create-repository --repository-name mythicalmysfits/python_newrelic
$(aws ecr get-login --no-include-email)
docker push
// ~/environment/MythicalMysfitsService-Repository/Dockerfile
FROM ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/mythicalmysfits/python_newrelic:latest
RUN echo Copying the Mythical Mysfits Flask service into a service directory.
COPY ./service /MythicalMysfitsService
WORKDIR /MythicalMysfitsService
RUN echo Installing Python packages listed in requirements.txt
RUN pip3 install -r ./requirements.txt
RUN echo Starting python and starting the Flask service...
CMD ["python3","mythicalMysfitsService.py"]

3. Logs in Context 用に Python コードを更新

上記を参考に Python ファイルに以下のようにコピペします。

// cd ~/environment/MythicalMysfitsService-Repository/service
from flask import Flask, jsonify, json, Response, request
from flask_cors import CORS
import mysfitsTableClient
# Import the logging module and the New Relic log formatter
import logging
from newrelic.agent import NewRelicContextFormatter

# Instantiate a new log handler
handler = logging.StreamHandler()

# Instantiate the log formatter and add it to the log handler
formatter = NewRelicContextFormatter()
handler.setFormatter(formatter)

# Get the root logger and add the handler to it
root_logger = logging.getLogger()
root_logger.addHandler(handler)

app = Flask(__name__)
...(省略)

ECR を使う場合は、 CodeBuild のロールに以下ポリシーを付与しておきましょう。

  • ecr:GetDownloadUrlForLayer
  • ecr:BatchGetImage
  • ecr:BatchCheckLayerAvailability
git add .
git commit -m "Add NewRelic APM Python Agent."
git push

設定完了すると、 APM 画面で以下のような表示がされます。

Logs 画面で以下のように表示されています。

trace.idspan.id を含んでいるログであることが分かります。

5 Browser

ついでなので、Browserモニタリングエージェントのインストール | New Relic Documentation を参考にしてエージェントをインストールします。

Browserモニタリングにより、リアルユーザーモニタリング(RUM)を行うことができます。

詳細は、Browserモニタリングの概要 | New Relic Documentation を参照ください。

今回は、S3 の静的サイトが対象なので、コピー/ペーストで有効化します。

埋め込む場所は、上記ページに記載されていますが、ページ内のできるだけ<head> 要素の最上部付近、ただし位置に敏感な <meta> タグ(例、X-UA-Compatibleまたはcharset情報)の後に埋め込む形でペーストします。

完了すると、Browser 画面で以下のような表示がされます。

オマケ Diagnostics CLI (nrdiag)

なぜだかうまくいかない状況を確認するのに、 Run the Diagnostics CLI (nrdiag) | New Relic Documentation が役立ちます。

以下のような感じで利用して、うまくいかない原因を随時調査します。

wget http://download.newrelic.com/nrdiag/nrdiag_latest.zip
unzip nrdiag_latest.zip
cd nrdiag/linux
./nrdiag --help suites
./nrdiag_x64 --suites python

さいごに

ようやく可観測の準備が整ってきました。(Synthetics は設定必須ですが)

本当は分析まで検討したかったのですが、本稿は以上となります。

次回以降に、どのようなアプローチで New Relic では分析を進めていくのか、各機能を掘り下げながら触っていきたいと考えています。

以上です。

どなたかのお役に立てば幸いです。