注意:
- 本教程需要访问 Oracle Cloud。要注册免费账户,请参阅开始使用 Oracle Cloud Infrastructure 免费套餐。
- 它对 Oracle Cloud Infrastructure 身份证明、租户和区间使用示例值。完成实验室后,请使用特定于云环境的那些值替换这些值。
使用 Datadog 通过 PostgreSQL 监视 Oracle Cloud Infrastructure Database
简介
Oracle Cloud Infrastructure (OCI) 是一个强大且高度可扩展的云平台,旨在满足现代企业的需求。它为计算、存储、网络、数据库和应用开发提供了一整套服务,并针对性能、安全性和成本效率进行了优化。OCI 非常适合运行云原生和传统工作负载,可为企业提供灵活可靠的基础设施。
Datadog 是一个基于云的全面监视和分析平台,旨在帮助组织获得 IT 基础设施、应用和服务的端到端可见性。它支持跨动态混合云环境的实时监视、故障排除和性能优化。Datadog 与各种工具、平台和服务无缝集成,使其成为现代 DevOps 和 IT 运营团队的通用解决方案。
本教程演示了 OCI Database with PostgreSQL 和 Datadog 用户如何设置高效、可扩展的解决方案,以使用 OCI Connector Hub 和 OCI Functions 将指标从 OCI 无缝传输到 Datadog。
目标
- 实现对数据库性能的实时监视、预警和可观察性,确保顺利运行和主动解决问题。
先决条件
-
访问 OCI 租户。
-
在专用子网中预配了 PostgreSQL 系统的 OCI 数据库。
-
堡垒主机(计算映像)。
任务 1:创建 Datadog 帐户
-
使用数据狗网站在 Datadog 集成工具中设置账户。提供必要的账户详细信息,并通过配置适当的环境设置完成代理设置。
-
安装 Datadog Agent 以使用 PostgreSQL 从 OCI 数据库收集度量和事件。有关设置和配置 Datadog 代理的更多信息,请参见 Setup Datadog Agent 。有关 Datadog Agent 的故障排除和调试的其他详细信息,请参见 Basic Datadog Agent Usage 。
-
选择 OCI 作为集成,然后继续安装。下图显示了 Datadog 的 OCI 集成的安装后。
-
单击添加租户并输入租户 OCID 和主区域信息。
任务 2:创建 Datadog 验证资源
在 Oracle Cloud Infrastructure (OCI) 中创建 Datadog 验证用户、组和策略。
-
要创建域,请导航到身份并创建一个名为
DataDog
的域。 -
创建一个名为
DatadogAuthGroup
的组。 -
使用您的电子邮件地址创建一个名为
DatadogAuthUser
的用户(用于登录 Datadog 监视工具的电子邮件相同),并将DatadogAuthGroup
指定为组。 -
复制用户 OCID 并将其粘贴到数据狗 OCI 集成磁贴上的用户 OCID 字段中以配置用户 OCID。
-
设置 API。
-
导航到您的配置文件并选择您的用户名。
-
导航到左下角的资源,然后选择 API 密钥。
-
单击添加 API 密钥,然后下载私有密钥,然后单击添加。
-
关闭配置文件预览窗口。不需要操作。
-
复制 Fingerprint 值并将其粘贴到 Datadog OCI 集成磁贴上的 Fingerprint 字段中。
-
-
配置私有密钥。
-
在文本编辑器中打开下载的私钥文件 (
.pem
),或使用终端命令(例如 cat)查看其内容。 -
复制整个密钥,包括行
-----BEGIN PRIVATE KEY-----
和-----END PRIVATE KEY-----
。 -
将私有密钥粘贴到数据狗 OCI 集成磁贴上的私有密钥字段中。
-
-
在
postgresqlinteg
(根)区间中创建名为DataDogPolicy
的策略。 -
在手动编辑器模式下使用策略构建器可输入所需的策略语句。
Allow group DatadogAuthGroup to read all-resources in tenancy
以下是添加租户和用户详细信息后的 Datadog OCI 集成磁贴示例。
任务 3:创建 OCI 堆栈
导航到身份部分并在根区间下创建策略堆栈。这允许连接器集线器通过以下语句读取度量和调用函数。
Allow dynamic-group DatadogAuthGroup to read metrics in tenancy
Allow dynamic-group DatadogAuthGroup to use fn-function in tenancy
Allow dynamic-group DatadogAuthGroup to use fn-invocation in tenancy
要在 OCI 中为 Datadog 集成配置身份策略和部署度量转发堆栈,请执行以下任务:
任务 3.1:创建策略堆栈 (ORM_policy_stack
)
-
单击数据狗 OCI 集成磁贴上的创建策略堆栈,确保使用提供的链接(其中包括必要的 Terraform 脚本)并接受 Oracle 使用条款。
-
单击 Working Directory(工作目录)下拉菜单,然后选择
datadog-oci-orm/policy-setup
。 -
取消选择使用定制 Terraform 提供程序。
-
输入描述性名称(例如
datadog-metrics-policy-setup
),然后选择要部署的区间。 -
单击下一步,为动态组和策略命名(或使用默认名称),确保选择了租户的主区域,然后单击创建。
任务 3.2:创建度量转发堆栈
资源将部署到指定的区间。确保运行堆栈的用户具有适当的访问权限。
-
单击数据狗 OCI 集成磁贴上的创建策略堆栈并接受 Oracle 使用条款。
-
单击工作目录下拉菜单,选择
datadog-oci-orm/metrics-setup
并取消选择使用定制 Terraform 提供程序。 -
命名堆栈并选择部署区间,然后单击下一步。
-
将租户值保留未修改状态,输入数据狗 API 密钥,然后选择 US5 端点 (
ocimetrics-intake.us5.datadoghq.com
)。 -
对于网络配置,请确保选中创建 VCN 并选择适当的区间以创建 VCN。
-
在函数设置部分中,将默认应用程序配置保留为
GENERIC_ARM
。输入 OCI Docker 注册表用户名和密码(构件密码)。 -
将 Service Connector Hub 批处理大小设置为 5000 ,然后单击 Next 。
-
单击创建。
任务 3.3:完成配置
-
返回到 Datadog OCI 集成磁贴,然后单击创建配置以完成设置。
-
此流程可确保正确配置 Datadog 指标和功能,以便与 OCI 集成。
任务 4:创建 OCI 函数
要在 OCI 控制台中创建应用程序,请按照以下步骤操作:
-
导航到应用程序,然后选择创建应用程序。
-
输入应用程序名称,选择相应的虚拟云网络 (Virtual Cloud Network,VCN) 和子网详细信息,然后单击创建。
-
要访问新创建的应用程序,请在资源下选择入门。
-
单击 Launch Cloud Shell 并从 Use the context for your region 复制以下命令。
fn list context fn use context <region name>
-
更新上下文以包括函数的区间 ID。
fn update context oracle.compartment-id <compartment-id>
-
更新上下文以包括要使用的注册表的位置。
fn update context registry phx.ocir.io/<tenancy_name>/[YOUR-OCIR-REPO]
注:将上下文中的
phx
替换为三位数区域代码。 -
使用验证标记作为密码来登录注册表。
docker login -u 'TENACNY_NAME/OCI_USERNAME' phx.ocir.io
-
将提示您使用口令。请提供适当的密码。
注:
- 将
phx
替换为三位数区域代码。 - 如果您使用的是 Oracle Identity Cloud Service,则您的用户名是
<tenancyname>/oracleidentitycloudservice/<username>
。
- 将
-
生成 hello-world 模板函数。
fn list apps fn init --runtime python datadog
fn init
命令将生成一个名为datadog
的文件夹,其中包含三个文件:func.py
、func.yaml
和requirements.txt
。 -
运行
cd datadog
命令。 -
打开
func.py
并将文件的内容替换为以下代码片段。# oci-monitoring-metrics-to-datadog version 1.0. # # Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. # Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. import io import json import logging import os import re import requests from fdk import response from datetime import datetime """ This sample OCI Function maps OCI Monitoring Service Metrics to the DataDog REST API 'submit-metrics' contract found here: https://docs.datadoghq.com/api/latest/metrics/#submit-metrics """ # Use OCI Application or Function configurations to override these environment variable defaults. api_endpoint = os.getenv('DATADOG_METRICS_API_ENDPOINT', 'not-configured') api_key = os.getenv('DATADOG_API_KEY', 'not-configured') is_forwarding = eval(os.getenv('FORWARD_TO_DATADOG', "True")) metric_tag_keys = os.getenv('METRICS_TAG_KEYS', 'name, namespace, displayName, resourceDisplayName, unit') metric_tag_set = set() # Set all registered loggers to the configured log_level logging_level = os.getenv('LOGGING_LEVEL', 'INFO') loggers = [logging.getLogger()] + [logging.getLogger(name) for name in logging.root.manager.loggerDict] [logger.setLevel(logging.getLevelName(logging_level)) for logger in loggers] # Exception stack trace logging is_tracing = eval(os.getenv('ENABLE_TRACING', "False")) # Constants TEN_MINUTES_SEC = 10 * 60 ONE_HOUR_SEC = 60 * 60 # Functions def handler(ctx, data: io.BytesIO = None): """ OCI Function Entry Point :param ctx: InvokeContext :param data: data payload :return: plain text response indicating success or error """ preamble = " {} / event count = {} / logging level = {} / forwarding to DataDog = {}" try: metrics_list = json.loads(data.getvalue()) logging.getLogger().info(preamble.format(ctx.FnName(), len(metrics_list), logging_level, is_forwarding)) logging.getLogger().debug(metrics_list) converted_event_list = handle_metric_events(event_list=metrics_list) send_to_datadog(event_list=converted_event_list) except (Exception, ValueError) as ex: logging.getLogger().error('error handling logging payload: {}'.format(str(ex))) if is_tracing: logging.getLogger().error(ex) def handle_metric_events(event_list): """ :param event_list: the list of metric formatted log records. :return: the list of DataDog formatted log records """ result_list = [] for event in event_list: single_result = transform_metric_to_datadog_format(log_record=event) result_list.append(single_result) logging.getLogger().debug(single_result) return result_list def transform_metric_to_datadog_format(log_record: dict): """ Transform metrics to DataDog format. See: https://github.com/metrics/spec/blob/v1.0/json-format.md :param log_record: metric log record :return: DataDog formatted log record """ series = [{ 'metric': get_metric_name(log_record), 'type' : get_metric_type(log_record), 'points' : get_metric_points(log_record), 'tags' : get_metric_tags(log_record), }] result = { 'series' : series } return result def get_metric_name(log_record: dict): """ Assembles a metric name that appears to follow DataDog conventions. :param log_record: :return: """ elements = get_dictionary_value(log_record, 'namespace').split('_') elements += camel_case_split(get_dictionary_value(log_record, 'name')) elements = [element.lower() for element in elements] return '.'.join(elements) def camel_case_split(str): """ :param str: :return: Splits camel case string to individual strings """ return re.findall(r'[A-Z](?:[a-z]+|[A-Z]*(?=[A-Z]|$))', str) def get_metric_type(log_record: dict): """ :param log_record: :return: The type of metric. The available types are 0 (unspecified), 1 (count), 2 (rate), and 3 (gauge). Allowed enum values: 0,1,2,3 """ return 0 def get_now_timestamp(): return datetime.now().timestamp() def adjust_metric_timestamp(timestamp_ms): """ DataDog Timestamps should be in POSIX time in seconds, and cannot be more than ten minutes in the future or more than one hour in the past. OCI Timestamps are POSIX in milliseconds, therefore a conversion is required. See https://docs.datadoghq.com/api/latest/metrics/#submit-metrics :param oci_timestamp: :return: """ # positive skew is expected timestamp_sec = int(timestamp_ms / 1000) delta_sec = get_now_timestamp() - timestamp_sec if (delta_sec > 0 and delta_sec > ONE_HOUR_SEC): logging.getLogger().warning('timestamp {} too far in the past per DataDog'.format(timestamp_ms)) if (delta_sec < 0 and abs(delta_sec) > TEN_MINUTES_SEC): logging.getLogger().warning('timestamp {} too far in the future per DataDog'.format(timestamp_ms)) return timestamp_sec def get_metric_points(log_record: dict): """ :param log_record: :return: an array of arrays where each array is a datapoint scalar pair """ result = [] datapoints = get_dictionary_value(dictionary=log_record, target_key='datapoints') for point in datapoints: dd_point = {'timestamp': adjust_metric_timestamp(point.get('timestamp')), 'value': point.get('value')} result.append(dd_point) return result def get_metric_tags(log_record: dict): """ Assembles tags from selected metric attributes. See https://docs.datadoghq.com/getting_started/tagging/ :param log_record: the log record to scan :return: string of comma-separated, key:value pairs matching DataDog tag format """ result = [] for tag in get_metric_tag_set(): value = get_dictionary_value(dictionary=log_record, target_key=tag) if value is None: continue if isinstance(value, str) and ':' in value: logging.getLogger().warning('tag contains a \':\' / ignoring {} ({})'.format(key, value)) continue tag = '{}:{}'.format(tag, value) result.append(tag) return result def get_metric_tag_set(): """ :return: the set metric payload keys that we would like to have converted to tags. """ global metric_tag_set if len(metric_tag_set) == 0 and metric_tag_keys: split_and_stripped_tags = [x.strip() for x in metric_tag_keys.split(',')] metric_tag_set.update(split_and_stripped_tags) logging.getLogger().debug("tag key set / {} ".format (metric_tag_set)) return metric_tag_set def send_to_datadog (event_list): """ Sends each transformed event to DataDog Endpoint. :param event_list: list of events in DataDog format :return: None """ if is_forwarding is False: logging.getLogger().debug("DataDog forwarding is disabled - nothing sent") return if 'v2' not in api_endpoint: raise RuntimeError('Requires API endpoint version "v2": "{}"'.format(api_endpoint)) # creating a session and adapter to avoid recreating # a new connection pool between each POST call try: session = requests.Session() adapter = requests.adapters.HTTPAdapter(pool_connections=10, pool_maxsize=10) session.mount('https://', adapter) for event in event_list: api_headers = {'Content-type': 'application/json', 'DD-API-KEY': api_key} logging.getLogger().debug("json to datadog: {}".format (json.dumps(event))) response = session.post(api_endpoint, data=json.dumps(event), headers=api_headers) if response.status_code != 202: raise Exception ('error {} sending to DataDog: {}'.format(response.status_code, response.reason)) finally: session.close() def get_dictionary_value(dictionary: dict, target_key: str): """ Recursive method to find value within a dictionary which may also have nested lists / dictionaries. :param dictionary: the dictionary to scan :param target_key: the key we are looking for :return: If a target_key exists multiple times in the dictionary, the first one found will be returned. """ if dictionary is None: raise Exception('dictionary None for key'.format(target_key)) target_value = dictionary.get(target_key) if target_value: return target_value for key, value in dictionary.items(): if isinstance(value, dict): target_value = get_dictionary_value(dictionary=value, target_key=target_key) if target_value: return target_value elif isinstance(value, list): for entry in value: if isinstance(entry, dict): target_value = get_dictionary_value(dictionary=entry, target_key=target_key) if target_value: return target_value def local_test_mode(filename): """ This routine reads a local json metrics file, converting the contents to DataDog format. :param filename: cloud events json file exported from OCI Logging UI or CLI. :return: None """ logging.getLogger().info("local testing started") with open(filename, 'r') as f: transformed_results = list() for line in f: event = json.loads(line) logging.getLogger().debug(json.dumps(event, indent=4)) transformed_result = transform_metric_to_datadog_format(event) transformed_results.append(transformed_result) logging.getLogger().debug(json.dumps(transformed_results, indent=4)) send_to_datadog(event_list=transformed_results) logging.getLogger().info("local testing completed") """ Local Debugging """ if __name__ == "__main__": local_test_mode('oci-metrics-test-file.json')
-
使用以下代码更新
func.yaml
。将DATADOG_TOKEN
替换为 Datadog API 密钥,将DATADOG_HOST
替换为 REST 端点 -https://http-intake.logs.datadoghq.com/v1/input
。有关 REST 端点的更多信息,请参阅日志收集和集成。schema_version: 20180708 name: datadogapp version: 0.0.1 runtime: python entrypoint: /python/bin/fdk /function/func.py handler memory: 1024 timeout: 120 config: DATADOG_HOST: https://http-intake.logs.datadoghq.com/v1/input DATADOG_TOKEN: ZZZZZzzzzzzzzzz
-
使用以下代码更新
requirements.txt
。fdk dattime requests oci
-
运行以下命令以创建应用程序并部署函数以完成设置。
fn create app datadog01 --annotation oracle.com/oci/subnetIds='["Provide your subnet OCID"]'
-
运行以下命令来部署函数以完成设置。
fn -v deploy --app datadog
任务 5:设置 OCI Connector Hub
-
转到 OCI 控制台,导航到日志记录、连接器,然后单击创建连接器。
-
将 Source 设置为 Monitoring ,将 Target 设置为 Functions 。
-
在配置源连接下,选择相应的度量区间和名称空间。例如,
oci_postgresql
用于数据库监视。 -
在配置目标下,选择在任务 4 中创建的区间、函数应用程序和函数。
-
如果出现提示,请单击创建以创建必需的策略。
-
单击创建以最终确定 OCI Connector Hub 设置。
任务 6:查看 Datadog 中的度量
OCI Connector Hub 现在配置为触发该功能,支持在检测到新度量时将度量摄取到 Datadog。在 Datadog Integration 磁贴中,导航到度量并查看概要以查看与 OCI 相关的度量。
在 Datadog 集成磁贴中,单击浏览器以根据需要分析和选择所需的 OCI 度量,
疑难解答
如果 Metrics Summary(度量概要)页上未显示任何数据,请选择 Enable log(启用日志)以允许对函数进行日志记录,从而查看日志并调试问题。
确认
- 作者 - Kaviya Selvaraj(高级技术人员)
更多学习资源
浏览 docs.oracle.com/learn 上的其他实验室,或者访问 Oracle Learning YouTube 渠道上的更多免费学习内容。此外,请访问 education.oracle.com/learning-explorer 成为 Oracle Learning Explorer。
有关产品文档,请访问 Oracle 帮助中心。
Monitor Oracle Cloud Infrastructure Database with PostgreSQL using Datadog
G28863-01
Copyright ©2025, Oracle and/or its affiliates.