👋

自宅サーバーにansibleでCloudWatch Agentの設定をしてCloudWatchで監視できるようにした

2021/10/24に公開

自宅サーバーに CloudWatch Agent を入れて CloudWatch 側で監視をしようと思い立ったので、メモ。
以前から Ansible で管理しているので、今回も頑張って Ansible で入れました。
サーバーの用途としては Google Photo の写真や動画を NAS に定期的にバックアップするジョブを流すのに使っています。
https://qiita.com/tokkun5552/items/38b4d678ccc3e2f796fa

環境

  • Local
    • Windows 10 Pro
    • Mac Book Pro (not M1)
    • Docker version 20.10.8, build 3967b7d

事前準備

  • Ansible の実行環境
    Ansible は Docker の中に構築してそれを使っています。
    本記事の内容とはそれるので、詳しく知りたい方は以下を参照してください。

https://tokku-engineer.tech/build_docker_ansible_devenv/

  • IAM ユーザーの追加
    サーバーに入れた CloudWatchAgent から AWS 側に情報を送信するために、あらかじめ IAM ユーザーを作成しておく必要があります。
    これも本記事と内容がそれるので、詳細は以下を参照ください。

https://tokku-engineer.tech/how-to-add-iam-user/

Ansible プレイブック

事前準備が整ったので実際のプレイブックを見ていきます。
フォルダ構成はベストプラクティスに沿って各々用意してください。
大きく分けて

  • AWSCLIv2 のインストールと設定
  • CloudWatch Agent のインストールと設定

の 2 つです。

AWSCLIv2

インストール方法は以前書いたこちらの記事を参考に、Ansbile のプレイブックに起こしました。
https://zenn.dev/tokku5552/articles/aws-container

やってることは

  • which aws でコマンドがインストールされているか確認
  • インストールされていなければ
    • インストーラが入っている zip ファイルをダウンロード
    • zip ファイルを解凍
    • インストーラを実行してインストール
  • .aws/config ファイルを上書き
  • .aws/credentials ファイルを上書き

です。

install_awscli.yml
# install_awscli
- name: check awscli
  command: "which aws"
  register: result
  check_mode: no
  changed_when: no
  failed_when: no

- name: download awscli installer
  get_url:
    url: https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip
    dest: /tmp/awscliv2.zip
    force: yes
  when: result.rc == 1

- name: unzrchive zip
  unarchive:
    src: /tmp/awscliv2.zip
    dest: /tmp/
    copy: no
  when: result.rc == 1

- name: install awscli
  become: yes
  command:
    cmd: ./aws/install
    chdir: /tmp
  when: result.rc == 1

- name: copy config file
  become: yes
  copy:
    src: config
    dest: /root/.aws/config

- name: copy credentials file
  become: yes
  copy:
    src: credentials
    dest: /root/.aws/credentials

ポイントとしては、頭のcheck awscliでコマンドの有無を確認した結果をresultに格納して、そのあとの一連のインストール処理を行うかどうかを振り分けているところです。
yum でインストールが出来ればモジュールを使って冪等性を担保しつつ記述できるのですが、公式のインストール方法に従うと、既にインストール済みかどうかを判別する必要が出てきてしまうのが難点です...

credentials

今回は CloudWatch Agent で使用するので、credentials に以下のようにAmazonCloudWatchAgentのセクションを追加してやる必要があります。
ACCESS_KEY_IDSECRET_ACCESS_KEYは作成した IAM ユーザーのものを指定してください。
regionは EC2 の場合は指定しない場合はインスタンスが実行されているリージョンに勝手になるようですが、今回の様に AWS 上のインスタンスでない場合は指定が必要なようです。

credentials
[default]
aws_access_key_id = ACCESS_KEY_ID
aws_secret_access_key = SECRET_ACCESS_KEY

[AmazonCloudWatchAgent]
aws_access_key_id=ACCESS_KEY_ID
aws_secret_access_key=SECRET_ACCESS_KEY
region = ap-northeast-1

CloudWatch Agent

今回は NUC に入れるので onPremiss モードとして動作させる必要があります。
公式の手順に従って、Ansible のプレイブックに起こします。
やっていることは

  • amazon-cloudwatch-agent.rpm がダウンロード済みでなければ
    • rpm をダウンロードして
    • yum で rpm をインストール
  • collectd を入れるために epel をインストール
  • collectd をインストール
  • amazon-cloudwatch-agent.json を設定

です。

install_cloudwatch_agent.yml
# install_cloudwatch_agent

- name: confirm exists cloudwatch rpm
  stat:
    path: /tmp/amazon-cloudwatch-agent.rpm
  register: cloudwatch_rpm

- name: cloudwatch rpm download
  get_url:
    url: https://s3.amazonaws.com/amazoncloudwatch-agent/centos/amd64/latest/amazon-cloudwatch-agent.rpm
    dest: /tmp
  when: cloudwatch_rpm.stat.exists  == false

- name: install cloudwatch agent (yum)
  become: yes
  yum:
    name:
     - /tmp/amazon-cloudwatch-agent.rpm
    state: present
  when: cloudwatch_rpm.stat.exists  == false

- name: install epel
  become: yes
  yum:
    name:
      - https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
    state: latest
    validate_certs: no
  notify: yum clean all

- name: install collectd
  become: yes
  yum:
    name:
     - collectd
    enablerepo: epel
    state: present

- name: copy amazon-cloudwatch-agent.json
  become: yes
  copy:
    src: amazon-cloudwatch-agent.json
    dest: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
  notify:
    - cloudwatch-agent fetch config
    - Restart CWAgent daemon

特筆すべきなのは、amazon-cloudwatch-agent.jsonの反映方法についてです。
オンプレミスだからなのか分かりませんが、単純に/opt/aws/amazon-cloudwatch-agent/etc/とか/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/とかに置いてサービスを再起動するだけでは認識できず、amazon-cloudwatch-agentの専用コマンドから設定ファイルをフェッチしてあげる必要があります。
それがcloudwatch-agent fetch configのプレイブックです。

handlers/cloudwatch-agent_fetch_config.yml
- name: cloudwatch-agent fetch config
  become: yes
  shell: |
    /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
      -a fetch-config \
      -m onPremise \
      -s \
      -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json

-m onPremiseを指定して-c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.jsonで設定ファイルの場所を示すことで、正常に設定を読み込めました。
ただこれをやると、Ansible で配置した/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.jsonがなくなり、新たに/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.jsonというファイルに置き換わります。
今のままのプレイブックだと、json に変更がなくても必ずchangedになってしまうので少々考え物ですが、正常に動くは動くのでとりあえず放置...

amazon-cloudwatch-agent.json

設定ファイルは以下のようにしています。
こちらを参考に、/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizardを実行して適当に作っています。

amazon-cloudwatch-agent.json
{
	"agent": {
		"metrics_collection_interval": 60,
		"run_as_user": "root"
	},
	"logs": {
		"logs_collected": {
			"files": {
				"collect_list": [
					{
						"file_path": "/var/log/messages",
						"log_group_name": "/var/log/messages",
						"log_stream_name": "{hostname}"
					},
					{
						"file_path": "/var/log/google_photos_backup.log",
						"log_group_name": "/var/log/google_photos_backup.log",
						"log_stream_name": "{hostname}"
					}
				]
			}
		}
	},
	"metrics": {
		"metrics_collected": {
			"collectd": {
				"metrics_aggregation_interval": 60
			},
			"cpu": {
				"measurement": [
					"cpu_usage_idle",
					"cpu_usage_iowait",
					"cpu_usage_steal",
					"cpu_usage_guest",
					"cpu_usage_user",
					"cpu_usage_system"
				],
				"metrics_collection_interval": 60,
				"resources": [
					"*"
				],
				"totalcpu": true
			},
			"disk": {
				"measurement": [
					"used_percent"
				],
				"metrics_collection_interval": 60,
				"resources": [
					"*"
				]
			},
			"diskio": {
				"measurement": [
					"io_time",
					"write_bytes",
					"read_bytes",
					"writes",
					"reads"
				],
				"metrics_collection_interval": 60,
				"resources": [
					"*"
				]
			},
			"mem": {
				"measurement": [
					"mem_used_percent"
				],
				"metrics_collection_interval": 60
			},
			"net": {
				"measurement": [
					"bytes_sent",
					"bytes_recv",
					"packets_sent",
					"packets_recv"
				],
				"metrics_collection_interval": 60,
				"resources": [
					"*"
				]
			},
			"netstat": {
				"measurement": [
					"tcp_established",
					"tcp_time_wait"
				],
				"metrics_collection_interval": 60
			},
			"statsd": {
				"metrics_aggregation_interval": 60,
				"metrics_collection_interval": 60,
				"service_address": ":8125"
			},
			"swap": {
				"measurement": [
					"swap_used_percent"
				],
				"metrics_collection_interval": 60
			}
		}
	}
}

流しているジョブ自体が特に過負荷になる類のものでもないので、一応一通りのメトリクスとアプリが出力するログを取得するようにしています。
/var/log/messages は何となく取っています笑

結果

うまいこと取得できました。

resule

一応 CPU のアラームを設定しておきます。
見た感じピーク時も数%にも満たない感じなので、ほぼ意味ないですが笑

90bcfff4409d9e6dbff42d4a

今後もう少し有用な監視方法とアラームの設定を検討していく予定です。

参考

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-on-premise.html
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/create-iam-roles-for-cloudwatch-agent.html#create-iam-roles-for-cloudwatch-agent-users
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/create-cloudwatch-agent-configuration-file-wizard.html
https://qiita.com/murata-tomohide/items/3e66d63b21c08d6481a2
https://qiita.com/tokkun5552/items/38b4d678ccc3e2f796fa

Discussion