1. はじめに
1-1 ご挨拶
初めまして、井村と申します。
AWSのAmazon Linux 2023にCloudWatchエージェントをインストールしApache HTTPの監視設定を行いました。
Apache HTTPのプロセスとログをCloudWatchに転送します。
備忘録の記事になります。
1-2 対象読者
- AWSに興味がある
- 監視に興味がある
1-3 CloudWatch エージェントの適用方法について
AWS Systems Manager のパラメータストアに予め作成したCloudWatch エージェント設定ファイルをEC2のユーザーデータを用いて適用させます。
2. 構築
2-1 test.yaml構成
CloudFormationで各リソースを作成します。
test.yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: Simple Web Architecture
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Common"
Parameters:
- SystemName
- EnvType
- Label:
default: "EC2 Configuration"
Parameters:
- LastestAmiId
Parameters:
SystemName:
Description: "Enter System name"
Type: String
Default: "timura"
EnvType:
Description: "Select Environment type"
Type: String
Default: "dev"
AllowedValues:
- "dev"
- "prod"
LatestAmiId:
Type: AWS::SSM::Parameter::Value<String>
Default: "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
Mappings:
EnvConfig:
prod:
EC2InstanceType: t3.micro
EC2DiskSize: 20
RDSInstanceType: db.t3.micro
RDSStorageSize: 20
dev:
EC2InstanceType: t2.micro
EC2DiskSize: 20
RDSInstanceType: db.t3.micro
RDSStorageSize: 20
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-vpc
PublicSubnet01:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
AvailabilityZone: !Select [ 0, !GetAZs "" ]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-public-subnet01
PublicSubnet02:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
AvailabilityZone: !Select [ 1, !GetAZs "" ]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-public-subnet02
PrivateSubnet01:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.3.0/24
AvailabilityZone: !Select [ 0, !GetAZs "" ]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-private-subnet01
PrivateSubnet02:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.4.0/24
AvailabilityZone: !Select [ 1, !GetAZs "" ]
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-private-subnet02
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-igw
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
NatGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayEIP.AllocationId
SubnetId: !Ref PublicSubnet01
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-ngw
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-public-rtb
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-private-rtb
PublicRoute:
Type: AWS::EC2::Route
DependsOn: AttachGateway
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref InternetGateway
PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: '0.0.0.0/0'
NatGatewayId: !Ref NatGateway
PublicSubnet01RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet01
PublicSubnet02RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet02
PrivateSubnet01RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet01
PrivateSubnet02RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet02
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${SystemName}-${EnvType}-alb-sg
GroupDescription: "Allow http https from users"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-alb-sg
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${SystemName}-${EnvType}-ec2-sg
GroupDescription: "Allow http https from alb"
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-ec2-sg
EC2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${SystemName}-${EnvType}-ec2-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
- arn:aws:iam::aws:policy/CloudWatchAgentAdminPolicy
- arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
EC2Profile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub ${SystemName}-${EnvType}-ec2-profile
Roles:
- !Ref EC2Role
EC2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref LatestAmiId
InstanceType: !FindInMap [ EnvConfig, !Ref EnvType, EC2InstanceType ]
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeType: gp3
VolumeSize: !FindInMap [ EnvConfig, !Ref EnvType, EC2DiskSize ]
SecurityGroupIds:
- !Ref EC2SecurityGroup
SubnetId: !Ref PrivateSubnet01
IamInstanceProfile: !Ref EC2Profile
UserData:
"Fn::Base64": |
#!/bin/bash
# パッケージの最新化
dnf update -y
# パッケージのインストール
dnf install -y httpd
dnf install -y amazon-cloudwatch-agent
# サービスの起動
systemctl enable httpd
systemctl start httpd
systemctl enable amazon-cloudwatch-agent
# SSM パラメータから設定を読み込んで CloudWatch エージェントを起動する
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:AmazonCloudWatch-config-qiita-web -s
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-ec2
ALB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub ${SystemName}-${EnvType}-alb
Scheme: internet-facing
Type: application
Subnets:
- !Ref PublicSubnet01
- !Ref PublicSubnet02
SecurityGroups:
- !Ref ALBSecurityGroup
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-alb
ALBTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub ${SystemName}-${EnvType}-alb-tg
VpcId: !Ref VPC
TargetType: instance
Port: 80
Protocol: HTTP
Targets:
- Id: !Ref EC2Instance
Port: 80
HealthCheckProtocol: HTTP
Tags:
- Key: Name
Value: !Sub ${SystemName}-${EnvType}-alb-tg
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
LoadBalancerArn: !Ref ALB
Port: 80
Protocol: HTTP
以下がCloudWatch エージェントで必要な部分になります。
1行目はCloudWatch エージェントをインストールします。
2行目はAWS Systems Manager のパラメータストアから設定ファイルを読込みエージェントを起動させます。
AmazonCloudWatch-config-qiita-webはパラメータストアの名前になります。
dnf install -y amazon-cloudwatch-agent
/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:AmazonCloudWatch-config-qiita-web -s
# 以下はコマンドのヘルプになります。
sh-5.2$ amazon-cloudwatch-agent-ctl -help
usage: amazon-cloudwatch-agent-ctl -a
stop|start|status|fetch-config|append-config|remove-config|set-log-level
[-m ec2|onPremise|onPrem|auto]
[-c default|all|ssm:<parameter-store-name>|file:<file-path>]
[-s]
[-l INFO|DEBUG|WARN|ERROR|OFF]
e.g.
1. apply a SSM parameter store config on EC2 instance and restart the agent afterwards:
amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c ssm:AmazonCloudWatch-Config.json -s
2. append a local json config file on onPremise host and restart the agent afterwards:
amazon-cloudwatch-agent-ctl -a append-config -m onPremise -c file:/tmp/config.json -s
3. query agent status:
amazon-cloudwatch-agent-ctl -a status
-a: action
stop: stop the agent process.
start: start the agent process.
status: get the status of the agent process.
fetch-config: apply config for agent, followed by -c. Target config can be based on location (ssm parameter store name, file name), or 'default'.
append-config: append json config with the existing json configs if any, followed by -c. Target config can be based on the location (ssm parameter store name, file name), or 'default'.
remove-config: remove config for agent, followed by -c. Target config can be based on the location (ssm parameter store name, file name), or 'all'.
set-log-level: sets the log level, followed by -l to provide the level in all caps.
-m: mode
ec2: indicate this is on ec2 host.
onPremise, onPrem: indicate this is on onPremise host.
auto: use ec2 metadata to determine the environment, may not be accurate if ec2 metadata is not available for some reason on EC2.
-c: amazon-cloudwatch-agent configuration
default: default configuration for quick trial.
ssm:<parameter-store-name>: ssm parameter store name.
file:<file-path>: file path on the host.
all: all existing configs. Only apply to remove-config action.
-s: optionally restart after configuring the agent configuration
this parameter is used for 'fetch-config', 'append-config', 'remove-config' action only.
-l: log level to set the agent to INFO, DEBUG, WARN, ERROR, or OFF
this parameter is used for 'set-log-level' only.
2-2 CloudWatch エージェント設定ファイル
パラメータストアに格納するjsonファイルがCloudWatch エージェント設定ファイルになります。
AmazonCloudWatch-config-qiita-web
{
"agent": {
"metrics_collection_interval": 60
},
"metrics": {
"namespace": "AWS/EC2",
"append_dimensions": {
"InstanceId": "${aws:InstanceId}"
},
"metrics_collected": {
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": 60
},
"cpu": {
"measurement": [
"cpu_usage_iowait"
],
"metrics_collection_interval": 60
},
"procstat": [
{
"pattern": "httpd",
"measurement": [
"pid_count"
],
"metrics_collection_interval": 60
},
{
"pattern": "amazon-cloudwatch-agent",
"measurement": [
"pid_count"
],
"metrics_collection_interval": 60
}
]
}
},
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/httpd/error_log",
"log_group_name": "/var/log/httpd/error_log",
"log_stream_name": "{instance_id}",
"timezone": "Local",
"retention_in_days": 7
}
]
}
}
}
}
3. 確認
CloudFormationにてリソースを作成しました。CloudWatchにてメトリクス、ログを確認します。
3-1 プロセスの確認
EC2のコンソール画面からインスタンス IDをコピーし、CloudWatchのメトリクス画面にコピーしたインスタンス IDを貼り付けた結果が以下になります。
EC2からコマンドを用いてプロセスを確認します。
sh-5.2$ ps aux | grep httpd
root 23628 0.0 1.1 18380 10780 ? Ss 07:44 0:00 /usr/sbin/httpd -DFOREGROUND
apache 23922 0.0 0.3 18036 3556 ? S 07:44 0:00 /usr/sbin/httpd -DFOREGROUND
apache 23924 0.0 0.9 1250040 9604 ? Sl 07:44 0:00 /usr/sbin/httpd -DFOREGROUND
apache 23925 0.0 0.7 1086136 7700 ? Sl 07:44 0:00 /usr/sbin/httpd -DFOREGROUND
apache 23926 0.0 0.9 1086000 9596 ? Sl 07:44 0:00 /usr/sbin/httpd -DFOREGROUND
ssm-user 29624 0.0 0.0 604 4 pts/0 R+ 08:43 0:00 grep httpd
sh-5.2$
大丈夫ですね。
3-2 ログの確認
今回はエラーログをパラメータストアに設定しましたので確認します。
ロググループにてhttpと入力すると"/var/log/httpd/error_log"がヒットします。対象のEC2のインスタンス IDを選択した画面が以下になります。
ログが取得できています。
EC2からコマンドを用いてログを確認します。
sh-5.2$
sh-5.2$ sudo ls -l /var/log/httpd/
total 176
-rw-r--r--. 1 root root 45119 May 24 08:53 access_log
-rw-r--r--. 1 root root 74672 May 24 08:53 error_log
sh-5.2$
sh-5.2$ sudo tail /var/log/httpd/error_log
[Sat May 24 08:51:37.206600 2025] [autoindex:error] [pid 23926:tid 24101] [client 10.0.2.128:61720] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:51:43.672449 2025] [autoindex:error] [pid 23926:tid 24103] [client 10.0.1.26:31552] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:52:07.236589 2025] [autoindex:error] [pid 23924:tid 24078] [client 10.0.2.128:28056] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:52:13.699488 2025] [autoindex:error] [pid 23926:tid 24105] [client 10.0.1.26:58628] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:52:37.264202 2025] [autoindex:error] [pid 23926:tid 24107] [client 10.0.2.128:56494] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:52:43.730390 2025] [autoindex:error] [pid 23924:tid 24055] [client 10.0.1.26:19354] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:53:07.288319 2025] [autoindex:error] [pid 23926:tid 24109] [client 10.0.2.128:29636] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:53:13.759869 2025] [autoindex:error] [pid 23926:tid 24111] [client 10.0.1.26:1700] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:53:37.307834 2025] [autoindex:error] [pid 23926:tid 24112] [client 10.0.2.128:45472] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
[Sat May 24 08:53:43.790564 2025] [autoindex:error] [pid 23926:tid 24114] [client 10.0.1.26:30770] AH01276: Cannot serve directory /var/www/html/: No matching DirectoryIndex (index.html) found, and server-generated directory index forbidden by Options directive
大丈夫ですね。
4. 終わりに
本記事を最後まで読んで頂きましてありがとうございます。
CloudWatchエージェントを扱った案件に初めて参画したのでとても勉強になりました。