Apache SparkデータをElasticSearchにPythonで書き込む方法

Apache SparkデータをElasticSearchにPythonで書き込む方法

  • オーケストレーション
6分で読めます 更新日 Oct 26, 2018

ポイント: Apache SparkデータをPythonを使用してElasticSearchに書き込むための指示が提供されており、ユーザーのためにプロセスが簡素化されています。このガイドには、ES-HadoopのダウンロードやPySparkの設定、Apacheログファイルの解析やElasticSearchにデータを保存するためのコードスニペットなどが含まれています。

  • 互換性のためにES-Hadoopをダウンロードします。
  • ElasticSearch形式でデータを保存するための特定の設定を使用します。

ここでは、Pythonを使用してApache SparkデータをElasticSearch(ES)に書き込む方法を説明します。ApacheログデータをESに書き込みます。

このトピックは、インターネット上のすべての悪い、複雑な例が原因で複雑になっています。しかし、ここでは簡単にします。

1つの複雑な要因は、SparkがScalaおよびJavaでElasticSearchへの書き込みをネイティブにサポートしているのに対し、Pythonではサポートしていないことです。そのため、ElasticSearchによって書かれたES-Hadoopをダウンロードする必要があります。こちらで入手できますここ。

次に、それをスコープに持ち込んで、次のようにpysparkで利用可能にします:

    pyspark --jars elasticsearch-hadoop-6.4.1.jar

PySparkを次のようにPython 3を使用するように設定します:

    export PYSPARK_PYTHON=/usr/bin/python3

ElasticSearchへの書き込みを理解するための鍵は、ESがJSONデータベースである一方、1つの要件があることです。データはこの形式である必要があります:

    { "id: { the rest of your json}}

以下にその変換を行う方法を示します。

下部には完全なコードがあり、オンラインではこちらで確認できます。ここではセクションごとに説明します:

Apacheログファイルの解析

ApacheログをSpark RDDに読み込みます。次に、各文字列を正規表現グループに読み込むparse()関数を作成し、必要なフィールドを選択して辞書として返します。

    rdd = sc.textFile("/home/ubuntu/walker/apache_logs")

regex='^(\S+) (\S+) (\S+) \[([\w:/]+\s[+\-]\d{4})\] "(\S+)\s?(\S+)?\s?(\S+)?" (\d{3}|-) (\d+|-)\s?"?([^"]*)"?\s?"?([^"]*)?"?$'
    p=re.compile(regex)

def parse(str):
    s=p.match(str)
    d = {}
    d['ip']=s.group(1)
    d['date']=s.group(4)
    d['operation']=s.group(5)
    d['uri']=s.group(6)
    return d

言い換えれば、最初にテキストファイルログをRDDに読み込むと、次のようになります:

    ['83.149.9.216 - - [17/May/2015:10:05:03 +0000] "GET /presentations/logstash-monitorama-2013/images/kibana-search.png HTTP/1.1" 200 203023 "http://semicomplete.com/presentations/logstash-monitorama-2013/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.77 Safari/537.36"']

その後、rddのmap()関数を使用して、各行をparse()関数に渡してこれを生成します。

    rdd2 = rdd.map(parse)

rdd2.take(1)

[{'date': '17/May/2015:10:05:03 +0000', 'ip': '83.149.9.216', 'operation': 'GET', 'uri': '/presentations/logstash-monitorama-2013/images/kibana-search.png'}]

今、これはJSONのように見えますが、まだJSONではありません。次に、json.dumpsを使用します。Python文書の技術的な説明によれば、これは「objをJSON形式のストリームとしてシリアル化する」となっています。

また、IDを追加します。以下のES設定で、ESにどのフィールドがユニークなドキュメント識別子になるかを伝えます: "es.mapping.id": "doc_id".

最初にSHAダイジェストを計算して、ユニークな数値としてそのIDを作成します。

結果は次のように返されます。IDは非常に長いSHA番号で、前にJSONが続きます。

    rdd3.take(1)

[('a5b086b04e1cc45fb4a19e2a641bf99ea3a378599ef62ba12563b75c', '{"date": "17/May/2015:10:05:03 +0000", "ip": "83.149.9.216", "operation": "GET", "doc_id": "a5b086b04e1cc45fb4a19e2a641bf99ea3a378599ef62ba12563b75c", "uri": "/presentations/logstash-monitorama-2013/images/kibana-search.png"}')]

次に、ElasticSearch設定を指定します。重要な項目は次のとおりです:

"es.resource" : 'walker/apache' "walker"はインデックスで、"apache"はタイプです。全体を合わせて「インデックス」と呼ばれることがよくあります。
"es.mapping.id": "doc_id" ここでは、ESにどのドキュメントをドキュメントIDとして使用するかを示しています。これは_idフィールドと同じことを意味します。

残りのフィールドは自己説明的です。

次に、saveAsNewAPIHadoopFile()メソッドを使用してRDDをESに保存します。文法は常にESに対して同じであるため、すべての部分を理解する必要はありません。

    es_write_conf = {
        "es.nodes" : "localhost",
        "es.port" : "9200",
        "es.resource" : 'walker/apache',
        "es.input.json": "yes",
        "es.mapping.id": "doc_id"
    }
       
rdd3.saveAsNewAPIHadoopFile(
        path='-',
     outputFormatClass="org.elasticsearch.hadoop.mr.EsOutputFormat",       keyClass="org.apache.hadoop.io.NullWritable",
        valueClass="org.elasticsearch.hadoop.mr.LinkedMapWritable",
        conf=es_write_conf)

rdd3 = rdd2.map(addID)

def addId(data):
    j=json.dumps(data).encode('ascii', 'ignore')
    data['doc_id'] = hashlib.sha224(j).hexdigest()
    return (data['doc_id'], json.dumps(data))

次に、コマンドラインからESにクエリを実行し、1つのドキュメントを確認できます:

    curl http://localhost:9200s/walker/apache/_search?pretty=true&?q=*


{
        "_index" : "walker",
        "_type" : "apache",
        "_id" : "227e977849bfd5f8d1fca69b04f7a766560745c6cb3712c106d590c2",
        "_score" : 1.0,
        "_source" : {
          "date" : "17/May/2015:10:05:32 +0000",
          "ip" : "91.177.205.119",
          "operation" : "GET",
          "doc_id" : "227e977849bfd5f8d1fca69b04f7a766560745c6cb3712c106d590c2",
          "uri" : "/favicon.ico"
        }

こちらが完全なコードです:

    import json
import hashlib
import re

def addId(data):
    j=json.dumps(data).encode('ascii', 'ignore')
    data['doc_id'] = hashlib.sha224(j).hexdigest()
    return (data['doc_id'], json.dumps(data))

def parse(str):
    s=p.match(str)
    d = {}
    d['ip']=s.group(1)
    d['date']=s.group(4)
    d['operation']=s.group(5)
    d['uri']=s.group(6)
    return d    

regex='^(\S+) (\S+) (\S+) \[([\w:/]+\s[+\-]\d{4})\] "(\S+)\s?(\S+)?\s?(\S+)?" (\d{3}|-) (\d+|-)\s?"?([^"]*)"?\s?"?([^"]*)?"?$'

p=re.compile(regex)

rdd = sc.textFile("/home/ubuntu/walker/apache_logs")

rdd2 = rdd.map(parse)

rdd3 = rdd2.map(addID)

es_write_conf = {
        "es.nodes" : "localhost",
        "es.port" : "9200",
        "es.resource" : 'walker/apache',
        "es.input.json": "yes",
        "es.mapping.id": "doc_id"
    }
     
rdd3.saveAsNewAPIHadoopFile(
        path='-',
     outputFormatClass="org.elasticsearch.hadoop.mr.EsOutputFormat",       keyClass="org.apache.hadoop.io.NullWritable",
        valueClass="org.elasticsearch.hadoop.mr.LinkedMapWritable",
        conf=es_write_conf)

これらの投稿は私自身の見解であり、BMCの立場、戦略、または意見を必ずしも表すものではありません。

オーケストレーションの関連記事

BMCからのさらなる情報に興味がありますか?

ザ・ブリーフは、メインフレームとエンタープライズオーケストレーションに関する月刊メールです:研究、顧客ストーリー、製品ニュース、イベント。

このオファーに登録することで、あなたはBMCのプライバシーポリシーを読み、同意したことを確認します。

ザ・ブリーフに登録しました

ザ・ブリーフは、メインフレームとエンタープライズオーケストレーション—BMC AMIおよびControl-M—に関するアナリストの研究、顧客の展開、両者にわたるイベントを網羅しています。