【AWS】実務で必ず知っておきたいCloudWatch Metrics・Logsをハンズオンで学んでみよう

# 本記事の目的

業務でAWSを触る方にとって`CloudWatch`に対してなんとなく苦手意識をもっている人は多いのではないでしょうか。特に私のようなDevOps/SRE領域を開発業務の片手間で担っている人たちにとって監視領域は後回しにしがちです。

今回の記事では、この`CloudWatch`、そして本サービスの軸となる`CloudWatch Metrics`,`CloudWatch logs`を自分自身の学習の備忘録がてらハンズオン形式で学べる記事を書いてみました。

対象となる読者は以下を想定します。

:::note info
対象読者
- AWSを業務で使い始めた＆使いたい人
- DevOps/SRE担当で`CloudWatch`を使った監視業務を求められてしまった人
- `CloudWatch logs`,`CloudWatch Metrics`を実際に手を動かして確認したい人
:::

なお利用するサンプルAPIは全て`Go`で書かれていますが、コード箇所は読み飛ばして頂いても構いません。

## 参考資料に関して

本記事では、以下の"AWS BlackBelt 2023 Amazon CloudWatchの概要と基本"をメインに参考しております。本記事で紹介しているスライドは同資料から引用・抜粋しているものになります。

https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AmazonCloudWatch_0330_v1.pdf

https://www.youtube.com/watch?v=fzVkJne3OMI&ab_channel=AmazonWebServicesJapan%E5%85%AC%E5%BC%8F

# CloudWatch 概要編

![スクリーンショット 2023-07-01 10.00.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/e010701b-c7e1-ed22-59b8-abafd5e680d2.png)

Amazon CloudWatchは、AWSサービスおよびアプリケーションを監視するためのツールです。リアルタイムでダッシュボードを通じてパフォーマンスデータを表示し、ログを分析する機能があります。さらに、アラームを設定して通知を受け取り、問題が発生した場合に自動的に対応することができます。CloudWatchは、システム全体の運用を効率的かつ効果的に管理するために重要な役割を果たします。

## "オブザーバビリティ"とは

![AWS-Black-Belt_2023_AmazonCloudWatch](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/9f97f502-4ae3-44d6-10f5-e2103cad8108.png)

![スクリーンショット 2023-06-30 17.51.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/18a5c9f8-3817-f7f5-63d8-c14ac0b88724.png)

AWSの最新のBlack Beltが非常にわかりやすいので引用しました。この画像にあることが全てです。`オブザーバビリティ`とは、抽象的な概念ではありますが、これまでの`モニタリング`を内包するものであり、ざっくり以下のようなものだと~~個人的に~~理解してます。
- システム全体の状態をより深く把握できる状態
- かつ変化・成長するシステムに対応すべくオブザーバビリティ強化を継続的に行える状態
- 上記を実現することで運用上の優秀性・ビジネス目標を達成できる

この`オブザーバビリティ`を高めるための手段として`CloudWatch`は中心的な役割を担っています。

## ３つの柱

![スクリーンショット 2023-06-30 17.47.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/15599dd7-9db5-d6f1-72c9-872afa82d3fa.png)
`CloudWatch`の理解しづらさの所以はそのサービス数の多さです。マネージメントコンソールから確認するだけでも合計で20近い機能がリリースされています。

一方で、この中でも上記`オブザーバビリティ`を実現する上で中心となっているのが以下の画像の通り、`ログ`, `メトリクス`, `トレース`になります。

※上記に加えて`Event Bridge(旧CloudWatch Event)`が担う`イベント`も中心的な役割を担いますが、本記事では説明は割愛します。

## 監視・運用の基本的な流れ
![スクリーンショット 2023-06-30 18.03.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/43a05209-3258-ac6e-e358-b86c0433a501.png)
![スクリーンショット 2023-07-01 7.50.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/dae46572-7df7-c0b2-6796-bba5ba17cede.png)

参考にさせて頂いた[AWS-Black-Belt_2023_AmazonCloudWatch](https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AmazonCloudWatch_0330_v1.pdf)の資料のYoutube版でも紹介がありますが、実際の運用の流れは"干し草の山から針を探す"のようなものです。

事前に設定されていたアラームや通知を受ける > `AWS X-Ray`等のトレースを確認して対象リソースを特定する > ダッシュボードを確認 > メトリクス分析 > ログ分析 > 実際の生ログを確認

という流れが大枠になります。そして、ダッシュボード以降の全ての根幹の元になっているのが、`CloudWatch Logs`・`Metrics`になのです。

## CloudWatch Metricsとは

![スクリーンショット 2023-07-01 7.51.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/0aaea0e8-0a44-3fe6-7f51-2992fec89c21.png)
CloudWatch Metricsは、AWSリソースとアプリケーションを監視するためのAWSサービスです。CloudWatch Metricsは、特定のリソースやアプリケーションのパフォーマンスデータを収集し、これをメトリクスとして保存します。これにより、ユーザーはリソースの使用率、アプリケーションのパフォーマンス、および運用上の健全性を監視し、適切なアクションを実行するためのアラートを設定することができます。

### 名前空間/メトリクス名/ディメンション/データポイント

![スクリーンショット 2023-07-01 7.51.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/6f34ab19-6608-414a-8e3b-8b639398f6af.png)

`CloudWatch Metrics`が少しとっつきにくい　理由として、サービスを利用する上でその仕組み/概念を理解する必要がある点が挙げられます。

具体的には、`名前空間`・`メトリクス名`・`ディメンション`・`データポイント`というもので構成されています。

上記の画像のにもある通り、それぞれの"切り口"を組み合わせることで対象となるデータポイントを取得してメトリクスとして扱えるようになります。

| 項目 | 内容 |
|:-|:-|
|  名前空間 | CloudWatchメトリクスのカテゴリです。これは、メトリクスを整理し、同じグループに属するメトリクスを分離するためのコンテナです。AWSサービスのメトリクスには通常、そのサービス名に基づく名前空間があります（例：AWS/EC2）。  |
|  メトリクス名 | CloudWatchで収集されるデータの種類を識別する名前です。例えば、CPU使用率、ディスク読み取り回数、ネットワークトラフィックなど、特定のリソースまたはアプリケーションの性能に関する具体的な情報を示します。  |
| ディメンション  | メトリクスをさらに具体的な単位で分類するためのキーと値のペアです。例えば、EC2インスタンスのCPU使用率メトリクスでは、インスタンスIDをディメンションとして使用して特定のインスタンスを識別します。  |
| データポイント  |  特定の時点でのメトリクスの具体的な値です。 |

## Cloud Watch logsとは

![スクリーンショット 2023-07-01 8.15.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/8a154ace-2483-f315-f93d-8fc6a6cb8914.png)
CloudWatch LogsはAWSの監視サービスの一部で、アプリケーションとシステムのログデータを監視して保存する機能を提供します。ユーザーはこれらのログをリアルタイムで監視し、特定のパターンを検索したり、リソースの問題を特定したりするのに使用できます。また、ログデータを長期間保持することも可能であり、適切なアクセス制御と共にセキュリティ分析やコンプライアンスの要件を満たすために利用されます。

### ロググループ/ログストリーム/ログイベント

![スクリーンショット 2023-07-01 8.17.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/1f118456-c7e8-f393-2ed0-4a89fd5bf917.png)

`CloudWatch Metrics`同様に`CloudWatch logs`においても、構成要素を理解する必要があります。具体的には三つになります。

| 項目 | 内容 |
|:-|:-|
| ロググループ  | ロググループは、関連するログストリームのコンテナです。設定やアクセス権を共通して管理するために使用されます。  |
| ログストリーム  | ログストリームは、特定のソースからのログイベントのシーケンスです。たとえば、アプリケーションのインスタンスごとにログストリームを作成します。  |
| ログイベント  | ログイベントは、アプリケーションやシステムからCloudWatch Logsに送信される記録です。タイムスタンプとメッセージで構成されます。  |

## CloudWatch Metricsやlogsはどのように発行されるのか

![スクリーンショット 2023-07-01 7.53.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/3b2beb84-a095-82de-b91d-b0c6d77ee7cb.png)

多くのAWSサービスでこれらの指標を標準でCloud Watchに発行しています。そのため、利用者は特に追加設定を行うことなくメトリクスの集計やログ分析を行う事ができます。

### CloudWatch Agentを使ってメトリクス・ログ転送

全てのAWSのリソースが標準で`CloudWatch`に出力されるわけではありません。また、オンプレミス環境やAWS以外の環境などでも、同様にメトリクスやログを標準で`CloudWatch`に出力できるわけではありません。

そこで、`CloudWatch Agent`を監視対象のリソースにインストール、`CloudWatch`側にメトリクスとログ情報を転送することでリソースのパフォーマンス監視を`CloudWatch`側で行う事ができます。

![スクリーンショット 2023-07-01 11.23.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/4741a304-3b72-f2f8-c4cc-04c510a7d714.png)

### メトリクスやログを独自に定義して分析する

あるケースではAWSが標準で設定されているメトリクスでは目的となる監視を行うには不十分なケースがあります。その場合、独自で`Metrics`を作成することができ、これが`カスタムメトリクス`と呼ばれるものです。

![スクリーンショット 2023-07-01 11.23.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/b63d367a-5854-1064-ff41-63a9976ff175.png)

# ハンズオン編

前段が多くなってしまいましたが、ここからは実際に本題のハンズオン内容に入っていきます。説明は割愛しますが以下の設定が事前に必要になります。下記記事を参考にそれぞれ設定をお願いします。

- AWS CLIのインストール
- rootに近い権限を持つIAMユーザーの作成

https://dev.classmethod.jp/articles/aws-cli_initial_setting/

https://www.idaten.ne.jp/portal/page/out/secolumn/multicloud/column009.html

## サンプルアプリの環境構築

本ハンズオンでは私が事前に作成したアプリケーション、各種AWSリソース構築テンプレートを用いて実施します。必要に応じてリポジトリクローンと設定をお願いします。

:::note alert
注意点
利用するAWSの各リソースは課金対象となるものが多く含まれています。個人利用される方は特に注意してください。また、発生した費用やトラブルに関しては一切責任を負いかねます。
:::

| ハンズオン編 | 必要な環境 |
|:-|:-|
| `CloudWatch logs編`, `CloudWatch Metrics編`  |  環境構築１ |
| `CloudWatch Agent編` | 環境構築2  |

### 環境構築１

`CloudWatch logs編`, `CloudWatch Metrics編`で利用するサンプルアプリケーションになります。

このサンプルアプリケーションは、`AWS Copilot`を利用してECS上にGo製のサンプルAPIをデプロイします。`README.md`をご覧頂き環境構築をお願いします。

https://github.com/WebEngrChild/cw-go-api


### 環境構築2

`CloudWatch Agent編`で利用するサンプルアプリケーションになります。

このサンプルアプリケーションは、`AWS CloudFormation`を利用してEC2上にnginxを起動していきます。同様に`README.md`をご覧頂き環境構築をお願いします。

https://github.com/WebEngrChild/cw-ec2-nginx

## CloudWatch metrics編

ここからは実際にAWS上で`CloudWatch Metrics`の関連機能を手を動かしながら確認していきます。環境構築とデプロイが完了していない方はご対応をお願いします。

https://github.com/WebEngrChild/cw-go-api

### 例1：レイテンシ計測

まず初めに、`TargetResponseTime`というメトリクスを用いて、ロードバランサがリクエストをターゲット（たとえば、EC2インスタンス、Lambda関数など）に転送、ターゲットが応答をロードバランサに送信するまでの時間（秒）、つまり、バックエンド処理の遅延を計測してみます。

**(1)メトリクス選択**
マネジメントコンソール > CloudWatch > メトリクス > すべてのメトリクス > ApplicationELB > AppELB 別メトリクス

**(2)ロードバランサーのDNS名を取得**
ここで一旦別タブを開き、EC2 > Load balancers > 今回新規で作成したロードバランサーの`name`をコピーします。

**(3)メトリクス選択**
(2)でコピーしたロードバランサーの`name`と`TargetResponseTime`を検索窓で検索 > チェックボックスに選択
![スクリーンショット 2023-06-29 6.44.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/7639bc3c-b0dc-be0b-0312-6c9634a53db0.png)

**(4)統計情報の収集方法と期間を選択**
グラフ化したメトリクスを選択して以下を設定します。

| 項目 | 値 |
|:-|:-|
| 統計　| p99 |
| 期間　| 1分 |

`パーセンタイル`は統計的な指標の1つで、データセット内の値が特定のパーセンテージ以下であることを示します。例えば、ここでの`p99`とは、データセットの99%がこの値以下であることを示しています。

**(5)レスポンス遅延を発生させる**
以下のスクリプトをAPIで叩きます。URLはEC2 > ロードバランサー > 今回新規で作成したロードバランサーの`DNS name`をコピーしてください。

```shell:Terminal
for i in {1..10}; do curl -sS http://${新規で作成したDNSから取得}.ap-northeast-1.elb.amazonaws.com/delayedResponse; sleep 1; done
```

このAPIは以下のような処理でリクエストが発生した場合に、3秒待機したのちレスポンスを返却します。

```go:main.go
func delayedResponseHandler(w http.ResponseWriter, r *http.Request) {
	// 3秒遅延
	time.Sleep(3 * time.Second)

	response := Response{
		Message: "Delay occurred",
	}
	if err := json.NewEncoder(w).Encode(response); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func main() {
    // 省略
	http.HandleFunc("/delayedResponse", delayedResponseHandler)

	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}
```

**(6)メトリクスを確認**
メトリクスの値が急激に上昇しているのが確認できるかと思います。
![スクリーンショット 2023-06-26 7.04.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/f24a4a11-8a70-b5c5-e739-185d694e31dd.png)

### 例2：ECS Service単位でのCPU利用率の計測

ここでは、**CloudWatch Metrics Insights**という機能を使って、ユーザーがAWSリソースとアプリケーションのパフォーマンスデータを監視、分析していきます。この機能は、柔軟なクエリ言語を使用して、カスタム分析を行うことができます。

![スクリーンショット 2023-07-02 9.32.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/fed426dc-7433-bab9-c47d-be841979d660.png)

**(1)ECS Service名を選択**
Amazon Elastic Container Service > クラスター > サービス名

ここではECS ServiceのCPU利用率を確認するために一旦、Service Nameをコピーしておきます。

**(2)クエリ設定**
マネジメントコンソール > CloudWatch > メトリクス > すべてのメトリクス > クエリタブ

画像と以下を参考に指定の内容を入力していきます。

| 項目 | 値 |
|:-|:-|
| 名前空間  | AWS/ECS  |
| メトリクス名  | MAX(CPUUtilization)  |
| フィルター条件  | ServiceName = 上記でコピーした内容  |

![スクリーンショット 2023-06-29 13.13.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/b8cb3a9c-4bf5-f056-e320-c2035fe4ff3e.png)

入力はGUIベースの`Builder`という機能を用いましたが、左側にある`Editor`ボタンを押すことでSQLライクなクエリ言語でメトリクスを設定することができます。

![修正_68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313832343734302f65333865363263332d663337632d336363632d626239342d3736623331323565313636372e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/0bcf493d-4354-7d27-cb08-fa09047ffab2.png)

```sql:Cloud watch Metrics　クエリ
SELECT MAX(CPUUtilization) FROM "AWS/ECS" WHERE ServiceName = 'cw-go-api-test-cw-go-api-Service-xxxxxxx'
```

**(3)CPU負荷を一時的に急上昇させる**
URLは以下からコピーしてください。

EC2 > ロードバランサー > 今回新規で作成したロードバランサーのDNS name > コピー
```shell:Terminal
curl http://${新規で作成したDNSから取得}.ap-northeast-1.elb.amazonaws.com/loadCpu                  
{"message":"Increased CPU load"}
```

:::note alert
本APIに紐づく処理は非常に負荷のかかる処理になるため、実行回数は必ず1回のみにしてください。
:::


このAPIは以下のような処理でリクエストが発生した場合に一定期間無限ループ処理を走らせてCPU負荷を上昇させます。

```golang:main.go
func loadCPUHandler(w http.ResponseWriter, r *http.Request) {
	done := make(chan int)

	go func() {
		// 無限ループ作成
		for {
			select {
			// doneチャネルからメッセージが送られてきたとき（またはチャネルが閉じられたとき）無限ループを抜ける
			case <-done:
				return
			// 何もせず次のループへ進む
			default:
			}
		}
	}()

	// 10秒後にチャネルを閉じる
	time.AfterFunc(10*time.Second, func() {
		close(done)
	})

	response := Response{
		Message: "Increased CPU load",
	}

	if err := json.NewEncoder(w).Encode(response); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// 省略

func main() {
    // 省略
	http.HandleFunc("/loadCpu", loadCPUHandler)
    // 省略
}
```

**(4)メトリクス確認**
特定の期間で`CPU`負荷が上昇したことが確認できます。

![スクリーンショット 2023-06-29 13.27.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/2fe5f0e4-b676-a2da-4ada-c5684c00e3ba.png)

### 例3：リクエスト成功率/可用性計測

ここでは、`Metric Math`という機能を使って、ロードバランサーに対して発生したリクエストのうち、ステータスが`200`だった割合を計測してみます。

この**Metric Math**という機能は、CloudWatchのメトリクスに数式を使⽤して、新しいメトリクスを作成することができます。METRICS関数、基本的な算術関数をはじめとした関数をサポートしており、メトリクスに[Id]フィールドを設定し、関数で利⽤できます。

![スクリーンショット 2023-07-02 9.49.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/1b179a15-99cd-1e88-379b-dfc6dbbaa7a5.png)

利用するメトリクスはこちらになります。

| 項目 | 内容 |
|:-|:-|
| RequestCount　| 選択したロードバランサーに対してクライアントが発行したリクエストの数 |
| HTTPCode_Target_2XX_Count　| ターゲットグループから各HTTP応答コードの数 |

**(1)対象となるメトリクス名前空間の選択**
マネジメントコンソール > CloudWatch > メトリクス > すべてのメトリクス > ApplicationELB > AppELB 別メトリクス

**(2)ロードバランサーのDNS名を取得**
ここで一旦別タブを開き、EC2 > Load balancers > 今回新規で作成したロードバランサーの`name`をコピーしてください。

**(3)メトリクス選択**
(2)でコピーしたロードバランサーの`name`を検索窓に入力 > `RequestCount`, `HTTPCode_Target_2XX_Count`の２つをチェックボックスで選択

![スクリーンショット 2023-06-29 7.34.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/6d818524-d0d8-8598-f77f-69724af06637.png)

**(4)Metric Mathの作成**
画像のように左側にある`数式を追加` > `空の式をで始まる`を選択します

![スクリーンショット 2023-06-29 7.37.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/a9334dfe-8fe4-53cf-0a36-16e1841570b8.png)

すると"グラフ化したメトリクス"にメトリクスが追加されるため以下のような計算式を入力します。期間は`1分`を選択してください。

```sh:Metric Math 計算式
SUM(METRICS("m2")) /SUM(METRICS("m1"))
```

![スクリーンショット 2023-06-29 9.50.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/1fa72e47-b2b1-a880-64e7-3a89d0729510.png)

| id | 内容 |
|:-|:-|
| e1  | Metric Mathが反映  |
| m1  | RequestCount |
| m2  | HTTPCode_Target_2XX_Count |

**(5)エラー出力**
以下のスクリプトをAPIで叩きます。URLはEC2 > ロードバランサー > 今回新規で作成したロードバランサーの`DNS name`をコピーしてください。

```shell:Terminal
# ランダムでエラーを返却
$ for i in {1..10}; do curl -sS http://${新規で作成したDNSから取得}.ap-northeast-1.elb.amazonaws.com/error; sleep 1; done

# 正常ステータスを返却
$ for i in {1..40}; do curl -sS http://${新規で作成したDNSから取得}.ap-northeast-1.elb.amazonaws.com/health; sleep 1; done
```

上記のうちエラー出力のAPIは以下のようにリクエスト毎にエラーステータスをランダムに出力します。

```go:main.go
func errorHandler(w http.ResponseWriter, r *http.Request) {
	errors := []int{
		http.StatusBadRequest,
		http.StatusUnauthorized,
		http.StatusForbidden,
		http.StatusNotFound,
		http.StatusInternalServerError,
		http.StatusBadGateway,
		http.StatusServiceUnavailable,
		http.StatusGatewayTimeout,
	}

	// Set log flags to 0 to disable date and time prefix
	log.SetFlags(0)

	// 任意のステータスエラーをランダム生成
	rand.Seed(time.Now().UnixNano())
	errorIndex := rand.Intn(len(errors))
	w.WriteHeader(errors[errorIndex])

	response := Response{
		Message: http.StatusText(errors[errorIndex]),
	}

	// ログ出力をjson形式に変換する
	logData := map[string]interface{}{
		"timestamp":   time.Now().Format(time.RFC3339),
		"status_code": errors[errorIndex],
		"message":     response.Message,
	}
	logJSON, err := json.Marshal(logData)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	log.Println(string(logJSON))

	if err := json.NewEncoder(w).Encode(response); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// 省略

func main() {
    // 省略
	http.HandleFunc("/health", healthHandler)
	http.HandleFunc("/error", errorHandler)

    // 省略
}

```

**(6)メトリクスを確認**
エラーレスポンス => 正常レスポンスの順番で当該APIを叩いているため徐々にリクエスト成功率が上昇していることが確認できます。
![スクリーンショット 2023-06-29 10.05.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/e404929f-60bc-0a6a-9194-3230eac1bb8e.png)


### 例4：SLOアラートの設定

ここでは上記で説明した`例1：レイテンシ計測`で説明したメトリクスを使ってアラートを作成します。上述の通り、ドメインや組織によって`SLO`も異なります。また、今回設定したメトリクスも`SLO`として正しいわけでもありません。

:::note info
ここでの`SLO`の定義
１分間にわたって99%以上のリクエストのレスポンス時間が500msを超えた場合、SLO違反が発生したと判断
:::

**(1)Cloud Watch Aramの作成**
アクション欄にある`🛎`マークを押すことでCloudWatch Aramを作成することができます。
![スクリーンショット 2023-06-29 14.02.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/d48f427c-af49-7d00-b335-6fc37c254d16.png)
以下の画像を参考に設定します。
![スクリーンショット 2023-06-29 14.06.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/e7b5cc7f-0c6f-96be-5abc-924b25a94392.png)

新しいトピックの作成　> 通知を受け取る E メールエンドポイント > 任意のメールアドレスを入力

![スクリーンショット 2023-06-29 14.13.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/e81d7618-4c28-212a-5ecb-17e5d279b8c7.png)

:::note warn!
新規にトピックを作成すると確認メールが送付されるので承認してください。
:::

![スクリーンショット 2023-06-29 14.18.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/15c6b105-3bfb-4e9d-65da-c5c35075bc5d.png)

アラームの名前は任意のもので構いません。

**(2)ロードバランサーのDNS名を取得**
URLは以下からコピーしてください。

EC2 > ロードバランサー > 今回新規で作成したロードバランサーのDNS name > コピー

**(3)遅延を発生させる**
```shell:Terminal
for i in {1..10}; do curl -sS http://.ap-northeast-1.elb.amazonaws.com/delayedResponse; sleep 1; done
```

**(4)アラート確認**
以下のようにアラート常態となり、アラートメールが届くことが確認できます。
![スクリーンショット 2023-06-29 14.29.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/f981678a-812b-6b30-d03d-533d12604dd6.png)
![スクリーンショット 2023-06-29 14.27.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/5ffb2c3e-d41f-cf4c-8b2b-de46fa34e6c9.png)

### 例5： Container Insightを用いた統合管理

![スクリーンショット 2023-07-02 10.03.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/5e5ed02c-28ad-4c20-7cb6-e5ca8f704244.png)

`Container Insight`はECSやFargateやEKSのメトリクスやログを、CloudWatchで取得〜可視化~分析するための統合的な機能です。各種メトリクスを設定することは上記の手順の通り比較的工数がかかりますが、本機能を利用することでワンタッチで利用することができるため非常に便利です。

**(1)Container Insightを有効化する**
本アプリケーションでは、`Copilot`の設定ファイルに記述することですぐに利用することができます。

```yml:copilot/environments/test/manifest.yml
# The manifest for the "test" environment.
# Read the full specification for the "Environment" type at:
#  https://aws.github.io/copilot-cli/docs/manifest/environment/

# Your environment name will be used in naming your resources like VPC, cluster, etc.
name: test
type: Environment

# Import your own VPC and subnets or configure how they should be created.
# network:
#   vpc:
#     id:

# Configure the load balancers in your environment, once created.
# http:
#   public:
#   private:

# Configure observability for your environment resources.
observability:
  container_insights: true # ここをtrueに設定します！！
```

**(2)ダッシュボードを確認**
CloudWatch > Container Insights > ECS Service

以下のような図の通り必要な指標が一通り揃っているので非常に便利です。

![スクリーンショット 2023-06-29 14.43.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/43e6db11-f66c-7c4d-e173-18a328bc30e8.png)

## CloudWatch logs編

次は、`CloudWatch logs`の関連機能を手を動かしながら確認していきます。引き続き上記で環境構築したサンプルアプリを用いて確認していきます。

https://github.com/WebEngrChild/cw-go-api

### 例１：特定の文字列"Error"が含まれているログのみを抽出

ここでは`CloudWatch Logs Insights`という機能を用いて抽出してみます。

![スクリーンショット 2023-07-02 10.05.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/429dca85-5ce1-135b-26d2-33e224b56c54.png)

本機能は、CloudWatch Logsに保存されているログデータを分析するための対話型のクエリ機能です。ユーザーは標準SQLに似たクエリ言語を使用してカスタムクエリを作成し、ログデータを分析して視覚的なグラフや表を生成します。これにより、迅速なトラブルシューティングと効果的な監視が可能となります。

ユースケースとしては、エラーログの中の特定の文字列のみを抽出して分析したいケースなどです。

**(1)エラー出力**
本ハンズオンで利用しているアプリケーションは`Copilot`経由でECS上にデプロイしています。ECSではエラー標準出力先がデフォルトで`CloudWatch`となっているため、アプリケーション側で特に指定がない場合、当該リソースにログが転送されます。

上記で既に利用した`/error`にリクエストを再度投げてみます。

```shell:Terminal
# ランダムでエラーを返却
$ for i in {1..100}; do curl -sS http://${新規で作成したDNSから取得}.ap-northeast-1.elb.amazonaws.com/error; sleep 1; done
```

**(2)ログ確認**
CloudWatch > ロググループ > /copilot/app-test-cw-go-api

以下のようにログが出力されていることが確認できます。

![スクリーンショット 2023-06-29 15.39.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/bce0f862-cd65-1583-6e70-8f4af0bebf7e.png)

**(3)モニタリング対象のロググループを選択**
CloudWatch > ログのインサイト > ロググループを選択 > `/copilot/app-test-cw-go-api`

**(4)クエリ作成**
以下のクエリを入力 > 実行

```sql: log insight query
fields @timestamp, @message
| filter @message like /Error|error/
| sort @timestamp desc
```

![スクリーンショット 2023-06-29 15.48.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/fa668e94-4139-fcd8-22d8-3768744b838b.png)

**(5)結果の確認**
以下のように`ロググループ`の中の`ログイベント`内に特定の文字列が含まれているレコードのみ抽出できています。

![スクリーンショット 2023-06-29 15.49.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/0d93922d-d971-8dee-ac34-44208edab0af.png)

### 例2：ログをメトリクス化して特定の文字列が含まれているかをカウント

ここでは`CloudWatch Logs メトリクスフィルター`という機能を用いて抽出してみます。

![スクリーンショット 2023-07-02 10.13.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/f7859838-9698-e2a8-7c7f-224a0a1f1491.png)

本機能は、ログデータ内のパターンを検索し、その情報をメトリクスとして抽出します。CloudWatchアラームと連携させることで、異常を検知した際に通知や自動的なアクションを行うことが可能です。

ユースケースとしては、特定の文字列(ex.`Error`)が含まれていた場合にカウントするカスタムメトリクスを作成 => 上記メトリクスを使ったアラートを作成 => 通知するといったような、ログの中身をベースにアラートやイベントを発生させるケースです。

**(1)ログのパターンをもとにカウントするメトリクスフィルターを作成**
Cloud watch > ロググループ > `/copilot/app-test-cw-go-api` > メトリクスフィルターを作成

| 項目 | 値 |
|:-|:-|
| フィルターパターン  |  Error |
|  テストするログデータを選択 | 上記で`/error`が!出力されたロググループ  |

パターンをテストで該当するログデータが表示されることも確認できます。

![修正_68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313832343734302f30316363346664652d656233642d616234382d373261642d6537643634313461326662312e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/4c78468a-7d3e-eda6-5891-034807256ec3.png)

| 項目 | 値 |
|:-|:-|
|  フィルター名 |  error-count |
|  メトリクス名前空間 | 任意の名前空間を指定してください  |
|  メトリクス名 |  error-count |
|  メトリクス値 |  1 |

![スクリーンショット 2023-06-29 16.34.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/8f8c558c-dfcc-8ae8-3a98-bca272f63036.png)

**(2)エラーを出力**
以下のスクリプトをAPIで叩きます。

URLはEC2 > ロードバランサー > 今回新規で作成したロードバランサーの`DNS name`をコピーしてください。

```shell:Terminal
# ランダムでエラーを返却
$ for i in {1..100}; do curl -sS http://${新規で作成したDNSから取得}.ap-northeast-1.elb.amazonaws.com/error; sleep 1; done
```

**(3)Cloud Watch Metricsから確認**
CloudWatch > すべてのメトリクス > 参照 > 上記で指定した任意の名前空間 > ディメンジョンなしのメトリクス > `error-count`

メトリクスが計測されるまで、少し時間がかかるのでご注意ください。

![スクリーンショット 2023-06-29 16.46.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/2cd219fa-227f-26a9-93c2-717e7f4970d5.png)

ここまでくれば、あとは既存のメトリクスと組み合わせたり、アラートを発生させることも可能ですね。

:::note warn
※アラート作成とメール通知に関しては重複する内容になるため割愛します。
:::

### 例３：Kinesis Data Firehose/S3/Amazon Athenaを使ったログ転送と分析

`CloudWatch logs`は便利なのですがログ保管はコストパフォーマンスがあまり良くありません。そこで、ログデータを他のAWS 上のデータウェアハウスに転送する事がよくあります。また、転送先で大量のログデータを分析するための基盤を設定することも重要です。

ここでは以下のリソースを用いてログ転送とログ分析基盤を構築します。

| リソース名 | 内容 |
|:-|:-|
|  Kinesis Data Firehose |  リアルタイムで大量のデータを収集し、S3, Redshift, Elasticsearchなどに簡単にロードできるストリーミングデータ配信ツールです |
|  S3 |  ストレージサービス。ここではCloud Watchから転送されたログを格納します。 |
| Amazon Athena  |  S3上の大量のデータに対してSQLを使用して分析を行うことができます。サーバーレスで、設定や管理が不要で、実行したクエリのみに課金されます。 |
|  Cloud Watch subscription filter |  AWS CloudWatch Logsからリアルタイムでログデータをフィルタリングし、指定したAWSサービス（例：Lambda, Kinesis）に転送する機能です。|

また本ハンズオンでは、`CloudWatch Log サブスクリプションフィルタ`という、外部にリアルタイムでログ転送ができる機能を使っていきます。

![スクリーンショット 2023-07-01 10.48.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/bfc4100b-ec87-7875-494a-de3cdafef9ca.png)

**(1)事前準備**
以下のリソースは説明が冗長になるため`cloudformation`を使って作成してしまいます。

| リソース名 | リソース | 目的 |
|:-|:-|:-|
|  KinesisDataBucket | s3  | Kinesis Data Firehoseの配信ストリーム送信先  |
|  AthenaQueryBucket | s3  | Amazon Athenaで実行したクエリ結果を保管する先 |
|  LogsRole | IAM  | CWLがFirehoseにログデータを渡すために必要になる`AssumeRole`権限 |

以下、スクリプトを実行してスタックを作成します。

```shell:Terminal
$ pwd
/Users/hogehoge/cw-go-api

# スタック作成
$ aws cloudformation create-stack \
  --stack-name s3-iam \
  --template-body file://cw-sbf.yml \
  --capabilities CAPABILITY_IAM
```

マネジメントコンソールの画面でステータスが`CREATE_COMPLETE`という表示になっていれば完成です。
![スクリーンショット 2023-06-30 7.12.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/efb26f63-479f-0fbe-d8ff-adc414817f0b.png)

**(2)Kinesis Data Firehoseを作成**
マネジメントコンソール > Amazon Kinesis > Data Firehose > 配信ストリームを作成

少し設定画面が多いですが画像を参考に入力してください。
![スクリーンショット 2023-06-30 7.30.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/235dd99e-d291-e590-ce7f-a8a9628bbfc5.png)
デフォルトのままだと`Clod Watch サブスクリプションフィルタ`から転送されたログでは、`Amazon Atena`側でデータ形式の問題で分析できません。そのため、一旦、Lamda側でデータ変換を行います。
![修正_68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313832343734302f65616136363531612d316362362d653237302d626135652d3332663430656465656638352e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/8c71f8d6-b8d2-f1b0-acff-ae6c4e7be7ee.png)

![スクリーンショット 2023-06-30 7.20.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/a750df34-0a0b-5ce8-4fdc-aabe0d96a9f4.png)
関数名 > `streamProcessor`と入力 > 関数の作成 > 以下の画像のように成功バーが表示されれば完成です
![スクリーンショット 2023-06-30 7.22.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/6d30284b-a126-0179-7a2a-9b651e0d7383.png)
![スクリーンショット 2023-06-30 7.24.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/50dc4ba9-bf6f-d851-d94c-be234fa9de9e.png)
Lamdaがデータ変換を完了する前にタイムアウトが発生しないようにするために、上記で作成したLamdaの以下の項目を画像の通り1分に修正します。
![修正_68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f313832343734302f32333665303533642d323566622d373432622d393961382d3132396365626533666538342e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/6629bd0a-b336-e7c3-4725-25929dfba5b1.png)
s3バケット > `kinesis-data-bucket-sample` > 選択
![スクリーンショット 2023-06-30 7.46.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/f5622603-631b-3ca2-a411-8195fb34733b.png)
以後はデフォルトの設定で問題ありません。`配信ストリームを作成`を選択して完了です。

**(2)Cloud Watch サブスクリプションフィルターの設定**

マネジメントコンソール > ロググループ > `/copilot/app-test-cw-go-api` > サブスクリプションフィルター > Kinesis Firehose サブスクリプションフィルターを作成 > 以下の項目を入力 > ストリーミングを開始

| 項目 | 値 |
|:-|:-|
| 送信先アカウント  | 現在のアカウント |
| Kinesis Firehose 配信ストリーム  | firehose-log |
| 既存のロールを選択  | cw-sf-role |
| ログの形式  | json  |
| サブスクリプションフィルター名  | firehose-filter-log  |

![スクリーンショット 2023-06-30 12.58.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/3096c3ce-7f26-032f-dc79-b2ea43577cd9.png)
![スクリーンショット 2023-06-30 12.59.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/26f2f211-4d18-1236-2982-9e31727d0637.png)
![スクリーンショット 2023-06-30 13.00.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/10bea5d5-7884-8cd1-d8a0-0c68ce41bed6.png)
![スクリーンショット 2023-06-30 13.07.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/03c97360-aab6-2570-1290-1cab0ac5d5a3.png)

**(3)S3にログ転送されることを確認**

ここで一旦動作確認をしておきます。
```shell:Terminal
# ランダムでエラーを返却
$ for i in {1..100}; do curl -sS http://${新規で作成したDNSから取得}.ap-northeast-1.elb.amazonaws.com/error; sleep 1; done
```

マネジメントコンソール >  S3 > バケット > `kinesis-data-bucket-sample` > 以下の画像のような階層で格納されます

画面に反映されるまで5分程度かかるので少し待ちます。
![スクリーンショット 2023-06-30 13.16.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/ee575041-b7c4-7c7c-f5d6-236d09fa2d3d.png)

**(4)Amazon Atena**
マネジメントコンソール > Amazon Athena > データをクエリする > クエリエディタを起動
![スクリーンショット 2023-06-30 13.27.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/604a6267-9d3e-b482-fc04-f16d0ab36757.png)

設定を管理 > クエリの結果の場所と暗号化 > S3を参照 > 以下を入力

| 項目 | 値 |
|:-|:-|
| クエリ結果の場所  | athena-query-bucket-sample  |

![スクリーンショット 2023-06-30 13.38.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/61b4a262-6017-89d1-68dd-30fbfadfed98.png)

| 項目 | 値 |
|:-|:-|
| データソース  | AwsDataCatalog  |
| データベース  | default  |

テーブルとビュー > データソースからテーブルを作成 > s3バケットデータ
![スクリーンショット 2023-06-30 13.39.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/7944e800-23e8-bca0-ebd5-1c9961dbf182.png)

| 項目 | 値 |
|:-|:-|
| テーブル名  | cw_go_api_table  |
| データベース設定  | 既存のデータベースを選択 > default  |
| 入力データセットの場所  | kinesis-data-bucket-sample  |

![スクリーンショット 2023-06-30 13.45.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/53372150-d2c7-0926-8c7b-4d80a9b5e4e9.png)

| 項目 | 値 |
|:-|:-|
| ファイル形式  | JSON  |

※上記を設定すると自動的にデフォルト設定が反映されます。特に修正不要です。
![スクリーンショット 2023-06-30 13.46.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/a49860c9-df9a-5ab9-771e-022cfbd1e9df.png)
![スクリーンショット 2023-06-30 15.50.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/0a94581c-5421-adcd-237a-d803628fd738.png)

上記で設定すると以下のようなクエリがエディタ上に反映されます。

```sql:Amazon Athena
CREATE EXTERNAL TABLE IF NOT EXISTS `default`.`cw_go_api_table` (
  `message` string,
  `status_code` int,
  `timestamp` timestamp
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
  'ignore.malformed.json' = 'FALSE',
  'dots.in.keys' = 'FALSE',
  'case.insensitive' = 'TRUE',
  'mapping' = 'TRUE'
)
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://kinesis-data-bucket-sample/'
TBLPROPERTIES ('classification' = 'json');
```

手動設定しましたが、このようにクエリだけでも同様な処理を記述することができます。

**(5)クエリ結果**
新しいタブを開き以下のクエリを入力します。

```sql:Amazon Athena
select * from cw_go_api_table
```
![スクリーンショット 2023-06-30 15.55.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/b5cd617d-d67d-5b9f-58ff-dc2cfab88725.png)
S3に格納されているログが取得できます。
![スクリーンショット 2023-06-30 15.55.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/8a849b6b-9513-c9d6-29af-7b8b41893b8c.png)

## Cloud Watch Agent編

ここからは以下のサンプルアプリを使って進めます。初期設定が完了していない方は、以下のリポジトリの`README.md`を参考にしながら設定をお願いします。

https://github.com/WebEngrChild/cw-ec2-nginx

### 例１：EC2上で稼働しているNginxのメトリクスとログを個別に作成してモニタリングする

**(1)Cloud Watch Agentの設定ファイル確認**

`README.md`の中で以下のコマンドを実行したかと思います。こちらのコマンドは`Systems Parameter`にCloud Watch Agentの設定ファイルを格納しております。

```shell:Terminal
$ aws ssm put-parameter --name "/config/cw-agent" --type "String" --value "$(cat cw-agent.json)"
```
この設定ファイル内で、メトリクスの`名前空間`/`メトリクス名`/`ディメンション`、ログに関しては`ロググループ`/`ログストリーム`を指定しています。

```json
{
  "agent": {
    "run_as_user": "cwagent"
  },
  "metrics": {
    "aggregation_dimensions": [
      [
        "InstanceId"
      ]
    ],
    "append_dimensions": {
      "InstanceId": "${aws:InstanceId}"
    },
    "namespace": "ec2-instance",
    "metrics_collected": {
      "mem": {
        "measurement": [
          "mem_used_percent"
        ],
        "metrics_collection_interval": 60
      }
    }
  },
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/nginx/access.log",
            "log_group_name": "/web/nginx_access_log",
            "log_stream_name": "{instance_id}",
            "timestamp_format": "%d/%b/%Y:%H:%M:%S %z",
            "timezone": "Local"
          }
        ]
      }
    }
  }
}
```

**(2)Cloud Watch Agentのインストール**

(1)で設定ファイルを`Systems Manager`の`Parameter Store`に格納できました。そこで、次はEC2内に`Cloud Watch Agent`をインストールしていきます。

まずはじめに、EC2のIPアドレスをコピーしておきます。
マネージメントコンソール > `EC2` > 該当のEC２インスタンスを選択 > `Elastic IP`アドレス > コピー

以下のコマンドを実行します。

```shell:EC2
# EC2インスタンスにSSHで接続します。'access-key.pem'は秘密鍵ファイルで、ec2-userはデフォルトのユーザー名です。
$ ssh -i ~/.ssh/access-key.pem ec2-user@<上記でコピーしたIPアドレス>

# Amazon CloudWatch Agentをインストールします。
$ sudo yum install -y amazon-cloudwatch-agent

# Amazon CloudWatch Agentの設定をAWS Systems Manager Parameter Storeから取得し、エージェントを開始します。
# -a fetch-config: 設定を取得する
# -m ec2: EC2インスタンス上で動作していることを指定
# -c ssm:/config/cw-agent: Parameter Storeのパスを指定して設定を取得
# -s: エージェントを開始
$ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:/config/cw-agent -s
```

**(3)該当EC2にリクエストをする**

```shell:Terminal
# ランダムでエラーを返却
$ for i in {1..10}; do curl -sS http://${上記で取得したIPアドレス}; sleep 1; done
```

**(4)Cloud Watch上で結果を確認**

CloudWatch > Metrics > すべて > ec2-instance > InstanceId > 
`ec2-dev-ec2-bastion`
![スクリーンショット 2023-07-01 17.34.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/79848450-e6d9-ecd7-1918-6d8d992bc38a.png)メトリクスが出力されていることが確認できます。続いてログも確認してみます。

CloudWatch > ロググループ > `/web/nginx_access_log` > ログストリーム
![スクリーンショット 2023-07-01 16.19.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1824740/e4a4cef3-7ad6-7ffa-6196-59978e31b516.png)ログが出力されていることが確認できます。

## リソースの削除

今回のハンズオンで利用した各種リソースを削除していきます。放置したままだと料金が発生するため個人アカウント等で利用している方はご対応ください。

削除方法は各リポジトリの`README.me`をご確認ください。

https://github.com/WebEngrChild/cw-go-api

https://github.com/WebEngrChild/cw-ec2-nginx

# 参考にした資料

本記事は以下の"AWS BlackBelt 2023 Amazon CloudWatchの概要と基本"をメインに参考にさせて頂きました。その他にも、非常に勉強になった書物がいくつかあるのでこの場を借りてご紹介させていただきます。

https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AmazonCloudWatch_0330_v1.pdf

https://www.youtube.com/watch?v=fzVkJne3OMI&ab_channel=AmazonWebServicesJapan%E5%85%AC%E5%BC%8F

https://www.amazon.co.jp/%E5%85%A5%E9%96%80-%E7%9B%A3%E8%A6%96-%E2%80%95%E3%83%A2%E3%83%80%E3%83%B3%E3%81%AA%E3%83%A2%E3%83%8B%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Mike-Julian/dp/4873118646/ref=sr_1_1

https://www.amazon.co.jp/dp/4815607656?_encoding=UTF8&psc=1&ref_=cm_sw_r_cp_ud_dp_G75322NEFBR4V7V8B48H

https://booth.pm/ja/items/4130172