[アドカレ2024]Amazon CloudWatch+Zabbixダッシュボード作成してみた!(part1)

はじめに

このような機会をいただけたので、書きためていたダッシュボード作ってみたと題して記事を投稿します。
AWS大阪オフィスで開催された「俺たち私たちのダッシュボード選手権(社内イベント)」に参加した時のものですが、設計構築する上で必要な知識経験を身に着ける良い機会でした。
CloudWatchの長所と、Zabbixの長所を掛け合わせて、機能性抜群かつ安いダッシュボードを作成することができたので、是非試しに構築してみてください。

参考資料:

Zabbix公式ドキュメント

jq関連で参考にしたサイト

余計なお節介

パスワードを多く入力するので、設定時は書き留めるなど、注意が必要です。
また、目隠ししていない箇所もありますが、既に環境は存在しませんのでご安心ください。

作る構成は次の画像のとおりです。EC2(Zabbixサーバ)は、自身を監視します。ZabbixエージェントやCloudWatchエージェントをインストールしています。
定時監視を行って、異状があればSNSによって通知を送付する魂胆です。

image.png

完成図

1.パット見でディスク状況を見たい!
image.png

2.アクセス、エラーログを参考に表示!
image.png

3.RUMでユーザーセッション収集!(part2の記事で記載します!)
image.png

Zabbixサーバ構築

(Zabbixサーバ自身を監視します。)

  • AmazonLinux2を使用します。
  • パブリックサブネットに配置し、EIPをアタッチします。
  • セキュリティグループは、SSH(EC2 Instance Connectから作業したりします。)、httpsを開けます。

ホストを確認しておきます。

uname -n

IPアドレスを確認しておきます。

curl ifconfig.io

ホストなどを確認したら作業を開始!!

rootで作業します。

sudo su -

タイムゾーンを設定します。

timedatectl set-timezone Asia/Tokyo

設定できたか確認します。

timedatectl status

ホスト名を「zabbix(任意)」にします。

hostnamectl set-hostname zabbix

ホスト名を確認します。

hostname

ここで再起動します。

reboot

php7.2を有効にします。

amazon-linux-extras enable php7.2

phpインストール

yum install php

mariaDBインストール

yum install mariadb mariadb-server

mariaDBを起動します。

systemctl start mariadb

ステータスを確認します。

systemctl status mariadb

サーバ起動時にmariaDBが起動するように設定します。

systemctl enable mariadb

対話形式でパスワード設定などを行います。

mysql_secure_installation

初期はパスワードなしで設定を進めるので、次の表示はそのまま空欄で進めます。
Enter current password for root (enter for none):
Set root password? [Y/n]
ここは「y」を入力して、pwを設定します。
以降全て「y」で大丈夫です。

Apacheインストールします。

yum install httpd

Apacheを起動します。

systemctl start httpd

ステータスを確認します。

systemctl status httpd

サーバ起動時にApacheが起動するように設定します。

systemctl enable httpd

この辺りからミソとなりますので、面倒ですが細かく設定します。(現状のAmazon Linux2に限ります。)
去年くらいまで使えていたリポジトリ(現在202409)が使用できなくなっているので、次の設定を参考にしてください!

リポジトリの追加

rpm -Uvh https://repo.zabbix.com/zabbix/5.0/rhel/7/x86_64/zabbix-release-5.0-1.el7.noarch.rpm

次の箇所を有効化します。

vi /etc/yum.repos.d/zabbix.repo

[zabbix-frontend]
enable=1へ!!

Software Collectionsのパッケージをインストールします。

yum install -y https://vault.centos.org/7.9.2009/extras/x86_64/Packages/centos-release-scl-rh-2-3.el7.centos.noarch.rpm

次のとおり編集します。

vi /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo

【コメントアウト】
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=sclo-rh
【追記】
baseurl=http://vault.centos.org/centos/7/sclo/x86_64/rh/
Zabbixサーバ、Zabbixエージェント、WEBインターフェイスをインストールします。

yum install zabbix-server-mysql zabbix-agent zabbix-web-japanese.noarch zabbix-web-mysql-scl zabbix-apache-conf-scl

起動します。

systemctl start zabbix-server zabbix-agent

ステータスを確認します。

systemctl status zabbix-server zabbix-agent

サーバ起動時にZabbixサーバ、エージェントが起動するように設定します。

systemctl enable zabbix-server zabbix-agent

mariaDBにログインします。

mysql -uroot -p

最低限の設定(DB作成など。)を行います。
(各種設定値が多く存在するので、あえてパスワードを残します。)

create database zabbix character set utf8 collate utf8_bin;
create user zabbix@localhost identified by 'abcdefghijk123';
grant all privileges on zabbix.* to zabbix@localhost;
quit;

作成したデータベースにZabbixの初期スキーマとデータをインポートします。
以下のコマンド実行後に上記で設定したZabbixユーザーのパスワードが求められますので入力します。

zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix

datetimeの箇所をAsia/Tokyoへ。

vi /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf

データベース名やユーザー名とパスワードを設定します。

vi /etc/zabbix/zabbix_server.conf

次はパスワード及びパスワード以外にチェックする箇所を記載しています。

...
DBName=zabbix
...
DBUser=zabbix
...
DBPassword=[zabbixユーザー(DB上)のパスワードを入力]
...

PHP-FPMもサーバ起動時に起動するように設定します。

systemctl enable rh-php72-php-fpm

Zabbixサーバ、Zabbixエージェント、PHP-FPMを再起動します。

systemctl restart zabbix-server zabbix-agent rh-php72-php-fpm httpd

ステータスを確認します。

systemctl status zabbix-server zabbix-agent rh-php72-php-fpm httpd

この設定は必ず行う必要があります。

vi /etc/php.ini

phpで東京時間に設定
/date.timezoneで検索し"Asia/Tokyo"を追記(ダブルクォート大事)

設定後、Apacheを再起動します。

systemctl restart httpd

ZabbixのWebインターフェイスに接続してみます。
次の箇所にIPアドレスを入力するだけで接続できます。

http://[Zabbix ServerのIP]/zabbix/

初回ログインは、ユーザー名「Admin(※先頭大文字!)」、パスワード「zabbix」でログインする。
特に決まりはありませんが次の値を参考までに記載します!
Host:localhost
Port:10051

CloudWatchエージェントをインストール

CloudWatchで様々なメトリクスを表示できるようにエージェントを先行的にインストールします。

yum install amazon-cloudwatch-agent

対象のEC2にポリシーをアタッチします。
CloudWatchAgentServerPolicy:CloudWatchエージェントのメトリクスとログの送信に必要なポリシー。

次のエージェントによってどのメトリクスを取得するか決めていきます。
記載するものと、記載方法は次の公式が作成したフルセットのJSONファイルを使用してそのまま動けばそれで良いですが、もし動かない場合などありましたら次のJSONを使用してみてください。

CloudWatch エージェント設定ファイルを手動で作成または編集する

JSONの成文用参考サイト

次の設定を作成します。

{
  "agent": {
    "metrics_collection_interval": 60,
    "logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log"
  },
  "metrics": {
    "namespace": "MyCustomNamespace",
    "metrics_collected": {
      "diskio": {
        "resources": [
          "*"
        ],
        "measurement": [
          "reads",
          "writes",
          "read_time",
          "write_time",
          "io_time"
        ],
        "metrics_collection_interval": 60
      },
      "disk": {
        "measurement": [
          "used_percent",
          "used",
          "free",
          "total"
        ],
        "metrics_collection_interval": 60,
        "resources": [
          "/"
        ]
      },
      "mem": {
        "measurement": [
          "mem_used_percent"
        ],
        "metrics_collection_interval": 60
      },
      "swap": {
        "measurement": [
          "swap_used_percent",
          "swap_free"
        ],
        "metrics_collection_interval": 60
      },
      "http_response": {
        "urls": [
          "http://52.68.98.111"
        ],
        "metrics_collection_interval": 60,
        "measurement": [
          "response_time",
          "status_code"
        ],
        "response_code": true,
        "response_time": true
      }
    }
  },
  "logs": {
    "logs_collected": {
      "files": {
        "collect_list": [
          {
            "file_path": "/var/log/messages",
            "log_group_name": "MyLogGroup",
            "log_stream_name": "{instance_id}",
            "timestamp_format": "%b %d %H:%M:%S"
          }
        ]
      }
    }
  }
}

ちなみに、viで設定ファイルを魔改造したあとは、次のコマンドで都度適用する必要がありますのでお節介しておきます。

・エージェントを適用します。

sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s

succeededと表示されることを確認しましょう!

・エージェントを再起動します。

systemctl restart amazon-cloudwatch-agent

ログローテートを設定します。

vi /etc/logrotate.d/amazon-cloudwatch-agent

次を記載します。

/opt/aws/amazon-cloudwatch-agent/logs/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 0640 root root
}

メトリクスを取得したいのでswap領域作成してみます。

容量を確認します。

df -h

swap領域の存在を確認します。(無いことを確認)

free -h

swap領域を作成します。

vi rc.local
SWAPFILENAME=/swapfile
SIZE=2097152
dd if=/dev/zero of=$SWAPFILENAME count=$SIZE bs=1K && mkswap $SWAPFILENAME && swapon $SWAPFILENAME

権限を確認します。

ls -la rc.local*

-rw-r--r--
実行権限を与えます。

chmod u+x rc.local

-rwxr--r--

ZabbixサーバにGo言語を使用するための環境を構築します。

Go言語をインストール

amazon-linux-extras install -y golang1.11

Golangがインストールされたか確認

go version

go version go1.22.5 linux/amd64こんな感じで表示されます。

GOPATH モードで進めてみて、問題があればexport GO111MODULE=on 又はexport GO111MODULE=autoの設定を行うことにしました。

Golangプロジェクトの作成

mkdir zabbix-cloudwatch-lambda
cd zabbix-cloudwatch-lambda

/home/ec2-user/zabbix-cloudwatch-lambda(現在地はココ)

プロジェクトを初期化

go mod init zabbix-cloudwatch-lambda

go: creating new go.mod: module zabbix-cloudwatch-lambda

Lambda関数設定とコードファイル作成

次のコードファイルを作成します。

vi main.go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudwatch"
)

func getZabbixData(apiURL, authToken string) ([]byte, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "item.get",
        "params": map[string]interface{}{
            "output": "extend",
            "hostids": "10084", // 環境変数または事前に取得したホストIDを使用
        },
        "auth": authToken,
        "id":   1,
    }
    payloadBytes, err := json.Marshal(payload)
    if err != nil {
        return nil, err
    }

    req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes))
    if err != nil {
        return nil, err
    }
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("failed to get data from Zabbix, status code: %d", resp.StatusCode)
    }

    return ioutil.ReadAll(resp.Body)
}

func putDataToCloudWatch(namespace, metricName string, value float64) error {
    sess := session.Must(session.NewSession())
    svc := cloudwatch.New(sess, aws.NewConfig().WithRegion("ap-northeast-1"))

    _, err := svc.PutMetricData(&cloudwatch.PutMetricDataInput{
        Namespace: aws.String(namespace),
        MetricData: []*cloudwatch.MetricDatum{
            {
                MetricName: aws.String(metricName),
                Unit:       aws.String("None"),
                Value:      aws.Float64(value),
                Timestamp:  aws.Time(time.Now()),
            },
        },
    })

    return err
}

func main() {
    apiURL := os.Getenv("ZABBIX_API_URL")
    authToken := os.Getenv("ZABBIX_API_TOKEN")

    data, err := getZabbixData(apiURL, authToken)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    // Zabbixデータから必要な値を抽出する処理
    // JSONレスポンスをデコードして、必要な値を抽出
    var result map[string]interface{}
    err = json.Unmarshal(data, &result)
    if err != nil {
        fmt.Println("Error decoding Zabbix data:", err)
        return
    }

    // 例: value := extractValueFromZabbixResponse(result)
    // 仮の値をCloudWatchに送信 (実際には抽出した値を使用)
    err = putDataToCloudWatch("ZabbixMetrics", "MiddlewareMetric", 123.45)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Data sent to CloudWatch")
}

※ホストIDを編集する。
ちなみにホストIDはWEBインターフェイスからでも参照可能。ログインした後、該当ホストを選択すると、ブラウザのURL欄に「例:http://サーバのIPアドレス/zabbix/hosts.php?form=update&hostid=10084
このように表示される。末尾の「10084」これ。その他のAZや、"123.45"ここの部分は編集の必要はありません。

依存パッケージをインストールする。

go get github.com/aws/aws-sdk-go/aws
go get github.com/aws/aws-sdk-go/aws/session
go get github.com/aws/aws-sdk-go/service/cloudwatch

bootstrapファイルを作成します。

vi bootstrap
#!/bin/sh
./main

Lambdaで使用できる形にGolangコードをビルドします。

GOOS=linux GOARCH=amd64 go build -o main

ZIPファイルにします。

zip function.zip main bootstrap

Lambda関数へのアップロード
マネジメントコンソールから
※Amazon Linux 2を選択。Go言語は非推奨となっているようですが早さを求めてGoを選択!

  • 一から作成

  • ランタイム
    Amazon Linux 2

  • アーキテクチャ
    x86_64

  • 基本的な Lambda アクセス権限で新しいロールを作成

  • コンソール上で、アップロード元のプルダウンから、「.zip」を選択

  • Zabbix APIのURLと認証トークンを設定
    環境変数タブに移動し、ZABBIX_API_URLとZABBIX_API_TOKENの環境変数を追加

  • key
    ZABBIX_API_URL
    ZABBIX_API_TOKEN

  • value
    http://IPアドレス/api_jsonrpc.php
    Zabbix APIで認証トークンを取得(ver5.0ではコンソールでの取得はできないようです。)
    Adminユーザーから、新規に作成したユーザー(権限)でログインしておきます。
    はじめにjq(JSON Query)をインストール
    一般にjqと呼ばれるもの。

yum install jq -y

ZabbixのWebインターフェイスから、ユーザーを作成し、ユーザーグループ”Zabbix administrators”に参加させ、表示/設定の権限を付与します(任意にグループを作成しちゃって大丈夫です。)。
ここでは、すべての権限を追加してあげます。
例:Zabbix administrators デフォルトでは、「権限:なし」これを、既存のホストグループ(Zabbix servers)を選択し、「表示/設定」にチェックを付けて、「追加」をクリックして追加します。最後に更新をクリックです。ここで「追加」ボタンをクリックすることを忘れがちなので注意です!

Zabbix APIにリクエストを送信して、jqを使って認証トークンを抽出します。

curl -X POST -H "Content-Type: application/json" -d '{
  "jsonrpc": "2.0",
  "method": "user.login",
  "params": {
      "user": "test-user(Zabbix側で作成したユーザー)",
      "password": "パスワード"
  },
  "id": 1
}' http://xxx.xxx.xxx.xxx(IPアドレス)/zabbix/api_jsonrpc.php | jq -r '.result'

これを実行することで、トークンが表示されます。
この取得したトークンをZABBIX_API_TOKENのvalueに貼り付けます。

Amazon EventBridgeの設定

  • 直接CloudWatchの画面から設定します。
    (細部はコンソールの説明などご確認ください。)

  • サイドバーのイベントの"ルール"から、ルールを作成

  • 任意の名前を付けて

  • スケジュールを選択

  • EventBridge Scheduler で続行

  • スケジュール名を決めて、定期的なスケジュール(スケジュールの種類は任意で)を決めます。

  • ターゲットの詳細

  • 以降Lambdaの設定及びEventBridgeの設定

Lambdaの画面から連携されている状態です。
image.png

EventBridgeのコンソールで作成できているか確認してみます。
image.png

CloudWatchダッシュボードを作ってみる。

簡単3ステップ!

  1. 最初に表示される画面では、どのような表示にしたいか選択します。
    image.png

  2. 赤枠内に対象のインスタンスIDを入力して検索すると表示させることができるメトリクスが表示されます。
    image.png
    (なお、ZabbixのAPIから取得したメトリクスもこのコンソールに表示されるようになります!)

  3. 画面右上のタイムゾーン設定、間隔を選択します。
    image.png

通信できているか確認

これまでのEventBridgeの設定、Lambdaの設定、コードが適正に機能しているか、CloudWatchダッシュボードから確認します。
画面のように、新たな表示(ZabbixMetrics)が追加されています。
image.png
すると、ディメンションなしのメトリクス(何もメトリクスなどを指定していません。)という表示があります。
image.png
次の画像のように表示されていることを確認します。
image.png
(縦軸は、Goのコードで設定した123.45を表示するための値です。)

値が取得できない場合などは次のコマンドでテストをしつつ、Amazon CloudWatch Logsで取得できたログを確認することで、解決に近づけます。
Zabbix APIからデータが正しく取得され、CloudWatchにもデータが送信されていることを確認します。
Amazon Linux2のCLIから次のコマンドを実行します。

export ZABBIX_API_URL="http://your-zabbix-server-ip/zabbix/api_jsonrpc.php"
export ZABBIX_API_TOKEN="your-zabbix-token"
go run main.go

正常な場合、多数の状態を表すログが出力されます。

また、Amazon CloudWatch Logsの中で、「Data sent to CloudWatch」この表示がある場合は、内容はともかく値をputできている状態のようです。

ApacheのメトリクスをZabbixで取得できるように設定する。

httpd -M 2>/dev/null | grep status_module

出力:status_module (shared)

sudo tee /etc/httpd/conf.d/server-status.conf <<EOF
<Location "/server-status">
  SetHandler server-status
  Require ip 127.0.0.1 ::1
</Location>
EOF
systemctl restart httpd
curl -I http://localhost/server-status
HTTP/1.1 200 OK
Date: Wed, 11 Sep 2024 09:02:46 GMT
Server: Apache/2.4.62 () PHP/7.2.34
Upgrade: h2,h2c
Connection: Upgrade
Content-Length: 6466
Content-Type: text/html; charset=ISO-8859-1

webインターフェイスから設定をすることで監視可能になります。
image.png

PHP-FPMのメトリクスをZabbixで取得できるように設定する。

sudo tee -a /etc/opt/rh/rh-php72/php-fpm.d/zabbix.conf <<EOF
pm.status_path = /status
ping.path = /ping
EOF

設定ファイルを編集するので、事前にコピーを取っておきます。

cp /etc/httpd/conf.d/zabbix.conf /etc/httpd/conf.d/zabbix.conf.20240912

PHP-FPMにProxyする設定

sudo tee -a /etc/httpd/conf.d/zabbix.conf <<EOF

<Location ~ ^/(status|ping)$>
  SetHandler "proxy:unix:/var/opt/rh/rh-php72/run/php-fpm/zabbix.sock|fcgi://localhost"
  Require ip 127.0.0.1 ::1
</Location>
EOF

設定を反映し、PHP-FPMのステータス情報にアクセスできることを確認します。
ステータスを確認

systemctl status rh-php72-php-fpm httpd

権限と所有権の設定

chown apache:apache /var/opt/rh/rh-php72/run/php-fpm/zabbix.sock
chmod 770 /var/opt/rh/rh-php72/run/php-fpm/zabbix.sock

リダイレクト先

chown -R apache:apache /usr/share/zabbix/
chmod -R 755 /usr/share/zabbix/

アクセスできているか確認します。

curl http://localhost/status

出力:

[root@zabbix etc]# `curl http://localhost/status`
pool:                 zabbix
process manager:      dynamic
start time:           12/Sep/2024:12:51:14 +0900
start since:          33
accepted conn:        16
listen queue:         0
max listen queue:     0
listen queue len:     0
idle processes:       5
active processes:     1
total processes:      6
max active processes: 4
max children reached: 0
slow requests:        0

困った時のエラーログたち

tail -f /var/log/httpd/error_log
tail -f /var/opt/rh/rh-php72/log/php-fpm/error.log

webインターフェイスでのPHP-FPMの設定

具体的にApacheをCloudWatchに表示させてみる。

次のコードに修正。
ユーザーのIDを追加する箇所の下にアイテムIDを追記したもの。

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "os"
    "strconv"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudwatch"
)

// Zabbix APIからデータを取得する関数
func getZabbixData(apiURL, authToken, itemKey string) ([]byte, error) {
    payload := map[string]interface{}{
        "jsonrpc": "2.0",
        "method":  "item.get",
        "params": map[string]interface{}{
            "output": "extend",
            "hostids": "10084", // ホストIDを指定
            "search": map[string]string{
                "key_": "apache.workers_total.busy", // Apacheの特定のメトリックキーを指定
            },
        },
        "auth": authToken,
        "id":   1,
    }
    payloadBytes, err := json.Marshal(payload)
    if err != nil {
        return nil, err
    }

    req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payloadBytes))
    if err != nil {
        return nil, err
    }
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("failed to get data from Zabbix, status code: %d", resp.StatusCode)
    }

    return ioutil.ReadAll(resp.Body)
}

// CloudWatchにデータを送信する関数
func putDataToCloudWatch(namespace, metricName string, value float64) error {
    sess := session.Must(session.NewSession())
    svc := cloudwatch.New(sess, aws.NewConfig().WithRegion("ap-northeast-1"))

    _, err := svc.PutMetricData(&cloudwatch.PutMetricDataInput{
        Namespace: aws.String(namespace),
        MetricData: []*cloudwatch.MetricDatum{
            {
                MetricName: aws.String(metricName),
                Unit:       aws.String("None"),
                Value:      aws.Float64(value),
                Timestamp:  aws.Time(time.Now()),
            },
        },
    })

    return err
}

// main関数
func main() {
    apiURL := os.Getenv("ZABBIX_API_URL")
    authToken := os.Getenv("ZABBIX_API_TOKEN")
    itemKey := "apache.workers_total.busy" // 例としてApacheのワーカー数のキー

    // Zabbix APIからデータを取得
    data, err := getZabbixData(apiURL, authToken, itemKey)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    // Zabbixデータをパース
    var result map[string]interface{}
    err = json.Unmarshal(data, &result)
    if err != nil {
        fmt.Println("Error decoding Zabbix data:", err)
        return
    }

    // 結果を確認して値を抽出
    fmt.Printf("Zabbix API result: %+v\n", result)

    // 実際の値を抽出
    var value float64
    if len(result["result"].([]interface{})) > 0 {
        firstItem := result["result"].([]interface{})[0].(map[string]interface{})
        lastValueStr := firstItem["lastvalue"].(string)  // Zabbixからのデータは文字列として返される
        value, err = strconv.ParseFloat(lastValueStr, 64) // float64に変換
        if err != nil {
            fmt.Println("Error converting lastvalue to float64:", err)
            return
        }
    } else {
        fmt.Println("No data found for the specified key")
        return
    }

    // CloudWatchにデータを送信
    err = putDataToCloudWatch("ZabbixMetrics", "ApacheTotalWorkersBusy", value)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    fmt.Println("Data sent to CloudWatch")
}

再びLambdaにアップロードし、Apacheの指定したメトリクスを表示することができた。

次にZabbixでMariaDBのメトリクスを取得するための設定をOSに行う。

現状のZabbixユーザーの権限を確認

mysql -u root -p
SHOW GRANTS FOR 'zabbix'@'localhost';

出力:

+---------------------------------------------------------------------------------------------------------------+
| Grants for zabbix@localhost                                                                                   |
+---------------------------------------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'zabbix'@'localhost' IDENTIFIED BY PASSWORD '*xxxxxxxxxx' |
| GRANT ALL PRIVILEGES ON `zabbix`.* TO 'zabbix'@'localhost'                                                    |
+---------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

メトリクスを取得する権限が不足

  • SELECT: データの読み取り。
  • PROCESS: サーバーのプロセス情報を取得(Zabbix監視用)。
  • REPLICATION CLIENT: レプリケーションステータスを確認。
    GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO 'zabbix'@'localhost';
    FLUSH PRIVILEGES;

認証するために使用する認証ファイルを作成

vi /var/lib/zabbix/.my.cnf

記載内容

[client]
user=zabbix
password=DBのパスワード
chown zabbix:zabbix /var/lib/zabbix/.my.cnf
chmod 600 /var/lib/zabbix/.my.cnf
vi /etc/zabbix/zabbix_agentd.d/userparameter_mysql.conf
UserParameter=mysql.ping[*], mysqladmin -h"$1" -P"$2" ping
UserParameter=mysql.get_status_variables[*], mysql -h"$1" -P"$2" -sNX -e "show global status"
UserParameter=mysql.version[*], mysqladmin -s -h"$1" -P"$2" version
UserParameter=mysql.db.discovery[*], mysql -h"$1" -P"$2" -sN -e "show databases"
UserParameter=mysql.dbsize[*], mysql -h"$1" -P"$2" -sN -e "SELECT SUM(DATA_LENGTH + INDEX_LENGTH) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='$3'"
UserParameter=mysql.replication.discovery[*], mysql -h"$1" -P"$2" -sNX -e "show slave status"
UserParameter=mysql.slave_status[*], mysql -h"$1" -P"$2" -sNX -e "show slave status"

Zabbixサーバ、Zabbixエージェント、PHP-FPMを再起動します。

systemctl restart zabbix-server zabbix-agent rh-php72-php-fpm httpd

ステータスを確認します。

systemctl status zabbix-server zabbix-agent rh-php72-php-fpm httpd
  • 次にwebインターフェイス(Admin)から設定を追加します。
    設定>ホスト>Template DB MySQL by Zabbix agentを選択して”更新”
    image.png

  • 作成したテンプレート(Template DB MySQL by Zabbix agent)を選択

  • マクロタブを選択し、値が次のとおりか確認し、更新します。
    {$MYSQL.HOST}:localhost,{$MYSQL.PORT}:3306

image.png

SELECTによって参照された状況を把握するために、「Bytes sent」を選択して負荷をかけてメトリクスの動きを確認してみます。既作成のDB内に仮にテーブル(100行)を作成し、SELECT文を繰り返し実行させることで少しだけ負荷を掛けます。

Zabbixユーザー又はrootでログインします。

mysql -u root -p

DBを選択します。

USE zabbix;

現状のテーブル名を確認しておきます。

SHOW TABLES;

テーブルを作成し、100行のレコードを作成します。
負荷を増やしたい場合は、行数を1000行にするなどします。

CREATE TABLE large_table (
    id INT AUTO_INCREMENT PRIMARY KEY,
    data VARCHAR(255)
);

INSERT INTO large_table (data)
SELECT REPEAT('sample_data', 50) FROM information_schema.tables LIMIT 100;

テーブル(large_table)が作成されたか確認します。

SHOW TABLES;

レコードが100行できたか確認します。

SELECT COUNT(*) FROM large_table;

実際に軽く負荷を掛けます。
ここで負荷を増やしたい場合は、10000などに編集しましょう。

SELECT BENCHMARK(1000, (SELECT id FROM large_table LIMIT 1));

実行した際の例:

+-------------------------------------------------------+
| BENCHMARK(1000, (SELECT id FROM large_table LIMIT 1)) |
+-------------------------------------------------------+
|                                                     0 |
+-------------------------------------------------------+

"0"が返ってきていると正常に実行できたということです。
そしてグラフを確認してみましょう。

他の監視メトリクスとして「Command Select per second」によって1秒あたりに実行されるSELECTクエリの回数を監視することができます。直感的に見やすいので、このメトリクスを使ってみたいと思います。

EventBridgeでのCron 式参考

Cron式の例(その時々に応じた適切な時間設定にしましょう!)

時間間隔 Cron式
1時間おき cron(0 * * * ? *)
1分おき cron(* * * * ? *)
5分おき cron(0/5 * * * ? *)
15分おき cron(0/15 * * * ? *)
30分おき cron(0/30 * * * ? *)

無視OK!Lambdaのコードをアップロードする時の手順

何度も修正してアップロードするので、記載しておきます。

  • 前提として既存のGoコードが、/home/ec2-user配下にfunction.zip、main、main.go及びbootstrapが存在するものとして記載します。
  • 慎重に”削除”していきます。
    rm -rf function.zip
    rm main
    ls
  • 削除されたことを確認したら、再び作成します。
  • 先にmain.goの編集を済ませ、次の工程を行います。
  • バイナリデータ作成
    GOOS=linux GOARCH=amd64 go build -o main

    ZIPファイルにパッケージングします。

    zip function.zip main bootstrap
    ls

    mainとfunction.zipが作成されているか確認します。
    このままローカルにデータをダウンロードし、Lambdaにアップロードします。

CloudWatchでApacheの”ログ”を表示する。

次のポリシーがアタッチされたロールがEC2にアタッチされているか確認します。
CloudWatchAgentServerPolicy

awslogsをインストールする

yum install awslogs

設定ファイルが存在するか確認します。

ls -l /etc/awslogs

出力:
[root@zabbix ~]# ls -l /etc/awslogs
total 20
-rw------- 1 root root 55 Sep 25 12:02 awscli.conf
-rw-r--r-- 1 root root 8355 Jul 26 2018 awslogs.conf
drwxr-xr-x 2 root root 6 Jul 26 2018 config
-rw-r--r-- 1 root root 147 Jul 26 2018 proxy.conf

awscli.confを編集します。

vi awscli.conf

次のように修正します。

[plugins]
cwlogs = cwlogs
[default]
region = ap-northeast-1

awslogs.confを編集します。

vi awslogs.conf

次の記載を末尾に追記します。

[HttpAccessLog]
file = /var/log/httpd/access_log
log_group_name = HttpAccessLog
log_stream_name = {instance_id}
datetime_format = %b %d %H:%M:%S

[HttpErrorLog]
file = /var/log/httpd/error_log
log_group_name = HttpErrorLog
log_stream_name = {instance_id}
datetime_format = %b %d %H:%M:%S

awslogsを起動する

systemctl start awslogsd

ステータスを確認しておきます。

systemctl status awslogsd

自動起動を有効にします。

systemctl enable awslogsd

これでログがCloudWatch logsに追加されました。
image.png

Apacheログをダッシュボードに表示する。

  • CloudWach logsの画面へ移動

画面上部の検索窓で検索し、Apacheのaccess logを選択します。
image.png

  • クエリによって表示させたい情報を表示させる。
    クエリの例:

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

    エラーログは表示されるフィールドが少ないためクエリはデフォルトです。

fields @timestamp, @message, @logStream, @log
| sort @timestamp desc
| limit 10000

見た目も特に問題ありません。
image.png

CloudWatch Alarmを設定(あくまでテスト環境です。)

最終的にメモリとスワップの複合アラームを設定する。
スケールアップの要否を見たいので、メモリの状況でアラームを設定してみる!

  • メトリクス名:mem_used_percent
  • host:zabbix
  • 統計:最大
  • 期間:5分
    条件
  • 静的
  • より大きい
  • しきい値:95%
  • アラームを実行するデータポイント(3(データポイント)回中、3回ダメな時にアラーム)
  • 欠落データを見つかりませんとして処理(メトリクスが飛ばなかった時の振る舞い)

スワップのメトリクスもスケールアップに役立てたい!

  • メトリクス名:swap_free
  • host:zabbix
  • 統計:最小
  • 期間:5分
  • 静的
  • より低い
  • 60%
  • アラームを実行するデータポイント(3(データポイント)回中、3回ダメな時にアラーム)
  • 欠落データを見つかりませんとして処理(メトリクスが飛ばなかった時の振る舞い)

複合アラームの作成
先ほど作成した両アラームを合体。

  • アラーム状態トリガー:アラーム状態
  • 次の SNS トピックに通知を送信:通知先任意
  • アラーム名:test-composite-alarms
  • ルール(任意)
    ALARM("zabbix-memory-alarm") AND 
    ALARM("test-swap-alarm")

おわりに

最後まで読んでいただきありがとうございます。
目標としていた構築する上で知っておきたい基礎知識は少しだけ身についたものとして、次の記事ではCloudWatch RUMを実装してきます!
では!

返信を残す

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

CAPTCHA