概要
やりたいこと
- EC2 のメモリ使用率・ディスク使用率などを CloudWatch で監視したい
- 各種サービスやアプリケーションが出力するログも CloudWatch に送信したい
- ただし管理コンソールや AWS Systems Manager を使わないで、Ansible と CloudFormation だけで構成・更新したい
上記 3. で試行錯誤したので、備忘録を記事にしました。
環境
- Ansible 2.9 と 2.10 で動作しました。3.x は未使用です
- Amazon Linux 2 を使用します
予備知識
まず CloudWatch の全体像について、以下の動画で勉強させていただきました。
- 【AWS】CloudWatch メトリクスを中心に6つの観点から徹底解説!|くろかわこうへい【渋谷で働いてたクラウドエンジニアTV】
- 【AWS Black Belt Online Seminar】Amazon CloudWatch|Amazon Web Services Japan 公式
CloudFormation で EC2 に IAM Role をアタッチする
監視対象の EC2 インスタンスが CloudWatch にメトリクスやログを送信するためには、適切な IAM ロールが必要です。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
MyEc2KeyName:
Type: "AWS::EC2::KeyPair::KeyName"
Resources:
# CloudWatchに送信するためのIAMロール定義
MyIamRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "ec2.amazonaws.com"
Action:
- "sts:AssumeRole"
Path: "/"
# EC2に適用したい管理ポリシーのARNをここに列挙する
ManagedPolicyArns:
- arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
# 上で定義したIAMロールをEC2インスタンスに適用するために
# IAMインスタンスプロファイルを定義する
MyInstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Path: "/"
# 上で定義したIAMロールを関連付ける
# 注意: 1つのEC2インスタンスに関連付けられるIAMロールは1つだけ
Roles:
- !Ref MyIamRole
# 監視対象のEC2インスタンス
MyEc2Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: ami-00d101850e971728d
InstanceType: t2.micro
KeyName: !Ref MyEc2KeyName
# 上で定義したIAMインスタンスプロファイルをアタッチする
IamInstanceProfile: !Ref MyInstanceProfile
EC2 インスタンス、IAM インスタンスプロファイル、IAM ロール、管理ポリシーという 4 種類のリソースの関係が分かりにくいのでクラス図を作ってみたら、下図のようになりました。
InstanceProfile の Roles プロパティはリスト型なので、複数の IAM ロールを指定できるように見えますが、「EC2 インスタンスに同時に関連付けられるロールは 1 つだけ」という成約があるため、結果的に関連付けられる IAM ロールは 1 つだけになります。 → 参考: AWS::IAM::InstanceProfile - AWS CloudFormation
IAM ロールと管理ポリシー
適用したい管理ポリシーの ARN を、AWS::IAM::Role
の ManagedPolicyArns
プロパティに列挙します。
上記の arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
は、メトリクスやログを CloudWatch に送信するために必要な管理ポリシーです。
これ以外にもアタッチしたい管理ポリシーがあれば、ここに並べて指定します。
例えば S3 へのアクセスが必要なら arn:aws:iam::aws:policy/AmazonS3FullAccess
などを追加します。
参考: 管理ポリシーとインラインポリシー|AWS ユーザーガイド
管理ポリシーの ARN が分からない場合は、AWS 管理コンソールの「サービス」→ 「IAM」→「ポリシー」に名前の一部を入力すれば簡単に見つけることができます。
下図は「s3」でポリシーを絞り込んで「AmazonS3FullAccess」を選択し、表示された ARN をクリップボードにコピーする例です。
IAM インスタンスプロファイル
IAM ロールを直接 EC2 インスタンスにアタッチすることができないので、IAM ロールを関連付けた IAM インスタンスプロファイルをアタッチすることで、間接的に IAM ロールを EC2 インスタンスに関連付けます。
ただし関連付けられる IAM ロールは 1 つだけです。
EC2 インスタンス
上で定義した IAM インスタンスプロファイルを、監視対象の AWS::EC2::Instance
の IamInstanceProfile
プロパティにアタッチします。
Ansible で統合 CloudWatch Agent を構成する
CloudWatch Agent 用の Ansible ロールを作成します。
roles/
├── cloudwatch_agent/
│ ├── handlers/
│ │ └── main.yml
│ ├── tasks/
│ │ └── main.yml
│ └── templates/
│ └── config.json.j2
テンプレート(設定ファイル)
Gunicorn と Nginx のログ、およびディスクとメモリの使用率を収集して CloudWatch に送信するための定義の例です。
{
"agent": {
"metrics_collection_interval": 60
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/gunicorn/access.log*",
"log_group_name": "gunicorn.access",
"log_stream_name": "{local_hostname}"
},
{
"file_path": "/var/log/gunicorn/error.log*",
"log_group_name": "gunicorn.error",
"log_stream_name": "{local_hostname}"
},
{
"file_path": "/var/log/nginx/access.log*",
"log_group_name": "nginx.access",
"log_stream_name": "{local_hostname}"
},
{
"file_path": "/var/log/nginx/error.log*",
"log_group_name": "nginx.error",
"log_stream_name": "{local_hostname}"
}
]
}
}
},
"metrics": {
"append_dimensions": {
"AutoScalingGroupName": "${aws:AutoScalingGroupName}",
"ImageId": "${aws:ImageId}",
"InstanceId": "${aws:InstanceId}",
"InstanceType": "${aws:InstanceType}"
},
"metrics_collected": {
"collectd": {
"metrics_aggregation_interval": 60
},
"disk": {
"measurement": [
"used_percent"
],
"metrics_collection_interval": {{ cw_interval_default }},
"resources": [
"*"
]
},
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": {{ cw_interval_default }}
},
"statsd": {
"metrics_aggregation_interval": 60,
"metrics_collection_interval": 10,
"service_address": ":8125"
}
}
}
}
紛らわしいですが、Ansible (Jinja2) のプレースホルダーは {{ cw_interval_default }}
だけで、{local_hostname}
と ${aws: … }
は CloudWatch Agent が使用するプレースホルダーと変数です。
agent セクション
CloudWatch Agent の全体的な設定です。
"run_as_user":
オプションを追加して実行ユーザーを指定することができます。省略すると root で実行されます。
logs セクション
収集するログファイルの設定です。
"file_path":
オプションでログファイルのパスを指定します。*
(アスタリスク)と **
(スーパーアスタリスク)を使用することができます。
agent セクションで root 以外の実行ユーザーを指定した場合は、読み取り権限のないログファイルを収集することができなくなります。
metrics セクション
カスタムメトリクスの定義と収集の設定です。
内部で使用する collectd と StatsD という 2 つのサービスの設定を含みます。
"namespace":
オプションでメトリクスの名前空間名を指定することができます。省略すると CWAgent
になります。
設定ファイルを編集するための詳細情報は、以下のページを参照にさせていただきました。
ウィザードを使用すれば、質問に答えるだけの対話方式で設定ファイルを作成することができます。以下を参考にさせていただきました。
- ウィザードを使用して CloudWatch エージェント設定ファイルを作成する|AWS ユーザーガイド
- CloudWatch で EC2 のメモリ・ディスク使用率を監視する|Wedding Park ブログ
- 新しいCloudWatch Agentでメトリクスとログの収集が行なえます|DevelopersIO
タスク
Amazon Linux 2 に CloudWatch Agent をインストールして、上記テンプレートの設定ファイルを配置する例です。
- name: Install or update Amazon packages
yum:
name:
- amazon-cloudwatch-agent
- amazon-linux-extras
- amazon-linux-extras-yum-plugin
state: latest
- name: Install collectd from Amazon Extras Library
shell: amazon-linux-extras install collectd
changed_when: false
- name: Enable and start collectd service
service:
name: collectd
enabled: yes
state: started
- name: Enable and start amazon-cloudwatch-agent service
service:
name: amazon-cloudwatch-agent
enabled: yes
state: started
- name: Deploy CloudWatch Agent configuration file from templates
template:
dest: "/opt/aws/amazon-cloudwatch-agent/bin/config.json"
src: "{{ role_path }}/templates/config.json.j2"
notify:
- "cloudwatch agent config changed"
2 番めの shell モジュールを使った collectd のインストールは、AWS 公式ドキュメントに合わせて amazon-linux-extras コマンドを使用していますが、yum でインストールしても問題ないと思います。
3 番目と 4 番目の service モジュールで各サービスを起動して自動起動に設定します。
最後に template モジュールで設定ファイルを /opt/aws/amazon-cloudwatch-agent/bin/
に配置し、ファイルに変更があればハンドラを実行します。
設定ファイルを /opt/aws/amazon-cloudwatch-agent/etc/
に配置して Playbook を実行するとファイルが削除されてしまうため、設定ファイルに変更がなくても changed
になってハンドラが実行されてしまいました。
これを避けるために、ウィザードの出力先と同じ bin/
に設定ファイルを配置するようにしました。
ハンドラ
設定ファイルをの変更をトリガーとして実行されます。
- name: Fetch configuraton file and restart CloudWatch Agent
shell: ./amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:config.json -s
args:
chdir: "/opt/aws/amazon-cloudwatch-agent/bin/"
listen: "cloudwatch agent config changed"
amazon-cloudwatch-agent-ctl
コマンドを実行して、設定ファイルの定義を CloudWatch Agent に反映します。
-
-a
オプションで実行するアクション(fetch-config)を指定 -
-c
オプションで設定ファイルのパスを指定 -
-s
オプションでサービスを再起動して、設定ファイルの内容を反映します
amazon-cloudwatch-agent-ctl
コマンドについては以下を参照してください。
Playbook
以上のロールを既存の Playbook に追記するか、以下のように Playbook を作成して ansible-playbook
コマンドで実行します。
- hosts: all
become: yes
roles:
- role: cloudwatch_agent
テンプレートで使用する変数は、どこかで cw_interval_default = 60
のように定義されているものとします。
確認
Playbook が完走すると、EC2 インスタンスがメトリクスとログを CloudWatch に送信し始めるので、AWS 管理コンソールにログインして確認することができます。
カスタムメトリクス
送信されたカスタムメトリクス名が CloudWatch → メトリクス → エクスプローラー → メトリクス に表示され、選択することができます。
ログ
送信されたロググループ名が CloudWatch → ログ → ロググループ に表示され、選択すると各 EC2 インスタンスの一覧が表示され、さらにインスタンスを選択するとログの内容が表示されます。
トラブルシューティング
意図したとおりに動作しない場合は、SSH でインスタンスにログインして、以下を確認してみてください。
- まず
/opt/aws/amazon-cloudwatch-agent/
に移動する - Ansible で配置した
bin/config.json
が存在するか、内容が意図したとおりかを確認する -
etc/amazon-cloudwatch-agent.toml
(実行時に生成される)の内容がconfig.json
の内容と一致するを確認する -
logs/
内のログを読む - SSH 端末から手動で
bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:bin/config.json -s
を実行して、出力されるメッセージを読む -
bin/amazon-cloudwatch-agent-ctl -a status
を実行して、CloudWatch Agent のサービスの動作状態を確認する