Percona Monitoring and Management2 Advisor Checks 機能を使用して独自のチェックを開発する
Percona Monitoring and Management 2(PMM2) では Security Threat Tool という監視対象サーバのセキュリティチェックを行い、結果をUIに表示する機能が実装されています。
PMM 2.27ではこのセキュリティチェック機能が「Advisor Checks」という名称に変更され、2.28 では 2.27の「Advisor Checks」を v1 として 「Advisor Checks v2」が実装されました。
現時点ではベータ版機能になりますが、今回はこの「Advisor Checks v2」を使用して独自のチェッカー機能の実装までの流れをご紹介します。
なお、PMM2の構築手順につきましては以下のURLをご確認ください。
Advisor Checks の概要
「Advisor Checks」は以下の流れでチェックを行います。
チェック結果はアラートとして画面上に表示されます。
「Advisor Checks」という名称からも、本機能はセキュリティやパラメータチューニング、バージョンごとの推奨アドバイスを行うための機能になります。
Client(Victria Metricsクライアント)へクエリを実行した結果から得られる内容であれば何にでもチェックをかけられますので、node_exporterが提供する実績値を元にアドバイスも可能です。
node_exporterが提供するメトリックは多岐に渡りますので、例えば以下のチェックが考えられます。
- データベースを起動した時点で過剰にリソースを使っていないか
- 非推奨のOSバージョンを使っていないか
- 推奨のカーネルパラメータが設定されているか
というようなデータベース・ソフトウェアにとどまらないチェックも可能です。
これによって、PMM2の監視対象サーバとしてデータベースを登録するだけで、独自に定義した必須・推奨パラメータに沿っているかわかりやすくなります。
例えばそれほどDBに詳しくないエンジニアが構築したデータベースもPMM2をみればどこを修正すればよいか一目瞭然になります。
Percona Platformを使用した Advisor Checks の実行
ここで、Percona Platform についてもご紹介しておきます。
PMM2の開発ベンダであるPercona社では、PMM2と自社プラットフォーム(Percona Platform)を連携させて自社の顧客向けに付加価値のあるサービスを提供しています。
事前定義済みのAdovisor Checks機能は、無償のBasic planでも一部利用することが可能です。詳しくは以下のリストをご確認ください。
https://docs.percona.com/percona-platform/advisors.html#how-to-get-more-advisors
PMM2をPercona Platformに参加させるための手順は以下URLにまとまっています。
ドキュメントの手順に従い、Percona Platformアカウントを登録し、PMM2をPercona platformに接続すると、「Advisor Checks」のメニューにチェック項目が追加されます。
あとは、表示されている interval
に従い、チェックが実行されます。
無償プランでもかなりの数のチェックが行われますので、PMM2からインターネットが接続可能であれば、まずはPercona Platformを使用して機能を試してみるのもよいかと思います。
Advisor Checks の設定ファイル
Advisor Checks の設定ファイルはYAMLフォーマットになります。
設定ファイルの完全な例はドキュメントをご確認ください。
それほど難しい内容ではありませんが、主要な設定値としてはqueries
、scripts
になるでしょう。
queries
は、PMMサーバからクライアントに実行する一連のクエリ定義です。
クエリ定義はtype
フィールド、 query
フィールドから構成され、type
フィールドは、query
フィールドがどの種別のクエリかを定義します。
MySQLに観点を絞ると、以下が現在設定できます。
type | 説明 |
---|---|
MYSQL_SHOW | SHOW GLOBAL から始まるクエリ |
MYSQL_SELECT | SELECT … から始まるMySQLクエリ |
METRICS_INSTANT | MetricsQL(※PromQL互換のVictriaMetrics用クエリ)でInstant Queryを使用する |
METRICS_RANGE | MetricsQL(※PromQL互換のVictriaMetrics用クエリ)でRange Queryを使用する |
MySQLのメトリックは MYSQL...
を、OSのメトリックは METRICS_...
を基本的には使用することになるでしょう。
集約や加工はクエリでも可能ですが、クライアント側(クエリ)でやったほうがよいか、サーバ側(スクリプト)でやったほうがよいかはリソース負荷を見て判断することになります。
1 2 3 4 5 6 7 8 9 10 11 12 |
--- checks: - version: 2 : 略 : queries: - type: MYSQL_SHOW query: VARIABLES - type: METRICS_INSTANT query: mysql_global_status_uptime{service_name=~"{{.ServiceName}}"} |
script
は queries
の結果を元にしたチェック処理です。
Starlark 言語というPython方言を使用してcheck_context()
関数を定義することで機能を実装します。
check_context関数は、docs
と context
を引数に取り、内容は以下の通りです。
変数 | 内容 |
---|---|
docs | クエリの結果配列。queries の並び順で各クエリの結果が格納される。 |
context | ユーティリティ関数を含むディクショナリ。ここで定義されています。 |
コードは定型的で、
docs
に格納されている結果から目的のフィールド取得(もしくは加工)- 1のフィールドを評価し、結果用リストに定型の結果用ディクショナリを格納
ということを繰り返し、最後に結果用リストを返すだけです。
以下に設定ファイル例の抜粋を記載します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
略 : script: | def check_context(docs, context): results = [] for row in docs[0]: name, value = row["Variable_name"], row["Value"] if name == "version": results.append({ "summary": "MySQL has version {}".format(value), "description": "Current version is {}".format(value), "read_more_url": "", "severity": "warning", "labels": {}, }) : 略 : return results |
例では特にしきい値などの指定はなく常にバージョンなどの情報を画面に表示しているだけですが、評価結果に応じてアラートを追加する、しきい値によってラベルを変えるといったことも可能です。
Advisor Checksの実行間隔について
Advisor Checks は柔軟な機能ですので、例えば通常アラート機能で実装するような内容も設定できます(例えばロードアベレージがしきい値を超えた場合に通知など)。
しかしながら、Advisor Checks はあくまで推奨事項をアドバイスする機能のため高頻度で実行する事は想定されていません。
実行間隔を設定する interval
パラメータには、秒単位といった値を指定することはできず、現在指定できる間隔は以下の3つです。
- Standard
- Frequent
- Rare
これらの実際の間隔は以下のコードで定義されています。
Starndard = 24時間ごと、 Rare = 78時間ごと、Frequent = 4時間ごとになりますので、ご注意ください。
なお、Advisor Checksは、ダッシュボード上の「Run Checks」ボタンを押下することでも実行されますので、追加したインスタンスに即座にチェックを行いたいといったことも可能です。
独自のアドバイザチェックの実装
それでは、独自のアドバイザチェックを実装してみたいと思います。
今回は以下の項目についてチェックを追加します。
- InnoDBバッファプールは物理メモリサイズの60~70%である
- GTIDを有効化している
- レプリケーションスレーブは、super_read_onlyが設定されている
以下は設定例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
--- checks: - version: 2 name: custom_check_innodb_buffer_pool_size summary: Innodb_buffer_pool_sizeのチェック description: Innodb_buffer_pool_size が物理メモリの60~70%であるかチェック interval: standard family: MYSQL category: performance queries: - type: MYSQL_SHOW query: VARIABLES - type: METRICS_INSTANT query: node_memory_MemTotal_bytes{node_name=~"{{.NodeName}}"} script: | def check_context(docs, context): results = [] innodb_buffer_pool_size = [x["Value"] for x in docs[0] if x["Variable_name"] == "innodb_buffer_pool_size"][0] total_memory = docs[1][0]["value"][1] innodb_buffer_pool_size_percent = 100 * (int(innodb_buffer_pool_size) / int(total_memory)) if innodb_buffer_pool_size_percent < 60 or innodb_buffer_pool_size_percent > 70: results.append({ "summary": "innodb_buffer_pool_sizeが物理メモリの60-70%の範囲ではありません {}%".format(int(innodb_buffer_pool_size_percent)), "description": "innodb_buffer_pool_sizeの一般的な推奨値は物理メモリの60-70%の範囲です。適切な値に修正してください。TotalMemory: {}, innodb_buffer_pool_size: {}, Percentage: {}%".format(total_memory, innodb_buffer_pool_size, int(innodb_buffer_pool_size_percent)), "read_more_url": "", "severity": "warning", "labels": {}, }) return results - version: 2 name: custom_check_gtid_enabled summary: GTIDのチェック description: GTIDが有効化されているかのチェック interval: standard family: MYSQL category: replication queries: - type: MYSQL_SHOW query: VARIABLES script: | def check_context(docs, context): results = [] vars = {x["Variable_name"]:x["Value"] for x in docs[0] if x["Variable_name"] in ["gtid_mode", "enforce_gtid_consistency"]} if vars["gtid_mode"] != "ON": results.append({ "summary": "gtid_modeが有効ではありません Current: {}".format(vars["gtid_mode"]), "description": "GTIDを有効化してください", "read_more_url": "https://dev.mysql.com/doc/refman/8.0/en/replication-gtids-howto.html", "severity": "warning", "labels": {}, }) return results - version: 2 name: custom_check_replica_is_super_read_only summary: レプリカサーバが読み取り専用になっているかのチェック description: レプリカサーバでsuper_read_only変数が有効化されていることを確認 interval: standard family: MYSQL category: replication queries: - type: MYSQL_SHOW query: VARIABLES - type: MYSQL_SHOW query: SLAVE STATUS script: | def check_context(docs, context): results = [] if len(docs[1]) > 0: super_read_only = [x["Value"] for x in docs[0] if x["Variable_name"] == "super_read_only"][0] if super_read_only != "ON": results.append({ "summary": "レプリカサーバでsuper_read_onlyが有効ではありません", "description": "レプリカサーバで書き込みが可能な状態で運用すると、意図しない更新によってソースと異なるデータの状態になる可能性があります。メンテナンス中でなければsuper_read_onlyを有効化して ください。", "read_more_url": "https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_super_read_only", "severity": "warning", "labels": {}, }) return results |
ローカルのカスタムチェックを有効化するために、PMM Server側の環境変数を設定した上で起動します。
本環境は、Podmanを使用した非特権ユーザの起動手順で起動しましたので、以下のファイルに環境変数を設定し、サービスを再起動します。
1 2 3 4 5 6 |
$ cat >> ~/.config/pmm-server/pmm-server.env <<EOF PMM_DEBUG=1 PERCONA_TEST_CHECKS_FILE=/srv/custom-checks.yml PERCONA_TEST_CHECKS_DISABLE_START_DELAY=true PERCONA_TEST_CHECKS_RESEND_INTERVAL=2s EOF |
設定ファイルをPERCONA_TEST_CHECKS_FILEのパスにコピーします。
1 |
$ podman cp custom-checks.yml pmm-server:/srv/custom-checks.yml |
再起動すると、「Advisor Checks」画面で設定したチェックが有効化されています。
1 |
$ systemctl --user restart pmm-server |
事前に以下の環境を監視対象に登録しています。
- mysql-test-1 (レプリケーションソースDB)
- パラメータはデフォルト
- mysql-test-2 (レプリケーションレプリカDB)
- GTIDは有効化済み(gtid_mode=ON)
- それ以外のパラメータはデフォルト
「Run Checks」ボタンですぐにチェックを実行してみましょう。
何回か画面を更新すると、「Failed Checks」タブにチェックが失敗したホストが表示されます。
mysql-test-1サーバの結果を表示すると、2つのチェックに失敗しています。
super_read_onlyは設定していませんが、レプリカサーバでは無いので、想定通りチェックはパスしています。
mysql-test-2サーバも2つのチェックに失敗しています。
GTIDを設定していますので、該当のチェックはパスしています。
レプリカであり、super_read_onlyを設定していませんのでチェックを通りませんでした。
まとめ
本機能を使用することで、これまでの経験を活かしたベストプラクティスを育てる仕組みを作ることができます。
チェックの実装もそれほど難しくはありませんので、現在でも実用的にすぐにでも使い始められる印象を受けました。
利用してみて以下が不満な点としてありました。
- チェックをパスした項目も画面に表示するか、選択できるようにして欲しい
- 内部エラーによってチェックの実行自体に失敗した場合も画面に表示してほしい
- (機能自体の不満ではありませんが)Python3とStarlarkの微妙な差異に躓くことがある
- Yaml上に直接クエリやStarlarkスクリプトを書くのではなくファイルをincludeする形式にするかPMMの画面上でそれらを直接登録するUIがほしい
特にStarlarkスクリプトが失敗した場合に/srv/logs/pmm-managed.log
の大量の出力の中からエラー行を探すのが面倒と感じるポイントでしたので、デバッグがしやすくなる仕組みは欲しいと感じました。
PMM2の新機能の実装、UIの改善スピートは非常に早いため、これからGAになるまでの間によりブラッシュアップされることを期待しましょう!