Connecteur HDFS pour Object Storage

Le connecteur HDFS (Hadoop Distributed File System) permet l'application Apache Hadoop de lire et d'écrire les données depuis et vers le service Oracle Cloud Infrastructure Object Storage.

Ce kit SDK et cet exemple sont concédés sous double licence : Universal Permissive License 1.0 et Apache 2.0. Le contenu tiers fait l'objet d'une licence distincte comme décrit dans le code.

Exigences

Pour utiliser le connecteur HDFS, vous devez disposer des éléments suivants :

Informations d'identification et mots de passe

Si vous utilisez un fichier PEM crypté pour les informations d'identification, la phrase de passe est lue à partir de la configuration à l'aide de la méthode de configuration Hadoop getPassword. L'option getPassword recherche un mot de passe dans un fournisseur de sécurité inscrit. Si le fournisseur de sécurité ne contient pas la clé demandée, il lit la phrase de passe en texte brut directement à partir du fichier de configuration.

Configuration d'une durée de vie de JVM pour les recherches de nom DNS

La JVM (Java Virtual Machine) met en mémoire cache les réponses DNS des recherches pendant une durée définie, appelée durée de vie. Cela permet de garantir un temps de réponse plus rapide dans le code nécessitant une résolution fréquente des noms.

La JVM utilise la propriété networkaddress.cache.ttl pour définir la stratégie de mise en mémoire cache des recherches de noms DNS. La valeur est un entier qui représente le nombre de secondes nécessaires à la mise en mémoire cache de la recherche effectuée. La valeur par défaut pour de nombreuses JVM, -1, indique que la recherche doit être mise en mémoire cache de manière permanente.

Etant donné que les ressources dans Oracle Cloud Infrastructure utilisent des noms DNS qui peuvent changer, nous vous recommandons de modifier la valeur de la période de temps sur 60 secondes. Cela garantit que la nouvelle adresse IP pour la ressource soit renvoyée lors de la prochaine requête DNS. Vous pouvez modifier cette valeur de manière globale ou spécifiquement pour votre application :

  • Afin de définir la durée de vie de manière globale pour toutes les applications utilisant la JVM, ajoutez ce qui suit au fichier $JAVA_HOME/jre/lib/security/java.security :

    networkaddress.cache.ttl=60
  • Afin de définir la durée de vie uniquement pour votre application, définissez ce qui suit dans le code d'initialisation de votre application :

    java.security.Security.setProperty("networkaddress.cache.ttl" , "60");

Installation

Copiez les fichiers JAR regroupés à partir de lib et de third-party/lib vers chaque noeud du cluster Hadoop afin qu'ils soient inclus dans la variable d'environnement CLASSPATH de Hadoop.

Kit SDK pour Java et artefacts Maven

La création d'un connecteur HDFS repose sur des artefacts Maven fournis par le kit SDK Oracle Cloud Infrastructure pour Java. Pour obtenir les artefacts, vous devez télécharger le kit SDK pour Java et le créer en local. Vous pouvez ensuite créer le connecteur HDFS.

Important

La version d'un fichier SDK pour Java que vous téléchargez à partir de la page des versions Oracle doit correspondre à la version d'un connecteur HDFS, que vous pouvez trouver dans le fichier hdfs-connector/pom.xml dans le bloc d'étiquettes de dépendance possédant l'attribut groupId.

Connecteur HDFS et artefacts Maven

Le connecteur HDFS est disponible sur Maven Central et JCenter .

Pour utiliser le connecteur HDFS dans votre projet, importez la dépendance de projet suivante. Par exemple :

<dependency>
  <groupId>com.oracle.oci.sdk</groupId>
  <artifactId>oci-hdfs-connector</artifactId>
  <!-- Replace the version below with your required version -->
  <version>2.9.2.0</version>
</dependency>

Propriétés

Vous pouvez définir les propriétés de connecteur HDFS suivantes dans le fichier core-site.xml. La page BmcProperties répertorie des propriétés supplémentaires que vous pouvez configurer pour une connexion à Object Storage.

Propriété Description Type Requise
fs.oci.client.hostname

URL de l'adresse hôte.

Par exemple, https://www.example.com.

Chaîne Oui
fs.oci.client.auth.tenantId

OCID de votre location.

Pour obtenir la valeur, reportez-vous à Clés et OCID requis.

Chaîne Oui
fs.oci.client.auth.userId

OCID de l'utilisateur appelant l'API.

Pour obtenir la valeur, reportez-vous à Clés et OCID requis.

Chaîne Oui
fs.oci.client.auth.fingerprint

Empreinte de la paire de clés utilisée.

Pour obtenir la valeur, reportez-vous à Clés et OCID requis.

Chaîne

Oui, sauf si vous fournissez un authentificateur personnalisé.

fs.oci.client.auth.pemfilepath Nom de fichier et chemin complet de la clé privée utilisée pour l'authentification. Le fichier doit se trouver sur le système de fichiers local. Chaîne Oui, sauf si vous fournissez un authentificateur personnalisé.
fs.oci.client.auth.passphrase Phrase de passe utilisée pour la clé, si elle est cryptée. Chaîne Uniquement si votre clé est cryptée.
fs.oci.client.regionCodeOrId Code ou identificateur de région utilisé pour établir le nom d'adresse Object Storage. Chaîne Non
Remarque

Vous pouvez indiquer que la valeur d'une propriété s'applique à un bucket spécifique en ajoutant .<bucket_name>.<namespace_name> au nom de la propriété.

Définition de l'adresse de région

Vous pouvez utiliser plusieurs méthodes afin de définir l'adresse de région pour le connecteur HDFS :

  • Spécification de la propriété de nom d'hôte dans core-site.xml
  • Spécification de la propriété de code de région ou d'identificateur de région dans core-site.xml
  • Autorisation de récupération de l'adresse via le service de métadonnées d'instance par le client ObjectStorage

Configuration de propriétés avec core-site.xml

Cet exemple montre comment les propriétés peuvent être configurées dans un fichier core-site.xml (les OCID sont abrégés dans un souci de concision) :

<configuration>
...
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-ashburn-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.hostname.myBucket.myNamespace</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value><!-- Use Phoenix for myBucket@myNamespace -->
  </property>
  <property>
    <name>fs.oci.client.auth.tenantId</name>
    <value>ocid1.tenancy.oc1..exampleuniqueID</value> 
  </property>
  <property>
    <name>fs.oci.client.auth.userId</name>
    <value>ocid1.user.oc1..exampleuniqueID</value>
  </property>
  <property>
    <name>fs.oci.client.auth.fingerprint</name>
    <value>20:3b:97:13:55:1c:5b:0d:d3:37:d8:50:4e:c5:3a:34</value>
  </property>
  <property>
    <name>fs.oci.client.auth.pemfilepath</name>
    <value>~/.oci/oci_api_key.pem</value>
  </property>
...
</configuration>

Utilisation de principaux d'instance pour l'authentification

Oracle fournit des principaux d'instance afin que vous n'ayez plus à configurer des informations d'identification utilisateur ou à fournir des fichiers PEM sur les services exécutés sur des instances. Chacune de ces instances possède sa propre identité et s'authentifie à l'aide de certificats ajoutés à l'instance par les principaux d'instance.

Pour utiliser l'authentification via un principal d'instance avec le connecteur HDFS, indiquez simplement la propriété fs.oci.client.custom.authenticator et définissez la valeur sur com.oracle.bmc.hdfs.auth.InstancePrincipalsCustomAuthenticator.

Etant donné que l'utilisation de principaux d'instance fournit au connecteur un authentificateur personnalisé, il n'est plus nécessaire de configurer les propriétés suivantes :

  • fs.oci.client.auth.tenantId
  • fs.oci.client.auth.userId
  • fs.oci.client.auth.fingerprint
  • fs.oci.client.auth.pemfilepath
  • fs.oci.client.auth.passphrase

L'exemple de code suivant illustre l'utilisation de principaux d'instance pour l'authentification auprès du connecteur HDFS :

<?xml version="1.0"?>
<configuration>
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.custom.authenticator</name>
    <value>com.oracle.bmc.hdfs.auth.InstancePrincipalsCustomAuthenticator</value>
  </property>
</configuration>

Pour plus d'informations sur les principaux d'instance, reportez-vous à Annonce des principaux d'instance pour Identity and Access Management.

Utilisation de principaux de ressource pour l'authentification

A l'instar des principaux d'instance, Oracle fournit desprincipaux de ressources pour authentifier les ressources qui ne sont pas une instance (comme un bloc-notes jupyter). Chaque ressource possède sa propre identité et est authentifiée à l'aide des certificats qui lui sont ajoutés.

Pour utiliser l'authentification via un principal de ressource avec le connecteur HDFS, indiquez simplement la propriété fs.oci.client.custom.authenticator et définissez la valeur sur com.oracle.bmc.hdfs.auth.ResourcePrincipalsCustomAuthenticator.

Etant donné que l'utilisation de principaux de ressource fournit au connecteur un authentificateur personnalisé, il n'est pas nécessaire de configurer les propriétés suivantes :

  • fs.oci.client.auth.tenantId
  • fs.oci.client.auth.userId
  • fs.oci.client.auth.fingerprint
  • fs.oci.client.auth.pemfilepath
  • fs.oci.client.auth.passphrase

L'exemple de code suivant illustre l'utilisation de principaux de ressource pour l'authentification auprès du connecteur HDFS :

<?xml version="1.0"?>
<configuration>
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.custom.authenticator</name>
    <value>com.oracle.bmc.hdfs.auth.ResourcePrincipalsCustomAuthenticator</value>
  </property>
</configuration>

Pour plus d'informations sur les principaux d'instance, reportez-vous à Utilisation de principaux de ressource dans le service Data Science.

Utilisation de l'authentification Kerberos

Oracle prend en charge l'authentification Kerberos pour la connexion à Object Storage à l'aide du connecteur HDFS.

Pour utiliser l'authentification Kerberos avec le connecteur HDFS :
  1. Dans core-site.xml, définissez la propriété fs.oci.client.custom.authenticator sur com.oracle.bmc.hdfs.auth.spnego.UPSTAuthenticationCustomAuthenticator.
  2. Dans core-site.xml, définissez les propriétés suivantes :
    • fs.oci.client.upst.domainUrl
    • fs.oci.client.upst.clientId

    • fs.oci.client.upst.clientSecret

    • fs.oci.client.upst.tokenExchangeServicePrincipal

    • fs.oci.client.upst.userPrincipal

    • fs.oci.client.upst.issuer
    • fs.oci.client.keytab.path
    • fs.oci.client.kinit.internal.mode

L'exemple de fichier core-site.xml suivant illustre l'utilisation de Kerberos avec l'authentification par jeton SPNEGO avec le connecteur HDFS :

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
  <property>
    <name>fs.oci.client.hostname</name>
    <value>https://objectstorage.us-phoenix-1.oraclecloud.com</value>
  </property>
  <property>
    <name>fs.oci.client.auth.tenantId</name>
    <value></value>
  </property>
  <property>
    <name>fs.oci.client.auth.userId</name>
    <value></value>
  </property>
   <property>
    <name>fs.oci.client.custom.authenticator</name>
    <value>com.oracle.bmc.hdfs.auth.spnego.UPSTAuthenticationCustomAuthenticator</value>
  </property>
  <property>
    <name>fs.oci.client.upst.tokenExchangeServicePrincipal</name>
    <value><!-- Service Principal for generating SPNEGO token --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.userPrincipal</name>
    <value><!-- User Principal for generating SPNEGO token --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.issuer</name>
    <value><!-- Issuer for generating SPNEGO token --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.domainUrl</name>
    <value><!-- Domain application client ID for IAM token exchange --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.clientId</name>
    <value><!-- Domain application client ID for IAM token exchange --></value>
  </property>
  <property>
    <name>fs.oci.client.upst.clientSecret</name>
    <value><!-- Domain application client secret for IAM token exchange --></value>
  </property>
  <property>
    <name>fs.oci.client.keytab.path</name>
    <value><!-- File path to the keytab used for token exchange --></value>
  </property>
 </configuration>

Pour plus d'informations sur Kerberos, reportez-vous au Tutoriel sur le protocole Kerberos.

Pour plus d'informations sur SPNEGO, reportez-vous à la norme RFC 4178.

Utilisation du connecteur par défaut Jersey HttpUrlConnectorProvider

A partir de la version 3.3.0.7.0.0, HDFS prend en charge l'utilisation par défaut du client Apache pour effectuer des appels de service OCI. En effet, le connecteur HDFS s'appuie sur le kit SDK pour Java afin d'envoyer des demandes au serveur. Le kit SDK pour Java prend en charge l'utilisation du connecteur Jersey ApacheConnectorProvider par défaut au lieu du connecteur Jersey HttpUrlConnectorProvider pour permettre au client Apache HttpClient d'effectuer des appels de service OCI.

Pour revenir à l'ancien client Jersey par défaut, définissez la propriété fs.oci.client.jersey.default.connector.enabled dans le fichier core-site.xml sur true. Par défaut, cette valeur est définie sur false.

Optimisation des performances avec le connecteur Apache pour HDFS

Le connecteur Apache prend en charge deux stratégies de fermeture de connexion : ApacheConnectionClosingStrategy.GracefulClosingStrategy et ApacheConnectionClosingStrategy.ImmediateClosingStrategy.

Avec ApacheConnectionClosingStrategy.GracefulClosingStrategy, les flux de données renvoyés à partir d'une réponse sont lus jusqu'à la fin du flux lors de sa fermeture. Cela peut entraîner un délai supplémentaire lors de la fermeture du flux de données avec une lecture partielle, en fonction de la taille du flux restant. Pour éviter ce délai, envisagez d'utiliser ApacheConnectionClosingStrategy.ImmediateClosingStrategy pour les fichiers volumineux avec des lectures partielles. Avec ApacheConnectionClosingStrategy.ImmediateClosingStrategy, les flux de données ne sont lus qu'à la fin lors de leur fermeture, ce qui peut améliorer les performances. ApacheConnectionClosingStrategy.ImmediateClosingStrategy prend plus de temps lors de l'utilisation de la lecture partielle pour des flux de données de plus petite taille (inférieurs à 1 Mo).

Définition de la stratégie de fermeture de connexion

Définissez la stratégie de fermeture de connexion en définissant la propriété fs.oci.client.apache.connection.closing.strategy dans le fichier core-site.xml :

  • Pour utiliser ApacheConnectionClosingStrategy.GracefulClosingStrategy, définissez fs.oci.client.apache.connection.closing.strategy sur graceful.
  • Pour utiliser ApacheConnectionClosingStrategy.ImmediateClosingStrategy, définissez fs.oci.client.apache.connection.closing.strategy sur immediate.
Remarque

Ces stratégies de fermeture fonctionnent uniquement avec le connecteur Apache pour HDFS et sont ignorées lors de l'utilisation du connecteur par défaut Jersey.

Retour au connecteur par défaut Jersey

Le connecteur par défaut Jersey lit les flux de données jusqu'à la fin, puis les réutilise, ce qui peut améliorer les performances par rapport au connecteur Apache pour HDFS dans certains scénarios. Si ces stratégies de fermeture de connexion Apache ne vous donnent pas des résultats optimaux pour vos cas d'emploi, vous pouvez revenir au connecteur par défaut Jersey 'HttpUrlConnectorProvider'. Vous pouvez revenir à l'ancien client par défaut Jersey en définissant la propriété fs.oci.client.jersey.default.connector.enabled dans le fichier core-site.xml sur true. Par défaut, cette valeur est définie sur false.

Pour plus d'informations, reportez-vous à : https://github.com/oracle/oci-java-sdk/blob/master/ApacheConnector-README.md.

Regroupement de connexions en pool dans HDFS

Vous pouvez définir le nombre maximal de connexions dans le pool de connexions du connecteur HDFS.

Pour ce faire, remplacez la propriété fs.oci.client.apache.max.connection.pool.size dans le fichier core-site.xml par un entier positif qui indique le nombre de connexions à regrouper dans le pool.

Remarque

Cette propriété est uniquement prise en charge lors de l'utilisation de ApacheConnector pour HDFS. Sinon, elle est ignorée.

Adresses dédiées

Les adresses dédiées sont les modèles d'adresse définis par un service pour un domaine spécifique au niveau du client. Le connecteur OCI HDFS vous permet d'activer l'utilisation de ces modèles d'adresse propres au domaine en définissant le nom d'hôte fs.oci.client.hostname ou la propriété fs.oci.realmspecific.endpoint.template.enabled de l'indicateur de modèle d'adresse dans core-site.xml.
Remarque

Si vous définissez la propriété de modèle d'adresse, vous devez également définir fs.oci.client.regionCodeOrId dans core-site.xml.
Remarque

La valeur définie via le nom d'hôte dans core-site.xml est prioritaire sur la valeur définie à l'aide de la propriété de modèle d'adresse dans core-site.xml.
Cet exemple montre comment activer la fonctionnalité de modèles d'adresse propre au domaine en définissant la propriété fs.oci.client.hostname :
<property>
  <name>fs.oci.client.hostname</name>
  <value>https://my-namespace.objectstorage.me-dubai-1.oci.customer-oci.com</value>
</property>
Cet exemple montre comment activer la fonctionnalité des modèles d'adresse propres au domaine en définissant la propriété fs.oci.realmspecific.endpoint.template.enabled :
<property>
  <name>fs.oci.client.regionCodeOrId</name>
  <value>me-dubai-1</value>
</property>
<property>
  <name>fs.oci.realmspecific.endpoint.template.enabled</name>
  <value>true</value>
</property>

Configuration d'un proxy HTTP

Vous pouvez définir les propriétés facultatives suivantes dans le fichier core-site.xml pour configurer un proxy HTTP :

Propriété Description Type Requise
fs.oci.client.proxy.uri

URI de l'adresse de proxy.

Par exemple, http://proxy.mydomain.com:80.

Chaîne Non
fs.oci.client.proxy.username Nom utilisateur pour l'authentification auprès du proxy. Chaîne Non
fs.oci.client.proxy.password Mot de passe pour l'authentification auprès du proxy. Chaîne Non
fs.oci.client.multipart.allowed Permet au gestionnaire de téléchargement de prendre en charge les téléchargements multipart vers le serveur. Valeur booléenne Non
fs.oci.client.multipart.minobjectsize.mb

Indique la taille minimale des objets en mébioctets afin d'utiliser le gestionnaire de téléchargement.

Entier Non
fs.oci.client.multipart.partsize.mb Indique la taille des parties en mébioctets pour le gestionnaire de téléchargement. Entier Non
Remarque

La configuration d'un proxy permet d'utiliser ApacheConnectorProvider lors de l'établissement de connexions à Object Storage. Il met les demandes en mémoire tampon et peut avoir une incidence sur l'utilisation de la mémoire lors du téléchargement d'objets volumineux vers le serveur. Il est recommandé d'activer les téléchargements multipart vers le serveur et d'ajuster les propriétés multipart pour gérer l'utilisation de la mémoire.

Téléchargements d'objets volumineux vers le serveur

Les objets volumineux sont téléchargés vers Object Storage via des téléchargements multipart. Le fichier est divisé en petites parties qui sont téléchargées en parallèle, ce qui réduit le temps de téléchargement. Cela permet également au connecteur HDFS de réessayer de télécharger les parties ayant échoué au lieu de faire échouer l'ensemble du téléchargement. Toutefois, les téléchargements peuvent échouer de façon temporaire, auquel cas le connecteur tente d'abandonner les fichiers partiellement téléchargés. Etant donné que ces fichiers s'accumulent (et que vous serez facturé pour le stockage), répertoriez les téléchargements régulièrement, puis après un certain nombre de jours, abandonnez-les manuellement en utilisant le kit SDK pour Java.

Pour plus d'informations sur l'utilisation de l'API Object Storage concernant la gestion des téléchargements multipart, reportez-vous à Utilisation des téléchargements multipart vers le serveur.

Remarque

Si vous préférez ne pas utiliser les téléchargements multipart vers le serveur, vous pouvez les désactiver en définissant la propriété fs.oci.client.multipart.allowed sur false.

Meilleures pratiques

Les sections suivantes présentent les meilleures pratiques pour l'optimisation des performances et de l'utilisation.

Noms de répertoire

Il n'existe aucun répertoire réel dans Object Storage. Le regroupement de répertoires est une fonction de convention de dénomination, où les objets utilisent des délimiteurs / dans leur nom. Par exemple, un objet nommé a/example.json implique l'existence d'un répertoire nommé a. Toutefois, si cet objet est supprimé, le répertoire a est également supprimé de façon implicite. Pour conserver la sémantique du système de fichiers où le répertoire peut exister sans la présence de fichiers, le connecteur HDFS crée un objet réel dont le nom se termine par / avec un chemin représentant le répertoire (en l'occurrence, un objet nommé a/). Ainsi, la suppression de a/example.json n'a pas d'incidence sur l'existence du répertoire a car l'objet a/ reste présent. Toutefois, il est tout à fait possible de supprimer cet objet a/ sans supprimer les fichiers/répertoires qui se trouvent dedans. Le connecteur HDFS supprime uniquement l'objet de dossier s'il n'existe aucun objet sous ce chemin. L'objet de dossier lui-même représente zéro octet.

Système de fichiers incohérent

La suppression d'un répertoire implique la suppression de tous les objets qui commencent par le préfixe représentant ce répertoire. HDFS vous permet d'interroger le statut de fichier d'un répertoire ou d'un fichier. Le statut de fichier d'un répertoire est implémenté en vérifiant que l'objet de dossier existe pour ce répertoire. Toutefois, il est possible que l'objet de dossier ait été supprimé, mais que certains des objets dotés de ce préfixe existent toujours. Par exemple, s'il s'agit de ces objets :

  • a/b/example.json
  • a/b/file.json
  • a/b/

HDFS saurait que le répertoire /a/b/ existe et qu'il s'agit d'un répertoire, et l'analyse de ce dernier génèrerait example.json et file.json. Toutefois, si l'objet a/b/ était supprimé, le système de fichiers aurait un état incohérent. Vous pouvez l'interroger pour tous les fichiers du répertoire /a/b/ et trouver les deux entrées. Toutefois, si vous interrogez le statut du répertoire /a/b/ réel, une exception serait générée car le répertoire n'existe pas. Le connecteur HDFS ne tente pas de corriger l'état du système de fichiers.

Création de fichiers

Object Storage prend en charge les objets de plusieurs gigaoctets. La création des fichiers est normalement effectuée par l'écriture dans un fichier temporaire, puis par le téléchargement vers le serveur du contenu du fichier à la fermeture du flux de données. L'espace temporaire doit être suffisant pour permettre la gestion de plusieurs téléchargements. Le répertoire temporaire utilisé est contrôlé par la propriété de configuration hadoop.tmp.dir.

Prise en charge de la lecture/recherche

Lorsque les tampons en mémoire sont activés (fs.oci.io.read.inmemory), la recherche est entièrement prise en charge car l'intégralité du fichier est mise en mémoire tampon dans un tableau d'octets. Lorsque le tampon en mémoire n'est pas activé (probablement parce que les objets sont volumineux), la recherche est implémentée en fermant le flux de données et en effectuant une nouvelle demande de plage commençant au niveau du décalage indiqué.

Liste des répertoires

La création de la liste des répertoires correspond à une opération de création de liste de buckets avec un préfixe et un délimiteur spécifiés. Pour créer une instance FileStatus HDFS pour chaque clé, le connecteur effectue une demande HEAD supplémentaire afin d'obtenir ObjectMetadata pour chaque clé. Cette étape est requise jusqu'à ce qu'Object Storage prenne en charge des données d'opération de liste plus riches.

Format d'URI pour les systèmes de fichiers et les fichiers

Les fichiers et les systèmes de fichiers HDFS sont référencés via des URI. Le modèle précise le type de système de fichiers, et la partie restante de l'URI est libre pour que l'implémentation du système de fichiers l'interprète comme elle le souhaite.

Object Storage étant une banque d'objets, sa capacité à nommer les objets comme s'ils étaient des fichiers dans un système à fichiers est utilisée pour imiter un système à fichiers réel.

Racine

La racine du système De fichiers Object Storage est signalée par un chemin dont le composant d'autorité inclut le Nom de bucket et le Nom d'espace de noms, comme indiqué ci-dessous :

Remarque

Dans les exemples, "MyBucket" et "MyNamespace" sont des espaces réservés et doivent être remplacés par les valeurs appropriées.

oci://MyBucket@MyNamespace/
		

Il s'agit toujours de la racine du système de fichiers. La raison pour laquelle l'autorité est utilisée pour le bucket et l'espace de noms est que HDFS autorise uniquement la partie dédiée à l'autorité à déterminer l'emplacement du système de fichiers. La partie du chemin indique uniquement le chemin vers la ressource (par exemple, "oci/MyNamespace/MyBucket" ne fonctionne pas). Le caractère @ n'est pas un caractère valide pour les buckets ou les espaces de noms, et doit permettre à l'autorité d'être analysée correctement.

Sous-répertoires

Les sous-répertoires n'existent pas réellement, mais vous pouvez les imiter en créant des objets avec des caractères /. Par exemple, deux fichiers nommés a/b/c/example.json et a/b/d/path.json apparaîtraient comme s'ils se trouvaient dans un même répertoire commun a/b. Pour ce faire, vous devez utiliser l'interrogation basée sur un préfixe et sur un délimiteur Object Storage. Dans l'exemple donné, le référencement d'un sous-répertoire en tant qu'URI correspondrait à ce qui suit :


oci://MyBucket@MyNamespace/a/b/
		

Objets/fichiers

Un objet nommé a/b/c/example.json est référencé comme suit :


oci://MyBucket@MyNamespace/a/b/c/example.json
		

Journalisation

La journalisation dans le connecteur est effectuée via SLF4J. SLF4J est une abstraction de journalisation qui permet d'utiliser une bibliothèque de journalisation fournie par l'utilisateur (par exemple, log4j). Pour plus d'informations, reportez-vous au manuel de SLF4J.

L'exemple suivant montre comment activer la journalisation de base sur une sortie standard.

  1. Téléchargez le fichier JAR de liaison simple SLF4J : liaison simple SLF4J.
  2. Ajoutez le fichier JAR à votre variable d'environnement CLASSPATH.
  3. Ajoutez l'argument de machine virtuelle suivant pour activer la journalisation de niveau débogage (par défaut, le niveau information est utilisé) : -Dorg.slf4j.simpleLogger.defaultLogLevel=debug.

Vous pouvez configurer des options de journalisation plus avancées à l'aide de la liaison log4j.

Utiliser la structure de surveillance

Le connecteur HDFS pour Object Storage inclut une structure de surveillance qui fournit des mesures sur les opérations effectuées à l'aide du connecteur. La structure de surveillance fournit une interface qui peut être implémentée pour utiliser/écouter les mesures générées par le connecteur. Vous pouvez fournir une implémentation personnalisée de cette interface ou utiliser l'implémentation de télémétrie publique OCI incluse dans cette structure.

Mise en route

Pour commencer à utiliser la structure de surveillance du connecteur HDFS, vous devez définir les propriétés suivantes. Une fois ces propriétés définies pour OCIMonitoring, vous pouvez utiliser la vue Explorateur de mesures dans la console OCI pour observer les mesures émises à partir du connecteur HDFS.

fs.oci.mon.consumer.plugins

fs.oci.mon.consumer.plugins prend une liste de noms de classe qualifiés complets des implémentations de l'interface de surveillance, séparés par des virgules. com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin doit être utilisé dans la liste si vous voulez que les mesures soient émises vers la télémétrie publique OCI.
<property>
    <name>fs.oci.mon.consumer.plugins</name>
    <value>com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin,com.your.new.plugin.PluginImpl1</value>
</property>

fs.oci.mon.grouping.cluster.id

La propriété fs.oci.mon.grouping.cluster.id indique l'identificateur du cluster HDFS ou de tout autre ID dans lequel regrouper les mesures. Cette propriété est obligatoire. Elle est également utilisée par OCIMonitorPlugin pour baliser les mesures. Cette propriété est visible en tant que dimension dans l'API et l'interface utilisateur de télémétrie publique OCI.
<property>
    <name>fs.oci.mon.grouping.cluster.id</name>
    <value>hdfs-sample-cluster-id</value>
</property>

Propriétés com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin

Si la propriété com.oracle.bmc.hdfs.monitoring.OCIMonitorPlugin est activée, les propriétés suivantes sont applicables :

fs.oci.mon.telemetry.ingestion.endpoint

La propriété fs.oci.mon.telemetry.ingestion.endpoint permet de configurer l'adresse d'inclusion de télémétrie de la surveillance OCI. Pour plus d'informations, voir la liste des points disponibles.
<property>
    <name>fs.oci.mon.telemetry.ingestion.endpoint</name>
    <value>https://telemetry-ingestion.us-ashburn-1.oraclecloud.com</value>
</property>

fs.oci.mon.compartment.ocid

La propriété fs.oci.mon.compartment.ocid permet de configurer le compartiment OCI auquel les mesures seront attachées. Il s'agit généralement du compartiment auquel appartiennent les buckets.
<property>
    <name>fs.oci.mon.compartment.ocid</name>
    <value>ocid1.compartment.oc1..sample.compartment.id</value>
</property>

fs.oci.mon.bucket.level.enabled

La propriété fs.oci.mon.bucket.level.enabled détermine si le nom du bucket doit être attaché en tant que dimension aux mesures émises.
<property>
    <name>fs.oci.mon.bucket.level.enabled</name>
    <value>true</value>
</property>

fs.oci.mon.ns.name

La propriété fs.oci.mon.ns.name contrôle l'espace de noms utilisé pour les mesures émises par le connecteur HDFS. Un exemple d'espace de noms peut être "hdfsconnector". Cela résidera à côté d'autres espaces de noms prédéfinis tels que oci_objectstorage dans la télémétrie publique.
<property>
    <name>fs.oci.mon.ns.name</name>
    <value>name.of.namespace.on.oci.telemetry</value>
</property>

fs.oci.mon.rg.name

La propriété fs.oci.mon.rg.name définit le nom du groupe de ressources utilisé pour contenir les mesures. Il peut s'agir de n'importe quel nom logique de regroupement de ressources qui sera surveillé ensemble. Ce nom de groupe de ressources apparaîtra sous l'espace de noms choisi précédemment dans la télémétrie publique OCI.
<property>
    <name>fs.oci.mon.rg.name</name>
    <value>name.of.resource.group.on.oci.telemetry</value>
</property>

Création de votre propre consommateur pour les mesures

Pour utiliser l'interface com.oracle.bmc.hdfs.monitoring.OCIMonitorConsumerPlugin, vous devez définir deux méthodes :
  • accept
  • shutdown

L'extension des classes doit avoir un constructeur avec la même signature que celle de la classe OCIMonitorConsumerPlugin.

Par exemple :
public OCIMonitorConsumerPlugin(BmcPropertyAccessor propertyAccessor, String bucketName, String monitoringGroupingID, String namespaceName);
Cet exemple illustre chaque méthode qui doit être implémentée par un module d'extension consommateur :
/**
 * This class that has to be extended by any plugin, that wants to consume the metrics emitted by OCI HDFS connector.
 */
public abstract class OCIMonitorConsumerPlugin {
    /**
     * This method will be called on each plugin, by the OCI monitoring framework, whenever it wants to emit out a metric.
     * This method should finish as quickly as possible, so the consumer of this should ideally handover the
     * ocimetric and stage it elsewhere for processing, instead of trying to deal with it in the accept call itself.
     * @param ociMetric The metric that is being emitted by the OCI HDFS connector
     */
    public void accept(OCIMetric ociMetric);
 
    /**
     * This shutdown method will be called on the implementing plugins, whenever the JVM is shutting down.
     * It could be used to cleanup, finish pending tasks before exit.
     */
    public void shutdown();
}

La classe OCIMetric peut être implémentée de trois manières :

Objet OCIMetric simple avec les champs suivants :
public class OCIMetric {
    /**
     * The time in milliseconds (epoch) when the metric was recorded.
     */
    private final long recordedTime;
    /**
     * The overall time taken by the operation to complete/error-out in milliseconds.
     */
    private final double overallTime;
    /**
     * The operation key. This will be one of {"LIST", "HEAD", "WRITE", "READ", "DELETE", "RENAME"}
     */
    private final String key;
    /**
     * The boolean error indicates whether the operation errored out.
     */
    private final boolean error;
    /**
     * The target OCI bucket where the operation was attempted to.
     */
    private final String bucketName;
}
Implémentation de l'objet OCIMetricWithThroughput qui étend OCIMetric et contient des champs supplémentaires pour le débit et les octets transférés. Cela s'applique aux opérations READ et WRITE :
public class OCIMetricWithThroughput extends OCIMetric {
    /**
     * The throughput that was recorded for the operation in bytes/second
     */
    private final double throughput;
    /**
     * The total count of bytes that were transferred in or out.
     */
    private final double bytesTransferred;
}
Objet OCIMetricWithFBLatency qui étend OCIMetricWithThroughput, avec un temps supplémentaire pour le premier champ de latence d'octet. S'applique uniquement aux opérations READ :
public class OCIMetricWithFBLatency extends OCIMetricWithThroughput {
    /**
     * The time to first byte when a read operation was performed in milliseconds.
     */
    private final double ttfb;
}

Exemple de travail Hadoop

hadoop_sample_hdfs :


package com.oracle.oci.hadoop.example;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import org.apache.commons.io.IOUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.oracle.oci.hdfs.BmcFilesystem;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class SampleOracleBmcHadoopJob
{
    private static final String SAMPLE_JOB_PATH = "/samplehadoopjob";
    private static final String INPUT_FILE = SAMPLE_JOB_PATH + "/input.dat";
    private static final String OUTPUT_DIR = SAMPLE_JOB_PATH + "/output";

    // non-static since this is the runner class it needs to initialize after we set the properties
    private final Logger log = LoggerFactory.getLogger(SampleOracleBmcHadoopJob.class);

    /**
     * Runner for sample hadoop job. This expects 3 args: path to configuration file, Object Store namespace, Object
     * Store bucket. To run this, you must:
     *{@code 


         * 
    Create a standard hadoop configuration file

         * 
    Create the bucket ahead of time.

         *} 


     * This runner will create a test input file in a file '/samplehadoopjob/input.dat', and job results will be written
     * to '/samplehadoopjob/output'.
     * 
     * @param args
     *            1) path to configuration file, 2) namespace, 3) bucket
     * @throws Exception
     */
    public static void main(final String[] args) throws Exception
    {
        if (args.length != 3)
        {
            throw new IllegalArgumentException(
                    "Must have 3 args: 1) path to config file, 2) object storage namespace, 3) object storage bucket");
        }

        // redirect all logs to sysout
        System.setProperty("org.slf4j.simpleLogger.logFile", "System.out");
        System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "debug");

        final SampleOracleBmcHadoopJob job = new SampleOracleBmcHadoopJob(args[0], args[1], args[2]);
        System.exit(job.execute());
    }

    private final String configurationFilePath;
    private final String namespace;
    private final String bucket;

    public int execute() throws IOException, ClassNotFoundException, InterruptedException, URISyntaxException
    {
        log.info("Creating hadoop configuration");
        final Configuration configuration = this.createConfiguration(this.configurationFilePath);

        final String authority = this.bucket + "@" + this.namespace;
        final String uri = "oci://" + authority;
        log.info("Using uri: {}", uri);

        log.info("Creating job inputs");
        this.setup(uri, configuration);

        log.info("Creating job");
        final Job job = this.createJob(configuration);

        final String in = uri + INPUT_FILE;
        final String out = uri + OUTPUT_DIR;
        log.info("Using input: {}", in);
        log.info("Using output: {}", out);

        FileInputFormat.addInputPath(job, new Path(in));
        FileOutputFormat.setOutputPath(job, new Path(out));

        log.info("Executing job...");
        final int response = job.waitForCompletion(true) ? 0 : 1;

        log.info("Attempting to read job results");
        this.tryReadResult(uri, configuration);
        return response;
    }

    private Configuration createConfiguration(final String configFilePath)
    {
        final Configuration configuration = new Configuration();
        configuration.addResource(new Path(configFilePath));
        return configuration;
    }

    private void setup(final String uri, final Configuration configuration) throws IOException, URISyntaxException
    {
        try (final BmcFilesystem fs = new BmcFilesystem())
        {
            fs.initialize(new URI(uri), configuration);
            fs.delete(new Path(SAMPLE_JOB_PATH), true);
            final FSDataOutputStream output = fs.create(new Path(INPUT_FILE));
            output.writeChars("example\npath\ngak\ntest\nexample\ngak\n\ngak");
            output.close();
        }
    }

    private Job createJob(final Configuration configuration) throws IOException
    {
        final Job job = Job.getInstance(configuration, "word count");
        job.setJarByClass(SampleOracleBmcHadoopJob.class);
        job.setMapperClass(SimpleMapper.class);
        job.setCombinerClass(SimpleReducer.class);
        job.setReducerClass(SimpleReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);
        return job;
    }

    private void tryReadResult(final String uri, final Configuration configuration)
            throws IOException, URISyntaxException
    {
        try (final BmcFilesystem fs = new BmcFilesystem())
        {
            fs.initialize(new URI(uri), configuration);
            // this should be the output file name, but that could change
            final FSDataInputStream input = fs.open(new Path(OUTPUT_DIR + "/part-r-00000"));

            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IOUtils.copy(input, baos);
            log.info("\n=====\n" + baos.toString() + "=====");
            input.close();
        }
    }
}


package com.oracle.oci.hadoop.example;

import java.io.IOException;
import java.util.StringTokenizer;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class SimpleMapper extends Mapper
{
    private final static IntWritable one = new IntWritable(1);
    private final Text word = new Text();

    @Override
    public void map(final Object key, final Text value, final Context context) throws IOException, InterruptedException
    {
        final StringTokenizer itr = new StringTokenizer(value.toString());
        while (itr.hasMoreTokens())
        {
            this.word.set(itr.nextToken());
            context.write(this.word, one);
        }
    }
}


package com.oracle.oci.hadoop.example;

import java.io.IOException;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class SimpleReducer extends Reducer
{
    private final IntWritable result = new IntWritable();

    @Override
    public void reduce(final Text key, final Iterable values, final Context context)
            throws IOException, InterruptedException
    {
        int sum = 0;
        for (final IntWritable val : values)
        {
            sum += val.get();
        }
        this.result.set(sum);
        context.write(key, this.result);
    }
}

Résolution des problèmes

Cette section contient des informations de résolution pour le connecteur HDFS.

Résolution des erreurs de service

Toute opération provoquant une erreur de service entraîne la génération d'une exception de type com.oracle.bmc.model.BmcException par le connecteur HDFS. Pour plus d'informations sur les erreurs de service courantes renvoyées par OCI, reportez-vous à Erreurs d'API.

Erreurs de taille de clé de cryptage Java

Le connecteur HDFS peut uniquement gérer les clés d'une longueur inférieure ou égale à 128 bits. Les utilisateurs obtiennent les erreurs "Exception de clé non valide" et "Taille de clé interdite" lorsqu'ils utilisent des clés plus longues, comme AES256. Appliquez l'une des solutions de contournement suivantes pour résoudre ce problème :

Notifications

Pour être averti lorsqu'une nouvelle version du connecteur HDFS est disponible, abonnez-vous au flux Atom .