Amazon Managed Service for Prometheus(AMP), Grafanaを組み込んでみた

はじめに
---

初めまして！日鉄ソリューションズ株式会社(NSSOL) 流通・サービスソリューション事業本部 流通・サービスソリューション第三事業部 リテイルDXビジネスソリューション部 所属のharukaと申します。

私の所属する部では、業務を通じて蓄積した技術知見を社外へ発信を行う取り組みを始めました。今回その第一弾としてQiitaの記事を作成しました。



監視のお勉強として、サンプルアプリにAmazon Managed Service for Prometheus(以下AMP)、Grafanaを組み込んでみました。



※**Prometheus**→オープンソースのデータ収集ツール。サーバーのリソースの使用状況など、各種メトリクスを収集して監視ができます。

※**Grafana**→オープンソースのデータ可視化ツール。Prometheus等で収集したデータをわかりやすく可視化できます。



PrometheusはOSS版もありますが、今回はAWSサービスと組み合わせるためAWSマネージド版のAMPを使うことにしました。

Grafanaは今回OSS版をAmazon Elastic Container Service (以下ECS)コンテナにデプロイして使用することにしました。

(GrafanaにもAWSマネージドサービスがありますが、Amazon Organizationsの設定が必要であり、社内環境では利用が難しかったため断念しました。。。)



サンプルアプリについて
---

今回、サンプルアプリとして[amazon-ecs-java-microservices/3_ECS_Java_Spring_PetClinic_CICD](https://github.com/aws-samples/amazon-ecs-java-microservices/tree/master/3_ECS_Java_Spring_PetClinic_CICD)を選びました。

単純なREST APIであり、機能ごとに5つのサービス(system,owner,pet,vet,visit)に分かれていて、リクエストのパスによってApplication Load Balancer (以下ALB)から各サービスへルーティングが行われる点が特徴です。

アプリケーションはECS上にデプロイするようになっています。



導入後のアーキテクチャイメージ
---
アプリケーションのメトリクスを収集するために、AWSがプレビューとして公開しているAWS Distro for OpenTelemetry Collector (以下ADOTCollector)をアプリケーションのサイドカーとしてデプロイします。

ADOTCollectorからAMPへメトリクスの送信を行い、その結果をECSクラスター上にデプロイしたGrafanaで確認しています。

今回、社内規定の関係でアプリケーションをAmazon Virtual Private Cloud (以下VPC)内のプライベートサブネットにデプロイしているので、各リソースからAMPへのアクセスの際にVPCエンドポイントを設置して、インターネットを介さない接続をしています。

![APMAndGrafana.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/94b93efb-70e8-cb1c-ebc5-e38c8d95f38d.jpeg)



導入手順
---
導入手順_AMP編
---

参考文献に従って、AMPをセットアップしていきます。

参考文献：https://docs.aws.amazon.com/ja_jp/prometheus/latest/userguide/AMP-onboard-ingest-metrics-OpenTelemetry-ECS.html

#### ①Amazon managed Service for Prometheusワークスペースを作成する
- Workspaceエイリアスに任意のワークスペース名を入力
- 「ワークスペースを作成する」ボタンを押下する
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/7f44460e-3e8b-f66f-6b5a-3c1c6bd0891c.png)


- リモート書き込みURLエンドポイント、クエリURLエンドポイントは後に使用するため控えておく
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/53073f86-c60b-76f2-ff1e-8b43a62c5ff8.png)




#### ②イメージを格納するためのAmazon Elastic Container Registry (以下ECR)リポジトリを作成する
- ECRコンソール画面右上の「リポジトリを作成」をクリック
- リポジトリ名に任意のリポジトリ名を入力
- それ以外はデフォルト設定のまま「リポジトリを作成」ボタンをクリック
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/ee842c71-8c13-b34e-f266-dbaf1ba46656.png)

- ECRリポジトリのURIは後に使用するため控えておく
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/755005c4-c6fa-a4da-45c4-c266036fcbce.png)


#### ③adot-config.yamlファイルを修正する
- adot-config.yamlファイルのexporters.awsprometheusremotewrite.endpoint　にPrometheusのリモート書き込みURLエンドポイントを記載する
*adot-config.yaml*
```
receivers:
  awsecscontainermetrics:
    collection_interval: 10s
processors:
  filter:
    metrics:
      include:
        match_type: strict
        metric_names:
          - ecs.task.memory.utilized
          - ecs.task.memory.reserved
          - ecs.task.cpu.utilized
          - ecs.task.cpu.reserved
          - ecs.task.network.rate.rx
          - ecs.task.network.rate.tx
          - ecs.task.storage.read_bytes
          - ecs.task.storage.write_bytes
exporters:
  awsprometheusremotewrite:
    endpoint: # Prometheusのリモート書き込みURLエンドポイントを記載
    aws_auth:
      region: ap-northeast-1
      service: aps
    resource_to_telemetry_conversion:
      enabled: true
  logging:
    loglevel: info
extensions:
  health_check:
  pprof:
    endpoint: :1888
  zpages:
    endpoint: :55679
service:
  extensions: [pprof, zpages, health_check]
  pipelines:
    metrics:
      receivers: [prometheus]
      exporters: [logging, awsprometheusremotewrite]
    metrics/ecs:
      receivers: [awsecscontainermetrics]
      processors: [filter]
      exporters: [logging, awsprometheusremotewrite]
```
#### ④buildspec.ymlファイルを修正する
- buildspec.ymlファイルのCOLLECTOR_REPOSITORYに②で控えておいたECRリポジトリのURIを記載する
    - AWSのドキュメントに従って、イメージのタグは「:ecs」としておきます。
*buildspec.yml*
```
version: 0.2
 
phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - COLLECTOR_REPOSITORY= # ECRリポジトリのURIを記載
      - aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $COLLECTOR_REPOSITORY
  build:
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build . -t $COLLECTOR_REPOSITORY:ecs
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $COLLECTOR_REPOSITORY:ecs
```
#### ⑤Dockerfile, buildspec.yml, adot-config.yamlをAWS CodeCommitリポジトリに配置する
- AWS CodeCommitに新規リポジトリを作成し、Dockerfile, ③adot-config.yaml, ④buildspec.ymlファイルを配置する
*Dockerfile*
```
FROM public.ecr.aws/aws-observability/aws-otel-collector:latest
COPY adot-config.yaml /etc/ecs/otel-config.yaml
CMD ["--config=/etc/ecs/otel-config.yaml"]
```
#### ⑥ADOTCollectorのコンテナイメージを作成しECRにプッシュする
- ⑤で作成したAWS CodeCommitリポジトリをソースとして、AWS CodeBuildにて新規ビルドプロジェクトを作成し、ECRにコンテナイメージをプッシュする
    - (AWS CodeBuildの利用方法は各自調べてください。)


#### ⑦アプリ側のECSタスク定義を修正する
- アプリ側の修正例として、参考までにpetclinicデモアプリのAWS CloudFormationテンプレートの一部を紹介する
- TaskDefinition.Properties.ContainerDefinitionsに、adot-collectorというコンテナを追加し、サイドカーとして定義する
- ImageにECRリポジトリのURI:ecs と記載する
- 修正が終わったら、改めてECSデプロイ作業を行うこと
*petclinic-service.yaml*
```
TaskDefinition:
  Type: AWS::ECS::TaskDefinition
  Properties:
    Family: !Sub ${ECRRepository}
    TaskRoleArn: !Ref TaskDefinitionServiceRole
    ExecutionRoleArn: arn:aws:iam::265407044485:role/ecsTaskExecutionRole
    ContainerDefinitions:
      - Name: !Sub ${ECRRepository}
        Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepository}:${ECRImageTag}
        LogConfiguration:
          LogDriver: awslogs
          Options:
            awslogs-region: !Sub ${AWS::Region}
            awslogs-group: !Ref ECSCluster
            awslogs-stream-prefix: !Sub ${ECRImageTag}
        Essential: true
        Memory: !Ref ContainerMemorySize
        PortMappings:
          - ContainerPort: !Ref ContainerPort
        Environment:
          - Name: SPRING_PROFILES_ACTIVE
            Value: mysql
          - Name: SPRING_DATASOURCE_URL
            Value: !Ref JDBCConnectionString
          - Name: SPRING_DATASOURCE_USERNAME
            Value: !Ref DBUsername
          - Name: SPRING_DATASOURCE_PASSWORD
            Value: cnabc2022
          - Name: AWS_Region
            Value: !Sub ${AWS::Region}
      - Name: adot-collector
        Image: # ECRリポジトリのURI:ecs と記載
        Essential: true
        Memory: 512
        LogConfiguration:
          LogDriver: awslogs
          Options:
            awslogs-region: !Sub ${AWS::Region}
            awslogs-group: !Ref ECSCluster
            awslogs-stream-prefix: !Sub ${ECRImageTag}
        Environment:
          - Name: ECS_SERVICE_NAME
            Value: !Sub ${ECRRepository}
```
#### ⑧タスク実行ロールにAWS Identity and Access Management (以下IAM)ポリシーをアタッチする
- タスク定義で定義されている、タスク実行ロールに対して、AmazonPrometheusRemoteWriteAccessポリシーをアタッチする
![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/2fd68db1-cbc6-169e-613c-666485414c2d.png)




#### ⑨VPCエンドポイントを作成し、ECSクラスターのセキュリティグループを設定する
- VPC>仮想プライベートクラウド>エンドポイント>右上の「エンドポイントを作成」をクリック
- 名前タグに任意の名前を入力する
- サービスカテゴリは「AWSのサービス」とし、サービスにて「com.amazonaws.ap-northeast-1.aps-workspaces」を選択する
- 使用しているVPCとサブネットを選択する
- ECSクラスターのセキュリティグループを設定する


導入手順_Grafana編
---
Grafanaの方も参考文献が用意されています。ここではAWS Fargateを利用します。

参考文献：https://docs.aws.amazon.com/ja_jp/prometheus/latest/userguide/AMP-onboard-query-standalone-grafana.html

#### ①ECSにGrafanaコンテナを立てる
- ECSにGrafanaコンテナを立てる
    - ECSのタスク定義に、以下2つの環境変数を定義しておくこと
        - AWS_SDK_LOAD_CONFIG=true
        - GF_AUTH_SIGV4_AUTH_ENABLED=true


- ブラウザで「http://(ALBのパス)/login」を打鍵するとGrafanaコンソール画面が表示される。
     - ユーザ名/パスワード両方adminと入力しログイン
    - ログイン後パスワードの変更を求められるため任意のパスワードを入力してsubmit

![image2022-7-19_13-3-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/850b0cb5-dd23-fe0b-6846-1a583ddda01f.png)
![image2022-7-19_13-3-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/7b343ea7-7687-fcc5-a09a-746dd2184285.png)
![image2022-7-19_13-4-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/d36d47e3-e245-b7f8-f5f6-514706d4bf64.png)





#### ②VPCエンドポイントに、Grafanaのセキュリティグループを設定する
- GrafanaからAMPにデータを取得しに行くために、作成済のVPCエンドポイントにGrafanaコンテナのセキュリティグループを追加する
    - 「セキュリティグループの管理」をクリック
    - Grafanaコンテナのセキュリティグループを選択し、「セキュリティグループの変更」をクリック


#### ③IAMユーザーを作成する
- 新規IAMユーザーを作成し、AmazonPrometheusQueryAccessポリシーをアタッチする
- アクセスキーとシークレットアクセスキーを払い出しておく
![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/c733d7aa-03ae-0956-fb7e-69a4721c1932.png)


#### ④PrometheusデータソースをGrafanaに追加する
- Grafanaコンソール画面にて、サイドバーから設定(歯車マーク)を選択
- Add data sourceをクリック
![image2022-7-19_13-9-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/7e0757ff-e434-df69-6456-185ef071b9b1.png)


- Promethusを選択
![image2022-7-19_13-9-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/3cea6959-1bf4-1fdf-4524-976117a4af24.png)


- HTTP URLの欄に、PrometheusのクエリURLエンドポイントから/api/v1/queryを抜いたものを入力する
    - 例) https://aps-workspaces.ap-northeast-1.amazonaws.com/workspaces/xxxxxxx

![image2022-7-19_13-11-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/c8314088-fc56-12bf-33ac-6f949edf0f2e.png)


- Authにて「SigV4 auth」を選択
- SigV4 Auth Detailsを以下のように入力
    - Authentication Provider：Access & secret key
    - Access Key ID：<③で作成したIAMユーザーのアクセスキー>
    - Secret Access Key：<③で作成したIAMユーザーのシークレットアクセスキー>
    - Default Region：ap-northeast-1


![image2022-7-19_13-24-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/294f4b09-19e0-9311-d3f0-a64b0880128e.png)


- 「Save & test」をクリックして、保存&接続テストを行う
    - 接続テストに成功すると「Data source is working」と表示される

![image2022-7-19_13-25-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/217dbe52-6967-39b9-d812-467df5394c53.png)


#### ⑤ダッシュボードでメトリクスを確認する
- Grafanaダッシュボードが確認できる
    - 各タスクのCPU使用量
    - 各タスクのメモリ使用量
    - 各タスクが受信したバイト数
    - 各タスクが送信したバイト数
等

![image2022-7-19_15-58-51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/f625975f-6d84-1db2-2a2a-5ec6e299d003.png)
![image2022-7-19_15-59-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2830436/3179c4bd-e6aa-ffac-6f5b-dee6f82e1beb.png)



※今回はちょうどよいダッシュボードがなかったので、自作しました。



感想
---
Web上に文献が多くなく、公式ドキュメントを読み解きながら進める必要があり、トラブルシューティングが大変でしたが、いい経験になりました！

CloudWatchとAMP+Grafanaのグラフを見比べてみると、以下のようなメリットを感じました。

- より短い時間間隔でデータが取れる(CloudWatchではECSのメトリクスはデフォルトで1分間隔でしか取れないが、AMP+Grafanaはもっと細かく取れる。今回は10秒で取得)
- ダッシュボードのカスタマイズの自由度が高く、監視しやすい

機会があれば、AWSマネージド版のGrafanaを使った構成にもチャレンジしてみたいです。



登録商標
---
NS Solutions、NSSOL は、日鉄ソリューションズ株式会社の登録商標です。
Amazon Web Services, AWS, Amazon Managed Service for Prometheus, Amazon Organizations, Amazon Elastic Container Service, AWS Distro for OpenTelemetry Collector, Application Load Balancer, Amazon Virtual Private Cloud, Amazon Elastic Container Registry, AWS CodeCommit, AWS CodeBuild, AWS CloudFormation, AWS Fargate, AWS Identity and Access Managementは、Amazon.com, Inc. またはその関連会社の商標です。
本文中の会社名および商品名は、各社の商標または登録商標です。