Observação:

Monitorar o Oracle Cloud Infrastructure Database com PostgreSQL usando o Datadog

Introdução

A Oracle Cloud Infrastructure (OCI) é uma plataforma de nuvem robusta e altamente escalável projetada para atender às necessidades das empresas modernas. Ele oferece um conjunto abrangente de serviços para computação, armazenamento, rede, banco de dados e desenvolvimento de aplicativos, otimizados para desempenho, segurança e eficiência de custos. A OCI é ideal para executar cargas de trabalho nativas da nuvem e tradicionais, oferecendo às empresas uma infraestrutura flexível e confiável.

O Datadog é uma plataforma abrangente de monitoramento e análise baseada em nuvem projetada para ajudar as organizações a obter visibilidade de ponta a ponta em sua infraestrutura, aplicativos e serviços de TI. Ele permite monitoramento em tempo real, solução de problemas e otimização de desempenho em ambientes de nuvem híbrida e dinâmica. O Datadog se integra perfeitamente a uma ampla gama de ferramentas, plataformas e serviços, tornando-o uma solução versátil para equipes modernas de operações de DevOps e TI.

Este tutorial demonstra como o OCI Database com PostgreSQL e os usuários do Datadog podem configurar soluções eficientes e escaláveis para transmitir facilmente métricas do OCI para o Datadog usando o OCI Connector Hub e o OCI Functions.

Objetivos

Pré-requisitos

Tarefa 1: Criar uma Conta do Datadog

  1. Configure uma conta na ferramenta de integração Datadog usando o Site Datadog. Forneça os detalhes da conta necessários e conclua a configuração do agente configurando as definições de ambiente apropriadas.

  2. Instale o Datadog Agent para coletar métricas e eventos do Banco de Dados do OCI com PostgreSQL. Para obter mais informações sobre como configurar e configurar o Agente Datadog, consulte Configurar Agente Datadog. Para obter detalhes adicionais sobre solução de problemas e depuração no Agente Datadog, consulte Uso Básico do Agente Datadog.

  3. Selecione o OCI como a integração e prossiga com sua instalação. A imagem a seguir mostra a pós-instalação da integração do OCI para Datadog.

    image

  4. Clique em Adicionar Tenancy e digite as informações do OCID da Tenancy e da Região Home.

    image

Tarefa 2: Criar Recursos de Autenticação do Datadog

Crie um usuário, grupo e política de autenticação do Datadog no OCI (Oracle Cloud Infrastructure).

  1. Para criar um domínio, navegue até Identidade e crie um domínio chamado DataDog.

  2. Crie um grupo chamado DatadogAuthGroup.

  3. Crie um usuário chamado DatadogAuthUser usando seu endereço de e-mail (o mesmo e-mail usado para fazer log-in na Ferramenta de Monitoramento do Datadog) e designe DatadogAuthGroup como o grupo.

  4. Copie o OCID do Usuário e cole-o no campo OCID do Usuário no tile de integração do Datadog OCI para configurar o OCID do usuário.

  5. Configurar API.

    1. Navegue até seu perfil e selecione seu nome de usuário.

    2. Navegue até Recursos no canto inferior esquerdo e selecione Chaves de API.

    3. Clique em Adicionar Chave de API, faça download da chave privada e clique em Adicionar.

    4. Feche a janela Visualização do Arquivo de Configuração. Não é necessária uma ação.

    5. Copie o valor de Impressão Digital e cole-o no campo Impressão Digital no tile de integração do Datadog OCI.

  6. Configure a chave privada.

    1. Abra o arquivo de chave privada baixado (.pem) em um editor de texto ou use um comando de terminal (por exemplo, cat) para exibir seu conteúdo.

    2. Copie a chave inteira, incluindo as linhas -----BEGIN PRIVATE KEY----- e -----END PRIVATE KEY-----.

    3. Cole a chave privada no campo Chave Privada no tile de integração Datadog OCI.

  7. Crie uma política chamada DataDogPolicy no compartimento postgresqlinteg (raiz).

  8. Use o construtor de políticas no modo de editor manual para informar a instrução de política necessária.

    Allow group DatadogAuthGroup to read all-resources in tenancy
    

Veja a seguir um mosaico de integração do Datadog OCI após adicionar detalhes da tenancy e do usuário.

image

Tarefa 3: Criar uma Pilha do OCI

image

Navegue até a seção Identidade e crie uma pilha de políticas no compartimento raiz. Isso permite que os hubs do conector leiam métricas e chamem funções com as instruções a seguir.

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

Para configurar políticas de identidade e implantar pilhas de encaminhamento de métricas na integração do OCI for Datadog, siga as tarefas:

Tarefa 3.1: Criar Pilha de Políticas (ORM_policy_stack)

  1. Clique em Criar Pilha de Políticas no tile de integração do Datadog OCI, certifique-se de usar o link fornecido, que inclui o script necessário do Terraform e aceite os Termos de Uso da Oracle.

  2. Clique no menu drop-down Diretório de Trabalho e selecione datadog-oci-orm/policy-setup.

  3. Desmarque Usar provedores Terraform personalizados.

  4. Digite um nome descritivo (por exemplo, datadog-metrics-policy-setup) e selecione o compartimento para implantação.

  5. Clique em Próximo, nomeie o grupo dinâmico e a política (ou use nomes padrão), certifique-se de que a região home da tenancy esteja selecionada e clique em Criar.

Tarefa 3.2: Criar Pilha de Encaminhamento de Métrica

Os recursos são implantados no compartimento especificado. Certifique-se de que o usuário que está executando a pilha tenha direitos de acesso apropriados.

  1. Clique em Criar Pilha de Políticas no tile de integração do Datadog OCI e aceite os Termos de Uso da Oracle.

  2. Clique no menu drop-down Diretório de Trabalho, selecione datadog-oci-orm/metrics-setup e desmarque Usar provedores Terraform personalizados.

  3. Nomeie a pilha e selecione o compartimento de implantação; em seguida, clique em Próximo.

  4. Deixe os valores da tenancy não modificados, digite sua chave de API Datadog e selecione o ponto final US5 (ocimetrics-intake.us5.datadoghq.com).

  5. Para configuração de rede, certifique-se de que Criar VCN esteja marcada e selecione o compartimento apropriado para criação de VCN.

  6. Na seção Definições de Função, mantenha a forma de aplicativo padrão como GENERIC_ARM. Informe o nome de usuário e a senha do registro do OCI Docker (senha do atributo).

  7. Defina o tamanho do batch do Service Connector Hub como 5000 e clique em Próximo.

  8. Clique em Criar.

Tarefa 3.3: Finalizar a Configuração

  1. Retorne ao tile de integração do Datadog OCI e clique em Criar configuração para concluir a configuração.

  2. Esse processo garante que as métricas e funções do Datadog estejam configuradas corretamente para integração com a OCI.

    image

Tarefa 4: Criar Funções do OCI

Para criar um aplicativo na Console do OCI, siga as etapas:

  1. Navegue até Aplicativos e selecione Criar Aplicativo.

  2. Informe o nome do aplicativo, selecione a Rede Virtual na Nuvem (VCN) e os detalhes da sub-rede apropriados e clique em Criar.

  3. Para acessar o aplicativo recém-criado, em Recursos, selecione Conceitos básicos.

    image

  4. Clique em Iniciar o Cloud Shell e copie os comandos a seguir de Usar o contexto da sua região.

    fn list context
    fn use context <region name>
    
  5. Atualize o contexto para incluir o ID do compartimento da função.

    fn update context oracle.compartment-id <compartment-id>
    
  6. Atualize o contexto para incluir o local do registro que deseja usar.

    fn update context registry phx.ocir.io/<tenancy_name>/[YOUR-OCIR-REPO]
    

    Observação: Substitua phx no contexto pelo código de região de três dígitos.

  7. Faça log-in no registro usando o token de autenticação como senha.

    docker login -u 'TENACNY_NAME/OCI_USERNAME' phx.ocir.io
    
  8. Será solicitada uma senha. Forneça sua senha apropriada.

    Observação:

    • Substitua phx pelo código de região de três dígitos.
    • Se você estiver usando o Oracle Identity Cloud Service, seu nome de usuário será <tenancyname>/oracleidentitycloudservice/<username>.
  9. Gerar uma função padrão hello-world.

    fn list apps
    fn init --runtime python datadog
    

    O comando fn init gerará uma pasta chamada datadog com três arquivos dentro; func.py, func.yaml e requirements.txt.

  10. Execute o comando cd datadog.

  11. Abra func.py e substitua o conteúdo do arquivo pelo trecho de código a seguir.

    # 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')
    
  12. Atualize func.yaml com o código a seguir. Substitua DATADOG_TOKEN pela sua chave de API Datadog e DATADOG_HOST pelo ponto final REST - https://http-intake.logs.datadoghq.com/v1/input . Para obter mais informações sobre o ponto final REST, consulte Coleta e Integrações de Logs.

    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
    
  13. Atualize requirements.txt com o código a seguir.

    fdk
    dattime
    requests
    oci
    
  14. Execute o comando a seguir para criar o aplicativo e implantar as funções para concluir a configuração.

    fn create app datadog01 --annotation oracle.com/oci/subnetIds='["Provide your subnet OCID"]'
    
  15. Execute o comando a seguir para implantar as funções para concluir a configuração.

    fn -v deploy --app datadog
    

Tarefa 5: Configurar um OCI Connector Hub

  1. Vá para a Console do OCI, navegue até Logging, Conectores e clique em Criar Conector.

  2. Defina a Origem como Monitoramento e o Destino como Funções.

  3. Em Configurar Conexão de Origem, selecione o Compartimento de Métricas e o Namespace apropriados. Por exemplo, oci_postgresql para monitoramento do banco de dados.

  4. Em Configurar Destino, selecione o Compartimento, o Aplicativo de Função e a Função criados na Tarefa 4.

  5. Se solicitado, clique em Criar para criar a política necessária.

  6. Clique em Criar para finalizar a configuração do OCI Connector Hub.

image

Tarefa 6: Exibir Métricas no Datadog

O OCI Connector Hub agora está configurado para acionar a função, permitindo a ingestão de métricas no Datadog sempre que novas métricas são detectadas. No mosaico Integração do Datadog, navegue até Métricas e revise o resumo para exibir métricas relacionadas ao OCI.

image

No mosaico Integração do Datadog, clique em Explorer para analisar e selecionar as métricas necessárias do OCI, conforme necessário,

image

Diagnosticando e Solucionando Problemas

Se nenhum dado aparecer na página Resumo de Métricas, selecione Ativar log para ativar o registro em log de suas funções para revisar logs e depurar o problema.

image

Confirmações

Mais Recursos de Aprendizagem

Explore outros laboratórios em docs.oracle.com/learn ou acesse mais conteúdo de aprendizado gratuito no canal Oracle Learning YouTube. Além disso, visite education.oracle.com/learning-explorer para se tornar um Oracle Learning Explorer.

Para obter a documentação do produto, visite o Oracle Help Center.