Qmonus SDK Core v23.1の監視設計ベストプラクティスについて教えてください。

公開日: 2023/6/21

本ページについて

v23.1で提供しているheartbeat APIを使用した監視設計のベストプラクティスについて紹介します。

Qmonus SDK Core v22.7以前の監視について

Qmonus SDK Core v22.7以前のバージョンでは、Qmonusチームが用意した独自の監視用pluginをユーザー環境に追加インストールすることで監視用のAPIを利用した監視設計を想定していました。

この監視用APIに対しリクエストすることで、APIGWを経由した各コンポーネントへの疎通があるかどうかをチェックすることが可能でした。 ex.) APIGW -> Scenario, APIGW -> Scenario -> DB, APIGW -> Schedule

この提供中のAPIについては、系として動作できているかどうかのみが担保されます。冗長構成の場合において、各コンポーネントpodのうち1台が処理を担うためです。したがって下記のように故障podが存在している場合も正常にリクエストを返却可能なため、特定podの故障には使用できないという課題がありました。

Qmonus SDK Core v23.1以降の監視について

Qmonus SDK Core v23.1以降のバージョンではheartbeat APIが機能拡張されたことにより、culster=trueパラメータをURIに付与することでnamespace内にデプロイされているコンポーネント全てに対してmasterlookup API/healthcheck APIをリクエストします。例としてAPIGW endpointにリクエストが着弾すると、リクエストを受け取ったAPIGWは冗長構成になっている他のAPIGWを含めたすべてのコンポーネントの全てのpodにheartbeat APIをリクエストし、レスポンスを整形しまとめて返却します。

監視ベストプラクティス(1) GET /heartbeat?cluster=true の利用

GET /heartbeat?cluster=trueのレスポンスボディを以下で簡単に解説します。

{
    "endpoint": "10.98.38.119:9000", //ネットワーク内部のエンドポイントです。
    "hostname": "apigw-guest-78b5f694f8-gj4jd", //pod名です。
    "bootuptime":1678163546, // コンテナ起動時のUNIX時間です。
    "host":"10.98.34.161",   // Serviceに紐づくClusterIP(GCPの場合)です。
    "port":9000, // endpointのport番号です。
    "outofservice":false, //サービス提供状態のbooleanです。データベースとの疎通が失われた場合などでtrueが値に入ります。
    "database":true,  //アプリケーションとしてdatabaseと疎通があることのbooleanです。
    "memorystore":true,  //アプリケーションとしてredisと疎通があることのbooleanです。
    "mode":"master",  //アプリケーションでのコンポーネントの動作モードです。"master"もしくは"guest"が値に入ります。
    "plugins_version": {
        "instance":"fa7916bcdf2a4bd9a979c345f844d754", //plugin_installに成功している場合に格納されるインスタンスIDです。
        "timestamp":"2023-03-07T13:34:29.789202+09:00" //plugin_installに成功した時間です。
    }
}

GET /heartbeat?cluster=trueのレスポンスボディ例

{
    "APIGW": [
        {
            "endpoint": "10.98.38.119:9000",
            "hostname": "apigw-guest-78b5f694f8-gj4jd",
            "bootuptime": 1678062677,
            "host": "10.98.38.119",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.33.219:9000",
            "hostname": "apigw-guest-78b5f694f8-vlcx6",
            "bootuptime": 1678062673,
            "host": "10.98.33.219",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.34.8:9000",
            "hostname": "apigw-7759c8c478-4vnmv",
            "bootuptime": 1678062682,
            "host": "10.98.34.8",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "master",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        }
    ],
    "TRANSACTION": [
        {
            "endpoint": "10.98.33.216:9000",
            "hostname": "transaction-f6c47c7-ffcrg",
            "bootuptime": 1678062777,
            "host": "10.98.33.216",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "master",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.41.168:9000",
            "hostname": "transaction-guest-7f8cc4868f-qfsgn",
            "bootuptime": 1678062759,
            "host": "10.98.41.168",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.33.215:9000",
            "hostname": "transaction-guest-7f8cc4868f-gp8rf",
            "bootuptime": 1678062779,
            "host": "10.98.33.215",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        }
    ],
    "SCENARIO": [
        {
            "endpoint": "10.98.33.217:9000",
            "hostname": "scenario-659c9b6944-zxq5q",
            "bootuptime": 1678062781,
            "host": "10.98.33.217",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "master",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.38.121:9000",
            "hostname": "scenario-guest-cf84dccc6-s97g2",
            "bootuptime": 1678062756,
            "host": "10.98.38.121",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.43.177:9000",
            "hostname": "scenario-guest-cf84dccc6-2wh28",
            "bootuptime": 1678062766,
            "host": "10.98.43.177",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        }
    ],
    "SCHEDULE": [
        {
            "endpoint": "10.98.36.216:9000",
            "hostname": "schedule-guest-6467b5c4c5-dlfg9",
            "bootuptime": 1678062744,
            "host": "10.98.36.216",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.36.214:9000",
            "hostname": "schedule-6c88b9fcd9-864r7",
            "bootuptime": 1678062744,
            "host": "10.98.36.214",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "master",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.36.75:9000",
            "hostname": "schedule-guest-6467b5c4c5-tkbg4",
            "bootuptime": 1678062766,
            "host": "10.98.36.75",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        }
    ],
    "LAMBDA": [
        {
            "endpoint": "10.98.35.205:9000",
            "hostname": "lambda-guest-8b4f6ddf-46ndw",
            "bootuptime": 1678062745,
            "host": "10.98.35.205",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.37.135:9000",
            "hostname": "lambda-85bccb78c9-z2rcc",
            "bootuptime": 1678062731,
            "host": "10.98.37.135",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "master",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.41.169:9000",
            "hostname": "lambda-guest-8b4f6ddf-ss4nd",
            "bootuptime": 1678062759,
            "host": "10.98.41.169",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        }
    ],
    "COLLECTOR": [
        {
            "endpoint": "10.98.33.218:9000",
            "hostname": "collector-f6cb9f48b-z99fz",
            "bootuptime": 1678062777,
            "host": "10.98.33.218",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "master",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.36.215:9000",
            "hostname": "collector-guest-7d4c955d68-wz57s",
            "bootuptime": 1678062744,
            "host": "10.98.36.215",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.34.7:9000",
            "hostname": "collector-guest-7d4c955d68-nfctm",
            "bootuptime": 1678062737,
            "host": "10.98.34.7",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        }
    ],
    "REFLECTOR": [
        {
            "endpoint": "10.98.38.120:9000",
            "hostname": "reflector-guest-6d5f4b4475-wwvhz",
            "bootuptime": 1678062755,
            "host": "10.98.38.120",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        },
        {
            "endpoint": "10.98.36.220:9000",
            "hostname": "reflector-557cfd8f4d-mq4s2",
            "bootuptime": 1678063274,
            "host": "10.98.36.220",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "master",
            "plugins_version": null
        },
        {
            "endpoint": "10.98.43.176:9000",
            "hostname": "reflector-guest-6d5f4b4475-z5bnf",
            "bootuptime": 1678062766,
            "host": "10.98.43.176",
            "port": 9000,
            "outofservice": false,
            "database": true,
            "memorystore": true,
            "mode": "guest",
            "plugins_version": {
                "instance": "c37e890bae8e44d9a340b28ec48c9a73",
                "timestamp": "2023-03-06T09:34:39.044561+09:00"
            }
        }
    ]
}

レスポンスボディに含まれる情報から、各コンポーネントがRDBやredisにそれぞれ接続できているかをチェックすることが可能です。また、podが立ち上がっているがplugin_installの成功可否についても、heartbeat APIの情報から取得することが可能です。

Warning

GET /heartbeat?cluster=true から派生するhttpリクエストがエラーとなった場合、レスポンスボディの対応箇所に情報を格納せず200返却します。したがって、GET /heartbeat?cluster=trueのレスポンスステータスコードで全コンポーネント監視はできません。

heartbeat APIにおける監視の起点を指定する

監視の要件として特定のコンポーネントを指定し、これを起点としたルートでhealthcheck APIをリクエストしたい場合はmasterlookup APIでエンドポイントを取得したのち、そのエンドポイントに対してGET /heartbeat?cluster=trueをリクエストすることで実現可能です。

必ずscenarioのmasterを起点としてhealthcheckを実施したい場合、以下のインタラクティブシェルの実行結果のような処理をスクリプトに組み込むことでscenario masterを起点とした全コンポーネントのhealthcheckが可能です。

>>> r = await callout("/masterlookup?role=SCENARIO")↵
... print(json.dumps(json.loads(r.body), indent=4))↵
... ↵
↵
{
    "role": "SCENARIO",
    "endpoint": "10.98.33.217:9000",
    "hostname": "scenario-659c9b6944-zxq5q"
}
>>> endpoint = json.loads(r.body)["endpoint"]↵
... r = await callout(url=f"http://{endpoint}/heartbeat?cluster=true")↵

監視ベストプラクティス(2) GET /masterlookup?scope=cluster と GET /heartbeatの併用

各コンポーネントに対するheartbeat APIのレスポンスで評価したい場合はmasterlookup APIを利用して、以下のインタラクティブシェルの実行結果のような処理をscenario等のスクリプトに組み込むことで各コンポーネントへのheartbeat APIのレスポンスコードの評価が可能です。

Warning

masterlookup APIでデプロイされているSDKコンポーネントとレスポンスが一致しない場合はredisで管理しているエンドポイント情報がTTL超過により削除されている可能性があります。起動パラメーターcluster_watch_interval(default:10000)にてより大きな値を指定することでTTLを長く設定することで事象が解消するケースがあります。TTLは指定した値の3倍に指定されます。単位はミリ秒となります。

>>> r = await callout("/masterlookup?scope=cluster")↵
... body = json.loads(r.body)↵
... l = sum(list(map(lambda x: body[x], body.keys())), [])↵
... endpoints = [_["endpoint"] for _ in l]↵
... endpoints↵
... ↵
↵
['10.98.38.119:9000', '10.98.33.219:9000', '10.98.34.8:9000', '10.98.33.216:9000', '10.98.41.168:9000', '10.98.33.215:9000', '10.98.33.217:9000', '10.98.38.121:9000', '10.98.43.177:9000', '10.98.36.216:9000', '10.98.36.214:9000', '10.98.36.75:9000', '10.98.35.205:9000', '10.98.37.135:9000', '10.98.41.169:9000', '10.98.33.218:9000', '10.98.36.215:9000', '10.98.34.7:9000', '10.98.38.120:9000', '10.98.36.220:9000', '10.98.43.176:9000']
>>> for endpoint in endpoints:↵
...     r = await callout(url=f"http://{endpoint}/heartbeat?cluster=true")↵
...     assert r.code == 200↵

監視の起点を特に問わない場合は、scenarioにて監視用APIを簡単に実装が可能です。サンプルの監視用APIは以下となります。

- transaction:
    enable: false
    async: true
    xname: ''
  global_variables: {}
  variable_groups: []
  spec:
    response:
      normal:
        codes:
          - 200
  commands:
    - command: script
      kwargs:
        code: |
          r = await callout("/masterlookup?scope=cluster")
          body = json.loads(r.body)
          l = sum(list(map(lambda x: body[x], body.keys())), [])
          services = {_["hostname"]:_["endpoint"] for _ in l}

          res_body = {}
          for service in services.items():
              r = await callout(url=f"http://{service[1]}/heartbeat?cluster=true")
              if r.code not in [200]:
                  res_body[f"{service[0]}"] = False
              else:
                  res_body[f"{service[0]}"] = True

          if not all(res_body.values()):
              context.session.set_status(500)

          context.session.finish(res_body)

      id: c1958e720ff311ee9ec44ead5b71e4ce
  category: ''
  name: SystemHealthcheck
  uri: /v1/healthcheck
  method: GET
  request_timeout: 60
  connect_timeout: 60
  additional_paths: []
  routing_auto_generation_mode: true
  routing_options:
    scope: local
  version: 1
  update: '2023-06-21T14:35:58.858432+09:00'