CloudWatch Logsでログを外出ししてみる

# はじめに
会社で運用しているシステムがどれもこれもログをEC2インスタンス内にしか保管しておらず、トラブルシューティング時に該当インスタンスを終了してしまった場合、ログが永久に失われ調査ができないという状況にあったため、`CloudWatch Logs`によってログを外出しすることにした。（よく今までこの状態でやってこれたな、、、）
※備忘録のため詳細を省く部分もある。
# CloudWatchエージェントのインストール
`CloudWatch Logs`を使用するためには、CloudWatchエージェントのインストールが必要となる。
CloudWatchエージェントとは、AWSから提供されているソフトウェアの1つで、EC2やオンプレミスサーバにインストールすることで、メトリクスやログを収集し、CloudWatchへ送信することができる。
以下、会社で使用しているOSであるAmazon LinuxとUbuntuへのインストールコマンド。
※これより下でサーバーに入らずにインストールする方法も記載しており、そちらを推奨だが念のため直接サーバーに入って作業する方法も先に記載
##### Amazon Linux
```
sudo yum install amazon-cloudwatch-agent
```
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-on-EC2-Instance.html
##### Ubuntu
```
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i -E ./amazon-cloudwatch-agent.deb
```
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html#:~:text=amazon%2Dcloudwatch%2Dagent-,%E3%81%99%E3%81%B9%E3%81%A6%E3%81%AE%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0,-%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E3%81%95%E3%82%8C

インストールができているか確認してみる。
```
cat /opt/aws/amazon-cloudwatch-agent/bin/CWAGENT_VERSION
```
以下のようにCloudWatchエージェントのバージョンが出力されればインストールは成功している。
```
1.247357.0b252275
```

### Systems Managerを使用したインストール法
CloudWatchエージェントのインストールは、直接サーバーに入らなくとも、Systems Managerのドキュメント及びRun Command機能を使用することで、複数EC2に対し一括でインストールすることも可能。
詳細は以下ドキュメント参照。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-on-EC2-Instance-fleet.html#:~:text=Systems%20Manager%20%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%20Amazon%20EC2%20%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%A7%20CloudWatch%20%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%99%E3%82%8B

上記方法であれば、一括でインストールができるため工数削減できるのと、直接サーバーに入らずに作業ができるため誤操作などのリスクを回避することもできる。
ここでは詳細は省くが、一括で適用するのがリスキーという判断もあるかと思うので、その場合はレート制御をすることで、一度に適用するターゲット数もしくは割合を指定したり、仮にエラーが発生した場合の挙動などを制御することもできる。

![スクリーンショット 2023-07-09 15.57.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1131391/065bc458-0552-cd11-16a8-5add9ced9f89.png)

### 収集メトリクス
CloudWatchエージェントをインストールすることで、以下ドキュメントに記載があるようにいくつかのメトリクスを取得することができるようになる。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/metrics-collected-by-CloudWatch-agent.html

この他、取得するメトリクスをカスタマイズしたいという場合、`collectd`をインストールすることで実現可能。
例えば`LoadAverage`を取得したい場合などに有効である。
以下、`collectd`のインストール。

#### Amazon Linux
```
sudo amazon-linux-extras install collectd
```
#### Ubuntu
```
sudo apt-get install collectd
```
インストール確認。
```
sudo systemctl status collectd
# or
sudo service collectd status
```
インストールに成功していると以下のような出力となる。
```
$ service collectd status
Redirecting to /bin/systemctl status collectd.service
● collectd.service - Collectd statistics daemon
   Loaded: loaded (/usr/lib/systemd/system/collectd.service; disabled; vendor preset: disabled)
   Active: active (running) since Mon 2023-02-27 12:40:19 JST; 2s ago
```

# 取得するログ及びメトリクスの設定
ここまででログ及びメトリクスの取得に必要となるソフトウェアのインストールが完了したため、実際に取得するログファイルのパスやメトリクスの指定をする。
サーバーに入ってウィザード（どのパスのログファイルを取得するか、どの程度の間隔でログを取得するかなどの各項目の設定を対話型で実施する）での作業も可能だが、ここでは上述のSystems Managerのドキュメント及びRun Command、パラメータストア（後述）を使用するものとする。
ウィザードでの設定については以下記事参照。

https://ops.jig-saw.com/tech-cate/amazon-cloudwatch-setup

### Systems Managerの設定
大まかな流れとしては、Systems Managerのパラメータストアを作成して取得するログやメトリクスをjson型で定義し、Run Commandを実行することで対象サーバーに適用させるというやり方になる。

#### パラメータストアの作成
アプリケーションに合わせ適切な設定をする。
今回はjson型の値を設定するが、「データ型」欄はあくまでtextとなる。（そもそもjson型の選択肢がない）

![スクリーンショット 2023-07-09 17.03.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1131391/8d657cc1-4578-8a0f-f215-1ae7ac822559.png)

「値」欄には以下のように入力する。
各設定値詳細は以下公式ドキュメント参照。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html


```json
{
	"agent": {
		"metrics_collection_interval": 60, # メトリクス収集の間隔を秒単位で指定
		"run_as_user": "root" # CloudWatchエージェントが実行されるユーザーを指定
	},
	"logs": {
		"logs_collected": {
			"files": {
				"collect_list": [
					{
						"file_path": "/var/log/apache2/hogehoge.com/access.log", # 取得するファイルパス
						"log_group_name": "hogehoge-access-log", # ログイベントを格納するCloudWatch Logsのロググループ名（CloudWatchのコンソール画面上でグループ分けされる）
						"log_stream_name": "{instance_id}" # ログイベントを識別するためのストリーム名を指定。"{instance_id}"となっており、EC2インスタンスのIDが自動的に使用される
					},
					{
						"file_path": "/var/log/apache2/hogehoge.com/error.log",
						"log_group_name": "hogehoge-error-log",
						"log_stream_name": "{instance_id}"
					},
					{
						"file_path": "/var/www/html/hogehoge.com/storage/logs/laravel*log",
						"log_group_name": "hogehoge-laravel-log",
						"log_stream_name": "{instance_id}"
					}
				]
			}
		}
	},
	"metrics": {
		"aggregation_dimensions": [ # 収集されたメトリクスが集計されるディメンションを指定
			[
				"InstanceId"
			]
		],
		"append_dimensions": { # 追加のディメンションを指定
			"AutoScalingGroupName": "${aws:AutoScalingGroupName}",
			"ImageId": "${aws:ImageId}",
			"InstanceId": "${aws:InstanceId}",
			"InstanceType": "${aws:InstanceType}"
		},
		"metrics_collected": { # collectdメトリクスの設定
			"collectd": {
				"metrics_aggregation_interval": 60
			},
			"cpu": {
				"measurement": [ # 収集する cpu メトリクスの配列を指定
					"cpu_usage_idle",
					"cpu_usage_iowait",
					"cpu_usage_user",
					"cpu_usage_system"
				],
				"metrics_collection_interval": 60,
				"resources": [ # ディスクのマウントポイントの配列を指定
					"*" # すべてのマウントポイントからメトリクスを収集
				],
				"totalcpu": false # 合計CPU使用率を含めるかどうか
			},
			"disk": {
				"measurement": [
					"used_percent",
					"inodes_free"
				],
				"metrics_collection_interval": 60,
				"resources": [
					"*"
				]
			},
			"diskio": {
				"measurement": [
					"io_time"
				],
				"metrics_collection_interval": 60,
				"resources": [
					"*"
				]
			},
			"mem": {
				"measurement": [
					"mem_used_percent"
				],
				"metrics_collection_interval": 60
			},
			"statsd": {
				"metrics_aggregation_interval": 60,
				"metrics_collection_interval": 10, # 最上部記載の agent セクションで定義した「metrics_collection_interval」の値を上書き
				"service_address": ":8125"
			},
			"swap": {
				"measurement": [
					"swap_used_percent"
				],
				"metrics_collection_interval": 60
			}
		}
	}
}
```
#### パラメータの適用
再度Run Commandを使用してパラメータストアで作成した設定をEC2に適用していく。
実行するコマンドは`AmazonCloudWatch-ManageAgent`。
`Optional Configuration Location`に先程作成したパラメータ名を入力し、対象となるインスタンスIDを設定し例のごとく実行すると、EC2にCloudWatch Logsの設定ファイルが生成される。
`/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/ssm_{{ パラメータ名 }}`にjson型パラメータが反映されていればOK。

##### ★一つのEC2に複数パラメータを適用させたい場合
1インスタンスに複数システムが相乗りしており、パラメータを分けたいという場合が想定される。（実際うちではそうだった）
その場合、Run Commandを複数回実行すると、設定値が上書きされてしまうため最後に実行されたパラメータのみが反映されることになる。
そのため、こういったケースでは直接インスタンスに入ってコマンドを実行してやる必要がある。
```
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a append-config -m ec2 -c ssm:{{ パラメータ名 }} -s
```
こうすることで複数のパラメータを一つのインスタンスに適用させることができる。
# ログ出力準備
これだけではまだログをCloudWatchに出力することはできない。
まず1つ目に、インスタンスに以下ロールを付与してやる必要がある。

`CloudWatchAgentAdminPolicy`
`CloudWatchAgentServerPolicy`

また、インスタンスが配置されているVPCからCloudWatchへの疎通が必要である。
VPCエンドポイントについては以下記事で記載。

https://qiita.com/charita/items/fe94680133419a03aade

これで準備は整ったので、CloudWatchのコンソールからログやメトリクスが収集できていることを確認する。
左側メニューより「ロググループ」→パラメータストアで設定したロググループ名を選択→対象インスタンスIDを選択することで確認が可能。
もしログが確認できない場合、以下を試してみると良い。

```
systemctl status amazon-cloudwatch-agent # CloudWatchエージェントのステータスを確認
systemctl restart amazon-cloudwatch-agent # CloudWatchエージェントが起動していないようであれば再起動
```

最後に、ログの保持期間を適宜設定して完了。

![スクリーンショット 2023-07-10 22.27.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1131391/6b695f08-625e-0904-32f8-e470f6ff94ff.png)

これでログ消失とは無縁の生活を送れる、、、