0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OCI Resource schedulerの実行エラーを検知

Last updated at Posted at 2024-11-02

概要

Oracle Cloud Infrastructure(OCI)Resource Schedulerで登録したリソース起動スケジュールが失敗した時にアラート通知します。

背景

Resource Schedulerはスケジュール定期実行できるサービスですが、2024年10月時点では実行がエラーになった場合の検出サービスが見当たらなかったため検出できる仕組みを作成しました。

構成

  • VMからResource schedulerの最終実行結果を定期取得
    • Resource schedulerの最終実行結果を取得
    • 最終実行=SUCCEEDED以外があればメトリックを1にしてOCI Monitoringに送信
    • 上記を定期実行
  • OCI Monitoringで監視
    • メトリック1の場合Notificationsで設定した送付先に通知
    • VM自体の障害を考慮して、メトリックが存在しない場合Notificationsで設定した送付先に通知
  • (オプション)失敗したSchedule名を抽出するFunctionを実行
    • Resource schedulerの最終実行結果を取得
    • 最終実行=SUCCEEDED以外のSchedule名を抽出
    • Notificationsで設定した送付先に通知

VMからResource schedulerの最終実行結果を定期取得

準備

Oracle Cloud Infrastructure (OCI)内に以下を準備します

Pythonコード

rscschcheck.py
import json
import oci
import datetime
from pytz import timezone

# User Principal
config = oci.config.from_file()

# Check resource schedule
resource_scheduler_client = oci.resource_scheduler.ScheduleClient(config,service_endpoint="https://resource-scheduler.ap-tokyo-1.oci.oraclecloud.com")

list_schedules_response = resource_scheduler_client.list_schedules(
    compartment_id="【COMPARTMENT OCID】",
    lifecycle_state="ACTIVE"
    )

with open('/tmp/jsonout.json', 'w') as f:
    print(list_schedules_response.data, file=f)

with open('/tmp/jsonout.json', 'r') as f:
    di = json.load(f)

di2=(di["items"])

runstatus=0
for item in di2:
    if item["last_run_status"] != "SUCCEEDED":
        runstatus=1

# Post value to OCI Monitor
monitoring_client = oci.monitoring.MonitoringClient(config,service_endpoint="https://telemetry-ingestion.ap-tokyo-1.oraclecloud.com")

times_stamp = datetime.datetime.now(timezone('UTC'))

post_metric_data_response = monitoring_client.post_metric_data(
post_metric_data_details=oci.monitoring.models.PostMetricDataDetails(
metric_data=[
    oci.monitoring.models.MetricDataDetails(
        namespace="custom_metrics",
        compartment_id="【COMPARTMENT OCID】",
        name="resource_scheduler",
        dimensions={'result': "scheduler2"},
        datapoints=[
            oci.monitoring.models.Datapoint(
                timestamp=datetime.datetime.strftime(
                    times_stamp,"%Y-%m-%dT%H:%M:%S.%fZ"),
                value=runstatus)]
        )]
    )
)

5分おきに実行するようcron設定します

$ crontab -l
*/5 * * * * python /path/to/rscschcheck.py

OCI Monitoringで監視

準備

監視設定

  • OCIコンソール ナビゲーションメニュー>>監視および管理>>モニタリング>>メトリック・エクスプローラにてメトリックが受信できていることを確認
  • このメトリックを利用して2つのアラート設定

メトリック値が1を検出

メトリックが存在しない(Absent)を検出

(オプション)失敗したSchedule名を抽出するFunctionを実行

準備

Resource schedulerはテナンシー単位の操作となります
私の環境はコンパートメント単位で操作許可が付与されており、テナンシーレベルでリソースプリンシパルができないため、FunctionsにResource schedulerの操作権限を付与することができません。
そのため、Policyは以下の方針としました。

  • Policy
    • Functionsにリソースプリンシパルを使用してOCI Vault情報を読み取る
    • FunctionsにOCI Vaultから取得したociユーザー資格情報を使用してResource schedulerの操作を行う
    • Functionsにリソースプリンシパルを使用してNotificationsで設定した送付先に通知

(FunctionsにResource schedulerの操作を行う権限が付与できればOCI Vaultは不要です)

  • OCI Valut
    • ociユーザー資格情報を登録

Functionsコード

func.py
import io
import json
import logging
import oci
import base64
import datetime
from pytz import timezone

def handler(ctx, data: io.BytesIO = None):
    try:
        signer = oci.auth.signers.get_resource_principals_signer()

        # User principal from OCI Vault
        secrets_client = oci.secrets.SecretsClient(config={},signer=signer,service_endpoint="https://secrets.vaults.ap-tokyo-1.oci.oraclecloud.com")

        get_secret_bundle_response = secrets_client.get_secret_bundle(secret_id="【config_userのVAULTSECRET OCID】")
        config_user=base64.b64decode(get_secret_bundle_response.data.secret_bundle_content.content).decode()

        get_secret_bundle_response = secrets_client.get_secret_bundle(secret_id="【config_tenancyのVAULTSECRET OCID】")
        config_tenancy=base64.b64decode(get_secret_bundle_response.data.secret_bundle_content.content).decode()

        get_secret_bundle_response = secrets_client.get_secret_bundle(secret_id="【config_fingerprintのVAULTSECRET OCID】")
        config_fingerprint=base64.b64decode(get_secret_bundle_response.data.secret_bundle_content.content).decode()

        get_secret_bundle_response = secrets_client.get_secret_bundle(secret_id="【config_key_contentのVAULTSECRET OCID】")
        config_key_content=base64.b64decode(get_secret_bundle_response.data.secret_bundle_content.content).decode()

        config = {
            "user": config_user,
            "key_content": config_key_content,
            "fingerprint": config_fingerprint,
            "tenancy": config_tenancy,
            "region": "ap-tokyo-1"
            }

        oci.config.validate_config(config)

        resource_scheduler_client = oci.resource_scheduler.ScheduleClient(config,service_endpoint="https://resource-scheduler.ap-tokyo-1.oci.oraclecloud.com")

        # Extract error
        list_schedules_response = resource_scheduler_client.list_schedules(
            compartment_id="【TENANCY OCID】",
            lifecycle_state="ACTIVE"
            )

        with open('/tmp/jsonout.json', 'w') as f:
            print(list_schedules_response.data, file=f)

        with open('/tmp/jsonout.json', 'r') as f:
            di = json.load(f)

        di2=(di["items"])

        with open('/tmp/displayname.json', 'w') as f2:
            print("display name", file=f2)

        for item in di2:
            if item["last_run_status"] != "SUCCEEDED":
                with open('/tmp/displayname.json', 'a') as f2:
                    print(item["display_name"], file=f2)

        with open('/tmp/displayname.json', 'r') as f2:
            displayname = f2.read()

        # Publish Message
        ons_client = oci.ons.NotificationDataPlaneClient(config={},signer=signer,service_endpoint="https://notification.ap-tokyo-1.oci.oraclecloud.com")

        publish_message_response = ons_client.publish_message(
            topic_id="【TOPIC OCID】",
            message_details=oci.ons.models.MessageDetails(
                body=displayname,
                title="Resource Schedule Error")
            )

    except Exception as e:
        print('ERROR: bad Event!', flush=True)
        raise
func.yaml
schema_version: 20180708
name: rscscherror
version: 0.0.1
runtime: python
build_image: fnproject/python:3.11-dev
run_image: fnproject/python:3.11
entrypoint: /python/bin/fdk /function/func.py handler
memory: 256

requirements.txt
fdk>=0.1.83
oci

デプロイして実行します

$ fn -v deploy --app fnapp
$ fn invoke fnapp rscscherror

成功すると以下のように失敗したスケジュールが通知されます

題名:Resource Schedule Error
本文:display name
SCH-EXAMPLE
 ←失敗したSchedule名

この作成したFunctionsを"OCI Monitoringで監視"で指定するNotifucationsに追加することによりアラート送付時にFunctionsを実行可能です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?