Amazon-CloudWatch

サーバーのモニタリングやってみた!

全般

AWS公式のハンズオンをやってみたという内容です。
CloudFormationを使用してEC2(WordPress),RDS,ALBを構築した後、CloudWatch、SNSを使用してアラートを通知する構成を構築します。

参考サイト

Amazon CloudWatchの概要と基本 AWS Black Belt

監視編 サーバーのモニタリング ハンズオン

構成

image.png
CloudWatchエージェント、WordPressをEC2にインストールします。

CloudFormation

AWS公式が作成したコードを使用して環境を構築します。
ファイルをサイトからダウンロードして少し編集してスタックを更新していきます。
※RDSのインスタンスクラスを「db.t2.micro」から「db.t3.micro」へ編集します。

AWSTemplateFormatVersion: "2010-09-09"
Description: AWS Hands-on for Beginners monitoing-1 template

Parameters:
  EC2InstanceType:
    Type: String
    Default: t2.micro
  EC2AMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2  
  RDSInstanceType:
    Type: String
    Default: db.t3.micro
  DBUser:
    Type: String
    Default: dbmaster
  DBPassword:
    Type: String
    Default: H&ppyHands0n
    NoEcho: true

Resources:
  CFnVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      InstanceTenancy: default
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-vpc

  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [0, !GetAZs ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [1, !GetAZs ]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet2

  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.2.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [0, !GetAZs ]
      Tags:
        - Key: Name
          Value: PrivateSubnet1

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.3.0/24
      VpcId: !Ref CFnVPC
      AvailabilityZone: !Select [1, !GetAZs ]
      Tags:
        - Key: Name
          Value: PrivateSubnet2

  CFnVPCIGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-igw

  CFnVPCIGWAttach:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref CFnVPCIGW
      VpcId: !Ref CFnVPC

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref CFnVPC
      Tags:
        - Key: Name
          Value: Public Route

  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref CFnVPCIGW

  PublicSubnet1Assoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  PublicSubnet2Assoc:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  EC2WebServer01:
    Type: AWS::EC2::Instance
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-WebServer01
      ImageId: !Ref EC2AMI
      InstanceType: !Ref EC2InstanceType
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: '0'
          SubnetId: !Ref PublicSubnet1
          GroupSet:
            - !Ref SecurityGroupWebServer
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y
        amazon-linux-extras install php7.2 -y
        yum -y install mysql httpd php-mbstring php-xml

        wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
        tar zxvf /tmp/latest-ja.tar.gz -C /tmp
        cp -r /tmp/wordpress/* /var/www/html/
        touch /var/www/html/.check_alive
        chown apache:apache -R /var/www/html

        systemctl enable httpd.service
        systemctl start httpd.service

        wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm -P /tmp/
        sudo rpm -U /tmp/amazon-cloudwatch-agent.rpm
        mv /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/default /home/ec2-user/
        wget https://raw.githubusercontent.com/nikuyoshi/aws-monitoring-hands-on-1/master/amazon-cloudwatch-agent.json -P /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/
        sudo systemctl restart amazon-cloudwatch-agent
      IamInstanceProfile: !Ref IAMInstanceProfile
    DependsOn: IAMInstanceProfile

  EC2WebServer02:
    Type: AWS::EC2::Instance
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-WebServer02
      ImageId: !Ref EC2AMI
      InstanceType: !Ref EC2InstanceType
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: '0'
          SubnetId: !Ref PublicSubnet2
          GroupSet:
            - !Ref SecurityGroupWebServer
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y
        amazon-linux-extras install php7.2 -y
        yum -y install mysql httpd php-mbstring php-xml

        wget http://ja.wordpress.org/latest-ja.tar.gz -P /tmp/
        tar zxvf /tmp/latest-ja.tar.gz -C /tmp
        cp -r /tmp/wordpress/* /var/www/html/
        touch /var/www/html/.check_alive
        chown apache:apache -R /var/www/html

        systemctl enable httpd.service
        systemctl start httpd.service

        wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm -P /tmp/
        sudo rpm -U /tmp/amazon-cloudwatch-agent.rpm
        mv /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/default /home/ec2-user/
        wget https://raw.githubusercontent.com/nikuyoshi/aws-monitoring-hands-on-1/master/amazon-cloudwatch-agent.json -P /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/
        sudo systemctl restart amazon-cloudwatch-agent
      IamInstanceProfile: !Ref IAMInstanceProfile
    DependsOn: IAMInstanceProfile

  SecurityGroupWebServer:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      VpcId: !Ref CFnVPC
      GroupName: !Sub ${AWS::StackName}-WebServer
      GroupDescription: !Sub ${AWS::StackName}-WebServer
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-WebServer
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

  Monitoring1EC2RoleforSSM:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Action: 'sts:AssumeRole'
          Effect: Allow
          Principal:
            Service: ec2.amazonaws.com
      Description: Allows EC2 instances to call AWS services on your behalf.
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
      - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
      MaxSessionDuration: 3600
      Path: "/"
      RoleName: Monitoring1EC2RoleforSSM

  IAMInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles: [ !Ref Monitoring1EC2RoleforSSM ]
      Path: "/"

  FrontLB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${AWS::StackName}-elb
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      SecurityGroups: 
        - !Ref SecurityGroupLB

  FrontLBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref FrontLB
      Port: 80
      Protocol: HTTP 
      DefaultActions: 
        - Type: forward
          TargetGroupArn: !Ref FrontLBTargetGroup

  FrontLBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-tg
      VpcId: !Ref CFnVPC
      Port: 80
      Protocol: HTTP
      HealthCheckPath: /.check_alive
      Targets:
        - Id: !Ref EC2WebServer01
        - Id: !Ref EC2WebServer02

  SecurityGroupLB:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      VpcId: !Ref CFnVPC
      GroupName: !Sub ${AWS::StackName}-lb-security-group
      GroupDescription: !Sub ${AWS::StackName}-lb-security-group
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-lb-security-group
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

  DBInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Delete
    Properties:
      DBInstanceIdentifier: !Sub ${AWS::StackName}-rds
      DBName: wordpress
      Engine: MySQL
      MasterUsername: !Ref DBUser
      MasterUserPassword: !Ref DBPassword
      DBInstanceClass: !Ref RDSInstanceType
      StorageType: gp2
      AllocatedStorage: '20'
      BackupRetentionPeriod: 0
      DBSubnetGroupName: !Ref DBSubnetGroup
      MultiAZ: true
      VPCSecurityGroups:
        - !Ref SecurityGroupDB

  DBSubnetGroup: 
    Type: AWS::RDS::DBSubnetGroup
    Properties: 
      DBSubnetGroupDescription: DB Subnet Group for Private Subnet
      SubnetIds: 
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2

  SecurityGroupDB:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      VpcId: !Ref CFnVPC
      GroupName: !Sub ${AWS::StackName}-MySQL
      GroupDescription: !Sub ${AWS::StackName}-MySQL
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-MySQL
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          CidrIp: 10.0.0.0/16

Outputs:
  VPCID:
    Description: VPC ID
    Value: !Ref CFnVPC

  PublicSubnet1:
    Description: PublicSubnet 1
    Value: !Ref PublicSubnet1

  PublicSubnet2:
    Description: PublicSubnet 2
    Value: !Ref PublicSubnet2

  PrivateSubnet1:
    Description: PrivateSubnet 1
    Value: !Ref PrivateSubnet1

  PrivateSubnet2:
    Description: PrivateSubnet 2
    Value: !Ref PrivateSubnet2

  EC2WebServer01:
    Value: !Ref EC2WebServer01

  EC2WebServer01DNS:
    Value: !GetAtt EC2WebServer01.PublicDnsName

  EC2WebServer02:
    Value: !Ref EC2WebServer02

  EC2WebServer02DNS:
    Value: !GetAtt EC2WebServer02.PublicDnsName

  FrontLBEndpoint:
    Value: !GetAtt FrontLB.DNSName

  RDSEndpointAddress:
    Value: !GetAtt DBInstance.Endpoint.Address

スタックを作成するためファイルをアップロードします。
image.png
image.png
任意にスタック名を決め、パラメータは表示されたもののまま進めます。
image.png
あとはデフォルトでスタックを作成しましょう。
作成完了まで10分ほど待ちます。

ここでCloudWatchの構成を改めて確認します。
image.png
SNSを使用して通知を飛ばしたいと考えています。

インストールしたWordPressを開きます。
CloudFormationの画面の出力タブから、webサーバー2台のDNS名からWordPressに接続していきます。
(ハンズオンに従って設定します。)
データベース名:wordpress
ユーザー名:dbmaster
パスワード:H&ppyHands0n
データベースのホスト名:データベースのエンドポイント

サイトのタイトル: Monitoring #1
ユーザー名: admin
パスワード: 出力された文字列をコピーしておきましょう。
メールアドレス: username@example.com
検索エンジンでの表示: チェックを入れる。

image.png

事象に応じた対応

・サーバーが過負荷状態
・サービスが使えない。
⇒リソース監視(CPU、メモリ使用率を確認)

メトリクスを見る場合は、リージョン毎に管理されたメトリクスの各インスタンス(ディメンション)にアクセスして確認していきます。
今回はエージェントをインストールしているので確認できる。

CloudWatch Metrics

CloudWatch⇒メトリクス⇒すべてのメトリクス⇒EC2
image.png

CPUを検索窓から検索

image.png
image.png

タイムゾーンを設定できます。
image.png
ネットワークのIN,OUTを見てみる。
image.png

CloudWatch⇒メトリクス⇒すべてのメトリクス⇒ELB⇒AppELB 別、TG 別メトリクス
image.png

CloudWatch⇒メトリクス⇒すべてのメトリクス⇒RDS⇒ADBInstanceIdentifier
読み書きをチェック
RDSはデフォルトで1分間隔でデータを取得
image.png
image.png
オプションから数値で見ることもできる。
image.png

CloudWatch⇒メトリクス⇒すべてのメトリクス⇒CWAgent⇒ImageId,InstanceId,InstanceType,device,fstype,path

ディスクの使用率を確認することができる。
image.png

CloudWatch⇒メトリクス⇒すべてのメトリクス⇒CWAgent⇒ImageId,InstanceId,InstanceType
メモリの使用率を確認することができる。
image.png

CloudWatch Alarm

ここでできるアクション例

  1. SNSで通知
  2. Server再起動や停止
  3. Auto Scaling
  4. Lambdaでコードを実行

今回はSNSで通知を送るためアラームを作成します。
image.png
「ディスク使用率90%を超えた場合」に通知を送る設定を行う。
メトリクスの選択
image.png
CWAgent⇒ImageId,InstanceId,InstanceType,device,fstype,path⇒
disk_used_percent
メトリクスの選択をクリック
image.png

ここで細部アラームの発報基準を決めていく。
image.png
今回は「静的」を選択して90%以上を示した時に発報。「異常検知」を選択した場合、これまでの履歴によって、ある基準を検知した時に発報するような設定にもできる。
image.png
次のような振る舞いもあるようです。
image.png
しきい値を超えた時にSNSトピックによってメールが欲しいので、設定していきます。
image.png
image.png
これでトピックの作成をクリック。
SNSのセットアップ用のメールが届きました。このリンクを踏んで設定を完了させます。
image.png
ここでLambda、EC2の振る舞いを決めることができるようです。
image.png
image.png
image.png

CloudWatch Logs

多くの台数を管理する際に1台1台確認するのは非効率であるため、ログを集約する機能を使用する。
ロググループを確認するとCloudFormationで作成されたロググループが表示され、ログの保持期間を設定できる。
デフォルトは失効しない。となっているので変更する。
image.png
作成されたロググループのaccessログを確認すると次の画面になり、文字列で検索する機能が存在する。
image.png
検索条件も「カスタム」から設定可能です。
image.png
CloudFormation内でwgetツールを使用し、S3から設定ファイルをダウンロードしますが、今回はカスタムのAmazon CloudWatch Agent設定ファイルを使用しています(CFnのコード内に記載済み。)。
次は参考としてウィザードを表示してエージェントを設定するドキュメントです。
CloudWatch エージェント設定ファイルを作成

CloudWatch Logs Insights

ログ分析エラーの根本原因を知るためにログをパース(非構造化データから特定の情報を抽出する)したりや、エラー発生時刻を調べたりなど、クエリを実行して、限定して表示させる。

image.png

「ログのインサイト」をクリックして試してみる。
どうやらこのリンクにクエリについて解説されているようです。

Amazon CloudWatch | Amazon Web Services(YouTube)

know-howの欠片が落ちています。。

CloudWatch Logs Insights クエリ構文

parseしてみる。まずは公式が示すクエリを実行

parse '* - * [*] "* * *" * *' as host, identity, dateTimeString, httpVerb, url, protocol, statusCode, bytes
| stats max(bytes) as maxBytes, avg(bytes) by url
| sort maxBytes desc

image.png

解説者が示すknow-howたっぷりのクエリを実行してみる。

parse '* - * [*] "* * *" * *' as host, identity, dateTimeString, httpVerb, url, protocol, statusCode, bytes
| sort maxBytes desc

分析したくなるような見た目になりました!!
image.png

次にここからHTTPのstatusCodeが400番台のレコードを確認していきます。

parse '* - * [*] "* * *" * *' as host, identity, dateTimeString, httpVerb, url, protocol, statusCode, bytes
| filter statusCode like /(4\d\d)/

image.png

CloudWatch Dashboards

横断的にステータスを確認できる。
サービスのメトリクスをまとめて一個の画面に表示できる。
image.png

EC2のCPU使用率を表示させるために「線」を選択
image.png
両方のEC2を追加する。
image.png
次はテキストで非常時のショートカットボタンを作成できる。
image.png
image.png
image.png
WordPressのaccesslogを表示する。
image.png
image.png
例えばエラー抽出する設定、また500番台を抽出する設定にしたログを表示するなどできる。
image.png
ダッシュボードの保存をクリックして完成。

自動で作成するダッシュボードも存在する。
次のサービスを検索する。
「AWS Resource Groups」
image.png
リソースグループを「タグベース」か「CloudFormation」で選択できる。
今回はCloudFormationで作成しているので、CloudFormationを選択
image.png
image.png
CloudWatch画面に戻り、サイドバーのCloudWatchボタンをクリックし、EC2を例にリソースグループで絞って表示させてみます。
CloudWatchボタン⇒概要⇒サービスダッシュボード⇒EC2⇒作成したリソースグループを選択
image.png
RDS
image.png
その他、様々な表示条件によって任意にカスタムできます。
サービス間ダッシュボードにし、リソースグループで限定すると、EC2とRDSがサービスの垣根を越えて表示させることができます。
image.png

次にすべてのサービスを表示させたい場合は、リソースグループを指定せず、サービス間ダッシュボードを選択することで、ELBやSNSなども一緒に表示させることができます。
image.png

EventBridge(CloudWatch Events)

起きた事象に応じて、トリガー役として機能。
イベントを収集し、Lambda関数、Step Functions、SQSキュー、SNSトピック、Kinesisストリームなどのターゲットにルーティングして動かすことが可能。
Cronのように定時実行設定も可能

今回はEC2が停止した場合、SNS通知を飛ばす設定にする。
image.png
EC2の状態変化があった場合を想定
aws.ec2@EC2InstanceStateChangeNotificationを選択
image.png
image.png
image.png
image.png
image.png
image.png
これでルールができたので、EC2を停止させ、通知が来るか確認します。
image.png
通知が問題なく来ていることが確認できました!
ここまででハンズオンは終了です。

後片付け

  1. CloudFormationのスタックを削除
  2. CloudWatch Dashboardsを削除
  3. CloudWatch Alarmを削除
  4. CloudWatch Logsを削除
  5. EventBridge(CloudWatch Events)を削除
  6. SNSトピックを削除
  7. S3バケットを削除
  8. Resource Groups

お疲れ様でした!
では!

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA